Merge branch 'master' of github.com:Chatterino/chatterino2

This commit is contained in:
fourtf 2021-04-14 15:53:40 +02:00
commit 0dd6934f02
42 changed files with 394 additions and 100 deletions

View file

@ -63,29 +63,32 @@ jobs:
version: ${{ matrix.qt-version }}
# WINDOWS
- name: Cache conan packages
if: startsWith(matrix.os, 'windows')
uses: actions/cache@v2.1.4
with:
key: ${{ runner.os }}-conan-${{ hashFiles('**/conanfile.txt') }}-20210307
key: ${{ runner.os }}-conan-${{ hashFiles('**/conanfile.txt') }}-20210412
path: C:/.conan/
- name: Add Conan to path
if: startsWith(matrix.os, 'windows')
run: echo "C:\Program Files\Conan\conan\" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Install dependencies (Windows)
if: startsWith(matrix.os, 'windows')
run: |
choco install conan -y
refreshenv
shell: cmd
- name: Enable Developer Command Prompt
if: startsWith(matrix.os, 'windows')
uses: ilammy/msvc-dev-cmd@v1.7.0
- name: Build (Windows)
if: startsWith(matrix.os, 'windows') && matrix.build-system == 'qmake'
run: |
call "%programfiles(x86)%\Microsoft Visual Studio\%vs_version%\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
mkdir build
cd build
"C:\Program Files\Conan\conan\conan.exe" install ..
conan install ..
qmake ..
set cl=/MP
nmake /S /NOLOGO
@ -93,15 +96,13 @@ jobs:
cp release/chatterino.exe Chatterino2/
echo nightly > Chatterino2/modes
7z a chatterino-windows-x86-64.zip Chatterino2/
shell: cmd
- name: Build with CMake (Windows)
if: startsWith(matrix.os, 'windows') && matrix.build-system == 'cmake'
run: |
call "%programfiles(x86)%\Microsoft Visual Studio\%vs_version%\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
mkdir build
cd build
"C:\Program Files\Conan\conan\conan.exe" install ..
conan install ..
cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DUSE_CONAN=ON ..
set cl=/MP
nmake /S /NOLOGO
@ -109,17 +110,10 @@ jobs:
cp bin/chatterino.exe Chatterino2/
echo nightly > Chatterino2/modes
7z a chatterino-windows-x86-64.zip Chatterino2/
shell: cmd
- name: Ensure build succeeded (Windows)
if: startsWith(matrix.os, 'windows')
run: |
cd build
ls Chatterino2/chatterino.exe
- name: Upload artifact (Windows)
if: startsWith(matrix.os, 'windows')
uses: actions/upload-artifact@v2.2.2
uses: actions/upload-artifact@v2.2.3
with:
name: chatterino-windows-x86-64-${{ matrix.qt-version }}-${{ matrix.build-system }}.zip
path: build/chatterino-windows-x86-64.zip
@ -176,7 +170,7 @@ jobs:
- name: Upload artifact (Ubuntu)
if: startsWith(matrix.os, 'ubuntu')
uses: actions/upload-artifact@v2.2.2
uses: actions/upload-artifact@v2.2.3
with:
name: Chatterino-x86_64-${{ matrix.qt-version }}-${{ matrix.build-system }}.AppImage
path: build/Chatterino-x86_64.AppImage
@ -221,7 +215,7 @@ jobs:
- name: Upload artifact (MacOS)
if: startsWith(matrix.os, 'macos')
uses: actions/upload-artifact@v2.2.2
uses: actions/upload-artifact@v2.2.3
with:
name: chatterino-osx-${{ matrix.qt-version }}-${{ matrix.build-system }}.dmg
path: build/chatterino-osx.dmg
@ -245,32 +239,32 @@ jobs:
Nightly Build
prerelease: true
- uses: actions/download-artifact@v2.0.8
- uses: actions/download-artifact@v2.0.9
with:
name: chatterino-windows-x86-64-5.15.2-qmake.zip
path: windows/
- uses: actions/download-artifact@v2.0.8
- uses: actions/download-artifact@v2.0.9
with:
name: chatterino-windows-x86-64-5.15.2-cmake.zip
path: windows-cmake/
- uses: actions/download-artifact@v2.0.8
- uses: actions/download-artifact@v2.0.9
with:
name: Chatterino-x86_64-5.15.2-qmake.AppImage
path: linux/
- uses: actions/download-artifact@v2.0.8
- uses: actions/download-artifact@v2.0.9
with:
name: Chatterino-x86_64-5.15.2-cmake.AppImage
path: linux-cmake/
- uses: actions/download-artifact@v2.0.8
- uses: actions/download-artifact@v2.0.9
with:
name: chatterino-osx-5.15.2-qmake.dmg
path: macos/
- uses: actions/download-artifact@v2.0.8
- uses: actions/download-artifact@v2.0.9
with:
name: chatterino-osx-5.15.2-cmake.dmg
path: macos-cmake/

3
.gitmodules vendored
View file

@ -25,3 +25,6 @@
[submodule "lib/websocketpp"]
path = lib/websocketpp
url = https://github.com/ziocleto/websocketpp
[submodule "cmake/sanitizers-cmake"]
path = cmake/sanitizers-cmake
url = https://github.com/arsenm/sanitizers-cmake

View file

@ -6,8 +6,25 @@ Note on Qt version compatibility: If you are installing Qt from a package manage
_most likely works the same for other Debian-like distros_
1. Install dependencies (and the C++ IDE Qt Creator) `sudo apt install qtcreator qtmultimedia5-dev libqt5svg5-dev libboost-dev libssl-dev libboost-system-dev libboost-filesystem-dev cmake`
1. Open `chatterino.pro` with QT Creator and build
1. Install dependencies `sudo apt install qttools5-dev qtmultimedia5-dev libqt5svg5-dev libboost-dev libssl-dev libboost-system-dev libboost-filesystem-dev cmake g++`
### Through Qt Creator
1. Install C++ IDE Qt Creator `sudo apt install qtcreator`
1. Open `chatterino.pro` with Qt Creator and select build
### Manually
1. go into project directory
1. create build folder `mkdir build && cd build`
#### Using QMake
1. `qmake .. && make`
#### Using CMake
1. `cmake .. && make`
## Arch Linux
@ -17,15 +34,15 @@ _most likely works the same for other Debian-like distros_
### Manually
1. `sudo pacman -S qt5-base qt5-multimedia qt5-svg gst-plugins-ugly gst-plugins-good boost rapidjson pkgconf openssl cmake`
1. `sudo pacman -S qt5-base qt5-multimedia qt5-svg qt5-tools gst-plugins-ugly gst-plugins-good boost rapidjson pkgconf openssl cmake`
1. go into project directory
1. create build folder `mkdir build && cd build`
### Using QMake
#### Using QMake
1. `qmake .. && make`
### Using CMake
#### Using CMake
1. `cmake .. && make`

View file

@ -33,7 +33,7 @@ Note: This installation will take about 1.5 GB of disk space.
### For our websocket library, we need OpenSSL 1.1
1. Download OpenSSL for windows, version `1.1.1i`: **[Download](https://slproweb.com/download/Win64OpenSSL-1_1_1i.exe)**
1. Download OpenSSL for windows, version `1.1.1j`: **[Download](https://slproweb.com/download/Win64OpenSSL-1_1_1j.exe)**
2. When prompted, install OpenSSL to `C:\local\openssl`
3. When prompted, copy the OpenSSL DLLs to "The OpenSSL binaries (/bin) directory".

View file

@ -5,7 +5,8 @@
- Major: Added clip creation support. You can create clips with `/clip` command, `Alt+X` keybind or `Create a clip` option in split header's context menu. This requires a new authentication scope so re-authentication will be required to use it. (#2271, #2377, #2528)
- Major: Added "Channel Filters". See https://wiki.chatterino.com/Filters/ for how they work or how to configure them. (#1748, #2083, #2090, #2200, #2225)
- Major: Added Streamer Mode configuration (under `Settings -> General`), where you can select which features of Chatterino should behave differently when you are in Streamer Mode. (#2001, #2316, #2342, #2376)
- Major: Color mentions to match the mentioned users. You can disable this by unchecking "Color @usernames" under `Settings -> General -> Advanced (misc.)`. (#1963, #2284)
- Major: Add `/settitle` and `/setgame` commands, originally made for Mm2PL/Dankerino. (#2534, #2609)
- Major: Color mentions to match the mentioned users. You can disable this by unchecking "Color @usernames" under `Settings -> General -> Advanced (misc.)`. (#1963, #2284, #2597)
- Major: Commands `/ignore` and `/unignore` have been renamed to `/block` and `/unblock` in order to keep consistency with Twitch's terms. (#2370)
- Major: Added support for bit emotes - the ones you unlock after cheering to streamer. (#2550)
- Minor: Added `/clearmessages` command - does what "Burger menu -> More -> Clear messages" does. (#2485)
@ -43,7 +44,7 @@
- Minor: Show channels live now enabled by default
- Minor: Bold usernames enabled by default
- Minor: Improve UX of the "Login expired!" message (#2029)
- Minor: PageUp and PageDown now scroll in the selected split (#2070, #2081)
- Minor: PageUp and PageDown now scroll in the selected split and in the emote popup (#2070, #2081, #2410, #2607)
- Minor: Allow highlights to be excluded from `/mentions`. Excluded highlights will not trigger tab highlights either. (#1793, #2036)
- Minor: Flag all popup dialogs as actual dialogs so they get the relevant window manager hints (#1843, #2182, #2185, #2232, #2234)
- Minor: Don't show update button for nightly builds on macOS and Linux, this was already the case for Windows (#2163, #2164)
@ -59,6 +60,7 @@
- Minor: Added `/streamlink` command. Usage: `/streamlink <channel>`. You can also use the command without arguments in any twitch channel to open it in streamlink. (#2443, #2495)
- Minor: Humanized all numbers visible to end-users. (#2488)
- Minor: Added a context menu to avatar in usercard. It opens on right-clicking the avatar in usercard. (#2517)
- Minor: Handle messages that users can share after unlocking a new bits badge. (#2611)
- Bugfix: Fix crash occurring when pressing Escape in the Color Picker Dialog (#1843)
- Bugfix: Fix bug where the "check user follow state" event could trigger a network request requesting the user to follow or unfollow a user. By itself its quite harmless as it just repeats to Twitch the same follow state we had, so no follows should have been lost by this but it meant there was a rogue network request that was fired that could cause a crash (#1906)
- Bugfix: /usercard command will now respect the "Automatically close user popup" setting (#1918)
@ -81,11 +83,14 @@
- Bugfix: Fix anonymous users being pinged by "username" justinfan64537 (#2156, #2352)
- Bugfix: Fixed hidden tooltips when always on top is active (#2384)
- Bugfix: Fix CLI arguments (`--help`, `--version`, `--channels`) not being respected (#2368, #2190)
- Bugfix: Fixed search field not being focused on popup open (#2540)
- Bugfix: Fix Twitch cheer emotes not displaying tooltips when hovered (#2434, #2503)
- Bugfix: Fix BTTV/FFZ channel emotes saying unknown error when no emotes found (#2542)
- Bugfix: Fix directory not opening when clicking "Open AppData Directory" setting button on macOS (#2531, #2537)
- Bugfix: Fix quickswitcher not respecting order of tabs when filtering (#2519, #2561)
- Bugfix: Fix GNOME not associating Chatterino's window with its desktop entry (#1863, #2587)
- Bugfix: Fix buffer overflow in emoji parsing. (#2602)
- Bugfix: Fix windows being brought back to life after the settings dialog was closed. (#1892, #2613)
- Dev: Updated minimum required Qt framework version to 5.12. (#2210)
- Dev: Migrated `Kraken::getUser` to Helix (#2260)
- Dev: Migrated `TwitchAccount::(un)followUser` from Kraken to Helix and moved it to `Helix::(un)followUser`. (#2306)

View file

@ -4,6 +4,7 @@ include(FeatureSummary)
list(APPEND CMAKE_MODULE_PATH
"${CMAKE_SOURCE_DIR}/cmake"
"${CMAKE_SOURCE_DIR}/cmake/sanitizers-cmake/cmake"
)
project(chatterino VERSION 2.2.3)
@ -44,6 +45,8 @@ if (WIN32)
find_package(WinToast REQUIRED)
endif ()
find_package(Sanitizers)
# Find boost on the system
find_package(Boost REQUIRED)

@ -0,0 +1 @@
Subproject commit 99e159ec9bc8dd362b08d18436bd40ff0648417b

View file

@ -13,8 +13,7 @@
</summary>
<description>
<p>
Chatterino 2 is the second installment of the Twitch chat client series
"Chatterino".
Chatterino is a chat client for Twitch chat. It aims to be an improved/extended version of the Twitch web chat.
</p>
</description>
<screenshots>

View file

@ -13,7 +13,7 @@ AB_SETTINGS_CLASS *AB_SETTINGS_CLASS::instance = nullptr;
void _actuallyRegisterSetting(
std::weak_ptr<pajlada::Settings::SettingData> setting)
{
_settings.push_back(setting);
_settings.push_back(std::move(setting));
}
AB_SETTINGS_CLASS::AB_SETTINGS_CLASS(const QString &settingsDirectory)

View file

@ -460,6 +460,7 @@ endif ()
source_group(TREE ${CMAKE_SOURCE_DIR} FILES ${SOURCE_FILES})
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
add_sanitizers(${PROJECT_NAME})
target_precompile_headers(${PROJECT_NAME} PRIVATE PrecompiledHeader.hpp)

View file

@ -106,7 +106,7 @@ void Args::applyCustomChannelLayout(const QString &argValue)
return QRect(-1, -1, -1, -1);
}();
window.geometry_ = std::move(configMainLayout);
window.geometry_ = configMainLayout;
QStringList channelArgList = argValue.split(";");
for (const QString &channelArg : channelArgList)

View file

@ -320,13 +320,13 @@ void Channel::onConnected()
// Indirect channel
//
IndirectChannel::Data::Data(ChannelPtr _channel, Channel::Type _type)
: channel(_channel)
: channel(std::move(_channel))
, type(_type)
{
}
IndirectChannel::IndirectChannel(ChannelPtr channel, Channel::Type type)
: data_(std::make_unique<Data>(channel, type))
: data_(std::make_unique<Data>(std::move(channel), type))
{
}
@ -339,7 +339,7 @@ void IndirectChannel::reset(ChannelPtr channel)
{
assert(this->data_->type != Channel::Type::Direct);
this->data_->channel = channel;
this->data_->channel = std::move(channel);
this->data_->changed.invoke();
}

View file

@ -6,7 +6,7 @@ namespace chatterino {
void _registerSetting(std::weak_ptr<pajlada::Settings::SettingData> setting)
{
_actuallyRegisterSetting(setting);
_actuallyRegisterSetting(std::move(setting));
}
} // namespace chatterino

View file

@ -19,6 +19,7 @@ enum class NetworkRequestType {
Post,
Put,
Delete,
Patch,
};
} // namespace chatterino

View file

@ -116,6 +116,19 @@ void loadUncached(const std::shared_ptr<NetworkData> &data)
return NetworkManager::accessManager.post(
data->request_, data->payload_);
}
case NetworkRequestType::Patch:
if (data->multiPartPayload_)
{
assert(data->payload_.isNull());
return NetworkManager::accessManager.sendCustomRequest(
data->request_, "PATCH", data->multiPartPayload_);
}
else
{
return NetworkManager::accessManager.sendCustomRequest(
data->request_, "PATCH", data->payload_);
}
}
return nullptr;
}();

View file

@ -643,7 +643,85 @@ void CommandController::initialize(Settings &, Paths &paths)
getApp()->windows->getMainWindow().getNotebook().getSelectedPage());
currentPage->getSelectedSplit()->getChannelView().clearMessages();
return "";
});
this->registerCommand("/settitle", [](const QStringList &words,
ChannelPtr channel) {
if (words.size() < 2)
{
channel->addMessage(
makeSystemMessage("Usage: /settitle <stream title>."));
return "";
}
if (auto twitchChannel = dynamic_cast<TwitchChannel *>(channel.get()))
{
auto status = twitchChannel->accessStreamStatus();
auto title = words.mid(1).join(" ");
getHelix()->updateChannel(
twitchChannel->roomId(), "", "", title,
[channel, title](NetworkResult) {
channel->addMessage(makeSystemMessage(
QString("Updated title to %1").arg(title)));
},
[channel] {
channel->addMessage(
makeSystemMessage("Title update failed! Are you "
"missing the required scope?"));
});
}
else
{
channel->addMessage(makeSystemMessage(
"Unable to set title of non-Twitch channel."));
}
return "";
});
this->registerCommand("/setgame", [](const QStringList &words,
ChannelPtr channel) {
if (words.size() < 2)
{
channel->addMessage(
makeSystemMessage("Usage: /setgame <stream game>."));
return "";
}
if (auto twitchChannel = dynamic_cast<TwitchChannel *>(channel.get()))
{
getHelix()->searchGames(
words.mid(1).join(" "),
[channel, twitchChannel](std::vector<HelixGame> games) {
if (games.empty())
{
channel->addMessage(
makeSystemMessage("Game not found."));
}
else // 1 or more games
{
auto status = twitchChannel->accessStreamStatus();
getHelix()->updateChannel(
twitchChannel->roomId(), games.at(0).id, "", "",
[channel, games](NetworkResult) {
channel->addMessage(makeSystemMessage(
QString("Updated game to %1")
.arg(games.at(0).name)));
},
[channel] {
channel->addMessage(makeSystemMessage(
"Game update failed! Are you "
"missing the required scope?"));
});
}
},
[channel] {
channel->addMessage(
makeSystemMessage("Failed to look up game."));
});
}
else
{
channel->addMessage(
makeSystemMessage("Unable to set game of non-Twitch channel."));
}
return "";
});
}

View file

@ -57,7 +57,7 @@ HighlightPhrase::HighlightPhrase(const QString &pattern, bool showInMentions,
, isRegex_(isRegex)
, isCaseSensitive_(isCaseSensitive)
, soundUrl_(soundUrl)
, color_(color)
, color_(std::move(color))
, regex_(isRegex_
? pattern
: REGEX_START_BOUNDARY + QRegularExpression::escape(pattern) +

View file

@ -39,7 +39,7 @@ namespace {
} // namespace
MessageLayout::MessageLayout(MessagePtr message)
: message_(message)
: message_(std::move(message))
, container_(std::make_shared<MessageLayoutContainer>())
{
DebugCount::increase("message layout");

View file

@ -88,7 +88,7 @@ FlagsEnum<MessageElementFlag> MessageLayoutElement::getFlags() const
ImageLayoutElement::ImageLayoutElement(MessageElement &creator, ImagePtr image,
const QSize &size)
: MessageLayoutElement(creator, size)
, image_(image)
, image_(std::move(image))
{
this->trailingSpace = creator.hasTrailingSpace();
}

View file

@ -26,7 +26,7 @@ const std::shared_ptr<QColor> ColorProvider::color(ColorType type) const
void ColorProvider::updateColor(ColorType type, QColor color)
{
auto colorPtr = this->typeColorMap_.at(type);
*colorPtr = color;
*colorPtr = std::move(color);
}
QSet<QColor> ColorProvider::recentColors() const

View file

@ -24,7 +24,7 @@ namespace {
const rapidjson::Value &unparsedEmoji,
QString shortCode = QString())
{
static uint unicodeBytes[4];
std::array<uint32_t, 7> unicodeBytes;
struct {
bool apple;
@ -91,11 +91,12 @@ namespace {
for (const QString &unicodeCharacter : unicodeCharacters)
{
unicodeBytes[numUnicodeBytes++] =
unicodeBytes.at(numUnicodeBytes++) =
QString(unicodeCharacter).toUInt(nullptr, 16);
}
emojiData->value = QString::fromUcs4(unicodeBytes, numUnicodeBytes);
emojiData->value =
QString::fromUcs4(unicodeBytes.data(), numUnicodeBytes);
}
// getToneNames takes a tones and returns their names in the same order

View file

@ -14,6 +14,7 @@
#include "singletons/Settings.hpp"
#include "singletons/WindowManager.hpp"
#include "util/FormatTime.hpp"
#include "util/Helpers.hpp"
#include "util/IrcHelpers.hpp"
#include <IrcMessage>
@ -579,10 +580,11 @@ std::vector<MessagePtr> IrcMessageHandler::parseUserNoticeMessage(
content = parameters[1];
}
if (msgType == "sub" || msgType == "resub" || msgType == "subgift")
if (msgType == "sub" || msgType == "resub" || msgType == "subgift" ||
msgType == "bitsbadgetier")
{
// Sub-specific message. I think it's only allowed for "resub" messages
// atm
// Sub-specific and bits badge upgrade specific message.
// It's only allowed for "resub" messages.
if (!content.isEmpty())
{
MessageParseArgs args;
@ -600,9 +602,18 @@ std::vector<MessagePtr> IrcMessageHandler::parseUserNoticeMessage(
if (it != tags.end())
{
auto b =
MessageBuilder(systemMessage, parseTagString(it.value().toString()),
calculateMessageTimestamp(message));
QString messageText = it.value().toString();
if (msgType == "bitsbadgetier")
{
messageText = QString("%1 just earned a new %2 Bits badge!")
.arg(tags.value("display-name").toString())
.arg(kFormatNumbers(
tags.value("msg-param-threshold").toInt()));
}
auto b = MessageBuilder(systemMessage, parseTagString(messageText),
calculateMessageTimestamp(message));
b->flags.set(MessageFlag::Subscription);
auto newMessage = b.release();
@ -628,10 +639,11 @@ void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message,
content = parameters[1];
}
if (msgType == "sub" || msgType == "resub" || msgType == "subgift")
if (msgType == "sub" || msgType == "resub" || msgType == "subgift" ||
msgType == "bitsbadgetier")
{
// Sub-specific message. I think it's only allowed for "resub" messages
// atm
// Sub-specific and bits badge upgrade specific message.
// It's only allowed for "resub" messages.
if (!content.isEmpty())
{
this->addMessage(message, target, content, server, true, false);
@ -642,9 +654,18 @@ void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message,
if (it != tags.end())
{
auto b =
MessageBuilder(systemMessage, parseTagString(it.value().toString()),
calculateMessageTimestamp(message));
QString messageText = it.value().toString();
if (msgType == "bitsbadgetier")
{
messageText = QString("%1 just earned a new %2 Bits badge!")
.arg(tags.value("display-name").toString())
.arg(kFormatNumbers(
tags.value("msg-param-threshold").toInt()));
}
auto b = MessageBuilder(systemMessage, parseTagString(messageText),
calculateMessageTimestamp(message));
b->flags.set(MessageFlag::Subscription);
auto newMessage = b.release();

View file

@ -64,7 +64,7 @@ QColor TwitchAccount::color()
void TwitchAccount::setColor(QColor color)
{
this->color_.set(color);
this->color_.set(std::move(color));
}
bool TwitchAccount::setOAuthClient(const QString &newClientID)
@ -131,7 +131,7 @@ void TwitchAccount::blockUser(QString userId, std::function<void()> onSuccess,
}
onSuccess();
},
onFailure);
std::move(onFailure));
}
void TwitchAccount::unblockUser(QString userId, std::function<void()> onSuccess,
@ -149,7 +149,7 @@ void TwitchAccount::unblockUser(QString userId, std::function<void()> onSuccess,
}
onSuccess();
},
onFailure);
std::move(onFailure));
}
void TwitchAccount::checkFollow(const QString targetUserID,

View file

@ -484,6 +484,7 @@ void TwitchMessageBuilder::addTextOrEmoji(const QString &string_)
if (match.hasMatch())
{
QString username = match.captured(1);
auto originalTextColor = textColor;
if (this->twitchChannel != nullptr && getSettings()->colorUsernames)
{
@ -495,13 +496,23 @@ void TwitchMessageBuilder::addTextOrEmoji(const QString &string_)
}
}
this->emplace<TextElement>(string, MessageElementFlag::BoldUsername,
auto prefixedUsername = '@' + username;
this->emplace<TextElement>(prefixedUsername,
MessageElementFlag::BoldUsername,
textColor, FontStyle::ChatMediumBold)
->setLink({Link::UserInfo, username});
->setLink({Link::UserInfo, username})
->setTrailingSpace(false);
this->emplace<TextElement>(prefixedUsername,
MessageElementFlag::NonBoldUsername,
textColor)
->setLink({Link::UserInfo, username})
->setTrailingSpace(false);
this->emplace<TextElement>(string.remove(prefixedUsername),
MessageElementFlag::Text,
originalTextColor);
this->emplace<TextElement>(
string, MessageElementFlag::NonBoldUsername, textColor)
->setLink({Link::UserInfo, username});
return;
}
}
@ -514,6 +525,8 @@ void TwitchMessageBuilder::addTextOrEmoji(const QString &string_)
if (match.hasMatch() && chatters->contains(username))
{
auto originalTextColor = textColor;
if (getSettings()->colorUsernames)
{
if (auto userColor =
@ -524,13 +537,21 @@ void TwitchMessageBuilder::addTextOrEmoji(const QString &string_)
}
}
this->emplace<TextElement>(string, MessageElementFlag::BoldUsername,
this->emplace<TextElement>(username,
MessageElementFlag::BoldUsername,
textColor, FontStyle::ChatMediumBold)
->setLink({Link::UserInfo, username});
->setLink({Link::UserInfo, username})
->setTrailingSpace(false);
this->emplace<TextElement>(
string, MessageElementFlag::NonBoldUsername, textColor)
->setLink({Link::UserInfo, username});
username, MessageElementFlag::NonBoldUsername, textColor)
->setLink({Link::UserInfo, username})
->setTrailingSpace(false);
this->emplace<TextElement>(string.remove(username),
MessageElementFlag::Text,
originalTextColor);
return;
}
}

View file

@ -58,7 +58,7 @@ void Helix::getUserByName(QString userName,
HelixFailureCallback failureCallback)
{
QStringList userIds;
QStringList userLogins{userName};
QStringList userLogins{std::move(userName)};
this->fetchUsers(
userIds, userLogins,
@ -78,7 +78,7 @@ void Helix::getUserById(QString userId,
ResultCallback<HelixUser> successCallback,
HelixFailureCallback failureCallback)
{
QStringList userIds{userId};
QStringList userIds{std::move(userId)};
QStringList userLogins;
this->fetchUsers(
@ -136,7 +136,8 @@ void Helix::getUserFollowers(
QString userId, ResultCallback<HelixUsersFollowsResponse> successCallback,
HelixFailureCallback failureCallback)
{
this->fetchUsersFollows("", userId, successCallback, failureCallback);
this->fetchUsersFollows("", std::move(userId), std::move(successCallback),
std::move(failureCallback));
}
void Helix::getUserFollow(
@ -145,7 +146,7 @@ void Helix::getUserFollow(
HelixFailureCallback failureCallback)
{
this->fetchUsersFollows(
userId, targetId,
std::move(userId), std::move(targetId),
[successCallback](const auto &response) {
if (response.data.empty())
{
@ -155,7 +156,7 @@ void Helix::getUserFollow(
successCallback(true, response.data[0]);
},
failureCallback);
std::move(failureCallback));
}
void Helix::fetchStreams(
@ -209,7 +210,7 @@ void Helix::getStreamById(QString userId,
ResultCallback<bool, HelixStream> successCallback,
HelixFailureCallback failureCallback)
{
QStringList userIds{userId};
QStringList userIds{std::move(userId)};
QStringList userLogins;
this->fetchStreams(
@ -230,7 +231,7 @@ void Helix::getStreamByName(QString userName,
HelixFailureCallback failureCallback)
{
QStringList userIds;
QStringList userLogins{userName};
QStringList userLogins{std::move(userName)};
this->fetchStreams(
userIds, userLogins,
@ -295,11 +296,47 @@ void Helix::fetchGames(QStringList gameIds, QStringList gameNames,
.execute();
}
void Helix::searchGames(QString gameName,
ResultCallback<std::vector<HelixGame>> successCallback,
HelixFailureCallback failureCallback)
{
QUrlQuery urlQuery;
urlQuery.addQueryItem("query", gameName);
this->makeRequest("search/categories", urlQuery)
.onSuccess([successCallback, failureCallback](auto result) -> Outcome {
auto root = result.parseJson();
auto data = root.value("data");
if (!data.isArray())
{
failureCallback();
return Failure;
}
std::vector<HelixGame> games;
for (const auto &jsonStream : data.toArray())
{
games.emplace_back(jsonStream.toObject());
}
successCallback(games);
return Success;
})
.onError([failureCallback](auto /*result*/) {
// TODO: make better xd
failureCallback();
})
.execute();
}
void Helix::getGameById(QString gameId,
ResultCallback<HelixGame> successCallback,
HelixFailureCallback failureCallback)
{
QStringList gameIds{gameId};
QStringList gameIds{std::move(gameId)};
QStringList gameNames;
this->fetchGames(
@ -409,7 +446,7 @@ void Helix::createClip(QString channelId,
break;
}
})
.finally(finallyCallback)
.finally(std::move(finallyCallback))
.execute();
}
@ -578,6 +615,48 @@ void Helix::unblockUser(QString targetUserId,
.execute();
}
void Helix::updateChannel(QString broadcasterId, QString gameId,
QString language, QString title,
std::function<void(NetworkResult)> successCallback,
HelixFailureCallback failureCallback)
{
QUrlQuery urlQuery;
auto data = QJsonDocument();
auto obj = QJsonObject();
if (!gameId.isEmpty())
{
obj.insert("game_id", gameId);
}
if (!language.isEmpty())
{
obj.insert("broadcaster_language", language);
}
if (!title.isEmpty())
{
obj.insert("title", title);
}
if (title.isEmpty() && gameId.isEmpty() && language.isEmpty())
{
qCDebug(chatterinoCommon) << "Tried to update channel with no changes!";
return;
}
data.setObject(obj);
urlQuery.addQueryItem("broadcaster_id", broadcasterId);
this->makeRequest("channels", urlQuery)
.type(NetworkRequestType::Patch)
.header("Content-Type", "application/json")
.payload(data.toJson())
.onSuccess([successCallback, failureCallback](auto result) -> Outcome {
successCallback(result);
return Success;
})
.onError([failureCallback](NetworkResult result) {
failureCallback();
})
.execute();
}
NetworkRequest Helix::makeRequest(QString url, QUrlQuery urlQuery)
{
assert(!url.startsWith("/"));
@ -611,8 +690,8 @@ NetworkRequest Helix::makeRequest(QString url, QUrlQuery urlQuery)
void Helix::update(QString clientId, QString oauthToken)
{
this->clientId = clientId;
this->oauthToken = oauthToken;
this->clientId = std::move(clientId);
this->oauthToken = std::move(oauthToken);
}
void Helix::initialize()

View file

@ -252,6 +252,11 @@ public:
ResultCallback<std::vector<HelixGame>> successCallback,
HelixFailureCallback failureCallback);
// https://dev.twitch.tv/docs/api/reference#search-categories
void searchGames(QString gameName,
ResultCallback<std::vector<HelixGame>> successCallback,
HelixFailureCallback failureCallback);
void getGameById(QString gameId, ResultCallback<HelixGame> successCallback,
HelixFailureCallback failureCallback);
@ -296,6 +301,12 @@ public:
std::function<void()> successCallback,
HelixFailureCallback failureCallback);
// https://dev.twitch.tv/docs/api/reference#modify-channel-information
void updateChannel(QString broadcasterId, QString gameId, QString language,
QString title,
std::function<void(NetworkResult)> successCallback,
HelixFailureCallback failureCallback);
void update(QString clientId, QString oauthToken);
static void initialize();

View file

@ -63,8 +63,8 @@ NetworkRequest Kraken::makeRequest(QString url, QUrlQuery urlQuery)
void Kraken::update(QString clientId, QString oauthToken)
{
this->clientId = clientId;
this->oauthToken = oauthToken;
this->clientId = std::move(clientId);
this->oauthToken = std::move(oauthToken);
}
void Kraken::initialize()

View file

@ -104,6 +104,16 @@ URL: https://dev.twitch.tv/docs/api/reference#get-channel-information
Used in:
- `TwitchChannel` to refresh stream title
### Update Channel
URL: https://dev.twitch.tv/docs/api/reference#modify-channel-information
Requires `channel:manage:broadcast` scope
- We implement this in `providers/twitch/api/Helix.cpp updateChannel`
Used in:
- `/setgame` to update the game in the current channel
- `/settitle` to update the title in the current channel
### Create Stream Marker
URL: https://dev.twitch.tv/docs/api/reference/#create-stream-marker
@ -142,6 +152,14 @@ Requires `user:manage:blocked_users` scope
- `widgets/dialogs/UserInfoPopup.cpp` to unblock a user via checkbox in the usercard
- `controllers/commands/CommandController.cpp` to unblock a user via "/unblock" command
### Search Categories
URL: https://dev.twitch.tv/docs/api/reference#search-categories
- We implement this in `providers/twitch/api/Helix.cpp searchGames`
Used in:
- `controllers/commands/CommandController.cpp` in `/setgame` command to fuzzy search for game titles
## TMI
The TMI api is undocumented.

View file

@ -33,7 +33,7 @@ TooltipPreviewImage::TooltipPreviewImage()
void TooltipPreviewImage::setImage(ImagePtr image)
{
this->image_ = image;
this->image_ = std::move(image);
this->refreshTooltipWidgetPixmap();
}

View file

@ -42,4 +42,9 @@ QString localizeNumbers(const int &number)
return locale.toString(number);
}
QString kFormatNumbers(const int &number)
{
return QString("%1K").arg(number / 1000);
}
} // namespace chatterino

View file

@ -15,4 +15,6 @@ QString shortenString(const QString &str, unsigned maxWidth = 50);
QString localizeNumbers(const int &number);
QString kFormatNumbers(const int &number);
} // namespace chatterino

View file

@ -17,7 +17,7 @@ class LambdaRunnable : public QRunnable
public:
LambdaRunnable(std::function<void()> action)
{
this->action_ = action;
this->action_ = std::move(action);
}
void run()

View file

@ -143,7 +143,7 @@ void AttachedWindow::detach(const QString &winId)
void AttachedWindow::setChannel(ChannelPtr channel)
{
this->ui_.split->setChannel(channel);
this->ui_.split->setChannel(std::move(channel));
}
void AttachedWindow::showEvent(QShowEvent *)

View file

@ -244,7 +244,10 @@ void BaseWindow::init()
getSettings()->windowTopMost.connect(
[this](bool topMost, auto) {
this->setWindowFlag(Qt::WindowStaysOnTopHint, topMost);
this->show();
if (this->isVisible())
{
this->show();
}
},
this->managedConnections_);
}

View file

@ -5,13 +5,13 @@
namespace chatterino {
Label::Label(QString text, FontStyle style)
: Label(nullptr, text, style)
: Label(nullptr, std::move(text), style)
{
}
Label::Label(BaseWidget *parent, QString text, FontStyle style)
: BaseWidget(parent)
, text_(text)
, text_(std::move(text))
, fontStyle_(style)
{
this->connections_.managedConnect(getFonts()->fontChanged, [this] {

View file

@ -26,7 +26,7 @@ StreamView::StreamView(ChannelPtr channel, const QUrl &url)
auto chat = layoutCreator.emplace<ChannelView>();
chat->setFixedWidth(300);
chat->setChannel(channel);
chat->setChannel(std::move(channel));
this->layout()->setSpacing(0);
this->layout()->setMargin(0);

View file

@ -11,6 +11,7 @@
#include "singletons/WindowManager.hpp"
#include "util/Shortcut.hpp"
#include "widgets/Notebook.hpp"
#include "widgets/Scrollbar.hpp"
#include "widgets/helper/ChannelView.hpp"
#include <QHBoxLayout>
@ -178,6 +179,20 @@ EmotePopup::EmotePopup(QWidget *parent)
createWindowShortcut(this, "CTRL+Shift+Tab", [=] {
notebook->selectPreviousTab();
});
// Scroll with Page Up / Page Down
createWindowShortcut(this, "PgUp", [=] {
auto &scrollbar =
dynamic_cast<ChannelView *>(notebook->getSelectedPage())
->getScrollBar();
scrollbar.offset(-scrollbar.getLargeChange());
});
createWindowShortcut(this, "PgDown", [=] {
auto &scrollbar =
dynamic_cast<ChannelView *>(notebook->getSelectedPage())
->getScrollBar();
scrollbar.offset(scrollbar.getLargeChange());
});
}
void EmotePopup::loadChannel(ChannelPtr _channel)

View file

@ -45,7 +45,7 @@ void NotificationPopup::updatePosition()
void NotificationPopup::addMessage(MessagePtr msg)
{
this->channel_->addMessage(msg);
this->channel_->addMessage(std::move(msg));
// QTimer::singleShot(5000, this, [this, msg] { this->channel->remove });
}

View file

@ -38,7 +38,7 @@ Button::Button(BaseWidget *parent)
void Button::setMouseEffectColor(boost::optional<QColor> color)
{
this->mouseEffectColor_ = color;
this->mouseEffectColor_ = std::move(color);
}
void Button::setPixmap(const QPixmap &_pixmap)

View file

@ -550,7 +550,7 @@ bool ChannelView::getEnableScrollingToBottom() const
void ChannelView::setOverrideFlags(boost::optional<MessageElementFlags> value)
{
this->overrideFlags_ = value;
this->overrideFlags_ = std::move(value);
}
const boost::optional<MessageElementFlags> &ChannelView::getOverrideFlags()
@ -647,7 +647,7 @@ void ChannelView::setChannel(ChannelPtr underlyingChannel)
this->channelConnections_.push_back(this->channel_->messageAppended.connect(
[this](MessagePtr &message,
boost::optional<MessageFlags> overridingFlags) {
this->messageAppended(message, overridingFlags);
this->messageAppended(message, std::move(overridingFlags));
}));
this->channelConnections_.push_back(
@ -753,7 +753,7 @@ ChannelPtr ChannelView::sourceChannel() const
void ChannelView::setSourceChannel(ChannelPtr sourceChannel)
{
this->sourceChannel_ = sourceChannel;
this->sourceChannel_ = std::move(sourceChannel);
}
bool ChannelView::hasSourceChannel() const

View file

@ -66,7 +66,7 @@ SearchPopup::SearchPopup(QWidget *parent)
void SearchPopup::setChannelFilters(FilterSetPtr filters)
{
this->channelFilters_ = filters;
this->channelFilters_ = std::move(filters);
}
void SearchPopup::setChannel(const ChannelPtr &channel)
@ -155,6 +155,8 @@ void SearchPopup::initLayout()
this->setLayout(layout1);
}
this->searchInput_->setFocus();
}
std::vector<std::unique_ptr<MessagePredicate>> SearchPopup::parsePredicates(

View file

@ -45,6 +45,7 @@ add_executable(${PROJECT_NAME}
${chatterino_SOURCES}
${test_SOURCES}
)
add_sanitizers(${PROJECT_NAME})
# Enable autogeneration of Qts MOC/RCC/UIC
set_target_properties(${PROJECT_NAME}