From 9d90de6b8c2e3f9573400fb0396e77aa48e31112 Mon Sep 17 00:00:00 2001 From: Tal Neoran Date: Sat, 7 Aug 2021 01:04:09 +0300 Subject: [PATCH 01/34] Handle moderation mode and filters in split popup (#3130) --- CHANGELOG.md | 2 ++ src/widgets/splits/Split.cpp | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6983eba34..ad4963847 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unversioned +- Bugfix: Moderation mode and active filters are now preserved when opening a split as a popup. (#3113, #3130) + ## 2.3.4 - Major: Newly uploaded Twitch emotes are once again present in emote picker and can be autocompleted with Tab as well. (#2992) diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index e601d35ab..2263ee7fc 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -624,8 +624,10 @@ void Split::popup() window.getNotebook().getOrAddSelectedPage())); split->setChannel(this->getIndirectChannel()); - window.getNotebook().getOrAddSelectedPage()->appendSplit(split); + split->setModerationMode(this->getModerationMode()); + split->setFilters(this->getFilters()); + window.getNotebook().getOrAddSelectedPage()->appendSplit(split); window.show(); } From cd7758a28ef430ff92c957d3dee9b13f65b5ef43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82?= Date: Sat, 7 Aug 2021 11:01:22 +0200 Subject: [PATCH 02/34] Ensure we don't attempt to index an empty array (#3122) --- src/providers/twitch/api/Helix.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/twitch/api/Helix.cpp b/src/providers/twitch/api/Helix.cpp index 12b812d71..7895995d2 100644 --- a/src/providers/twitch/api/Helix.cpp +++ b/src/providers/twitch/api/Helix.cpp @@ -712,7 +712,7 @@ void Helix::getEmoteSetData(QString emoteSetId, QJsonObject root = result.parseJson(); auto data = root.value("data"); - if (!data.isArray()) + if (!data.isArray() || data.toArray().isEmpty()) { failureCallback(); return Failure; From 5cb1022ccf1bd9e7189600279bac9204949d7057 Mon Sep 17 00:00:00 2001 From: Tal Neoran Date: Sat, 7 Aug 2021 12:30:56 +0300 Subject: [PATCH 03/34] Fix badge highlights using the same color (#3134) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paweł --- CHANGELOG.md | 1 + src/widgets/settingspages/HighlightingPage.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad4963847..f779cc74a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unversioned - Bugfix: Moderation mode and active filters are now preserved when opening a split as a popup. (#3113, #3130) +- Bugfix: Fixed a bug that caused all badge highlights to use the same color. (#3132, #3134) ## 2.3.4 diff --git a/src/widgets/settingspages/HighlightingPage.cpp b/src/widgets/settingspages/HighlightingPage.cpp index d8aa170ca..29c627dba 100644 --- a/src/widgets/settingspages/HighlightingPage.cpp +++ b/src/widgets/settingspages/HighlightingPage.cpp @@ -195,7 +195,7 @@ HighlightingPage::HighlightingPage() } getSettings()->highlightedBadges.append(HighlightBadge{ s->badgeName(), s->displayName(), false, false, "", - ColorProvider::instance().color( + *ColorProvider::instance().color( ColorType::SelfHighlight)}); } }); From 961803debad3c8db8753bca302887d633c8769ad Mon Sep 17 00:00:00 2001 From: nilyt <78598394+nilyt@users.noreply.github.com> Date: Sat, 7 Aug 2021 10:37:02 +0000 Subject: [PATCH 04/34] Allow building against Qt 5.11 (#3105) Co-authored-by: pajlada --- CHANGELOG.md | 1 + chatterino.pro | 2 +- src/widgets/splits/SplitInput.cpp | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f779cc74a..cb22f6b22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ - Bugfix: Fixed comma appended to username completion when not at the beginning of the message. (#3060) - Bugfix: Fixed bug misplacing chat when zooming on Chrome with Chatterino Native Host extension (#1936) - Bugfix: Channel point redemptions from ignored users are now properly blocked. (#3102) +- Dev: Allow building against Qt 5.11 (#3105) - Dev: Ubuntu packages are now available (#2936) - Dev: Disabled update checker on Flatpak. (#3051) - Dev: Add logging for HTTP requests (#2991) diff --git a/chatterino.pro b/chatterino.pro index cb5104bb9..2586ac790 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -14,7 +14,7 @@ CCACHE_BIN = $$system(which ccache) CONFIG+=ccache } -MINIMUM_REQUIRED_QT_VERSION = 5.12.0 +MINIMUM_REQUIRED_QT_VERSION = 5.11.0 !versionAtLeast(QT_VERSION, $$MINIMUM_REQUIRED_QT_VERSION) { error("You're trying to compile with Qt $$QT_VERSION, but minimum required Qt version is $$MINIMUM_REQUIRED_QT_VERSION") diff --git a/src/widgets/splits/SplitInput.cpp b/src/widgets/splits/SplitInput.cpp index 41aea0132..26888f365 100644 --- a/src/widgets/splits/SplitInput.cpp +++ b/src/widgets/splits/SplitInput.cpp @@ -134,14 +134,18 @@ void SplitInput::themeChangedEvent() QPalette palette, placeholderPalette; palette.setColor(QPalette::WindowText, this->theme->splits.input.text); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) placeholderPalette.setColor( QPalette::PlaceholderText, this->theme->messages.textColors.chatPlaceholder); +#endif this->updateEmoteButton(); this->ui_.textEditLength->setPalette(palette); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) this->ui_.textEdit->setPalette(placeholderPalette); +#endif this->ui_.textEdit->setStyleSheet(this->theme->splits.input.styleSheet); this->ui_.hbox->setMargin( From 54f6e4f9e9ade55a28dd2d35961466323f217ee2 Mon Sep 17 00:00:00 2001 From: pajlada Date: Sat, 7 Aug 2021 13:13:05 +0200 Subject: [PATCH 05/34] Remove twitchemotes.com-related functionality (#3136) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paweł --- CHANGELOG.md | 1 + src/providers/twitch/TwitchEmotes.cpp | 2 +- src/widgets/helper/ChannelView.cpp | 6 +----- src/widgets/settingspages/AboutPage.cpp | 1 - 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb22f6b22..8021d7f58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unversioned +- Minor: Remove TwitchEmotes.com attribution and the open/copy options when right-clicking a Twitch Emote. (#2214, #3136) - Bugfix: Moderation mode and active filters are now preserved when opening a split as a popup. (#3113, #3130) - Bugfix: Fixed a bug that caused all badge highlights to use the same color. (#3132, #3134) diff --git a/src/providers/twitch/TwitchEmotes.cpp b/src/providers/twitch/TwitchEmotes.cpp index 38593a03a..9a84bcabf 100644 --- a/src/providers/twitch/TwitchEmotes.cpp +++ b/src/providers/twitch/TwitchEmotes.cpp @@ -57,7 +57,7 @@ EmotePtr TwitchEmotes::getOrCreateEmote(const EmoteId &id, Image::fromUrl(getEmoteLink(id, "3.0"), 0.25), }, Tooltip{name.toHtmlEscaped() + "
Twitch Emote"}, - Url{QString("https://twitchemotes.com/emotes/%1").arg(id.string)}}); + }); } return shared; diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index c28108449..064a25cd9 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -108,11 +108,7 @@ namespace { }); }; - if (creatorFlags.has(MessageElementFlag::TwitchEmote)) - { - addPageLink("TwitchEmotes"); - } - else if (creatorFlags.has(MessageElementFlag::BttvEmote)) + if (creatorFlags.has(MessageElementFlag::BttvEmote)) { addPageLink("BTTV"); } diff --git a/src/widgets/settingspages/AboutPage.cpp b/src/widgets/settingspages/AboutPage.cpp index 44033ac6c..ea0c7bd43 100644 --- a/src/widgets/settingspages/AboutPage.cpp +++ b/src/widgets/settingspages/AboutPage.cpp @@ -161,7 +161,6 @@ AboutPage::AboutPage() l.emplace("Google emojis provided by Google")->setOpenExternalLinks(true); l.emplace("Emoji datasource provided by Cal Henderson" "(show license)")->setOpenExternalLinks(true); - l.emplace("Twitch emote data provided by twitchemotes.com through the Chatterino API")->setOpenExternalLinks(true); // clang-format on } From 359db173862adb32eb26ddab20e53fa445b859a6 Mon Sep 17 00:00:00 2001 From: apa420 <17131426+apa420@users.noreply.github.com> Date: Sat, 7 Aug 2021 23:59:25 +0200 Subject: [PATCH 06/34] Added links to discussions when trying to create an issue (#3145) --- .github/ISSUE_TEMPLATE/config.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..8ac1ca177 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Suggestions or feature request + url: https://github.com/chatterino/chatterino2/discussions/categories/ideas + about: Got something you think should change or be added? Search for or start a new discussion! + - name: Help + url: https://github.com/chatterino/chatterino2/discussions/categories/q-a + about: Chatterino2 not working as you'd expect? Not sure it's a bug? Check the Q&A section! From d0d32583a2b0815f7d6786feb584517bb43536ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82?= Date: Sun, 8 Aug 2021 12:37:37 +0200 Subject: [PATCH 07/34] Prepare CMake for Qt6 (#3103) --- CHANGELOG.md | 1 + CMakeLists.txt | 25 ++++++++++++++++--------- src/CMakeLists.txt | 22 +++++++++++----------- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8021d7f58..6d864f1d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Minor: Remove TwitchEmotes.com attribution and the open/copy options when right-clicking a Twitch Emote. (#2214, #3136) - Bugfix: Moderation mode and active filters are now preserved when opening a split as a popup. (#3113, #3130) - Bugfix: Fixed a bug that caused all badge highlights to use the same color. (#3132, #3134) +- Dev: Renamed CMake's build option `USE_SYSTEM_QT5KEYCHAIN` to `USE_SYSTEM_QTKEYCHAIN`. (#3103) ## 2.3.4 diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e69412d8..5d8419cc5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,11 +13,18 @@ option(BUILD_APP "Build Chatterino" ON) option(BUILD_TESTS "Build the tests for Chatterino" OFF) option(USE_SYSTEM_PAJLADA_SETTINGS "Use system pajlada settings library" OFF) option(USE_SYSTEM_LIBCOMMUNI "Use system communi library" OFF) -option(USE_SYSTEM_QT5KEYCHAIN "Use system Qt5Keychain library" OFF) +option(USE_SYSTEM_QTKEYCHAIN "Use system QtKeychain library" OFF) option(USE_PRECOMPILED_HEADERS "Use precompiled headers" ON) +option(BUILD_WITH_QT6 "Use Qt6 instead of default Qt5" OFF) option(USE_CONAN "Use conan" OFF) +if (BUILD_WITH_QT6) + set(MAJOR_QT_VERSION "6") +else() + set(MAJOR_QT_VERSION "5") +endif() + if (USE_CONAN OR CONAN_EXPORTED) include(${CMAKE_CURRENT_BINARY_DIR}/conanbuildinfo.cmake) conan_basic_setup(TARGETS NO_OUTPUT_DIRS) @@ -31,7 +38,7 @@ endif () include(${CMAKE_CURRENT_LIST_DIR}/cmake/GIT.cmake) -find_package(Qt5 REQUIRED +find_package(Qt${MAJOR_QT_VERSION} REQUIRED COMPONENTS Core Widgets @@ -72,17 +79,17 @@ endif() # Link QtKeychain statically option(QTKEYCHAIN_STATIC "" ON) -if (USE_SYSTEM_QT5KEYCHAIN) - find_package(Qt5Keychain REQUIRED) +if (USE_SYSTEM_QTKEYCHAIN) + find_package(Qt${MAJOR_QT_VERSION}Keychain REQUIRED) else() - set(QT5KEYCHAIN_ROOT_LIB_FOLDER "${CMAKE_SOURCE_DIR}/lib/qtkeychain") - if (NOT EXISTS "${QT5KEYCHAIN_ROOT_LIB_FOLDER}/CMakeLists.txt") + set(QTKEYCHAIN_ROOT_LIB_FOLDER "${CMAKE_SOURCE_DIR}/lib/qtkeychain") + if (NOT EXISTS "${QTKEYCHAIN_ROOT_LIB_FOLDER}/CMakeLists.txt") message(FATAL_ERROR "Submodules probably not loaded, unable to find lib/qtkeychain/CMakeLists.txt") endif() - add_subdirectory("${QT5KEYCHAIN_ROOT_LIB_FOLDER}" EXCLUDE_FROM_ALL) - if (NOT TARGET qt5keychain) - message(FATAL_ERROR "qt5keychain target was not created :@") + add_subdirectory("${QTKEYCHAIN_ROOT_LIB_FOLDER}" EXCLUDE_FROM_ALL) + if (NOT TARGET qt${MAJOR_QT_VERSION}keychain) + message(FATAL_ERROR "qt${MAJOR_QT_VERSION}keychain target was not created :@") endif() endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 024e6ff62..78015123f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -486,16 +486,16 @@ add_library(${LIBRARY_PROJECT} OBJECT ${SOURCE_FILES}) target_link_libraries(${LIBRARY_PROJECT} PUBLIC - Qt5::Core - Qt5::Widgets - Qt5::Gui - Qt5::Network - Qt5::Multimedia - Qt5::Svg - Qt5::Concurrent + Qt${MAJOR_QT_VERSION}::Core + Qt${MAJOR_QT_VERSION}::Widgets + Qt${MAJOR_QT_VERSION}::Gui + Qt${MAJOR_QT_VERSION}::Network + Qt${MAJOR_QT_VERSION}::Multimedia + Qt${MAJOR_QT_VERSION}::Svg + Qt${MAJOR_QT_VERSION}::Concurrent LibCommuni::LibCommuni - qt5keychain + qt${MAJOR_QT_VERSION}keychain Pajlada::Serialize Pajlada::Settings Pajlada::Signals @@ -524,8 +524,8 @@ if (BUILD_APP) ) if (MSVC) - get_target_property(Qt5_Core_Location Qt5::Core LOCATION) - get_filename_component(QT_BIN_DIR ${Qt5_Core_Location} DIRECTORY) + get_target_property(Qt_Core_Location Qt${MAJOR_QT_VERSION}::Core LOCATION) + get_filename_component(QT_BIN_DIR ${Qt_Core_Location} DIRECTORY) set(WINDEPLOYQT_COMMAND "${QT_BIN_DIR}/windeployqt.exe" $ --release --no-compiler-runtime --no-translations --no-opengl-sw) install(TARGETS ${EXECUTABLE_PROJECT} @@ -582,7 +582,7 @@ target_compile_definitions(${LIBRARY_PROJECT} PUBLIC CHATTERINO_GIT_RELEASE=\"${GIT_RELEASE}\" CHATTERINO_GIT_COMMIT=\"${GIT_COMMIT}\" ) -if (USE_SYSTEM_QT5KEYCHAIN) +if (USE_SYSTEM_QTKEYCHAIN) target_compile_definitions(${LIBRARY_PROJECT} PUBLIC CMAKE_BUILD ) From 6151cd5b058c1623d652800effc088971d66e0a3 Mon Sep 17 00:00:00 2001 From: James Upjohn Date: Sun, 8 Aug 2021 22:59:28 +1200 Subject: [PATCH 08/34] Show system message when reloading subscriber emotes (#3135) Co-authored-by: Rasmus Karlsson --- CHANGELOG.md | 1 + src/providers/twitch/TwitchAccount.cpp | 9 +++++++-- src/providers/twitch/TwitchAccount.hpp | 2 +- src/widgets/splits/Split.cpp | 2 +- src/widgets/splits/SplitHeader.cpp | 7 ++----- src/widgets/splits/SplitHeader.hpp | 1 - 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d864f1d7..397e15c2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unversioned - Minor: Remove TwitchEmotes.com attribution and the open/copy options when right-clicking a Twitch Emote. (#2214, #3136) +- Minor: Display a system message when reloading subscription emotes to match BTTV/FFZ behavior (#3135) - Bugfix: Moderation mode and active filters are now preserved when opening a split as a popup. (#3113, #3130) - Bugfix: Fixed a bug that caused all badge highlights to use the same color. (#3132, #3134) - Dev: Renamed CMake's build option `USE_SYSTEM_QT5KEYCHAIN` to `USE_SYSTEM_QTKEYCHAIN`. (#3103) diff --git a/src/providers/twitch/TwitchAccount.cpp b/src/providers/twitch/TwitchAccount.cpp index 237ba2df0..786a0cbe3 100644 --- a/src/providers/twitch/TwitchAccount.cpp +++ b/src/providers/twitch/TwitchAccount.cpp @@ -198,7 +198,7 @@ SharedAccessGuard> TwitchAccount::accessBlockedUserIds() return this->ignoresUserIds_.accessConst(); } -void TwitchAccount::loadEmotes() +void TwitchAccount::loadEmotes(std::weak_ptr weakChannel) { qCDebug(chatterinoTwitch) << "Loading Twitch emotes for user" << this->getUserName(); @@ -220,9 +220,14 @@ void TwitchAccount::loadEmotes() // TODO(zneix): Once Helix adds Get User Emotes we could remove this hacky solution // For now, this is necessary as Kraken's equivalent doesn't return all emotes // See: https://twitch.uservoice.com/forums/310213-developers/suggestions/43599900 - this->loadUserstateEmotes([=] { + this->loadUserstateEmotes([this, weakChannel] { // Fill up emoteData with emote sets that were returned in a Kraken call, but aren't present in emoteData. this->loadKrakenEmotes(); + if (auto channel = weakChannel.lock(); channel != nullptr) + { + channel->addMessage( + makeSystemMessage("Twitch subscriber emotes reloaded.")); + } }); } diff --git a/src/providers/twitch/TwitchAccount.hpp b/src/providers/twitch/TwitchAccount.hpp index 36e7d0ad6..034912a24 100644 --- a/src/providers/twitch/TwitchAccount.hpp +++ b/src/providers/twitch/TwitchAccount.hpp @@ -111,7 +111,7 @@ public: SharedAccessGuard> accessBlockedUserIds() const; SharedAccessGuard> accessBlocks() const; - void loadEmotes(); + void loadEmotes(std::weak_ptr weakChannel = {}); // loadUserstateEmotes loads emote sets that are part of the USERSTATE emote-sets key // this function makes sure not to load emote sets that have already been loaded void loadUserstateEmotes(std::function callback); diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index 2263ee7fc..8c84a1476 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -870,8 +870,8 @@ void Split::showSearch() void Split::reloadChannelAndSubscriberEmotes() { - getApp()->accounts->twitch.getCurrent()->loadEmotes(); auto channel = this->getChannel(); + getApp()->accounts->twitch.getCurrent()->loadEmotes(channel); if (auto twitchChannel = dynamic_cast(channel.get())) { diff --git a/src/widgets/splits/SplitHeader.cpp b/src/widgets/splits/SplitHeader.cpp index 894391b38..635e3135f 100644 --- a/src/widgets/splits/SplitHeader.cpp +++ b/src/widgets/splits/SplitHeader.cpp @@ -916,10 +916,6 @@ void SplitHeader::themeChangedEvent() } } -void SplitHeader::moveSplit() -{ -} - void SplitHeader::reloadChannelEmotes() { auto channel = this->split_->getChannel(); @@ -933,7 +929,8 @@ void SplitHeader::reloadChannelEmotes() void SplitHeader::reloadSubscriberEmotes() { - getApp()->accounts->twitch.getCurrent()->loadEmotes(); + auto channel = this->split_->getChannel(); + getApp()->accounts->twitch.getCurrent()->loadEmotes(channel); } void SplitHeader::reconnect() diff --git a/src/widgets/splits/SplitHeader.hpp b/src/widgets/splits/SplitHeader.hpp index 4e68c717d..dea611ac2 100644 --- a/src/widgets/splits/SplitHeader.hpp +++ b/src/widgets/splits/SplitHeader.hpp @@ -85,7 +85,6 @@ private: std::vector channelConnections_; public slots: - void moveSplit(); void reloadChannelEmotes(); void reloadSubscriberEmotes(); void reconnect(); From 7309fd8668a92b213a109067021efaab38cbe81f Mon Sep 17 00:00:00 2001 From: sando Date: Sun, 8 Aug 2021 21:23:54 +1000 Subject: [PATCH 09/34] Strip leading @ and trailing , from /user and /usercard commands (#3143) Co-authored-by: Rasmus Karlsson --- CHANGELOG.md | 1 + .../commands/CommandController.cpp | 41 ++++++++++++++++--- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 397e15c2c..f7cca4f52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unversioned - Minor: Remove TwitchEmotes.com attribution and the open/copy options when right-clicking a Twitch Emote. (#2214, #3136) +- Minor: Strip leading @ and trailing , from username in /user and /usercard commands. (#3143) - Minor: Display a system message when reloading subscription emotes to match BTTV/FFZ behavior (#3135) - Bugfix: Moderation mode and active filters are now preserved when opening a split as a popup. (#3113, #3130) - Bugfix: Fixed a bug that caused all badge highlights to use the same color. (#3132, #3134) diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 333c29257..8e977af9b 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -70,6 +70,32 @@ static const QStringList twitchDefaultCommands{ static const QStringList whisperCommands{"/w", ".w"}; +// stripUserName removes any @ prefix or , suffix to make it more suitable for command use +void stripUserName(QString &userName) +{ + if (userName.startsWith('@')) + { + userName.remove(0, 1); + } + if (userName.endsWith(',')) + { + userName.chop(1); + } +} + +// stripChannelName removes any @ prefix or , suffix to make it more suitable for command use +void stripChannelName(QString &channelName) +{ + if (channelName.startsWith('@') || channelName.startsWith('#')) + { + channelName.remove(0, 1); + } + if (channelName.endsWith(',')) + { + channelName.chop(1); + } +} + void sendWhisperMessage(const QString &text) { // (hemirt) pajlada: "we should not be sending whispers through jtv, but @@ -430,16 +456,17 @@ void CommandController::initialize(Settings &, Paths &paths) makeSystemMessage("Usage /user [user] (channel)")); return ""; } + QString userName = words[1]; + stripUserName(userName); + QString channelName = channel->getName(); + if (words.size() > 2) { channelName = words[2]; - if (channelName[0] == '#') - { - channelName.remove(0, 1); - } + stripChannelName(channelName); } - openTwitchUsercard(channelName, words[1]); + openTwitchUsercard(channelName, userName); return ""; }); @@ -451,10 +478,12 @@ void CommandController::initialize(Settings &, Paths &paths) return ""; } + QString userName = words[1]; + stripUserName(userName); auto *userPopup = new UserInfoPopup( getSettings()->autoCloseUserPopup, static_cast(&(getApp()->windows->getMainWindow()))); - userPopup->setData(words[1], channel); + userPopup->setData(userName, channel); userPopup->move(QCursor::pos()); userPopup->show(); return ""; From d0f817a60bda0011c7811ca63772d8d5cd51b02d Mon Sep 17 00:00:00 2001 From: pajlada Date: Sun, 8 Aug 2021 14:16:30 +0200 Subject: [PATCH 10/34] Add basic benchmark (#3038) * Add basic benchmark * Add basic documentation for how to run and add tests/benchmarks * Update benchmark example output * Add changelog entry Co-authored-by: zneix --- CHANGELOG.md | 1 + CMakeLists.txt | 12 +++++- benchmarks/.clang-format | 35 +++++++++++++++ benchmarks/CMakeLists.txt | 28 ++++++++++++ benchmarks/src/Emojis.cpp | 57 ++++++++++++++++++++++++ benchmarks/src/main.cpp | 18 ++++++++ docs/test-and-benchmark.md | 88 ++++++++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 1 + 8 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 benchmarks/.clang-format create mode 100644 benchmarks/CMakeLists.txt create mode 100644 benchmarks/src/Emojis.cpp create mode 100644 benchmarks/src/main.cpp create mode 100644 docs/test-and-benchmark.md diff --git a/CHANGELOG.md b/CHANGELOG.md index f7cca4f52..1c62bf8bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Bugfix: Moderation mode and active filters are now preserved when opening a split as a popup. (#3113, #3130) - Bugfix: Fixed a bug that caused all badge highlights to use the same color. (#3132, #3134) - Dev: Renamed CMake's build option `USE_SYSTEM_QT5KEYCHAIN` to `USE_SYSTEM_QTKEYCHAIN`. (#3103) +- Dev: Add benchmarks that can be compiled with the `BUILD_BENCHMARKS` CMake flag. Off by default. (#3038) ## 2.3.4 diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d8419cc5..6b5e7a470 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ project(chatterino VERSION 2.3.4) option(BUILD_APP "Build Chatterino" ON) option(BUILD_TESTS "Build the tests for Chatterino" OFF) +option(BUILD_BENCHMARKS "Build the benchmarks for Chatterino" OFF) option(USE_SYSTEM_PAJLADA_SETTINGS "Use system pajlada settings library" OFF) option(USE_SYSTEM_LIBCOMMUNI "Use system communi library" OFF) option(USE_SYSTEM_QTKEYCHAIN "Use system QtKeychain library" OFF) @@ -101,6 +102,11 @@ if (BUILD_TESTS) find_package(GTest REQUIRED) endif () +if (BUILD_BENCHMARKS) + # Include system benchmark (Google Benchmark) + find_package(benchmark REQUIRED) +endif () + find_package(PajladaSerialize REQUIRED) find_package(PajladaSignals REQUIRED) find_package(LRUCache REQUIRED) @@ -118,7 +124,7 @@ endif() set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -if (BUILD_TESTS) +if (BUILD_TESTS OR BUILD_BENCHMARKS) add_definitions(-DCHATTERINO_TEST) endif () @@ -129,4 +135,8 @@ if (BUILD_TESTS) add_subdirectory(tests) endif () +if (BUILD_BENCHMARKS) + add_subdirectory(benchmarks) +endif () + feature_summary(WHAT ALL) diff --git a/benchmarks/.clang-format b/benchmarks/.clang-format new file mode 100644 index 000000000..f34c1465b --- /dev/null +++ b/benchmarks/.clang-format @@ -0,0 +1,35 @@ +Language: Cpp + +AccessModifierOffset: -4 +AlignEscapedNewlinesLeft: true +AllowShortFunctionsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortLambdasOnASingleLine: Empty +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: false +AlwaysBreakBeforeMultilineStrings: false +BasedOnStyle: Google +BraceWrapping: { + AfterClass: 'true' + AfterControlStatement: 'true' + AfterFunction: 'true' + AfterNamespace: 'false' + BeforeCatch: 'true' + BeforeElse: 'true' +} +BreakBeforeBraces: Custom +BreakConstructorInitializersBeforeComma: true +ColumnLimit: 80 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +DerivePointerBinding: false +FixNamespaceComments: true +IndentCaseLabels: true +IndentWidth: 4 +IndentWrappedFunctionNames: true +IndentPPDirectives: AfterHash +IncludeBlocks: Preserve +NamespaceIndentation: Inner +PointerBindsToType: false +SpacesBeforeTrailingComments: 2 +Standard: Auto +ReflowComments: false diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt new file mode 100644 index 000000000..6435f1398 --- /dev/null +++ b/benchmarks/CMakeLists.txt @@ -0,0 +1,28 @@ +project(chatterino-benchmark) + +set(benchmark_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/src/main.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/Emojis.cpp + # Add your new file above this line! + ) + +add_executable(${PROJECT_NAME} ${benchmark_SOURCES}) +add_sanitizers(${PROJECT_NAME}) + +target_link_libraries(${PROJECT_NAME} PRIVATE chatterino-lib) + +target_link_libraries(${PROJECT_NAME} PRIVATE benchmark::benchmark) + +target_compile_definitions(${PROJECT_NAME} PRIVATE + CHATTERINO_TEST + ) + +set_target_properties(${PROJECT_NAME} + PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/bin" + ) diff --git a/benchmarks/src/Emojis.cpp b/benchmarks/src/Emojis.cpp new file mode 100644 index 000000000..7eb5106e3 --- /dev/null +++ b/benchmarks/src/Emojis.cpp @@ -0,0 +1,57 @@ +#include "providers/emoji/Emojis.hpp" + +#include +#include +#include + +using namespace chatterino; + +static void BM_ShortcodeParsing(benchmark::State &state) +{ + Emojis emojis; + + emojis.load(); + + struct TestCase { + QString input; + QString expectedOutput; + }; + + std::vector tests{ + { + // input + "foo :penguin: bar", + // expected output + "foo 🐧 bar", + }, + { + // input + "foo :nonexistantcode: bar", + // expected output + "foo :nonexistantcode: bar", + }, + { + // input + ":male-doctor:", + // expected output + "👨‍⚕️", + }, + }; + + for (auto _ : state) + { + for (const auto &test : tests) + { + auto output = emojis.replaceShortCodes(test.input); + + auto matches = output == test.expectedOutput; + if (!matches && !output.endsWith(QChar(0xFE0F))) + { + // Try to append 0xFE0F if needed + output = output.append(QChar(0xFE0F)); + } + } + } +} + +BENCHMARK(BM_ShortcodeParsing); diff --git a/benchmarks/src/main.cpp b/benchmarks/src/main.cpp new file mode 100644 index 000000000..501b3aa51 --- /dev/null +++ b/benchmarks/src/main.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + ::benchmark::Initialize(&argc, argv); + + QtConcurrent::run([&app] { + ::benchmark::RunSpecifiedBenchmarks(); + + app.exit(0); + }); + + return app.exec(); +} diff --git a/docs/test-and-benchmark.md b/docs/test-and-benchmark.md new file mode 100644 index 000000000..e881db6be --- /dev/null +++ b/docs/test-and-benchmark.md @@ -0,0 +1,88 @@ +# Test and Benchmark + +Chatterino includes a set of unit tests and benchmarks. These can be built using cmake by adding the `-DBUILD_TESTS=On` and `-DBUILD_BENCHMARKS=On` flags respectively. + +## Adding your own test + +1. Create a new file for the file you're adding tests for. If you're creating tests for `src/providers/emoji/Emojis.cpp`, create `tests/src/Emojis.cpp`. +2. Add the newly created file to `tests/CMakeLists.txt` in the `test_SOURCES` variable (see the comment near it) + +See `tests/src/Emojis.cpp` for simple tests you can base your tests off of. + +Read up on http://google.github.io/googletest/primer.html to figure out how GoogleTest works. + +## Building and running tests + +```sh +mkdir build-tests +cd build-tests +cmake -DBUILD_TESTS=On .. +make +./bin/chatterino-test +``` + +### Example output + +``` +[==========] Running 26 tests from 8 test suites. +[----------] Global test environment set-up. +[----------] 2 tests from AccessGuardLocker +[ RUN ] AccessGuardLocker.NonConcurrentUsage +[ OK ] AccessGuardLocker.NonConcurrentUsage (0 ms) +[ RUN ] AccessGuardLocker.ConcurrentUsage +[ OK ] AccessGuardLocker.ConcurrentUsage (686 ms) +[----------] 2 tests from AccessGuardLocker (686 ms total) + +[----------] 4 tests from NetworkCommon +[ RUN ] NetworkCommon.parseHeaderList1 +[ OK ] NetworkCommon.parseHeaderList1 (0 ms) +[ RUN ] NetworkCommon.parseHeaderListTrimmed +[ OK ] NetworkCommon.parseHeaderListTrimmed (0 ms) +[ RUN ] NetworkCommon.parseHeaderListColonInValue +... +[ RUN ] TwitchAccount.NotEnoughForMoreThanOneBatch +[ OK ] TwitchAccount.NotEnoughForMoreThanOneBatch (0 ms) +[ RUN ] TwitchAccount.BatchThreeParts +[ OK ] TwitchAccount.BatchThreeParts (0 ms) +[----------] 3 tests from TwitchAccount (2 ms total) + +[----------] Global test environment tear-down +[==========] 26 tests from 8 test suites ran. (10297 ms total) +[ PASSED ] 26 tests. +``` + +## Adding your own benchmark + +1. Create a new file for the file you're adding benchmark for. If you're creating benchmarks for `src/providers/emoji/Emojis.cpp`, create `benchmarks/src/Emojis.cpp`. +2. Add the newly created file to `benchmarks/CMakeLists.txt` in the `benchmark_SOURCES` variable (see the comment near it) + +See `benchmarks/src/Emojis.cpp` for simple benchmark you can base your benchmarks off of. + +## Building and running benchmarks + +```sh +mkdir build-benchmarks +cd build-benchmarks +cmake -DBUILD_BENCHMARKS=On .. +make +./bin/chatterino-benchmark +``` + +### Example output + +``` +2021-07-18T13:12:11+02:00 +Running ./bin/chatterino-benchmark +Run on (12 X 4000 MHz CPU s) +CPU Caches: + L1 Data 32 KiB (x6) + L1 Instruction 32 KiB (x6) + L2 Unified 256 KiB (x6) + L3 Unified 15360 KiB (x1) +Load Average: 2.86, 3.08, 3.51 +***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead. +-------------------------------------------------------------- +Benchmark Time CPU Iterations +-------------------------------------------------------------- +BM_ShortcodeParsing 2394 ns 2389 ns 278933 +``` diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 74474c2e5..8b1fafe99 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -12,6 +12,7 @@ set(test_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/TwitchAccount.cpp ${CMAKE_CURRENT_LIST_DIR}/src/Helpers.cpp ${CMAKE_CURRENT_LIST_DIR}/src/RatelimitBucket.cpp + # Add your new file above this line! ) add_executable(${PROJECT_NAME} ${test_SOURCES}) From a216e11755aaa13968c5104460e631604420edef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 8 Aug 2021 13:18:05 +0000 Subject: [PATCH 11/34] Bump lib/libcommuni from `c613600` to `95f0547` (#3149) Bumps [lib/libcommuni](https://github.com/Chatterino/libcommuni) from `c613600` to `95f0547`. - [Release notes](https://github.com/Chatterino/libcommuni/releases) - [Commits](https://github.com/Chatterino/libcommuni/compare/c613600e6a52e6d3166247a05205cf1c755d4868...95f05478de1623767282d8019ea8f3a4b1178b35) --- updated-dependencies: - dependency-name: lib/libcommuni dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- lib/libcommuni | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/libcommuni b/lib/libcommuni index c613600e6..95f05478d 160000 --- a/lib/libcommuni +++ b/lib/libcommuni @@ -1 +1 @@ -Subproject commit c613600e6a52e6d3166247a05205cf1c755d4868 +Subproject commit 95f05478de1623767282d8019ea8f3a4b1178b35 From b3bb7e2d3e2496f4e815cecbda185da230e722f6 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 9 Aug 2021 00:04:25 +0200 Subject: [PATCH 12/34] Make single deletion messages for moderators obey the setting (#3121) Co-authored-by: ALazyMeme Co-authored-by: pajlada --- CHANGELOG.md | 1 + src/Application.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c62bf8bd..e6cb014f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Minor: Remove TwitchEmotes.com attribution and the open/copy options when right-clicking a Twitch Emote. (#2214, #3136) - Minor: Strip leading @ and trailing , from username in /user and /usercard commands. (#3143) - Minor: Display a system message when reloading subscription emotes to match BTTV/FFZ behavior (#3135) +- Bugfix: Notifications for moderators about other moderators deleting messages can now be disabled. (#3121) - Bugfix: Moderation mode and active filters are now preserved when opening a split as a popup. (#3113, #3130) - Bugfix: Fixed a bug that caused all badge highlights to use the same color. (#3132, #3134) - Dev: Renamed CMake's build option `USE_SYSTEM_QT5KEYCHAIN` to `USE_SYSTEM_QTKEYCHAIN`. (#3103) diff --git a/src/Application.cpp b/src/Application.cpp index f0f79c42f..4de581187 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -286,7 +286,7 @@ void Application::initPubsub() auto chan = this->twitch.server->getChannelOrEmptyByID(action.roomID); - if (chan->isEmpty()) + if (chan->isEmpty() || getSettings()->hideDeletionActions) { return; } From 75458a1a258169c4c06a811a5322bacc819a9796 Mon Sep 17 00:00:00 2001 From: Auro <35087590+MrAuro@users.noreply.github.com> Date: Sat, 14 Aug 2021 06:08:55 -0400 Subject: [PATCH 13/34] Change 3rd party uploader popup text capitalization (#3158) --- src/widgets/splits/Split.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index 8c84a1476..e2cd37c7e 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -262,10 +262,11 @@ Split::Split(QWidget *parent) if (getSettings()->askOnImageUpload.getValue()) { QMessageBox msgBox; + msgBox.setWindowTitle("Chatterino"); msgBox.setText("Image upload"); msgBox.setInformativeText( "You are uploading an image to a 3rd party service not in " - "control of the chatterino team. You may not be able to " + "control of the Chatterino team. You may not be able to " "remove the image from the site. Are you okay with this?"); msgBox.addButton(QMessageBox::Cancel); msgBox.addButton(QMessageBox::Yes); From d5ecba3d3068b30eabcab2aac6e53484049585a1 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sat, 14 Aug 2021 13:06:58 +0200 Subject: [PATCH 14/34] Rewrite logic for when to show moderation buttons next to messages (#2800) Co-authored-by: Rasmus Karlsson --- src/providers/twitch/TwitchMessageBuilder.cpp | 42 +++++++++---------- src/providers/twitch/TwitchMessageBuilder.hpp | 2 + 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 97c36f0d7..f91f2f742 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -184,29 +184,7 @@ MessagePtr TwitchMessageBuilder::build() this->emplace( calculateMessageTimestamp(this->ircMessage)); - bool addModerationElement = true; - if (this->senderIsBroadcaster) - { - addModerationElement = false; - } - else - { - bool hasUserType = this->tags.contains("user-type"); - if (hasUserType) - { - QString userType = this->tags.value("user-type").toString(); - - if (userType == "mod") - { - if (!args.isStaffOrBroadcaster) - { - addModerationElement = false; - } - } - } - } - - if (addModerationElement) + if (this->shouldAddModerationElements()) { this->emplace(); } @@ -1230,6 +1208,24 @@ Outcome TwitchMessageBuilder::tryParseCheermote(const QString &string) return Success; } +bool TwitchMessageBuilder::shouldAddModerationElements() const +{ + if (this->senderIsBroadcaster) + { + // You cannot timeout the broadcaster + return false; + } + + if (this->tags.value("user-type").toString() == "mod" && + !this->args.isStaffOrBroadcaster) + { + // You cannot timeout moderators UNLESS you are Twitch Staff or the broadcaster of the channel + return false; + } + + return true; +} + void TwitchMessageBuilder::appendChannelPointRewardMessage( const ChannelPointReward &reward, MessageBuilder *builder, bool isMod, bool isBroadcaster) diff --git a/src/providers/twitch/TwitchMessageBuilder.hpp b/src/providers/twitch/TwitchMessageBuilder.hpp index 7cf68494a..8f1217c8e 100644 --- a/src/providers/twitch/TwitchMessageBuilder.hpp +++ b/src/providers/twitch/TwitchMessageBuilder.hpp @@ -90,6 +90,8 @@ private: void appendFfzBadges(); Outcome tryParseCheermote(const QString &string); + bool shouldAddModerationElements() const; + QString roomID_; bool hasBits_ = false; QString bits; From 113a7795f426c071d6eadd393ea3b122e43de551 Mon Sep 17 00:00:00 2001 From: Marko Date: Sat, 14 Aug 2021 12:28:08 +0000 Subject: [PATCH 15/34] Hide similar messages by any user (#2716) Co-authored-by: pajlada --- CHANGELOG.md | 1 + src/providers/twitch/IrcMessageHandler.cpp | 12 ++++++------ src/singletons/Settings.hpp | 2 ++ src/widgets/settingspages/GeneralPage.cpp | 6 +++--- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6cb014f6..9579c2c0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Minor: Remove TwitchEmotes.com attribution and the open/copy options when right-clicking a Twitch Emote. (#2214, #3136) - Minor: Strip leading @ and trailing , from username in /user and /usercard commands. (#3143) - Minor: Display a system message when reloading subscription emotes to match BTTV/FFZ behavior (#3135) +- Minor: Added a setting to hide similar messages by any user. (#2716) - Bugfix: Notifications for moderators about other moderators deleting messages can now be disabled. (#3121) - Bugfix: Moderation mode and active filters are now preserved when opening a split as a popup. (#3113, #3130) - Bugfix: Fixed a bug that caused all badge highlights to use the same color. (#3132, #3134) diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index 456d05223..0e720d0ee 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -112,11 +112,10 @@ float IrcMessageHandler::similarity( MessagePtr msg, const LimitedQueueSnapshot &messages) { float similarityPercent = 0.0f; - int bySameUser = 0; - for (int i = 1; bySameUser < getSettings()->hideSimilarMaxMessagesToCheck; - ++i) + int checked = 0; + for (int i = 1; i <= messages.size(); ++i) { - if (messages.size() < i) + if (checked >= getSettings()->hideSimilarMaxMessagesToCheck) { break; } @@ -126,11 +125,12 @@ float IrcMessageHandler::similarity( { break; } - if (msg->loginName != prevMsg->loginName) + if (getSettings()->hideSimilarBySameUser && + msg->loginName != prevMsg->loginName) { continue; } - ++bySameUser; + ++checked; similarityPercent = std::max( similarityPercent, relativeSimilarity(msg->messageText, prevMsg->messageText)); diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index 703fefbde..af61779f9 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -392,6 +392,8 @@ public: BoolSetting colorSimilarDisabled = {"/similarity/colorSimilarDisabled", true}; BoolSetting hideSimilar = {"/similarity/hideSimilar", false}; + BoolSetting hideSimilarBySameUser = {"/similarity/hideSimilarBySameUser", + true}; BoolSetting hideSimilarMyself = {"/similarity/hideSimilarMyself", false}; BoolSetting shownSimilarTriggerHighlights = { "/similarity/shownSimilarTriggerHighlights", false}; diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index 0254c1360..24780ed41 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -520,11 +520,11 @@ void GeneralPage::initLayout(GeneralPageView &layout) layout.addCheckbox("Title", s.headerStreamTitle); layout.addSubtitle("R9K"); - layout.addDescription( - "Hide similar messages by the same user. Toggle hidden " - "messages by pressing Ctrl+H."); + layout.addDescription("Hide similar messages. Toggle hidden " + "messages by pressing Ctrl+H."); layout.addCheckbox("Hide similar messages", s.similarityEnabled); //layout.addCheckbox("Gray out matches", s.colorSimilarDisabled); + layout.addCheckbox("By the same user", s.hideSimilarBySameUser); layout.addCheckbox("Hide my own messages", s.hideSimilarMyself); layout.addCheckbox("Receive notification sounds from hidden messages", s.shownSimilarTriggerHighlights); From d46589ca26d6a3bfaa81d2ec0defe7e15a9a7b46 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sat, 14 Aug 2021 15:16:16 +0200 Subject: [PATCH 16/34] Duplicate spaces now count towards the display message length (#3002) --- CHANGELOG.md | 1 + src/widgets/splits/SplitInput.cpp | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9579c2c0e..5c8caeb71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Minor: Strip leading @ and trailing , from username in /user and /usercard commands. (#3143) - Minor: Display a system message when reloading subscription emotes to match BTTV/FFZ behavior (#3135) - Minor: Added a setting to hide similar messages by any user. (#2716) +- Minor: Duplicate spaces now count towards the display message length. (#3002) - Bugfix: Notifications for moderators about other moderators deleting messages can now be disabled. (#3121) - Bugfix: Moderation mode and active filters are now preserved when opening a split as a popup. (#3113, #3130) - Bugfix: Fixed a bug that caused all badge highlights to use the same color. (#3132, #3134) diff --git a/src/widgets/splits/SplitInput.cpp b/src/widgets/splits/SplitInput.cpp index 26888f365..3afe85960 100644 --- a/src/widgets/splits/SplitInput.cpp +++ b/src/widgets/splits/SplitInput.cpp @@ -639,9 +639,6 @@ void SplitInput::editTextChanged() this->textChanged.invoke(text); text = text.trimmed(); - static QRegularExpression spaceRegex("\\s\\s+"); - text = text.replace(spaceRegex, " "); - text = app->commands->execCommand(text, this->split_->getChannel(), true); } From 9a1e4055634e17985e5fd36b074708ff3b398537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82?= Date: Sun, 15 Aug 2021 11:52:32 +0200 Subject: [PATCH 17/34] Fix /me not going through upon sending an identical message (#3166) --- CHANGELOG.md | 1 + src/providers/twitch/TwitchChannel.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c8caeb71..096f1209f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Minor: Display a system message when reloading subscription emotes to match BTTV/FFZ behavior (#3135) - Minor: Added a setting to hide similar messages by any user. (#2716) - Minor: Duplicate spaces now count towards the display message length. (#3002) +- Bugfix: Restored ability to send duplicate `/me` messages. (#3166) - Bugfix: Notifications for moderators about other moderators deleting messages can now be disabled. (#3121) - Bugfix: Moderation mode and active filters are now preserved when opening a split as a popup. (#3113, #3130) - Bugfix: Fixed a bug that caused all badge highlights to use the same color. (#3132, #3134) diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index 94c458a55..db35de7b2 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -377,6 +377,16 @@ void TwitchChannel::sendMessage(const QString &message) if (parsedMessage == this->lastSentMessage_) { auto spaceIndex = parsedMessage.indexOf(' '); + // If the message starts with either '/' or '.' Twitch will treat it as a command, omitting + // first space and only rest of the arguments treated as actual message content + // In cases when user sends a message like ". .a b" first character and first space are omitted as well + bool ignoreFirstSpace = + parsedMessage.at(0) == '/' || parsedMessage.at(0) == '.'; + if (ignoreFirstSpace) + { + spaceIndex = parsedMessage.indexOf(' ', spaceIndex + 1); + } + if (spaceIndex == -1) { // no spaces found, fall back to old magic character From 4b251c64b69ed2d05e78f754b09ccf3dbf551b29 Mon Sep 17 00:00:00 2001 From: James Upjohn Date: Sun, 15 Aug 2021 22:39:58 +1200 Subject: [PATCH 18/34] Allow resub messages to show in /mentions tab (#3148) Co-authored-by: pajlada --- CHANGELOG.md | 1 + src/messages/SharedMessageBuilder.cpp | 20 +++++++++++++------- src/providers/twitch/IrcMessageHandler.cpp | 7 ++----- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 096f1209f..2a2144b87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Minor: Remove TwitchEmotes.com attribution and the open/copy options when right-clicking a Twitch Emote. (#2214, #3136) - Minor: Strip leading @ and trailing , from username in /user and /usercard commands. (#3143) - Minor: Display a system message when reloading subscription emotes to match BTTV/FFZ behavior (#3135) +- Minor: Allow resub messages to show in `/mentions` tab (#3148) - Minor: Added a setting to hide similar messages by any user. (#2716) - Minor: Duplicate spaces now count towards the display message length. (#3002) - Bugfix: Restored ability to send duplicate `/me` messages. (#3166) diff --git a/src/messages/SharedMessageBuilder.cpp b/src/messages/SharedMessageBuilder.cpp index cdc8e59f0..3af860f9f 100644 --- a/src/messages/SharedMessageBuilder.cpp +++ b/src/messages/SharedMessageBuilder.cpp @@ -157,10 +157,6 @@ void SharedMessageBuilder::parseHighlights() this->message().flags.set(MessageFlag::Highlighted); this->message().highlightColor = ColorProvider::instance().color(ColorType::Subscription); - - // This message was a subscription. - // Don't check for any other highlight phrases. - return; } // XXX: Non-common term in SharedMessageBuilder @@ -220,7 +216,10 @@ void SharedMessageBuilder::parseHighlights() << "sent a message"; this->message().flags.set(MessageFlag::Highlighted); - this->message().highlightColor = userHighlight.getColor(); + if (!this->message().flags.has(MessageFlag::Subscription)) + { + this->message().highlightColor = userHighlight.getColor(); + } if (userHighlight.showInMentions()) { @@ -289,7 +288,10 @@ void SharedMessageBuilder::parseHighlights() } this->message().flags.set(MessageFlag::Highlighted); - this->message().highlightColor = highlight.getColor(); + if (!this->message().flags.has(MessageFlag::Subscription)) + { + this->message().highlightColor = highlight.getColor(); + } if (highlight.showInMentions()) { @@ -344,7 +346,11 @@ void SharedMessageBuilder::parseHighlights() if (!badgeHighlightSet) { this->message().flags.set(MessageFlag::Highlighted); - this->message().highlightColor = highlight.getColor(); + if (!this->message().flags.has(MessageFlag::Subscription)) + { + this->message().highlightColor = highlight.getColor(); + } + badgeHighlightSet = true; } diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index 0e720d0ee..a4ce3c513 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -313,12 +313,9 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message, const auto highlighted = msg->flags.has(MessageFlag::Highlighted); const auto showInMentions = msg->flags.has(MessageFlag::ShowInMentions); - if (!isSub) + if (highlighted && showInMentions) { - if (highlighted && showInMentions) - { - server.mentionsChannel->addMessage(msg); - } + server.mentionsChannel->addMessage(msg); } chan->addMessage(msg); From b2d9b678a288b31640a2d64ccf1861d0c7fb91a6 Mon Sep 17 00:00:00 2001 From: pajlada Date: Sun, 15 Aug 2021 14:33:31 +0200 Subject: [PATCH 19/34] Enable backup for commands (#3168) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paweł --- CHANGELOG.md | 1 + src/controllers/commands/CommandController.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a2144b87..b48ade145 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Minor: Allow resub messages to show in `/mentions` tab (#3148) - Minor: Added a setting to hide similar messages by any user. (#2716) - Minor: Duplicate spaces now count towards the display message length. (#3002) +- Minor: Commands are now backed up. (#3168) - Bugfix: Restored ability to send duplicate `/me` messages. (#3166) - Bugfix: Notifications for moderators about other moderators deleting messages can now be disabled. (#3121) - Bugfix: Moderation mode and active filters are now preserved when opening a split as a popup. (#3113, #3130) diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 8e977af9b..13368917e 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -267,6 +267,8 @@ void CommandController::initialize(Settings &, Paths &paths) auto path = combinePath(paths.settingsDirectory, "commands.json"); this->sm_ = std::make_shared(); this->sm_->setPath(path.toStdString()); + this->sm_->setBackupEnabled(true); + this->sm_->setBackupSlots(9); // Delayed initialization of the setting storing all commands this->commandsSetting_.reset( From 1d664f88e56179a0f5da0cd687df2e19bfb79c4b Mon Sep 17 00:00:00 2001 From: pajlada Date: Sun, 15 Aug 2021 15:59:52 +0200 Subject: [PATCH 20/34] Keyboard integration for Streamlink quality confirmation (#3169) Co-authored-by: zneix --- src/util/StreamLink.cpp | 2 +- src/widgets/BasePopup.cpp | 64 ++++++++++++++++++++++++++++ src/widgets/BasePopup.hpp | 8 ++++ src/widgets/dialogs/QualityPopup.cpp | 50 +++++++++++++--------- src/widgets/dialogs/QualityPopup.hpp | 18 ++++---- 5 files changed, 111 insertions(+), 31 deletions(-) diff --git a/src/util/StreamLink.cpp b/src/util/StreamLink.cpp index 2ac42cfa6..b631cad51 100644 --- a/src/util/StreamLink.cpp +++ b/src/util/StreamLink.cpp @@ -200,7 +200,7 @@ void openStreamlinkForChannel(const QString &channel) if (preferredQuality == "choose") { getStreamQualities(channelURL, [=](QStringList qualityOptions) { - QualityPopup::showDialog(channel, qualityOptions); + QualityPopup::showDialog(channelURL, qualityOptions); }); return; diff --git a/src/widgets/BasePopup.cpp b/src/widgets/BasePopup.cpp index 0f3ed3c1d..afae740b6 100644 --- a/src/widgets/BasePopup.cpp +++ b/src/widgets/BasePopup.cpp @@ -1,5 +1,7 @@ #include "widgets/BasePopup.hpp" +#include +#include #include namespace chatterino { @@ -20,4 +22,66 @@ void BasePopup::keyPressEvent(QKeyEvent *e) BaseWindow::keyPressEvent(e); } +bool BasePopup::handleEscape(QKeyEvent *e, QDialogButtonBox *buttonBox) +{ + assert(buttonBox != nullptr); + + if (e->key() == Qt::Key_Escape) + { + auto buttons = buttonBox->buttons(); + for (auto *button : buttons) + { + if (auto role = buttonBox->buttonRole(button); + role == QDialogButtonBox::ButtonRole::RejectRole) + { + button->click(); + return true; + } + } + } + + return false; +} + +bool BasePopup::handleEnter(QKeyEvent *e, QDialogButtonBox *buttonBox) +{ + assert(buttonBox != nullptr); + + if (!e->modifiers() || + (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter)) + { + switch (e->key()) + { + case Qt::Key_Enter: + case Qt::Key_Return: { + auto buttons = buttonBox->buttons(); + QAbstractButton *acceptButton = nullptr; + for (auto *button : buttons) + { + if (button->hasFocus()) + { + button->click(); + return true; + } + + if (auto role = buttonBox->buttonRole(button); + role == QDialogButtonBox::ButtonRole::AcceptRole) + { + acceptButton = button; + } + } + + if (acceptButton != nullptr) + { + acceptButton->click(); + return true; + } + } + break; + } + } + + return false; +} + } // namespace chatterino diff --git a/src/widgets/BasePopup.hpp b/src/widgets/BasePopup.hpp index 5741b0006..d7942f2fc 100644 --- a/src/widgets/BasePopup.hpp +++ b/src/widgets/BasePopup.hpp @@ -3,6 +3,8 @@ #include "common/FlagsEnum.hpp" #include "widgets/BaseWindow.hpp" +class QDialogButtonBox; + namespace chatterino { class BasePopup : public BaseWindow @@ -13,6 +15,12 @@ public: protected: void keyPressEvent(QKeyEvent *e) override; + + // handleEscape is a helper function for clicking the "Reject" role button of a button box when the Escape button is pressed + bool handleEscape(QKeyEvent *e, QDialogButtonBox *buttonBox); + + // handleEnter is a helper function for clicking the "Accept" role button of a button box when Return or Enter is pressed + bool handleEnter(QKeyEvent *e, QDialogButtonBox *buttonBox); }; } // namespace chatterino diff --git a/src/widgets/dialogs/QualityPopup.cpp b/src/widgets/dialogs/QualityPopup.cpp index 414ab2548..52f7dba47 100644 --- a/src/widgets/dialogs/QualityPopup.cpp +++ b/src/widgets/dialogs/QualityPopup.cpp @@ -7,35 +7,32 @@ namespace chatterino { -QualityPopup::QualityPopup(const QString &_channelName, QStringList options) +QualityPopup::QualityPopup(const QString &channelURL, QStringList options) : BasePopup({}, static_cast(&(getApp()->windows->getMainWindow()))) - , channelName_(_channelName) + , channelURL_(channelURL) { - this->ui_.okButton.setText("OK"); - this->ui_.cancelButton.setText("Cancel"); + this->ui_.selector = new QComboBox(this); + this->ui_.vbox = new QVBoxLayout(this); + this->ui_.buttonBox = new QDialogButtonBox( + QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); - QObject::connect(&this->ui_.okButton, &QPushButton::clicked, this, + QObject::connect(this->ui_.buttonBox, &QDialogButtonBox::accepted, this, &QualityPopup::okButtonClicked); - QObject::connect(&this->ui_.cancelButton, &QPushButton::clicked, this, + QObject::connect(this->ui_.buttonBox, &QDialogButtonBox::rejected, this, &QualityPopup::cancelButtonClicked); - this->ui_.buttonBox.addButton(&this->ui_.okButton, - QDialogButtonBox::ButtonRole::AcceptRole); - this->ui_.buttonBox.addButton(&this->ui_.cancelButton, - QDialogButtonBox::ButtonRole::RejectRole); + this->ui_.selector->addItems(options); - this->ui_.selector.addItems(options); + this->ui_.vbox->addWidget(this->ui_.selector); + this->ui_.vbox->addWidget(this->ui_.buttonBox); - this->ui_.vbox.addWidget(&this->ui_.selector); - this->ui_.vbox.addWidget(&this->ui_.buttonBox); - - this->setLayout(&this->ui_.vbox); + this->setLayout(this->ui_.vbox); } -void QualityPopup::showDialog(const QString &channelName, QStringList options) +void QualityPopup::showDialog(const QString &channelURL, QStringList options) { - QualityPopup *instance = new QualityPopup(channelName, options); + QualityPopup *instance = new QualityPopup(channelURL, options); instance->window()->setWindowTitle("Chatterino - select stream quality"); instance->setAttribute(Qt::WA_DeleteOnClose, true); @@ -43,16 +40,27 @@ void QualityPopup::showDialog(const QString &channelName, QStringList options) instance->show(); instance->activateWindow(); instance->raise(); - instance->setFocus(); +} + +void QualityPopup::keyPressEvent(QKeyEvent *e) +{ + if (this->handleEscape(e, this->ui_.buttonBox)) + { + return; + } + if (this->handleEnter(e, this->ui_.buttonBox)) + { + return; + } + + BasePopup::keyPressEvent(e); } void QualityPopup::okButtonClicked() { - QString channelURL = "twitch.tv/" + this->channelName_; - try { - openStreamlink(channelURL, this->ui_.selector.currentText()); + openStreamlink(this->channelURL_, this->ui_.selector->currentText()); } catch (const Exception &ex) { diff --git a/src/widgets/dialogs/QualityPopup.hpp b/src/widgets/dialogs/QualityPopup.hpp index cb5f06205..f0820218d 100644 --- a/src/widgets/dialogs/QualityPopup.hpp +++ b/src/widgets/dialogs/QualityPopup.hpp @@ -4,7 +4,6 @@ #include #include -#include #include namespace chatterino { @@ -12,22 +11,23 @@ namespace chatterino { class QualityPopup : public BasePopup { public: - QualityPopup(const QString &_channelName, QStringList options); - static void showDialog(const QString &_channelName, QStringList options); + QualityPopup(const QString &channelURL, QStringList options); + static void showDialog(const QString &channelURL, QStringList options); + +protected: + void keyPressEvent(QKeyEvent *e) override; private: void okButtonClicked(); void cancelButtonClicked(); struct { - QVBoxLayout vbox; - QComboBox selector; - QDialogButtonBox buttonBox; - QPushButton okButton; - QPushButton cancelButton; + QVBoxLayout *vbox; + QComboBox *selector; + QDialogButtonBox *buttonBox; } ui_; - QString channelName_; + QString channelURL_; }; } // namespace chatterino From f36951101df7921cdae52f5e1a8d4cf50fbbbb8f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Aug 2021 21:03:53 +0200 Subject: [PATCH 21/34] Bump dangoslen/changelog-enforcer from 2.2.0 to 2.3.0 (#3174) Bumps [dangoslen/changelog-enforcer](https://github.com/dangoslen/changelog-enforcer) from 2.2.0 to 2.3.0. - [Release notes](https://github.com/dangoslen/changelog-enforcer/releases) - [Changelog](https://github.com/dangoslen/changelog-enforcer/blob/master/CHANGELOG.md) - [Commits](https://github.com/dangoslen/changelog-enforcer/compare/v2.2.0...v2.3.0) --- updated-dependencies: - dependency-name: dangoslen/changelog-enforcer dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/changelog-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/changelog-check.yml b/.github/workflows/changelog-check.yml index 7fbceea74..30285396a 100644 --- a/.github/workflows/changelog-check.yml +++ b/.github/workflows/changelog-check.yml @@ -13,7 +13,7 @@ jobs: # Gives an error if there's no change in the changelog (except using label) - name: Changelog check - uses: dangoslen/changelog-enforcer@v2.2.0 + uses: dangoslen/changelog-enforcer@v2.3.0 with: changeLogPath: 'CHANGELOG.md' skipLabels: 'no changelog entry needed, ci, submodules' From f74b2770243204391e885ebd2cbcc43e393d19ea Mon Sep 17 00:00:00 2001 From: apa420 <17131426+apa420@users.noreply.github.com> Date: Tue, 17 Aug 2021 13:26:19 +0200 Subject: [PATCH 22/34] Browser extension issue template now properly redirects (#3171) --- .github/ISSUE_TEMPLATE/config.yml | 3 +++ .github/ISSUE_TEMPLATE/z_browser_extension.md | 7 ------- 2 files changed, 3 insertions(+), 7 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/z_browser_extension.md diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 8ac1ca177..47dbbb2a4 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,8 @@ blank_issues_enabled: false contact_links: + - name: Issue about the Chatterino Browser Extension + url: https://github.com/Chatterino/chatterino-browser-ext/issues + about: Make a suggestion or report a bug about the Chatterino browser extension. - name: Suggestions or feature request url: https://github.com/chatterino/chatterino2/discussions/categories/ideas about: Got something you think should change or be added? Search for or start a new discussion! diff --git a/.github/ISSUE_TEMPLATE/z_browser_extension.md b/.github/ISSUE_TEMPLATE/z_browser_extension.md deleted file mode 100644 index 2ae9cdf67..000000000 --- a/.github/ISSUE_TEMPLATE/z_browser_extension.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: Issue about the Chatterino Browser Extension -about: Make a suggestion or report a bug about the Chatterino browser extension. - ---- - -Issues for the extension are tracked here: https://github.com/chatterino/chatterino-browser-ext From 07454d053738e8340cbd978806eda34191b4369b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Aug 2021 21:59:46 +0200 Subject: [PATCH 23/34] Bump dangoslen/changelog-enforcer from 2.3.0 to 2.3.1 (#3179) Bumps [dangoslen/changelog-enforcer](https://github.com/dangoslen/changelog-enforcer) from 2.3.0 to 2.3.1. - [Release notes](https://github.com/dangoslen/changelog-enforcer/releases) - [Changelog](https://github.com/dangoslen/changelog-enforcer/blob/master/CHANGELOG.md) - [Commits](https://github.com/dangoslen/changelog-enforcer/compare/v2.3.0...v2.3.1) --- updated-dependencies: - dependency-name: dangoslen/changelog-enforcer dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/changelog-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/changelog-check.yml b/.github/workflows/changelog-check.yml index 30285396a..05a2454e8 100644 --- a/.github/workflows/changelog-check.yml +++ b/.github/workflows/changelog-check.yml @@ -13,7 +13,7 @@ jobs: # Gives an error if there's no change in the changelog (except using label) - name: Changelog check - uses: dangoslen/changelog-enforcer@v2.3.0 + uses: dangoslen/changelog-enforcer@v2.3.1 with: changeLogPath: 'CHANGELOG.md' skipLabels: 'no changelog entry needed, ci, submodules' From d7fd08b1d647b701d563af34c01cbc3c66a7744a Mon Sep 17 00:00:00 2001 From: pajlada Date: Sat, 21 Aug 2021 12:38:38 +0200 Subject: [PATCH 24/34] Fix color @usernames sometimes not working at all (#3170) Definitely memory fuckery involved - The comment from @lubieerror https://github.com/Chatterino/chatterino2/issues/2822#issuecomment-897252673 is finally what led me to adding tests and hopefully fixing this. --- CHANGELOG.md | 1 + lib/lrucache/lrucache/lrucache.hpp | 2 + src/common/ChannelChatters.cpp | 6 ++ src/common/ChannelChatters.hpp | 6 +- tests/CMakeLists.txt | 1 + tests/src/ChannelChatters.cpp | 113 +++++++++++++++++++++++++++++ 6 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 tests/src/ChannelChatters.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index b48ade145..d79f8c9c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Minor: Added a setting to hide similar messages by any user. (#2716) - Minor: Duplicate spaces now count towards the display message length. (#3002) - Minor: Commands are now backed up. (#3168) +- Bugfix: Fixed colored usernames sometimes not working. (#3170) - Bugfix: Restored ability to send duplicate `/me` messages. (#3166) - Bugfix: Notifications for moderators about other moderators deleting messages can now be disabled. (#3121) - Bugfix: Moderation mode and active filters are now preserved when opening a split as a popup. (#3113, #3130) diff --git a/lib/lrucache/lrucache/lrucache.hpp b/lib/lrucache/lrucache/lrucache.hpp index aa2c6c511..ef7d031db 100644 --- a/lib/lrucache/lrucache/lrucache.hpp +++ b/lib/lrucache/lrucache/lrucache.hpp @@ -35,6 +35,7 @@ public: lru_cache(lru_cache &&other) : _cache_items_list(std::move(other._cache_items_list)) , _cache_items_map(std::move(other._cache_items_map)) + , _max_size(other._max_size) { other._cache_items_list.clear(); other._cache_items_map.clear(); @@ -44,6 +45,7 @@ public: { _cache_items_list = std::move(other._cache_items_list); _cache_items_map = std::move(other._cache_items_map); + _max_size = other._max_size; other._cache_items_list.clear(); other._cache_items_map.clear(); return *this; diff --git a/src/common/ChannelChatters.cpp b/src/common/ChannelChatters.cpp index 8b72f7b17..f9eb1ea65 100644 --- a/src/common/ChannelChatters.cpp +++ b/src/common/ChannelChatters.cpp @@ -74,6 +74,12 @@ void ChannelChatters::updateOnlineChatters( chatters_->updateOnlineChatters(chatters); } +size_t ChannelChatters::colorsSize() const +{ + auto size = this->chatterColors_.access()->size(); + return size; +} + const QColor ChannelChatters::getUserColor(const QString &user) { const auto chatterColors = this->chatterColors_.access(); diff --git a/src/common/ChannelChatters.hpp b/src/common/ChannelChatters.hpp index 3a2c4e49a..70ca53924 100644 --- a/src/common/ChannelChatters.hpp +++ b/src/common/ChannelChatters.hpp @@ -25,9 +25,13 @@ public: void setUserColor(const QString &user, const QColor &color); void updateOnlineChatters(const std::unordered_set &chatters); -private: + // colorsSize returns the amount of colors stored in `chatterColors_` + // NOTE: This function is only meant to be used in tests and benchmarks + size_t colorsSize() const; + static constexpr int maxChatterColorCount = 5000; +private: Channel &channel_; // maps 2 char prefix to set of names diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8b1fafe99..4c15a4902 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -2,6 +2,7 @@ project(chatterino-test) set(test_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/main.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/ChannelChatters.cpp ${CMAKE_CURRENT_LIST_DIR}/src/AccessGuard.cpp ${CMAKE_CURRENT_LIST_DIR}/src/NetworkCommon.cpp ${CMAKE_CURRENT_LIST_DIR}/src/NetworkRequest.cpp diff --git a/tests/src/ChannelChatters.cpp b/tests/src/ChannelChatters.cpp new file mode 100644 index 000000000..e0006a205 --- /dev/null +++ b/tests/src/ChannelChatters.cpp @@ -0,0 +1,113 @@ +#include "common/ChannelChatters.hpp" + +#include +#include +#include + +namespace chatterino { + +class MockChannel : public Channel +{ +public: + MockChannel(const QString &name) + : Channel(name, Channel::Type::Twitch) + { + } +}; + +} // namespace chatterino + +using namespace chatterino; + +// Ensure inserting the same user does not increase the size of the stored colors +TEST(ChatterChatters, insertSameUser) +{ + MockChannel channel("test"); + + ChannelChatters chatters(channel); + + EXPECT_EQ(chatters.colorsSize(), 0); + chatters.setUserColor("pajlada", QColor("#fff")); + EXPECT_EQ(chatters.colorsSize(), 1); + chatters.setUserColor("pajlada", QColor("#fff")); + EXPECT_EQ(chatters.colorsSize(), 1); +} + +// Ensure we can update a chatters color +TEST(ChatterChatters, insertSameUserUpdatesColor) +{ + MockChannel channel("test"); + + ChannelChatters chatters(channel); + + chatters.setUserColor("pajlada", QColor("#fff")); + EXPECT_EQ(chatters.getUserColor("pajlada"), QColor("#fff")); + chatters.setUserColor("pajlada", QColor("#f0f")); + EXPECT_EQ(chatters.getUserColor("pajlada"), QColor("#f0f")); +} + +// Ensure getting a non-existant users color returns an invalid QColor +TEST(ChatterChatters, getNonExistantUser) +{ + MockChannel channel("test"); + + ChannelChatters chatters(channel); + + EXPECT_EQ(chatters.getUserColor("nonexistantuser"), QColor()); +} + +// Ensure getting a user doesn't create an entry +TEST(ChatterChatters, getDoesNotCreate) +{ + MockChannel channel("test"); + + ChannelChatters chatters(channel); + + EXPECT_EQ(chatters.colorsSize(), 0); + chatters.getUserColor("nonexistantuser"); + EXPECT_EQ(chatters.colorsSize(), 0); +} + +// Ensure the least recently used entry is purged when we reach MAX_SIZE +TEST(ChatterChatters, insertMaxSize) +{ + MockChannel channel("test"); + + ChannelChatters chatters(channel); + + // Prime chatters with 2 control entries + chatters.setUserColor("pajlada", QColor("#f00")); + chatters.setUserColor("zneix", QColor("#f0f")); + + EXPECT_EQ(chatters.getUserColor("pajlada"), QColor("#f00")); + EXPECT_EQ(chatters.getUserColor("zneix"), QColor("#f0f")); + EXPECT_EQ(chatters.getUserColor("nonexistantuser"), QColor()); + + EXPECT_EQ(chatters.colorsSize(), 2); + + for (int i = 0; i < ChannelChatters::maxChatterColorCount - 1; ++i) + { + auto username = QString("user%1").arg(i); + chatters.setUserColor(username, QColor("#00f")); + } + + // Should have bumped ONE entry out (pajlada) + + EXPECT_EQ(chatters.getUserColor("pajlada"), QColor()); + EXPECT_EQ(chatters.getUserColor("zneix"), QColor("#f0f")); + EXPECT_EQ(chatters.getUserColor("user1"), QColor("#00f")); + + chatters.setUserColor("newuser", QColor("#00e")); + + for (int i = 0; i < ChannelChatters::maxChatterColorCount; ++i) + { + auto username = QString("user%1").arg(i); + chatters.setUserColor(username, QColor("#00f")); + } + + // One more entry should be bumped out (zneix) + + EXPECT_EQ(chatters.getUserColor("pajlada"), QColor()); + EXPECT_EQ(chatters.getUserColor("zneix"), QColor()); + EXPECT_EQ(chatters.getUserColor("user1"), QColor("#00f")); +} From a7ef7e6f7f4e040746d0faddea34a15c3f07c511 Mon Sep 17 00:00:00 2001 From: ilyazzz Date: Sat, 21 Aug 2021 14:00:01 +0300 Subject: [PATCH 25/34] Use flatpak-spawn to run streamlink when running as a flatpak (#3178) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Leon Richardt Co-authored-by: Paweł --- CHANGELOG.md | 1 + src/common/Version.cpp | 5 +++++ src/common/Version.hpp | 1 + src/singletons/Updates.cpp | 6 +++-- src/util/StreamLink.cpp | 45 ++++++++++++++++++++++++-------------- 5 files changed, 40 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d79f8c9c2..d77657086 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Bugfix: Notifications for moderators about other moderators deleting messages can now be disabled. (#3121) - Bugfix: Moderation mode and active filters are now preserved when opening a split as a popup. (#3113, #3130) - Bugfix: Fixed a bug that caused all badge highlights to use the same color. (#3132, #3134) +- Bugfix: Allow starting Streamlink from Chatterino when running as a Flatpak. (#3178) - Dev: Renamed CMake's build option `USE_SYSTEM_QT5KEYCHAIN` to `USE_SYSTEM_QTKEYCHAIN`. (#3103) - Dev: Add benchmarks that can be compiled with the `BUILD_BENCHMARKS` CMake flag. Off by default. (#3038) diff --git a/src/common/Version.cpp b/src/common/Version.cpp index aa8639dce..67a3bb477 100644 --- a/src/common/Version.cpp +++ b/src/common/Version.cpp @@ -71,4 +71,9 @@ const bool &Version::isSupportedOS() const return this->isSupportedOS_; } +bool Version::isFlatpak() const +{ + return QFileInfo::exists("/.flatpak-info"); +} + } // namespace chatterino diff --git a/src/common/Version.hpp b/src/common/Version.hpp index 1a858c0ea..967363358 100644 --- a/src/common/Version.hpp +++ b/src/common/Version.hpp @@ -29,6 +29,7 @@ public: const QString &dateOfBuild() const; const QString &fullVersion() const; const bool &isSupportedOS() const; + bool isFlatpak() const; private: Version(); diff --git a/src/singletons/Updates.cpp b/src/singletons/Updates.cpp index 5751b59cc..cf32b30ff 100644 --- a/src/singletons/Updates.cpp +++ b/src/singletons/Updates.cpp @@ -232,7 +232,9 @@ void Updates::installUpdates() void Updates::checkForUpdates() { - if (!Version::instance().isSupportedOS()) + auto version = Version::instance(); + + if (!version.isSupportedOS()) { qCDebug(chatterinoUpdate) << "Update checking disabled because OS doesn't appear to be one " @@ -241,7 +243,7 @@ void Updates::checkForUpdates() } // Disable updates on Flatpak - if (QFileInfo::exists("/.flatpak-info")) + if (version.isFlatpak()) { return; } diff --git a/src/util/StreamLink.cpp b/src/util/StreamLink.cpp index b631cad51..8b61ca9d7 100644 --- a/src/util/StreamLink.cpp +++ b/src/util/StreamLink.cpp @@ -10,6 +10,7 @@ #include #include #include "common/QLogging.hpp" +#include "common/Version.hpp" #include @@ -35,18 +36,6 @@ namespace { #endif } - QString getStreamlinkProgram() - { - if (getSettings()->streamlinkUseCustomPath) - { - return getSettings()->streamlinkPath + "/" + getBinaryName(); - } - else - { - return getBinaryName(); - } - } - bool checkStreamlinkPath(const QString &path) { QFileInfo fileinfo(path); @@ -83,7 +72,27 @@ namespace { QProcess *createStreamlinkProcess() { auto p = new QProcess; - p->setProgram(getStreamlinkProgram()); + + const QString path = [] { + if (getSettings()->streamlinkUseCustomPath) + { + return getSettings()->streamlinkPath + "/" + getBinaryName(); + } + else + { + return QString{getBinaryName()}; + } + }(); + + if (Version::instance().isFlatpak()) + { + p->setProgram("flatpak-spawn"); + p->setArguments({"--host", path}); + } + else + { + p->setProgram(path); + } QObject::connect(p, &QProcess::errorOccurred, [=](auto err) { if (err == QProcess::FailedToStart) @@ -165,7 +174,8 @@ void getStreamQualities(const QString &channelURL, } }); - p->setArguments({channelURL, "--default-stream=KKona"}); + p->setArguments(p->arguments() + + QStringList{channelURL, "--default-stream=KKona"}); p->start(); } @@ -173,7 +183,9 @@ void getStreamQualities(const QString &channelURL, void openStreamlink(const QString &channelURL, const QString &quality, QStringList extraArguments) { - QStringList arguments = extraArguments << channelURL << quality; + auto proc = createStreamlinkProcess(); + auto arguments = proc->arguments() + << extraArguments << channelURL << quality; // Remove empty arguments before appending additional streamlink options // as the options might purposely contain empty arguments @@ -182,7 +194,8 @@ void openStreamlink(const QString &channelURL, const QString &quality, QString additionalOptions = getSettings()->streamlinkOpts.getValue(); arguments << splitCommand(additionalOptions); - bool res = QProcess::startDetached(getStreamlinkProgram(), arguments); + proc->setArguments(std::move(arguments)); + bool res = proc->startDetached(); if (!res) { From 773c4bb9e7806c2a6881480dae111ed396c6d7d2 Mon Sep 17 00:00:00 2001 From: LosFarmosCTL <80157503+LosFarmosCTL@users.noreply.github.com> Date: Sat, 21 Aug 2021 13:37:57 +0200 Subject: [PATCH 26/34] Add optional parameter to /usercard command for opening a usercard in a different channel context. (#3172) Co-authored-by: apa420 <17131426+apa420@users.noreply.github.com> Co-authored-by: Mm2PL Co-authored-by: Leon Richardt --- CHANGELOG.md | 1 + .../commands/CommandController.cpp | 23 ++++++++++++++++++- src/widgets/dialogs/UserInfoPopup.cpp | 7 +++--- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d77657086..01c7faf31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Minor: Added a setting to hide similar messages by any user. (#2716) - Minor: Duplicate spaces now count towards the display message length. (#3002) - Minor: Commands are now backed up. (#3168) +- Minor: Added optional parameter to /usercard command for opening a usercard in a different channel context. (#3172) - Bugfix: Fixed colored usernames sometimes not working. (#3170) - Bugfix: Restored ability to send duplicate `/me` messages. (#3166) - Bugfix: Notifications for moderators about other moderators deleting messages can now be disabled. (#3121) diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 13368917e..1ca76023f 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -476,12 +476,33 @@ void CommandController::initialize(Settings &, Paths &paths) this->registerCommand("/usercard", [](const auto &words, auto channel) { if (words.size() < 2) { - channel->addMessage(makeSystemMessage("Usage /usercard [user]")); + channel->addMessage( + makeSystemMessage("Usage: /usercard [channel]")); return ""; } QString userName = words[1]; stripUserName(userName); + + if (words.size() > 2) + { + QString channelName = words[2]; + stripChannelName(channelName); + + ChannelPtr channelTemp = + getApp()->twitch2->getChannelOrEmpty(channelName); + + if (channelTemp->isEmpty()) + { + channel->addMessage(makeSystemMessage( + "A usercard can only be displayed for a channel that is " + "currently opened in Chatterino.")); + return ""; + } + + channel = channelTemp; + } + auto *userPopup = new UserInfoPopup( getSettings()->autoCloseUserPopup, static_cast(&(getApp()->windows->getMainWindow()))); diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index 706ca839d..6138962e2 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -33,7 +33,7 @@ const QString TEXT_VIEWS("Views: %1"); const QString TEXT_FOLLOWERS("Followers: %1"); const QString TEXT_CREATED("Created: %1"); -const QString TEXT_TITLE("%1's Usercard"); +const QString TEXT_TITLE("%1's Usercard - #%2"); #define TEXT_USER_ID "ID: " #define TEXT_UNAVAILABLE "(not available)" @@ -513,7 +513,7 @@ void UserInfoPopup::setData(const QString &name, const ChannelPtr &channel) { this->userName_ = name; this->channel_ = channel; - this->setWindowTitle(TEXT_TITLE.arg(name)); + this->setWindowTitle(TEXT_TITLE.arg(name, channel->getName())); this->ui_.nameLabel->setText(name); this->ui_.nameLabel->setProperty("copy-text", name); @@ -598,7 +598,8 @@ void UserInfoPopup::updateUserData() this->avatarUrl_ = user.profileImageUrl; this->ui_.nameLabel->setText(user.displayName); - this->setWindowTitle(TEXT_TITLE.arg(user.displayName)); + this->setWindowTitle( + TEXT_TITLE.arg(user.displayName, this->channel_->getName())); this->ui_.viewCountLabel->setText( TEXT_VIEWS.arg(localizeNumbers(user.viewCount))); this->ui_.createdDateLabel->setText( From ad4a0c28d132557f4128e41414b3a926de9c5c9a Mon Sep 17 00:00:00 2001 From: Tal Neoran Date: Sat, 21 Aug 2021 15:16:00 +0300 Subject: [PATCH 27/34] Add opening tab in popup (#3082) Co-authored-by: zneix Co-authored-by: Rasmus Karlsson --- CHANGELOG.md | 1 + src/common/Version.cpp | 2 + src/common/WindowDescriptors.cpp | 100 ++++++++-------- src/common/WindowDescriptors.hpp | 2 + src/singletons/WindowManager.cpp | 109 ++++++++++-------- src/singletons/WindowManager.hpp | 5 +- src/widgets/Window.cpp | 8 ++ src/widgets/helper/NotebookTab.cpp | 10 ++ .../settingspages/KeyboardSettingsPage.cpp | 2 + src/widgets/splits/SplitContainer.cpp | 28 +++++ src/widgets/splits/SplitContainer.hpp | 2 + 11 files changed, 167 insertions(+), 102 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01c7faf31..277fcd90b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Minor: Added a setting to hide similar messages by any user. (#2716) - Minor: Duplicate spaces now count towards the display message length. (#3002) - Minor: Commands are now backed up. (#3168) +- Minor: Added the ability to open an entire tab as a popup. (#3082) - Minor: Added optional parameter to /usercard command for opening a usercard in a different channel context. (#3172) - Bugfix: Fixed colored usernames sometimes not working. (#3170) - Bugfix: Restored ability to send duplicate `/me` messages. (#3166) diff --git a/src/common/Version.cpp b/src/common/Version.cpp index 67a3bb477..b678fe48a 100644 --- a/src/common/Version.cpp +++ b/src/common/Version.cpp @@ -2,6 +2,8 @@ #include "common/Modes.hpp" +#include + #define UGLYMACROHACK1(s) #s #define FROM_EXTERNAL_DEFINE(s) UGLYMACROHACK1(s) diff --git a/src/common/WindowDescriptors.cpp b/src/common/WindowDescriptors.cpp index 7a82596f3..902de665f 100644 --- a/src/common/WindowDescriptors.cpp +++ b/src/common/WindowDescriptors.cpp @@ -110,6 +110,42 @@ void SplitDescriptor::loadFromJSON(SplitDescriptor &descriptor, descriptor.filters_ = loadFilters(root.value("filters")); } +TabDescriptor TabDescriptor::loadFromJSON(const QJsonObject &tabObj) +{ + TabDescriptor tab; + // Load tab custom title + QJsonValue titleVal = tabObj.value("title"); + if (titleVal.isString()) + { + tab.customTitle_ = titleVal.toString(); + } + + // Load tab selected state + tab.selected_ = tabObj.value("selected").toBool(false); + + // Load tab "highlightsEnabled" state + tab.highlightsEnabled_ = tabObj.value("highlightsEnabled").toBool(true); + + QJsonObject splitRoot = tabObj.value("splits2").toObject(); + + // Load tab splits + if (!splitRoot.isEmpty()) + { + // root type + auto nodeType = splitRoot.value("type").toString(); + if (nodeType == "split") + { + tab.rootNode_ = loadNodes(splitRoot); + } + else if (nodeType == "horizontal" || nodeType == "vertical") + { + tab.rootNode_ = loadNodes(splitRoot); + } + } + + return tab; +} + WindowLayout WindowLayout::loadFromFile(const QString &path) { WindowLayout layout; @@ -117,15 +153,15 @@ WindowLayout WindowLayout::loadFromFile(const QString &path) bool hasSetAMainWindow = false; // "deserialize" - for (const QJsonValue &window_val : loadWindowArray(path)) + for (const QJsonValue &windowVal : loadWindowArray(path)) { - QJsonObject window_obj = window_val.toObject(); + QJsonObject windowObj = windowVal.toObject(); WindowDescriptor window; // Load window type - QString type_val = window_obj.value("type").toString(); - auto type = type_val == "main" ? WindowType::Main : WindowType::Popup; + QString typeVal = windowObj.value("type").toString(); + auto type = typeVal == "main" ? WindowType::Main : WindowType::Popup; if (type == WindowType::Main) { @@ -142,21 +178,21 @@ WindowLayout WindowLayout::loadFromFile(const QString &path) window.type_ = type; // Load window state - if (window_obj.value("state") == "minimized") + if (windowObj.value("state") == "minimized") { window.state_ = WindowDescriptor::State::Minimized; } - else if (window_obj.value("state") == "maximized") + else if (windowObj.value("state") == "maximized") { window.state_ = WindowDescriptor::State::Maximized; } // Load window geometry { - int x = window_obj.value("x").toInt(-1); - int y = window_obj.value("y").toInt(-1); - int width = window_obj.value("width").toInt(-1); - int height = window_obj.value("height").toInt(-1); + int x = windowObj.value("x").toInt(-1); + int y = windowObj.value("y").toInt(-1); + int width = windowObj.value("width").toInt(-1); + int height = windowObj.value("height").toInt(-1); window.geometry_ = QRect(x, y, width, height); } @@ -164,23 +200,10 @@ WindowLayout WindowLayout::loadFromFile(const QString &path) bool hasSetASelectedTab = false; // Load window tabs - QJsonArray tabs = window_obj.value("tabs").toArray(); - for (QJsonValue tab_val : tabs) + QJsonArray tabs = windowObj.value("tabs").toArray(); + for (QJsonValue tabVal : tabs) { - TabDescriptor tab; - - QJsonObject tab_obj = tab_val.toObject(); - - // Load tab custom title - QJsonValue title_val = tab_obj.value("title"); - if (title_val.isString()) - { - tab.customTitle_ = title_val.toString(); - } - - // Load tab selected state - tab.selected_ = tab_obj.value("selected").toBool(false); - + TabDescriptor tab = TabDescriptor::loadFromJSON(tabVal.toObject()); if (tab.selected_) { if (hasSetASelectedTab) @@ -192,34 +215,11 @@ WindowLayout WindowLayout::loadFromFile(const QString &path) } hasSetASelectedTab = true; } - - // Load tab "highlightsEnabled" state - tab.highlightsEnabled_ = - tab_obj.value("highlightsEnabled").toBool(true); - - QJsonObject splitRoot = tab_obj.value("splits2").toObject(); - - // Load tab splits - if (!splitRoot.isEmpty()) - { - // root type - auto nodeType = splitRoot.value("type").toString(); - if (nodeType == "split") - { - tab.rootNode_ = loadNodes(splitRoot); - } - else if (nodeType == "horizontal" || nodeType == "vertical") - { - tab.rootNode_ = - loadNodes(splitRoot); - } - } - window.tabs_.emplace_back(std::move(tab)); } // Load emote popup position - QJsonObject emote_popup_obj = window_obj.value("emotePopup").toObject(); + QJsonObject emote_popup_obj = windowObj.value("emotePopup").toObject(); layout.emotePopupPos_ = QPoint(emote_popup_obj.value("x").toInt(), emote_popup_obj.value("y").toInt()); diff --git a/src/common/WindowDescriptors.hpp b/src/common/WindowDescriptors.hpp index 9c2aa2888..4740bc919 100644 --- a/src/common/WindowDescriptors.hpp +++ b/src/common/WindowDescriptors.hpp @@ -67,6 +67,8 @@ struct ContainerNodeDescriptor { }; struct TabDescriptor { + static TabDescriptor loadFromJSON(const QJsonObject &root); + QString customTitle_; bool selected_{false}; bool highlightsEnabled_{true}; diff --git a/src/singletons/WindowManager.cpp b/src/singletons/WindowManager.cpp index af66ffcad..254b8b767 100644 --- a/src/singletons/WindowManager.cpp +++ b/src/singletons/WindowManager.cpp @@ -379,20 +379,20 @@ void WindowManager::save() QJsonDocument document; // "serialize" - QJsonArray window_arr; + QJsonArray windowArr; for (Window *window : this->windows_) { - QJsonObject window_obj; + QJsonObject windowObj; // window type switch (window->getType()) { case WindowType::Main: - window_obj.insert("type", "main"); + windowObj.insert("type", "main"); break; case WindowType::Popup: - window_obj.insert("type", "popup"); + windowObj.insert("type", "popup"); break; case WindowType::Attached:; @@ -400,68 +400,48 @@ void WindowManager::save() if (window->isMaximized()) { - window_obj.insert("state", "maximized"); + windowObj.insert("state", "maximized"); } else if (window->isMinimized()) { - window_obj.insert("state", "minimized"); + windowObj.insert("state", "minimized"); } // window geometry auto rect = window->getBounds(); - window_obj.insert("x", rect.x()); - window_obj.insert("y", rect.y()); - window_obj.insert("width", rect.width()); - window_obj.insert("height", rect.height()); + windowObj.insert("x", rect.x()); + windowObj.insert("y", rect.y()); + windowObj.insert("width", rect.width()); + windowObj.insert("height", rect.height()); - QJsonObject emote_popup_obj; - emote_popup_obj.insert("x", this->emotePopupPos_.x()); - emote_popup_obj.insert("y", this->emotePopupPos_.y()); - window_obj.insert("emotePopup", emote_popup_obj); + QJsonObject emotePopupObj; + emotePopupObj.insert("x", this->emotePopupPos_.x()); + emotePopupObj.insert("y", this->emotePopupPos_.y()); + windowObj.insert("emotePopup", emotePopupObj); // window tabs - QJsonArray tabs_arr; + QJsonArray tabsArr; - for (int tab_i = 0; tab_i < window->getNotebook().getPageCount(); - tab_i++) + for (int tabIndex = 0; tabIndex < window->getNotebook().getPageCount(); + tabIndex++) { - QJsonObject tab_obj; + QJsonObject tabObj; SplitContainer *tab = dynamic_cast( - window->getNotebook().getPageAt(tab_i)); + window->getNotebook().getPageAt(tabIndex)); assert(tab != nullptr); - // custom tab title - if (tab->getTab()->hasCustomTitle()) - { - tab_obj.insert("title", tab->getTab()->getCustomTitle()); - } - - // selected - if (window->getNotebook().getSelectedPage() == tab) - { - tab_obj.insert("selected", true); - } - - // highlighting on new messages - tab_obj.insert("highlightsEnabled", - tab->getTab()->hasHighlightsEnabled()); - - // splits - QJsonObject splits; - - this->encodeNodeRecursively(tab->getBaseNode(), splits); - - tab_obj.insert("splits2", splits); - tabs_arr.append(tab_obj); + bool isSelected = window->getNotebook().getSelectedPage() == tab; + WindowManager::encodeTab(tab, isSelected, tabObj); + tabsArr.append(tabObj); } - window_obj.insert("tabs", tabs_arr); - window_arr.append(window_obj); + windowObj.insert("tabs", tabsArr); + windowArr.append(windowObj); } QJsonObject obj; - obj.insert("windows", window_arr); + obj.insert("windows", windowArr); document.setObject(obj); // save file @@ -497,6 +477,32 @@ void WindowManager::queueSave() this->saveTimer->start(10s); } +void WindowManager::encodeTab(SplitContainer *tab, bool isSelected, + QJsonObject &obj) +{ + // custom tab title + if (tab->getTab()->hasCustomTitle()) + { + obj.insert("title", tab->getTab()->getCustomTitle()); + } + + // selected + if (isSelected) + { + obj.insert("selected", true); + } + + // highlighting on new messages + obj.insert("highlightsEnabled", tab->getTab()->hasHighlightsEnabled()); + + // splits + QJsonObject splits; + + WindowManager::encodeNodeRecursively(tab->getBaseNode(), splits); + + obj.insert("splits2", splits); +} + void WindowManager::encodeNodeRecursively(SplitNode *node, QJsonObject &obj) { switch (node->getType()) @@ -506,11 +512,12 @@ void WindowManager::encodeNodeRecursively(SplitNode *node, QJsonObject &obj) obj.insert("moderationMode", node->getSplit()->getModerationMode()); QJsonObject split; - encodeChannel(node->getSplit()->getIndirectChannel(), split); + WindowManager::encodeChannel(node->getSplit()->getIndirectChannel(), + split); obj.insert("data", split); QJsonArray filters; - encodeFilters(node->getSplit(), filters); + WindowManager::encodeFilters(node->getSplit(), filters); obj.insert("filters", filters); } break; @@ -520,14 +527,14 @@ void WindowManager::encodeNodeRecursively(SplitNode *node, QJsonObject &obj) ? "horizontal" : "vertical"); - QJsonArray items_arr; + QJsonArray itemsArr; for (const std::unique_ptr &n : node->getChildren()) { QJsonObject subObj; - this->encodeNodeRecursively(n.get(), subObj); - items_arr.append(subObj); + WindowManager::encodeNodeRecursively(n.get(), subObj); + itemsArr.append(subObj); } - obj.insert("items", items_arr); + obj.insert("items", itemsArr); } break; } diff --git a/src/singletons/WindowManager.hpp b/src/singletons/WindowManager.hpp index 42a6f5d51..87151d204 100644 --- a/src/singletons/WindowManager.hpp +++ b/src/singletons/WindowManager.hpp @@ -30,6 +30,8 @@ public: WindowManager(); ~WindowManager() override; + static void encodeTab(SplitContainer *tab, bool isSelected, + QJsonObject &obj); static void encodeChannel(IndirectChannel channel, QJsonObject &obj); static void encodeFilters(Split *split, QJsonArray &arr); static IndirectChannel decodeChannel(const SplitDescriptor &descriptor); @@ -99,7 +101,8 @@ public: pajlada::Signals::Signal selectSplitContainer; private: - void encodeNodeRecursively(SplitContainer::Node *node, QJsonObject &obj); + static void encodeNodeRecursively(SplitContainer::Node *node, + QJsonObject &obj); // Load window layout from the window-layout.json file WindowLayout loadWindowLayoutFromFile() const; diff --git a/src/widgets/Window.cpp b/src/widgets/Window.cpp index 932e05fd5..dea06c69c 100644 --- a/src/widgets/Window.cpp +++ b/src/widgets/Window.cpp @@ -338,6 +338,14 @@ void Window::addShortcuts() } }); + createWindowShortcut(this, "CTRL+SHIFT+N", [this] { + if (auto page = dynamic_cast( + this->notebook_->getSelectedPage())) + { + page->popup(); + } + }); + // Zoom in { auto s = new QShortcut(QKeySequence::ZoomIn, this); diff --git a/src/widgets/helper/NotebookTab.cpp b/src/widgets/helper/NotebookTab.cpp index 9fdd41db0..bb53599fc 100644 --- a/src/widgets/helper/NotebookTab.cpp +++ b/src/widgets/helper/NotebookTab.cpp @@ -64,6 +64,16 @@ NotebookTab::NotebookTab(Notebook *notebook) this->notebook_->removePage(this->page); }); + this->menu_.addAction( + "Popup Tab", + [=]() { + if (auto container = dynamic_cast(this->page)) + { + container->popup(); + } + }, + QKeySequence("Ctrl+Shift+N")); + highlightNewMessagesAction_ = new QAction("Mark Tab as Unread on New Messages", &this->menu_); highlightNewMessagesAction_->setCheckable(true); diff --git a/src/widgets/settingspages/KeyboardSettingsPage.cpp b/src/widgets/settingspages/KeyboardSettingsPage.cpp index 7cad92a5f..fcdc69a00 100644 --- a/src/widgets/settingspages/KeyboardSettingsPage.cpp +++ b/src/widgets/settingspages/KeyboardSettingsPage.cpp @@ -44,6 +44,8 @@ KeyboardSettingsPage::KeyboardSettingsPage() form->addRow(new QLabel("Ctrl + Shift + T"), new QLabel("Create new tab")); form->addRow(new QLabel("Ctrl + Shift + W"), new QLabel("Close current tab")); + form->addRow(new QLabel("Ctrl + Shift + N"), + new QLabel("Open current tab as a popup")); form->addRow(new QLabel("Ctrl + H"), new QLabel("Hide/Show similar messages (See General->R9K)")); diff --git a/src/widgets/splits/SplitContainer.cpp b/src/widgets/splits/SplitContainer.cpp index 4a04ba199..5e1543c95 100644 --- a/src/widgets/splits/SplitContainer.cpp +++ b/src/widgets/splits/SplitContainer.cpp @@ -9,6 +9,7 @@ #include "util/Helpers.hpp" #include "util/LayoutCreator.hpp" #include "widgets/Notebook.hpp" +#include "widgets/Window.hpp" #include "widgets/helper/ChannelView.hpp" #include "widgets/helper/NotebookTab.hpp" #include "widgets/splits/ClosedSplits.hpp" @@ -761,6 +762,33 @@ void SplitContainer::applyFromDescriptor(const NodeDescriptor &rootNode) this->layout(); } +void SplitContainer::popup() +{ + Window &window = getApp()->windows->createWindow(WindowType::Popup); + auto popupContainer = window.getNotebook().getOrAddSelectedPage(); + + QJsonObject encodedTab; + WindowManager::encodeTab(this, true, encodedTab); + TabDescriptor tab = TabDescriptor::loadFromJSON(encodedTab); + + // custom title + if (!tab.customTitle_.isEmpty()) + { + popupContainer->getTab()->setCustomTitle(tab.customTitle_); + } + + // highlighting on new messages + popupContainer->getTab()->setHighlightsEnabled(tab.highlightsEnabled_); + + // splits + if (tab.rootNode_) + { + popupContainer->applyFromDescriptor(*tab.rootNode_); + } + + window.show(); +} + void SplitContainer::applyFromDescriptorRecursively( const NodeDescriptor &rootNode, Node *node) { diff --git a/src/widgets/splits/SplitContainer.hpp b/src/widgets/splits/SplitContainer.hpp index bb26e43dc..1662f0b68 100644 --- a/src/widgets/splits/SplitContainer.hpp +++ b/src/widgets/splits/SplitContainer.hpp @@ -202,6 +202,8 @@ public: void applyFromDescriptor(const NodeDescriptor &rootNode); + void popup(); + protected: void paintEvent(QPaintEvent *event) override; From fe8aa33980b16adcf4ee352930f424db151ec5ed Mon Sep 17 00:00:00 2001 From: pajlada Date: Sat, 21 Aug 2021 14:41:06 +0200 Subject: [PATCH 28/34] Update Usage messages to conform to new Usage message contributor guidelines (#3180) --- CONTRIBUTING.md | 25 +++++++++++++++++++ .../commands/CommandController.cpp | 16 ++++++------ src/providers/twitch/IrcMessageHandler.cpp | 3 +-- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7135e908b..094d60bb4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -215,3 +215,28 @@ Keep the element on the stack if possible. If you need a pointer or have complex - Use the [object tree](https://doc.qt.io/qt-5/objecttrees.html#) to manage lifetimes where possible. Objects are destroyed when their parent object is destroyed. - If you have to explicitly delete an object use `variable->deleteLater()` instead of `delete variable`. This ensures that it will be deleted on the correct thread. - If an object doesn't have a parent, consider using `std::unique_ptr` with `DeleteLater` from "src/common/Common.hpp". This will call `deleteLater()` on the pointer once it goes out of scope, or the object is destroyed. + +## Conventions + +#### Usage strings + +When informing the user about how a command is supposed to be used, we aim to follow [this standard](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html) where possible. + +- Square brackets are reserved for `[optional arguments]`. +- Angle brackets are reserved for ``. +- The word _Usage_ should be capitalized and must be followed by a colon. +- If the usage deserves a description, put a dot after all parameters and explain it briefly. + +##### Good + +- `Usage: /block ` +- `Usage: /unblock . Unblocks a user.` +- `Usage: /streamlink ` +- `Usage: /usercard [channel]` + +##### Bad + +- `Usage /streamlink ` - Missing colon after _Usage_. +- `usage: /streamlink ` - _Usage_ must be capitalized. +- `Usage: /streamlink channel` - The required argument `channel` must be wrapped in angle brackets. +- `Usage: /streamlink .` - Don't put a dot after usage if it's not followed by a description. diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 1ca76023f..82beac036 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -296,7 +296,7 @@ void CommandController::initialize(Settings &, Paths &paths) auto blockLambda = [](const auto &words, auto channel) { if (words.size() < 2) { - channel->addMessage(makeSystemMessage("Usage: /block [user]")); + channel->addMessage(makeSystemMessage("Usage: /block ")); return ""; } @@ -341,7 +341,7 @@ void CommandController::initialize(Settings &, Paths &paths) auto unblockLambda = [](const auto &words, auto channel) { if (words.size() < 2) { - channel->addMessage(makeSystemMessage("Usage: /unblock [user]")); + channel->addMessage(makeSystemMessage("Usage: /unblock ")); return ""; } @@ -455,7 +455,7 @@ void CommandController::initialize(Settings &, Paths &paths) if (words.size() < 2) { channel->addMessage( - makeSystemMessage("Usage /user [user] (channel)")); + makeSystemMessage("Usage: /user [channel]")); return ""; } QString userName = words[1]; @@ -625,7 +625,7 @@ void CommandController::initialize(Settings &, Paths &paths) (!channel->isTwitchChannel() || channel->isEmpty())) { channel->addMessage(makeSystemMessage( - "Usage: /streamlink [channel]. You can also use the " + "Usage: /streamlink . You can also use the " "command without arguments in any Twitch channel to open " "it in streamlink.")); return ""; @@ -646,7 +646,7 @@ void CommandController::initialize(Settings &, Paths &paths) (!channel->isTwitchChannel() || channel->isEmpty())) { channel->addMessage(makeSystemMessage( - "Usage: /popout [channel]. You can also use the command " + "Usage: /popout . You can also use the command " "without arguments in any Twitch channel to open its " "popout chat.")); return ""; @@ -673,7 +673,7 @@ void CommandController::initialize(Settings &, Paths &paths) if (words.size() < 2) { channel->addMessage( - makeSystemMessage("Usage: /settitle .")); + makeSystemMessage("Usage: /settitle ")); return ""; } if (auto twitchChannel = dynamic_cast(channel.get())) @@ -704,7 +704,7 @@ void CommandController::initialize(Settings &, Paths &paths) if (words.size() < 2) { channel->addMessage( - makeSystemMessage("Usage: /setgame .")); + makeSystemMessage("Usage: /setgame ")); return ""; } if (auto twitchChannel = dynamic_cast(channel.get())) @@ -769,7 +769,7 @@ void CommandController::initialize(Settings &, Paths &paths) const ChannelPtr channel) { if (words.size() < 2) { - channel->addMessage(makeSystemMessage("Usage: /openurl .")); + channel->addMessage(makeSystemMessage("Usage: /openurl ")); return ""; } diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index a4ce3c513..e15362a5e 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -810,8 +810,7 @@ void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message) if (tags == "bad_delete_message_error" || tags == "usage_delete") { channel->addMessage(makeSystemMessage( - "Usage: \"/delete \" - can't take more " - "than one argument")); + "Usage: /delete . Can't take more than one argument")); } else if (tags == "host_on" || tags == "host_target_went_offline") { From e4994864188747de381e3eab149f1676c8677347 Mon Sep 17 00:00:00 2001 From: apa420 <17131426+apa420@users.noreply.github.com> Date: Sun, 22 Aug 2021 13:30:17 +0200 Subject: [PATCH 29/34] Added regex option to Nicknames (#3146) Co-authored-by: Mm2PL Co-authored-by: Rasmus Karlsson --- CHANGELOG.md | 1 + src/controllers/nicknames/Nickname.hpp | 86 +++++++++++++++++-- src/controllers/nicknames/NicknamesModel.cpp | 8 +- src/providers/twitch/TwitchMessageBuilder.cpp | 4 +- src/widgets/settingspages/NicknamesPage.cpp | 11 ++- 5 files changed, 95 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 277fcd90b..44e3e73e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Minor: Commands are now backed up. (#3168) - Minor: Added the ability to open an entire tab as a popup. (#3082) - Minor: Added optional parameter to /usercard command for opening a usercard in a different channel context. (#3172) +- Minor: Added regex option to Nicknames. (#3146) - Bugfix: Fixed colored usernames sometimes not working. (#3170) - Bugfix: Restored ability to send duplicate `/me` messages. (#3166) - Bugfix: Notifications for moderators about other moderators deleting messages can now be disabled. (#3121) diff --git a/src/controllers/nicknames/Nickname.hpp b/src/controllers/nicknames/Nickname.hpp index 67e7d3f40..3fb0b23b9 100644 --- a/src/controllers/nicknames/Nickname.hpp +++ b/src/controllers/nicknames/Nickname.hpp @@ -1,7 +1,6 @@ #pragma once #include "controllers/accounts/AccountController.hpp" - #include "util/RapidJsonSerializeQString.hpp" #include "util/RapidjsonHelpers.hpp" @@ -15,24 +14,92 @@ namespace chatterino { class Nickname { public: - Nickname(const QString &name, const QString &replace) + Nickname(const QString &name, const QString &replace, const bool isRegex, + const bool isCaseSensitive) : name_(name) , replace_(replace) + , isRegex_(isRegex) + , isCaseSensitive_(isCaseSensitive) + , caseSensitivity_(this->isCaseSensitive_ ? Qt::CaseSensitive + : Qt::CaseInsensitive) { + if (this->isRegex()) + { + this->regex_ = QRegularExpression( + name, QRegularExpression::UseUnicodePropertiesOption | + (this->isCaseSensitive() + ? QRegularExpression::NoPatternOption + : QRegularExpression::CaseInsensitiveOption)); + } } - const QString &name() const + [[nodiscard]] const QString &name() const { return this->name_; } - const QString &replace() const + + [[nodiscard]] const QString &replace() const { return this->replace_; } + [[nodiscard]] bool isRegex() const + { + return this->isRegex_; + } + + [[nodiscard]] Qt::CaseSensitivity caseSensitivity() const + { + return this->caseSensitivity_; + } + + [[nodiscard]] const bool &isCaseSensitive() const + { + return this->isCaseSensitive_; + } + + [[nodiscard]] bool match(QString &usernameText) const + { + if (this->isRegex()) + { + if (!this->regex_.isValid()) + { + return false; + } + if (this->name().isEmpty()) + { + return false; + } + + auto workingCopy = usernameText; + workingCopy.replace(this->regex_, this->replace()); + if (workingCopy != usernameText) + { + usernameText = workingCopy; + return true; + } + } + else + { + auto res = + this->name().compare(usernameText, this->caseSensitivity()); + if (res == 0) + { + usernameText = this->replace(); + return true; + } + } + + return false; + } + private: QString name_; QString replace_; + bool isRegex_; + bool isCaseSensitive_; + Qt::CaseSensitivity caseSensitivity_; + QRegularExpression regex_{}; }; } // namespace chatterino @@ -48,6 +115,8 @@ struct Serialize { chatterino::rj::set(ret, "name", value.name(), a); chatterino::rj::set(ret, "replace", value.replace(), a); + chatterino::rj::set(ret, "isRegex", value.isRegex(), a); + chatterino::rj::set(ret, "isCaseSensitive", value.isCaseSensitive(), a); return ret; } @@ -61,16 +130,21 @@ struct Deserialize { if (!value.IsObject()) { PAJLADA_REPORT_ERROR(error) - return chatterino::Nickname(QString(), QString()); + return chatterino::Nickname(QString(), QString(), false, false); } QString _name; QString _replace; + bool _isRegex; + bool _isCaseSensitive; chatterino::rj::getSafe(value, "name", _name); chatterino::rj::getSafe(value, "replace", _replace); + chatterino::rj::getSafe(value, "isRegex", _isRegex); + chatterino::rj::getSafe(value, "isCaseSensitive", _isCaseSensitive); - return chatterino::Nickname(_name, _replace); + return chatterino::Nickname(_name, _replace, _isRegex, + _isCaseSensitive); } }; diff --git a/src/controllers/nicknames/NicknamesModel.cpp b/src/controllers/nicknames/NicknamesModel.cpp index 703c6661b..2748f49b6 100644 --- a/src/controllers/nicknames/NicknamesModel.cpp +++ b/src/controllers/nicknames/NicknamesModel.cpp @@ -8,7 +8,7 @@ namespace chatterino { NicknamesModel::NicknamesModel(QObject *parent) - : SignalVectorModel(2, parent) + : SignalVectorModel(4, parent) { } @@ -17,7 +17,9 @@ Nickname NicknamesModel::getItemFromRow(std::vector &row, const Nickname &original) { return Nickname{row[0]->data(Qt::DisplayRole).toString(), - row[1]->data(Qt::DisplayRole).toString()}; + row[1]->data(Qt::DisplayRole).toString(), + row[2]->data(Qt::CheckStateRole).toBool(), + row[3]->data(Qt::CheckStateRole).toBool()}; } // turns a row in the model into a vector item @@ -26,6 +28,8 @@ void NicknamesModel::getRowFromItem(const Nickname &item, { setStringItem(row[0], item.name()); setStringItem(row[1], item.replace()); + setBoolItem(row[2], item.isRegex()); + setBoolItem(row[3], item.isCaseSensitive()); } } // namespace chatterino diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index f91f2f742..42fd676ae 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -639,13 +639,11 @@ void TwitchMessageBuilder::appendUsername() } auto nicknames = getCSettings().nicknames.readOnly(); - auto loginLower = this->message().loginName.toLower(); for (const auto &nickname : *nicknames) { - if (nickname.name().toLower() == loginLower) + if (nickname.match(usernameText)) { - usernameText = nickname.replace(); break; } } diff --git a/src/widgets/settingspages/NicknamesPage.cpp b/src/widgets/settingspages/NicknamesPage.cpp index 2a464d7c0..3a34288b2 100644 --- a/src/widgets/settingspages/NicknamesPage.cpp +++ b/src/widgets/settingspages/NicknamesPage.cpp @@ -29,19 +29,22 @@ NicknamesPage::NicknamesPage() ->initialized(&getSettings()->nicknames)) .getElement(); - view->setTitles({"Username", "Nickname"}); + view->setTitles({"Username", "Nickname", "Enable regex", "Case Sensitive"}); view->getTableView()->horizontalHeader()->setSectionResizeMode( - QHeaderView::Interactive); + QHeaderView::Fixed); + view->getTableView()->horizontalHeader()->setSectionResizeMode( + 0, QHeaderView::Stretch); view->getTableView()->horizontalHeader()->setSectionResizeMode( 1, QHeaderView::Stretch); view->addButtonPressed.connect([] { - getSettings()->nicknames.append(Nickname{"Username", "Nickname"}); + getSettings()->nicknames.append( + Nickname{"Username", "Nickname", false, false}); }); QTimer::singleShot(1, [view] { view->getTableView()->resizeColumnsToContents(); - view->getTableView()->setColumnWidth(0, 250); + view->getTableView()->setColumnWidth(0, 200); }); } From c156094d708a3334fb11f66f8e73672ef06750e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82?= Date: Mon, 23 Aug 2021 14:03:38 +0200 Subject: [PATCH 30/34] Fixed away command in IRC (#3190) --- src/providers/irc/IrcCommands.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/irc/IrcCommands.cpp b/src/providers/irc/IrcCommands.cpp index 4fbaeb59d..e6f5e2382 100644 --- a/src/providers/irc/IrcCommands.cpp +++ b/src/providers/irc/IrcCommands.cpp @@ -47,7 +47,7 @@ Outcome invokeIrcCommand(const QString &commandName, const QString &allParams, } else if (cmd == "away") { - sendRaw("AWAY" + params[0] + " :" + paramsAfter(0)); + sendRaw("AWAY " + params[0] + " :" + paramsAfter(0)); } else if (cmd == "knock") { From d0338b853d200c0a8427ea917411751a95ea99ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82?= Date: Wed, 25 Aug 2021 20:04:44 +0200 Subject: [PATCH 31/34] Updated OpenSSL 1.1 link in building docs (#3198) --- BUILDING_ON_WINDOWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILDING_ON_WINDOWS.md b/BUILDING_ON_WINDOWS.md index a075761f8..da19515ed 100644 --- a/BUILDING_ON_WINDOWS.md +++ b/BUILDING_ON_WINDOWS.md @@ -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.1k`: **[Download](https://slproweb.com/download/Win64OpenSSL-1_1_1k.exe)** +1. Download OpenSSL for windows, version `1.1.1l`: **[Download](https://slproweb.com/download/Win64OpenSSL-1_1_1L.exe)** 2. When prompted, install OpenSSL to `C:\local\openssl` 3. When prompted, copy the OpenSSL DLLs to "The OpenSSL binaries (/bin) directory". From 25cdeb8d9f52f5939c01e2328086663244a1a6cd Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sun, 29 Aug 2021 13:35:19 +0200 Subject: [PATCH 32/34] Make Chatterino colorize usernames on IRC (#3206) --- CHANGELOG.md | 1 + src/providers/irc/IrcMessageBuilder.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44e3e73e1..64a81ee1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Minor: Added the ability to open an entire tab as a popup. (#3082) - Minor: Added optional parameter to /usercard command for opening a usercard in a different channel context. (#3172) - Minor: Added regex option to Nicknames. (#3146) +- Minor: Colorizing usernames on IRC, originally made for Mm2PL/dankerino (#3206) - Bugfix: Fixed colored usernames sometimes not working. (#3170) - Bugfix: Restored ability to send duplicate `/me` messages. (#3166) - Bugfix: Notifications for moderators about other moderators deleting messages can now be disabled. (#3121) diff --git a/src/providers/irc/IrcMessageBuilder.cpp b/src/providers/irc/IrcMessageBuilder.cpp index fab99afb1..574e2aca9 100644 --- a/src/providers/irc/IrcMessageBuilder.cpp +++ b/src/providers/irc/IrcMessageBuilder.cpp @@ -11,6 +11,7 @@ #include "singletons/Settings.hpp" #include "singletons/Theme.hpp" #include "singletons/WindowManager.hpp" +#include "util/Helpers.hpp" #include "util/IrcHelpers.hpp" #include "widgets/Window.hpp" @@ -36,6 +37,7 @@ MessagePtr IrcMessageBuilder::build() { // PARSE this->parse(); + this->usernameColor_ = getRandomColor(this->ircMessage->nick()); // PUSH ELEMENTS this->appendChannelName(); From cd2923c52a4b9cddf2f259435a0f813c997fcbaf Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sun, 29 Aug 2021 14:05:45 +0200 Subject: [PATCH 33/34] Fix missing information in locally-generated IRC messages (#3203) Co-authored-by: pajlada --- CHANGELOG.md | 1 + src/providers/irc/IrcChannel2.cpp | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64a81ee1d..1c5b2d992 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Bugfix: Moderation mode and active filters are now preserved when opening a split as a popup. (#3113, #3130) - Bugfix: Fixed a bug that caused all badge highlights to use the same color. (#3132, #3134) - Bugfix: Allow starting Streamlink from Chatterino when running as a Flatpak. (#3178) +- Bugfix: Fixed own IRC messages not having metadata and a link to a usercard. (#3203) - Dev: Renamed CMake's build option `USE_SYSTEM_QT5KEYCHAIN` to `USE_SYSTEM_QTKEYCHAIN`. (#3103) - Dev: Add benchmarks that can be compiled with the `BUILD_BENCHMARKS` CMake flag. Off by default. (#3038) diff --git a/src/providers/irc/IrcChannel2.cpp b/src/providers/irc/IrcChannel2.cpp index b7b17fc72..940a74410 100644 --- a/src/providers/irc/IrcChannel2.cpp +++ b/src/providers/irc/IrcChannel2.cpp @@ -1,6 +1,7 @@ #include "IrcChannel2.hpp" #include "debug/AssertInGuiThread.hpp" +#include "messages/Message.hpp" #include "messages/MessageBuilder.hpp" #include "providers/irc/IrcCommands.hpp" #include "providers/irc/IrcServer.hpp" @@ -33,9 +34,14 @@ void IrcChannel::sendMessage(const QString &message) MessageBuilder builder; builder.emplace(); - builder.emplace(this->server()->nick() + ":", - MessageElementFlag::Username); + const auto &nick = this->server()->nick(); + builder.emplace(nick + ":", MessageElementFlag::Username) + ->setLink({Link::UserInfo, nick}); builder.emplace(message, MessageElementFlag::Text); + builder.message().messageText = message; + builder.message().searchText = nick + ": " + message; + builder.message().loginName = nick; + builder.message().displayName = nick; this->addMessage(builder.release()); } } From 5f8106e9fc6d971f2c1a9c5723da8403da51e2a5 Mon Sep 17 00:00:00 2001 From: Felanbird <41973452+Felanbird@users.noreply.github.com> Date: Sun, 29 Aug 2021 09:06:07 -0400 Subject: [PATCH 34/34] Normalize usages of 'case-sensitive' (#3188) Co-authored-by: pajlada --- src/widgets/settingspages/IgnoresPage.cpp | 2 +- src/widgets/settingspages/NicknamesPage.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widgets/settingspages/IgnoresPage.cpp b/src/widgets/settingspages/IgnoresPage.cpp index ccb494d17..77b27d372 100644 --- a/src/widgets/settingspages/IgnoresPage.cpp +++ b/src/widgets/settingspages/IgnoresPage.cpp @@ -49,7 +49,7 @@ void addPhrasesTab(LayoutCreator layout) ->initialized(&getSettings()->ignoredMessages)) .getElement(); view->setTitles( - {"Pattern", "Regex", "Case Sensitive", "Block", "Replacement"}); + {"Pattern", "Regex", "Case-sensitive", "Block", "Replacement"}); view->getTableView()->horizontalHeader()->setSectionResizeMode( QHeaderView::Fixed); view->getTableView()->horizontalHeader()->setSectionResizeMode( diff --git a/src/widgets/settingspages/NicknamesPage.cpp b/src/widgets/settingspages/NicknamesPage.cpp index 3a34288b2..d328ac4bf 100644 --- a/src/widgets/settingspages/NicknamesPage.cpp +++ b/src/widgets/settingspages/NicknamesPage.cpp @@ -29,7 +29,7 @@ NicknamesPage::NicknamesPage() ->initialized(&getSettings()->nicknames)) .getElement(); - view->setTitles({"Username", "Nickname", "Enable regex", "Case Sensitive"}); + view->setTitles({"Username", "Nickname", "Enable regex", "Case-sensitive"}); view->getTableView()->horizontalHeader()->setSectionResizeMode( QHeaderView::Fixed); view->getTableView()->horizontalHeader()->setSectionResizeMode(