From 4e2da540d26cb7a2ea1dba91063d0e5b144540dd Mon Sep 17 00:00:00 2001 From: nerix Date: Sat, 8 Oct 2022 16:25:32 +0200 Subject: [PATCH] refactor: Remove Leading Mention in Replies and Highlight Participated Threads (#4047) --- CHANGELOG.md | 2 +- .../highlights/HighlightController.cpp | 66 ++++++++++++-- .../highlights/HighlightController.hpp | 6 +- src/controllers/highlights/HighlightModel.cpp | 62 +++++++++++++ src/controllers/highlights/HighlightModel.hpp | 1 + .../highlights/HighlightPhrase.cpp | 2 + .../highlights/HighlightPhrase.hpp | 1 + src/messages/Message.hpp | 1 + src/messages/MessageThread.cpp | 10 +++ src/messages/MessageThread.hpp | 5 ++ src/messages/SharedMessageBuilder.cpp | 3 +- src/providers/colors/ColorProvider.cpp | 14 +++ src/providers/colors/ColorProvider.hpp | 1 + src/providers/twitch/IrcMessageHandler.cpp | 87 +++++++++++++++++-- src/providers/twitch/TwitchMessageBuilder.cpp | 11 ++- src/providers/twitch/TwitchMessageBuilder.hpp | 13 +++ src/singletons/Settings.hpp | 14 +++ src/widgets/settingspages/GeneralPage.cpp | 1 + tests/src/HighlightController.cpp | 6 +- 19 files changed, 286 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fb9ed70..a1050b3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unversioned -- Major: Added support for Twitch's Chat Replies. [Wiki Page](https://wiki.chatterino.com/Features/#message-replies) (#3722, #3989, #4041) +- Major: Added support for Twitch's Chat Replies. [Wiki Page](https://wiki.chatterino.com/Features/#message-replies) (#3722, #3989, #4041, #4047) - Major: Added multi-channel searching to search dialog via keyboard shortcut. (Ctrl+Shift+F by default) (#3694, #3875) - Minor: Added highlights for `Elevated Messages`. (#4016) - Minor: Removed total views from the usercard, as Twitch no longer updates the number. (#3792) diff --git a/src/controllers/highlights/HighlightController.cpp b/src/controllers/highlights/HighlightController.cpp index a01e5439..912274da 100644 --- a/src/controllers/highlights/HighlightController.cpp +++ b/src/controllers/highlights/HighlightController.cpp @@ -11,10 +11,12 @@ auto highlightPhraseCheck(const HighlightPhrase &highlight) -> HighlightCheck return HighlightCheck{ [highlight](const auto &args, const auto &badges, const auto &senderName, const auto &originalMessage, + const auto &flags, const auto self) -> boost::optional { (void)args; // unused (void)badges; // unused (void)senderName; // unused + (void)flags; // unused if (self) { @@ -60,11 +62,12 @@ void rebuildSubscriptionHighlights(Settings &settings, checks.emplace_back(HighlightCheck{ [=](const auto &args, const auto &badges, const auto &senderName, - const auto &originalMessage, + const auto &originalMessage, const auto &flags, const auto self) -> boost::optional { (void)badges; // unused (void)senderName; // unused (void)originalMessage; // unused + (void)flags; // unused (void)self; // unused if (!args.isSubscriptionMessage) @@ -105,11 +108,12 @@ void rebuildWhisperHighlights(Settings &settings, checks.emplace_back(HighlightCheck{ [=](const auto &args, const auto &badges, const auto &senderName, - const auto &originalMessage, + const auto &originalMessage, const auto &flags, const auto self) -> boost::optional { (void)badges; // unused (void)senderName; // unused (void)originalMessage; // unused + (void)flags; // unused (void)self; // unused if (!args.isReceivedWhisper) @@ -128,6 +132,44 @@ void rebuildWhisperHighlights(Settings &settings, } } +void rebuildReplyThreadHighlight(Settings &settings, + std::vector &checks) +{ + if (settings.enableThreadHighlight) + { + auto highlightSound = settings.enableThreadHighlightSound.getValue(); + auto highlightAlert = settings.enableThreadHighlightTaskbar.getValue(); + auto highlightSoundUrlValue = + settings.threadHighlightSoundUrl.getValue(); + boost::optional highlightSoundUrl; + if (!highlightSoundUrlValue.isEmpty()) + { + highlightSoundUrl = highlightSoundUrlValue; + } + auto highlightInMentions = + settings.showThreadHighlightInMentions.getValue(); + checks.emplace_back(HighlightCheck{ + [=](const auto & /*args*/, const auto & /*badges*/, + const auto & /*senderName*/, const auto & /*originalMessage*/, + const auto &flags, + const auto self) -> boost::optional { + if (flags.has(MessageFlag::ParticipatedThread) && !self) + { + return HighlightResult{ + highlightAlert, + highlightSound, + highlightSoundUrl, + ColorProvider::instance().color( + ColorType::ThreadMessageHighlight), + highlightInMentions, + }; + } + + return boost::none; + }}); + } +} + void rebuildMessageHighlights(Settings &settings, std::vector &checks) { @@ -163,10 +205,12 @@ void rebuildUserHighlights(Settings &settings, checks.emplace_back(HighlightCheck{ [highlight](const auto &args, const auto &badges, const auto &senderName, const auto &originalMessage, + const auto &flags, const auto self) -> boost::optional { (void)args; // unused (void)badges; // unused (void)originalMessage; // unused + (void)flags; // unused (void)self; // unused if (!highlight.isMatch(senderName)) @@ -201,10 +245,12 @@ void rebuildBadgeHighlights(Settings &settings, checks.emplace_back(HighlightCheck{ [highlight](const auto &args, const auto &badges, const auto &senderName, const auto &originalMessage, + const auto &flags, const auto self) -> boost::optional { (void)args; // unused (void)senderName; // unused (void)originalMessage; // unused + (void)flags; // unused (void)self; // unused for (const Badge &badge : badges) @@ -247,6 +293,11 @@ void HighlightController::initialize(Settings &settings, Paths & /*paths*/) this->rebuildListener_.addSetting(settings.enableSubHighlight); this->rebuildListener_.addSetting(settings.enableSubHighlightSound); this->rebuildListener_.addSetting(settings.enableSubHighlightTaskbar); + this->rebuildListener_.addSetting(settings.enableThreadHighlight); + this->rebuildListener_.addSetting(settings.enableThreadHighlightSound); + this->rebuildListener_.addSetting(settings.enableThreadHighlightTaskbar); + this->rebuildListener_.addSetting(settings.threadHighlightSoundUrl); + this->rebuildListener_.addSetting(settings.showThreadHighlightInMentions); this->rebuildListener_.setCB([this, &settings] { qCDebug(chatterinoHighlights) @@ -294,7 +345,7 @@ void HighlightController::rebuildChecks(Settings &settings) checks->clear(); // CURRENT ORDER: - // Subscription -> Whisper -> User -> Message -> Badge + // Subscription -> Whisper -> User -> Message -> Reply Threads -> Badge rebuildSubscriptionHighlights(settings, *checks); @@ -304,12 +355,15 @@ void HighlightController::rebuildChecks(Settings &settings) rebuildMessageHighlights(settings, *checks); + rebuildReplyThreadHighlight(settings, *checks); + rebuildBadgeHighlights(settings, *checks); } std::pair HighlightController::check( const MessageParseArgs &args, const std::vector &badges, - const QString &senderName, const QString &originalMessage) const + const QString &senderName, const QString &originalMessage, + const MessageFlags &messageFlags) const { bool highlighted = false; auto result = HighlightResult::emptyResult(); @@ -322,8 +376,8 @@ std::pair HighlightController::check( for (const auto &check : *checks) { - if (auto checkResult = - check.cb(args, badges, senderName, originalMessage, self); + if (auto checkResult = check.cb(args, badges, senderName, + originalMessage, messageFlags, self); checkResult) { highlighted = true; diff --git a/src/controllers/highlights/HighlightController.hpp b/src/controllers/highlights/HighlightController.hpp index 0a1ecb2a..ad7cc7ce 100644 --- a/src/controllers/highlights/HighlightController.hpp +++ b/src/controllers/highlights/HighlightController.hpp @@ -142,7 +142,8 @@ struct HighlightResult { struct HighlightCheck { using Checker = std::function( const MessageParseArgs &args, const std::vector &badges, - const QString &senderName, const QString &originalMessage, bool self)>; + const QString &senderName, const QString &originalMessage, + const MessageFlags &messageFlags, bool self)>; Checker cb; }; @@ -156,7 +157,8 @@ public: **/ [[nodiscard]] std::pair check( const MessageParseArgs &args, const std::vector &badges, - const QString &senderName, const QString &originalMessage) const; + const QString &senderName, const QString &originalMessage, + const MessageFlags &messageFlags) const; private: /** diff --git a/src/controllers/highlights/HighlightModel.cpp b/src/controllers/highlights/HighlightModel.cpp index 3b5b30cc..fdeb9acf 100644 --- a/src/controllers/highlights/HighlightModel.cpp +++ b/src/controllers/highlights/HighlightModel.cpp @@ -213,6 +213,36 @@ void HighlightModel::afterInit() this->insertCustomRow(elevatedMessageRow, HighlightRowIndexes::ElevatedMessageRow); + + // Highlight settings for reply threads + std::vector threadMessageRow = this->createRow(); + setBoolItem(threadMessageRow[Column::Pattern], + getSettings()->enableThreadHighlight.getValue(), true, false); + threadMessageRow[Column::Pattern]->setData("Participated Reply Threads", + Qt::DisplayRole); + setBoolItem(threadMessageRow[Column::ShowInMentions], + getSettings()->showThreadHighlightInMentions.getValue(), true, + false); + setBoolItem(threadMessageRow[Column::FlashTaskbar], + getSettings()->enableThreadHighlightTaskbar.getValue(), true, + false); + setBoolItem(threadMessageRow[Column::PlaySound], + getSettings()->enableThreadHighlightSound.getValue(), true, + false); + threadMessageRow[Column::UseRegex]->setFlags({}); + threadMessageRow[Column::CaseSensitive]->setFlags({}); + + QUrl threadMessageSound = + QUrl(getSettings()->threadHighlightSoundUrl.getValue()); + setFilePathItem(threadMessageRow[Column::SoundPath], threadMessageSound, + false); + + auto threadMessageColor = + ColorProvider::instance().color(ColorType::ThreadMessageHighlight); + setColorItem(threadMessageRow[Column::Color], *threadMessageColor, false); + + this->insertCustomRow(threadMessageRow, + HighlightRowIndexes::ThreadMessageRow); } void HighlightModel::customRowSetData(const std::vector &row, @@ -252,6 +282,11 @@ void HighlightModel::customRowSetData(const std::vector &row, getSettings()->enableElevatedMessageHighlight.setValue( value.toBool()); } + else if (rowIndex == HighlightRowIndexes::ThreadMessageRow) + { + getSettings()->enableThreadHighlight.setValue( + value.toBool()); + } } } break; @@ -263,6 +298,11 @@ void HighlightModel::customRowSetData(const std::vector &row, getSettings()->showSelfHighlightInMentions.setValue( value.toBool()); } + else if (rowIndex == HighlightRowIndexes::ThreadMessageRow) + { + getSettings()->showThreadHighlightInMentions.setValue( + value.toBool()); + } } } break; @@ -300,6 +340,11 @@ void HighlightModel::customRowSetData(const std::vector &row, // ->enableElevatedMessageHighlightTaskbar.setvalue( // value.toBool()); } + else if (rowIndex == HighlightRowIndexes::ThreadMessageRow) + { + getSettings()->enableThreadHighlightTaskbar.setValue( + value.toBool()); + } } } break; @@ -336,6 +381,11 @@ void HighlightModel::customRowSetData(const std::vector &row, // getSettings()->enableElevatedMessageHighlightSound.setValue( // value.toBool()); } + else if (rowIndex == HighlightRowIndexes::ThreadMessageRow) + { + getSettings()->enableThreadHighlightSound.setValue( + value.toBool()); + } } } break; @@ -381,6 +431,11 @@ void HighlightModel::customRowSetData(const std::vector &row, getSettings()->elevatedMessageHighlightSoundUrl.setValue( value.toString()); } + else if (rowIndex == HighlightRowIndexes::ThreadMessageRow) + { + getSettings()->threadHighlightSoundUrl.setValue( + value.toString()); + } } } break; @@ -424,6 +479,13 @@ void HighlightModel::customRowSetData(const std::vector &row, .updateColor(ColorType::ElevatedMessageHighlight, QColor(colorName)); } + else if (rowIndex == HighlightRowIndexes::ThreadMessageRow) + { + getSettings()->threadHighlightColor.setValue(colorName); + const_cast(ColorProvider::instance()) + .updateColor(ColorType::ThreadMessageHighlight, + QColor(colorName)); + } } } break; diff --git a/src/controllers/highlights/HighlightModel.hpp b/src/controllers/highlights/HighlightModel.hpp index e18306fb..f1407f18 100644 --- a/src/controllers/highlights/HighlightModel.hpp +++ b/src/controllers/highlights/HighlightModel.hpp @@ -32,6 +32,7 @@ public: RedeemedRow = 3, FirstMessageRow = 4, ElevatedMessageRow = 5, + ThreadMessageRow = 6, }; protected: diff --git a/src/controllers/highlights/HighlightPhrase.cpp b/src/controllers/highlights/HighlightPhrase.cpp index 079d3686..d7b20e16 100644 --- a/src/controllers/highlights/HighlightPhrase.cpp +++ b/src/controllers/highlights/HighlightPhrase.cpp @@ -16,6 +16,8 @@ QColor HighlightPhrase::FALLBACK_FIRST_MESSAGE_HIGHLIGHT_COLOR = QColor(72, 127, 63, 60); QColor HighlightPhrase::FALLBACK_ELEVATED_MESSAGE_HIGHLIGHT_COLOR = QColor(255, 174, 66, 60); +QColor HighlightPhrase::FALLBACK_THREAD_HIGHLIGHT_COLOR = + QColor(143, 48, 24, 60); QColor HighlightPhrase::FALLBACK_SUB_COLOR = QColor(196, 102, 255, 100); bool HighlightPhrase::operator==(const HighlightPhrase &other) const diff --git a/src/controllers/highlights/HighlightPhrase.hpp b/src/controllers/highlights/HighlightPhrase.hpp index 99d3fe37..695fc93d 100644 --- a/src/controllers/highlights/HighlightPhrase.hpp +++ b/src/controllers/highlights/HighlightPhrase.hpp @@ -84,6 +84,7 @@ public: static QColor FALLBACK_SUB_COLOR; static QColor FALLBACK_FIRST_MESSAGE_HIGHLIGHT_COLOR; static QColor FALLBACK_ELEVATED_MESSAGE_HIGHLIGHT_COLOR; + static QColor FALLBACK_THREAD_HIGHLIGHT_COLOR; private: QString pattern_; diff --git a/src/messages/Message.hpp b/src/messages/Message.hpp index 6c7a7590..0468627a 100644 --- a/src/messages/Message.hpp +++ b/src/messages/Message.hpp @@ -43,6 +43,7 @@ enum class MessageFlag : int64_t { FirstMessage = (1LL << 23), ReplyMessage = (1LL << 24), ElevatedMessage = (1LL << 25), + ParticipatedThread = (1LL << 26), }; using MessageFlags = FlagsEnum; diff --git a/src/messages/MessageThread.cpp b/src/messages/MessageThread.cpp index dc798d99..0ffdc3f1 100644 --- a/src/messages/MessageThread.cpp +++ b/src/messages/MessageThread.cpp @@ -58,4 +58,14 @@ size_t MessageThread::liveCount( return count; } +bool MessageThread::participated() const +{ + return this->participated_; +} + +void MessageThread::markParticipated() +{ + this->participated_ = true; +} + } // namespace chatterino diff --git a/src/messages/MessageThread.hpp b/src/messages/MessageThread.hpp index f2a8e57d..ae0d2479 100644 --- a/src/messages/MessageThread.hpp +++ b/src/messages/MessageThread.hpp @@ -23,6 +23,10 @@ public: /// Returns the number of live reply references size_t liveCount(const std::shared_ptr &exclude) const; + bool participated() const; + + void markParticipated(); + const QString &rootId() const { return rootMessageId_; @@ -42,6 +46,7 @@ private: const QString rootMessageId_; const std::shared_ptr rootMessage_; std::vector> replies_; + bool participated_ = false; }; } // namespace chatterino diff --git a/src/messages/SharedMessageBuilder.cpp b/src/messages/SharedMessageBuilder.cpp index 49508c0b..5fc76c87 100644 --- a/src/messages/SharedMessageBuilder.cpp +++ b/src/messages/SharedMessageBuilder.cpp @@ -149,7 +149,8 @@ void SharedMessageBuilder::parseHighlights() auto badges = SharedMessageBuilder::parseBadgeTag(this->tags); auto [highlighted, highlightResult] = getIApp()->getHighlights()->check( - this->args, badges, this->ircMessage->nick(), this->originalMessage_); + this->args, badges, this->ircMessage->nick(), this->originalMessage_, + this->message().flags); if (!highlighted) { diff --git a/src/providers/colors/ColorProvider.cpp b/src/providers/colors/ColorProvider.cpp index 39f30778..342ce45a 100644 --- a/src/providers/colors/ColorProvider.cpp +++ b/src/providers/colors/ColorProvider.cpp @@ -147,6 +147,20 @@ void ColorProvider::initTypeColorMap() std::make_shared( HighlightPhrase::FALLBACK_ELEVATED_MESSAGE_HIGHLIGHT_COLOR)}); } + + customColor = getSettings()->threadHighlightColor; + if (QColor(customColor).isValid()) + { + this->typeColorMap_.insert({ColorType::ThreadMessageHighlight, + std::make_shared(customColor)}); + } + else + { + this->typeColorMap_.insert( + {ColorType::ThreadMessageHighlight, + std::make_shared( + HighlightPhrase::FALLBACK_THREAD_HIGHLIGHT_COLOR)}); + } } void ColorProvider::initDefaultColors() diff --git a/src/providers/colors/ColorProvider.hpp b/src/providers/colors/ColorProvider.hpp index d7fe80f9..b2143c4c 100644 --- a/src/providers/colors/ColorProvider.hpp +++ b/src/providers/colors/ColorProvider.hpp @@ -14,6 +14,7 @@ enum class ColorType { RedeemedHighlight, FirstMessageHighlight, ElevatedMessageHighlight, + ThreadMessageHighlight, }; class ColorProvider diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index 0afef8cc..da32a6a8 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -67,6 +67,65 @@ MessagePtr generateBannedMessage(bool confirmedBan) return builder.release(); } +int stripLeadingReplyMention(const QVariantMap &tags, QString &content) +{ + if (!getSettings()->stripReplyMention) + { + return 0; + } + + if (const auto it = tags.find("reply-parent-display-name"); + it != tags.end()) + { + auto displayName = it.value().toString(); + if (content.startsWith('@') && + content.at(1 + displayName.length()) == ' ' && + content.indexOf(displayName, 1) == 1) + { + int messageOffset = 1 + displayName.length() + 1; + content.remove(0, messageOffset); + return messageOffset; + } + } + return 0; +} + +void updateReplyParticipatedStatus(const QVariantMap &tags, + const QString &senderLogin, + TwitchMessageBuilder &builder, + std::shared_ptr &thread, + bool isNew) +{ + const auto ¤tLogin = + getApp()->accounts->twitch.getCurrent()->getUserName(); + if (thread->participated()) + { + builder.message().flags.set(MessageFlag::ParticipatedThread); + return; + } + + if (isNew) + { + if (const auto it = tags.find("reply-parent-user-login"); + it != tags.end()) + { + auto name = it.value().toString(); + if (name == currentLogin) + { + thread->markParticipated(); + builder.message().flags.set(MessageFlag::ParticipatedThread); + return; // already marked as participated + } + } + } + + if (senderLogin == currentLogin) + { + thread->markParticipated(); + // don't set the highlight here + } +} + } // namespace namespace chatterino { @@ -259,9 +318,12 @@ std::vector IrcMessageHandler::parseMessageWithReply( return this->parsePrivMessage(channel, privMsg); } + QString content = privMsg->content(); + int messageOffset = stripLeadingReplyMention(privMsg->tags(), content); MessageParseArgs args; - TwitchMessageBuilder builder(channel, message, args, privMsg->content(), + TwitchMessageBuilder builder(channel, message, args, content, privMsg->isAction()); + builder.setMessageOffset(messageOffset); this->populateReply(tc, message, otherLoaded, builder); @@ -295,10 +357,12 @@ void IrcMessageHandler::populateReply( auto threadIt = channel->threads_.find(replyID); if (threadIt != channel->threads_.end()) { - const auto owned = threadIt->second.lock(); + auto owned = threadIt->second.lock(); if (owned) { // Thread already exists (has a reply) + updateReplyParticipatedStatus(tags, message->nick(), builder, + owned, false); builder.setThread(owned); return; } @@ -331,6 +395,8 @@ void IrcMessageHandler::populateReply( { std::shared_ptr newThread = std::make_shared(foundMessage); + updateReplyParticipatedStatus(tags, message->nick(), builder, + newThread, true); builder.setThread(newThread); // Store weak reference to thread in channel @@ -341,7 +407,7 @@ void IrcMessageHandler::populateReply( void IrcMessageHandler::addMessage(Communi::IrcMessage *_message, const QString &target, - const QString &content, + const QString &content_, TwitchIrcServer &server, bool isSub, bool isAction) { @@ -384,7 +450,7 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message, [=, &server](ChannelPointReward reward) { if (reward.id == rewardId) { - this->addMessage(clone, target, content, server, isSub, + this->addMessage(clone, target, content_, server, isSub, isAction); clone->deleteLater(); return true; @@ -396,7 +462,11 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message, args.channelPointRewardId = rewardId; } + QString content = content_; + int messageOffset = stripLeadingReplyMention(tags, content); + TwitchMessageBuilder builder(chan.get(), _message, args, content, isAction); + builder.setMessageOffset(messageOffset); if (const auto it = tags.find("reply-parent-msg-id"); it != tags.end()) { @@ -405,7 +475,10 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message, if (threadIt != channel->threads_.end() && !threadIt->second.expired()) { // Thread already exists (has a reply) - builder.setThread(threadIt->second.lock()); + auto thread = threadIt->second.lock(); + updateReplyParticipatedStatus(tags, _message->nick(), builder, + thread, false); + builder.setThread(thread); } else { @@ -414,7 +487,9 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message, if (root) { // Found root reply message - const auto newThread = std::make_shared(root); + auto newThread = std::make_shared(root); + updateReplyParticipatedStatus(tags, _message->nick(), builder, + newThread, true); builder.setThread(newThread); // Store weak reference to thread in channel diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index d63b6b1f..47d4009e 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -999,8 +999,10 @@ void TwitchMessageBuilder::appendTwitchEmote( return; } - auto start = correctPositions[coords.at(0).toUInt()]; - auto end = correctPositions[coords.at(1).toUInt()]; + auto start = + correctPositions[coords.at(0).toUInt() - this->messageOffset_]; + auto end = + correctPositions[coords.at(1).toUInt() - this->messageOffset_]; if (start >= end || start < 0 || end > this->originalMessage_.length()) { @@ -1589,4 +1591,9 @@ void TwitchMessageBuilder::setThread(std::shared_ptr thread) this->thread_ = std::move(thread); } +void TwitchMessageBuilder::setMessageOffset(int offset) +{ + this->messageOffset_ = offset; +} + } // namespace chatterino diff --git a/src/providers/twitch/TwitchMessageBuilder.hpp b/src/providers/twitch/TwitchMessageBuilder.hpp index 130c89c3..68da6a75 100644 --- a/src/providers/twitch/TwitchMessageBuilder.hpp +++ b/src/providers/twitch/TwitchMessageBuilder.hpp @@ -47,6 +47,7 @@ public: MessagePtr build() override; void setThread(std::shared_ptr thread); + void setMessageOffset(int offset); static void appendChannelPointRewardMessage( const ChannelPointReward &reward, MessageBuilder *builder, bool isMod, @@ -110,6 +111,18 @@ private: bool historicalMessage_ = false; std::shared_ptr thread_; + /** + * Starting offset to be used on index-based operations on `originalMessage_`. + * + * For example: + * originalMessage_ = "there" + * messageOffset_ = 4 + * (the irc message is "hey there") + * + * then the index 6 would resolve to 6 - 4 = 2 => 'e' + */ + int messageOffset_ = 0; + QString userId_; bool senderIsBroadcaster{}; }; diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index 82dc1fb6..b341b9b3 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -118,6 +118,7 @@ public: // BoolSetting collapseLongMessages = // {"/appearance/messages/collapseLongMessages", false}; BoolSetting showReplyButton = {"/appearance/showReplyButton", false}; + BoolSetting stripReplyMention = {"/appearance/stripReplyMention", true}; IntSetting collpseMessagesMinLines = { "/appearance/messages/collapseMessagesMinLines", 0}; BoolSetting alternateMessages = { @@ -327,6 +328,19 @@ public: ""}; QStringSetting subHighlightColor = {"/highlighting/subHighlightColor", ""}; + BoolSetting enableThreadHighlight = { + "/highlighting/thread/nameIsHighlightKeyword", true}; + BoolSetting showThreadHighlightInMentions = { + "/highlighting/thread/showSelfHighlightInMentions", true}; + BoolSetting enableThreadHighlightSound = { + "/highlighting/thread/enableSound", true}; + BoolSetting enableThreadHighlightTaskbar = { + "/highlighting/thread/enableTaskbarFlashing", true}; + QStringSetting threadHighlightSoundUrl = { + "/highlighting/threadHighlightSoundUrl", ""}; + QStringSetting threadHighlightColor = {"/highlighting/threadHighlightColor", + ""}; + QStringSetting highlightColor = {"/highlighting/color", ""}; BoolSetting longAlerts = {"/highlighting/alerts", false}; diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index 74a885b7..1e704bb4 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -719,6 +719,7 @@ void GeneralPage::initLayout(GeneralPageView &layout) layout.addCheckbox("Combine multiple bit tips into one", s.stackBits); layout.addCheckbox("Messages in /mentions highlights tab", s.highlightMentions); + layout.addCheckbox("Strip leading mention in replies", s.stripReplyMention); // Helix timegate settings auto helixTimegateGetValue = [](auto val) { diff --git a/tests/src/HighlightController.cpp b/tests/src/HighlightController.cpp index 8e8b2ed7..e5bdb04b 100644 --- a/tests/src/HighlightController.cpp +++ b/tests/src/HighlightController.cpp @@ -485,6 +485,7 @@ struct TestCase { std::vector badges; QString senderName; QString originalMessage; + MessageFlags flags; } input; struct { @@ -727,8 +728,9 @@ TEST_F(HighlightControllerTest, A) for (const auto &[input, expected] : tests) { - auto [isMatch, matchResult] = this->controller->check( - input.args, input.badges, input.senderName, input.originalMessage); + auto [isMatch, matchResult] = + this->controller->check(input.args, input.badges, input.senderName, + input.originalMessage, input.flags); EXPECT_EQ(isMatch, expected.state) << qUtf8Printable(input.senderName) << ": "