From db060d5b5a545fb557b83a800581043a521a2dc7 Mon Sep 17 00:00:00 2001 From: dp <107190374+devPoland@users.noreply.github.com> Date: Sun, 16 Nov 2025 15:12:43 +0100 Subject: [PATCH] feat: seperate watchstreak highlights from sub highlights (#6571) Co-authored-by: pajlada --- CHANGELOG.md | 1 + src/controllers/highlights/HighlightModel.cpp | 28 ++++++++++++++++ src/controllers/highlights/HighlightModel.hpp | 1 + .../highlights/HighlightPhrase.cpp | 1 + .../highlights/HighlightPhrase.hpp | 1 + src/messages/Message.cpp | 8 +++++ src/messages/MessageFlag.hpp | 1 + src/messages/layouts/MessageLayout.cpp | 6 ++++ src/messages/layouts/MessageLayoutContext.cpp | 6 ++++ src/messages/layouts/MessageLayoutContext.hpp | 1 + src/providers/colors/ColorProvider.cpp | 4 +++ src/providers/colors/ColorProvider.hpp | 1 + src/providers/twitch/IrcMessageHandler.cpp | 33 ++++++++++++++++--- src/providers/twitch/IrcMessageHandler.hpp | 3 +- src/singletons/Settings.hpp | 5 +++ 15 files changed, 94 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b676145..33eb30a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unversioned +- Minor: Add a separate highlight option for watchstreak notifications. (#6571) - Minor: Badges now link to their home page like emotes in the context menu. (#6437) - Minor: Fixed usercard resizing improperly without recent messages. (#6496) - Minor: Added setting for character limit of deleted messages. (#6491) diff --git a/src/controllers/highlights/HighlightModel.cpp b/src/controllers/highlights/HighlightModel.cpp index 52f67937..dcb75641 100644 --- a/src/controllers/highlights/HighlightModel.cpp +++ b/src/controllers/highlights/HighlightModel.cpp @@ -259,6 +259,24 @@ void HighlightModel::afterInit() setColorItem(automodRow[Column::Color], *automodColor, false); this->insertCustomRow(automodRow, HighlightRowIndexes::AutomodRow); + + std::vector watchStreakRow = this->createRow(); + setBoolItem(watchStreakRow[Column::Pattern], + getSettings()->enableWatchStreakHighlight.getValue(), true, + false); + watchStreakRow[Column::Pattern]->setData("Watch Streaks", Qt::DisplayRole); + watchStreakRow[Column::ShowInMentions]->setFlags({}); + watchStreakRow[Column::FlashTaskbar]->setFlags({}); + watchStreakRow[Column::PlaySound]->setFlags({}); + watchStreakRow[Column::UseRegex]->setFlags({}); + watchStreakRow[Column::CaseSensitive]->setFlags({}); + watchStreakRow[Column::SoundPath]->setFlags(Qt::NoItemFlags); + + auto watchStreakColor = + ColorProvider::instance().color(ColorType::WatchStreak); + setColorItem(watchStreakRow[Column::Color], *watchStreakColor, false); + + this->insertCustomRow(watchStreakRow, HighlightRowIndexes::WatchStreakRow); } void HighlightModel::customRowSetData(const std::vector &row, @@ -283,6 +301,11 @@ void HighlightModel::customRowSetData(const std::vector &row, { getSettings()->enableSubHighlight.setValue(value.toBool()); } + else if (rowIndex == HighlightRowIndexes::WatchStreakRow) + { + getSettings()->enableWatchStreakHighlight.setValue( + value.toBool()); + } else if (rowIndex == HighlightRowIndexes::RedeemedRow) { getSettings()->enableRedeemedHighlight.setValue( @@ -489,6 +512,11 @@ void HighlightModel::customRowSetData(const std::vector &row, setColor(getSettings()->subHighlightColor, ColorType::Subscription); } + else if (rowIndex == HighlightRowIndexes::WatchStreakRow) + { + setColor(getSettings()->watchStreakHighlightColor, + ColorType::WatchStreak); + } else if (rowIndex == HighlightRowIndexes::RedeemedRow) { setColor(getSettings()->redeemedHighlightColor, diff --git a/src/controllers/highlights/HighlightModel.hpp b/src/controllers/highlights/HighlightModel.hpp index be807d33..99e70c2b 100644 --- a/src/controllers/highlights/HighlightModel.hpp +++ b/src/controllers/highlights/HighlightModel.hpp @@ -35,6 +35,7 @@ public: ElevatedMessageRow = 5, ThreadMessageRow = 6, AutomodRow = 7, + WatchStreakRow = 8, }; enum UserHighlightRowIndexes { diff --git a/src/controllers/highlights/HighlightPhrase.cpp b/src/controllers/highlights/HighlightPhrase.cpp index 33f8ded1..81679bd0 100644 --- a/src/controllers/highlights/HighlightPhrase.cpp +++ b/src/controllers/highlights/HighlightPhrase.cpp @@ -24,6 +24,7 @@ QColor HighlightPhrase::FALLBACK_THREAD_HIGHLIGHT_COLOR = QColor(143, 48, 24, 60); QColor HighlightPhrase::FALLBACK_SUB_COLOR = QColor(196, 102, 255, 100); QColor HighlightPhrase::FALLBACK_AUTOMOD_HIGHLIGHT_COLOR = QColor(64, 64, 64); +QColor HighlightPhrase::FALLBACK_WATCH_STREAK_COLOR = QColor(0, 130, 255, 70); bool HighlightPhrase::operator==(const HighlightPhrase &other) const { diff --git a/src/controllers/highlights/HighlightPhrase.hpp b/src/controllers/highlights/HighlightPhrase.hpp index 8f130665..48d3dd41 100644 --- a/src/controllers/highlights/HighlightPhrase.hpp +++ b/src/controllers/highlights/HighlightPhrase.hpp @@ -83,6 +83,7 @@ public: static QColor FALLBACK_SELF_MESSAGE_HIGHLIGHT_COLOR; static QColor FALLBACK_REDEEMED_HIGHLIGHT_COLOR; static QColor FALLBACK_SUB_COLOR; + static QColor FALLBACK_WATCH_STREAK_COLOR; static QColor FALLBACK_FIRST_MESSAGE_HIGHLIGHT_COLOR; static QColor FALLBACK_ELEVATED_MESSAGE_HIGHLIGHT_COLOR; static QColor FALLBACK_THREAD_HIGHLIGHT_COLOR; diff --git a/src/messages/Message.cpp b/src/messages/Message.cpp index da1c8034..3cc81889 100644 --- a/src/messages/Message.cpp +++ b/src/messages/Message.cpp @@ -39,6 +39,14 @@ ScrollbarHighlight Message::getScrollBarHighlight() const }; } + if (this->flags.has(MessageFlag::WatchStreak) && + getSettings()->enableWatchStreakHighlight) + { + return { + ColorProvider::instance().color(ColorType::WatchStreak), + }; + } + if (this->flags.has(MessageFlag::Subscription) && getSettings()->enableSubHighlight) { diff --git a/src/messages/MessageFlag.hpp b/src/messages/MessageFlag.hpp index 12609360..7a8283fe 100644 --- a/src/messages/MessageFlag.hpp +++ b/src/messages/MessageFlag.hpp @@ -72,6 +72,7 @@ enum class MessageFlag : std::int64_t { /// - message was deleted via chat clear user messages (IRC: CLEARCHAT(user), EVENTSUB: channel.chat.clear_user_messages) /// Note: If this message is inside a reply thread, the root must not have the flag either. InvalidReplyTarget = (1LL << 42), + WatchStreak = (1LL << 43), }; using MessageFlags = FlagsEnum; diff --git a/src/messages/layouts/MessageLayout.cpp b/src/messages/layouts/MessageLayout.cpp index 9807c4c7..66110f5b 100644 --- a/src/messages/layouts/MessageLayout.cpp +++ b/src/messages/layouts/MessageLayout.cpp @@ -396,6 +396,12 @@ void MessageLayout::updateBuffer(QPixmap *buffer, backgroundColor, *ctx.colorProvider.color(ColorType::FirstMessageHighlight)); } + else if (this->message_->flags.has(MessageFlag::WatchStreak) && + ctx.preferences.enableWatchStreakHighlight) + { + backgroundColor = blendColors( + backgroundColor, *ctx.colorProvider.color(ColorType::WatchStreak)); + } else if ((this->message_->flags.has(MessageFlag::Highlighted) || this->message_->flags.has(MessageFlag::HighlightedWhisper)) && !this->flags.has(MessageLayoutFlag::IgnoreHighlights)) diff --git a/src/messages/layouts/MessageLayoutContext.cpp b/src/messages/layouts/MessageLayoutContext.cpp index 650892c3..67d052da 100644 --- a/src/messages/layouts/MessageLayoutContext.cpp +++ b/src/messages/layouts/MessageLayoutContext.cpp @@ -70,6 +70,12 @@ void MessagePreferences::connectSettings(Settings *settings, }, holder); + settings->enableWatchStreakHighlight.connect( + [this](const auto &newValue) { + this->enableWatchStreakHighlight = newValue; + }, + holder); + settings->enableAutomodHighlight.connect( [this](const auto &newValue) { this->enableAutomodHighlight = newValue; diff --git a/src/messages/layouts/MessageLayoutContext.hpp b/src/messages/layouts/MessageLayoutContext.hpp index c8088f89..5577dfb2 100644 --- a/src/messages/layouts/MessageLayoutContext.hpp +++ b/src/messages/layouts/MessageLayoutContext.hpp @@ -50,6 +50,7 @@ struct MessagePreferences { bool enableElevatedMessageHighlight{}; bool enableFirstMessageHighlight{}; bool enableSubHighlight{}; + bool enableWatchStreakHighlight{}; bool enableAutomodHighlight{}; bool alternateMessages{}; diff --git a/src/providers/colors/ColorProvider.cpp b/src/providers/colors/ColorProvider.cpp index 017dd0d8..d8bef8eb 100644 --- a/src/providers/colors/ColorProvider.cpp +++ b/src/providers/colors/ColorProvider.cpp @@ -46,6 +46,7 @@ QSet ColorProvider::recentColors() const // Insert preset highlight colors retVal.insert(*this->color(ColorType::SelfHighlight)); retVal.insert(*this->color(ColorType::Subscription)); + retVal.insert(*this->color(ColorType::WatchStreak)); retVal.insert(*this->color(ColorType::Whisper)); return retVal; @@ -110,6 +111,9 @@ void ColorProvider::initTypeColorMap() initColor(ColorType::Subscription, getSettings()->subHighlightColor, HighlightPhrase::FALLBACK_SUB_COLOR); + initColor(ColorType::WatchStreak, getSettings()->watchStreakHighlightColor, + HighlightPhrase::FALLBACK_WATCH_STREAK_COLOR); + initColor(ColorType::Whisper, getSettings()->whisperHighlightColor, HighlightPhrase::FALLBACK_HIGHLIGHT_COLOR); diff --git a/src/providers/colors/ColorProvider.hpp b/src/providers/colors/ColorProvider.hpp index c35da054..f5cdd336 100644 --- a/src/providers/colors/ColorProvider.hpp +++ b/src/providers/colors/ColorProvider.hpp @@ -13,6 +13,7 @@ enum class ColorType { Subscription, Whisper, RedeemedHighlight, + WatchStreak, FirstMessageHighlight, ElevatedMessageHighlight, ThreadMessageHighlight, diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index 8d741fa2..27b2a326 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -720,7 +720,7 @@ void IrcMessageHandler::parseUserNoticeMessageInto(Communi::IrcMessage *message, if (!content.isEmpty()) { addMessage(message, sink, channel, content, *getApp()->getTwitch(), - true, false); + true, false, msgType); } } @@ -783,7 +783,15 @@ void IrcMessageHandler::parseUserNoticeMessageInto(Communi::IrcMessage *message, parseTagString(messageText), tags, calculateMessageTime(message).time(), channel); - msg->flags.set(MessageFlag::Subscription); + if (msgType == "viewermilestone") + { + msg->flags.set(MessageFlag::WatchStreak); + } + else + { + msg->flags.set(MessageFlag::Subscription); + } + if (mirrored) { msg->flags.set(MessageFlag::SharedMessage); @@ -852,7 +860,15 @@ void IrcMessageHandler::parseUserNoticeMessageInto(Communi::IrcMessage *message, parseTagString(messageText), login, displayName, userColor, calculateMessageTime(message).time()); - msg->flags.set(MessageFlag::Subscription); + if (msgType == "viewermilestone") + { + msg->flags.set(MessageFlag::WatchStreak); + } + else + { + msg->flags.set(MessageFlag::Subscription); + } + if (mirrored) { msg->flags.set(MessageFlag::SharedMessage); @@ -1014,7 +1030,7 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *message, MessageSink &sink, TwitchChannel *chan, const QString &originalContent, ITwitchIrcServer &twitch, bool isSub, - bool isAction) + bool isAction, const QString &msgType) { assert(chan); @@ -1138,7 +1154,14 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *message, { if (isSub) { - msg->flags.set(MessageFlag::Subscription); + if (msgType == "viewermilestone") + { + msg->flags.set(MessageFlag::WatchStreak); + } + else + { + msg->flags.set(MessageFlag::Subscription); + } if (tags.value("msg-id") != "announcement") { diff --git a/src/providers/twitch/IrcMessageHandler.hpp b/src/providers/twitch/IrcMessageHandler.hpp index 60fcacd4..d81f282a 100644 --- a/src/providers/twitch/IrcMessageHandler.hpp +++ b/src/providers/twitch/IrcMessageHandler.hpp @@ -61,7 +61,8 @@ public: static void addMessage(Communi::IrcMessage *message, MessageSink &sink, TwitchChannel *chan, const QString &originalContent, - ITwitchIrcServer &twitch, bool isSub, bool isAction); + ITwitchIrcServer &twitch, bool isSub, bool isAction, + const QString &msgType = ""); private: static float similarity(const MessagePtr &msg, diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index e61ddcc9..79db70ee 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -526,6 +526,11 @@ public: ""}; QStringSetting subHighlightColor = {"/highlighting/subHighlightColor", ""}; + BoolSetting enableWatchStreakHighlight = { + "/highlighting/watchStreak/enabled", true}; + QStringSetting watchStreakHighlightColor = { + "/highlighting/watchStreak/color", ""}; + BoolSetting enableAutomodHighlight = { "/highlighting/automod/enabled", true,