diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b602b1c6..41af9f894 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Minor: Fixed `/streamlink` command not stripping leading @'s or #'s (#3215) - Minor: Strip leading @ and trailing , from username in `/popout` command. (#3217) - Minor: Added `flags.reward_message` filter variable (#3231) +- Minor: Added highlights for first messages (#3267) - 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/highlights/HighlightModel.cpp b/src/controllers/highlights/HighlightModel.cpp index c24616665..aae88e502 100644 --- a/src/controllers/highlights/HighlightModel.cpp +++ b/src/controllers/highlights/HighlightModel.cpp @@ -150,6 +150,29 @@ void HighlightModel::afterInit() setColorItem(redeemedRow[Column::Color], *RedeemedColor, false); this->insertCustomRow(redeemedRow, 3); + + std::vector firstMessageRow = this->createRow(); + setBoolItem(firstMessageRow[Column::Pattern], + getSettings()->enableFirstMessageHighlight.getValue(), true, + false); + firstMessageRow[Column::Pattern]->setData("First Messages", + Qt::DisplayRole); + firstMessageRow[Column::ShowInMentions]->setFlags({}); + firstMessageRow[Column::FlashTaskbar]->setFlags({}); + firstMessageRow[Column::PlaySound]->setFlags({}); + firstMessageRow[Column::UseRegex]->setFlags({}); + firstMessageRow[Column::CaseSensitive]->setFlags({}); + + QUrl FirstMessageSound = + QUrl(getSettings()->firstMessageHighlightSoundUrl.getValue()); + setFilePathItem(firstMessageRow[Column::SoundPath], FirstMessageSound, + false); + + auto FirstMessageColor = + ColorProvider::instance().color(ColorType::FirstMessageHighlight); + setColorItem(firstMessageRow[Column::Color], *FirstMessageColor, false); + + this->insertCustomRow(firstMessageRow, 4); } void HighlightModel::customRowSetData(const std::vector &row, @@ -304,6 +327,14 @@ void HighlightModel::customRowSetData(const std::vector &row, .updateColor(ColorType::RedeemedHighlight, QColor(colorName)); } + else if (rowIndex == 4) + { + getSettings()->firstMessageHighlightColor.setValue( + colorName); + const_cast(ColorProvider::instance()) + .updateColor(ColorType::FirstMessageHighlight, + QColor(colorName)); + } } } break; diff --git a/src/controllers/highlights/HighlightPhrase.cpp b/src/controllers/highlights/HighlightPhrase.cpp index 635904131..ab8436c31 100644 --- a/src/controllers/highlights/HighlightPhrase.cpp +++ b/src/controllers/highlights/HighlightPhrase.cpp @@ -12,6 +12,8 @@ namespace { QColor HighlightPhrase::FALLBACK_HIGHLIGHT_COLOR = QColor(127, 63, 73, 127); QColor HighlightPhrase::FALLBACK_REDEEMED_HIGHLIGHT_COLOR = QColor(28, 126, 141, 60); +QColor HighlightPhrase::FALLBACK_FIRST_MESSAGE_HIGHLIGHT_COLOR = + QColor(72, 127, 63, 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 496da667b..41ab8b682 100644 --- a/src/controllers/highlights/HighlightPhrase.hpp +++ b/src/controllers/highlights/HighlightPhrase.hpp @@ -82,6 +82,7 @@ public: static QColor FALLBACK_HIGHLIGHT_COLOR; static QColor FALLBACK_REDEEMED_HIGHLIGHT_COLOR; static QColor FALLBACK_SUB_COLOR; + static QColor FALLBACK_FIRST_MESSAGE_HIGHLIGHT_COLOR; private: QString pattern_; diff --git a/src/messages/Message.cpp b/src/messages/Message.cpp index b6708aca2..7f3129050 100644 --- a/src/messages/Message.cpp +++ b/src/messages/Message.cpp @@ -42,6 +42,12 @@ SBHighlight Message::getScrollBarHighlight() const ColorProvider::instance().color(ColorType::RedeemedHighlight), SBHighlight::Default, true); } + else if (this->flags.has(MessageFlag::FirstMessage)) + { + return SBHighlight( + ColorProvider::instance().color(ColorType::FirstMessageHighlight), + SBHighlight::Default, true); + } return SBHighlight(); } diff --git a/src/messages/Message.hpp b/src/messages/Message.hpp index b91313545..9321b570b 100644 --- a/src/messages/Message.hpp +++ b/src/messages/Message.hpp @@ -38,6 +38,7 @@ enum class MessageFlag : uint32_t { RedeemedHighlight = (1 << 20), RedeemedChannelPointReward = (1 << 21), ShowInMentions = (1 << 22), + FirstMessage = (1 << 23), }; using MessageFlags = FlagsEnum; diff --git a/src/messages/layouts/MessageLayout.cpp b/src/messages/layouts/MessageLayout.cpp index b59b12ce7..1135556e0 100644 --- a/src/messages/layouts/MessageLayout.cpp +++ b/src/messages/layouts/MessageLayout.cpp @@ -293,9 +293,16 @@ void MessageLayout::updateBuffer(QPixmap *buffer, int /*messageIndex*/, } }(); - if ((this->message_->flags.has(MessageFlag::Highlighted) || - this->message_->flags.has(MessageFlag::HighlightedWhisper)) && - !this->flags.has(MessageLayoutFlag::IgnoreHighlights)) + if (this->message_->flags.has(MessageFlag::FirstMessage) && + getSettings()->enableFirstMessageHighlight.getValue()) + { + backgroundColor = blendColors( + backgroundColor, + *ColorProvider::instance().color(ColorType::FirstMessageHighlight)); + } + else if ((this->message_->flags.has(MessageFlag::Highlighted) || + this->message_->flags.has(MessageFlag::HighlightedWhisper)) && + !this->flags.has(MessageLayoutFlag::IgnoreHighlights)) { // Blend highlight color with usual background color backgroundColor = diff --git a/src/providers/colors/ColorProvider.cpp b/src/providers/colors/ColorProvider.cpp index b29488ef3..350db3536 100644 --- a/src/providers/colors/ColorProvider.cpp +++ b/src/providers/colors/ColorProvider.cpp @@ -119,6 +119,20 @@ void ColorProvider::initTypeColorMap() std::make_shared( HighlightPhrase::FALLBACK_REDEEMED_HIGHLIGHT_COLOR)}); } + + customColor = getSettings()->firstMessageHighlightColor; + if (QColor(customColor).isValid()) + { + this->typeColorMap_.insert({ColorType::FirstMessageHighlight, + std::make_shared(customColor)}); + } + else + { + this->typeColorMap_.insert( + {ColorType::FirstMessageHighlight, + std::make_shared( + HighlightPhrase::FALLBACK_FIRST_MESSAGE_HIGHLIGHT_COLOR)}); + } } void ColorProvider::initDefaultColors() diff --git a/src/providers/colors/ColorProvider.hpp b/src/providers/colors/ColorProvider.hpp index 5d2d50128..4540caaf3 100644 --- a/src/providers/colors/ColorProvider.hpp +++ b/src/providers/colors/ColorProvider.hpp @@ -11,7 +11,8 @@ enum class ColorType { SelfHighlight, Subscription, Whisper, - RedeemedHighlight + RedeemedHighlight, + FirstMessageHighlight, }; class ColorProvider diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 42fd676ae..1c594ceb0 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -180,6 +180,12 @@ MessagePtr TwitchMessageBuilder::build() this->message().flags.set(MessageFlag::RedeemedHighlight); } + if (this->tags.contains("first-msg") && + this->tags["first-msg"].toString() == "1") + { + this->message().flags.set(MessageFlag::FirstMessage); + } + // timestamp this->emplace( calculateMessageTimestamp(this->ircMessage)); diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index 73c603453..1d967a9d8 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -273,6 +273,13 @@ public: QStringSetting redeemedHighlightColor = { "/highlighting/redeemedHighlightColor", ""}; + BoolSetting enableFirstMessageHighlight = { + "/highlighting/firstMessageHighlight/highlighted", true}; + QStringSetting firstMessageHighlightSoundUrl = { + "/highlighting/firstMessageHighlightSoundUrl", ""}; + QStringSetting firstMessageHighlightColor = { + "/highlighting/firstMessageHighlightColor", ""}; + BoolSetting enableSubHighlight = { "/highlighting/subHighlight/subsHighlighted", true}; BoolSetting enableSubHighlightSound = { diff --git a/src/widgets/Window.cpp b/src/widgets/Window.cpp index dea06c69c..7233e735a 100644 --- a/src/widgets/Window.cpp +++ b/src/widgets/Window.cpp @@ -219,6 +219,9 @@ void Window::addDebugStuff() miscMessages.emplace_back(R"(@badges=;color=#00AD2B;display-name=Iamme420\s;emotes=;id=d47a1e4b-a3c6-4b9e-9bf1-51b8f3dbc76e;mod=0;room-id=11148817;subscriber=0;tmi-sent-ts=1529670347537;turbo=0;user-id=56422869;user-type= :iamme420!iamme420@iamme420.tmi.twitch.tv PRIVMSG #pajlada :offline chat gachiBASS)"); miscMessages.emplace_back(R"(@badge-info=founder/47;badges=moderator/1,founder/0,premium/1;color=#00FF80;display-name=gempir;emotes=;flags=;id=d4514490-202e-43cb-b429-ef01a9d9c2fe;mod=1;room-id=11148817;subscriber=0;tmi-sent-ts=1575198233854;turbo=0;user-id=77829817;user-type=mod :gempir!gempir@gempir.tmi.twitch.tv PRIVMSG #pajlada :offline chat gachiBASS)"); + // "first time chat" message + miscMessages.emplace_back(R"(@badge-info=;badges=glhf-pledge/1;client-nonce=5d2627b0cbe56fa05faf5420def4807d;color=#1E90FF;display-name=oldcoeur;emote-only=1;emotes=84608:0-7;first-msg=1;flags=;id=7412fea4-8683-4cc9-a506-4228127a5c2d;mod=0;room-id=11148817;subscriber=0;tmi-sent-ts=1623429859222;turbo=0;user-id=139147886;user-type= :oldcoeur!oldcoeur@oldcoeur.tmi.twitch.tv PRIVMSG #pajlada :cmonBruh)"); + // various link tests linkMessages.emplace_back(R"(@badge-info=subscriber/48;badges=broadcaster/1,subscriber/36,partner/1;color=#CC44FF;display-name=pajlada;emotes=;flags=;id=3c23cf3c-0864-4699-a76b-089350141147;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1577628844607;turbo=0;user-id=11148817;user-type= :pajlada!pajlada@pajlada.tmi.twitch.tv PRIVMSG #pajlada : Links that should pass: )" + getValidLinks().join(' ')); linkMessages.emplace_back(R"(@badge-info=subscriber/48;badges=broadcaster/1,subscriber/36,partner/1;color=#CC44FF;display-name=pajlada;emotes=;flags=;id=3c23cf3c-0864-4699-a76b-089350141147;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1577628844607;turbo=0;user-id=11148817;user-type= :pajlada!pajlada@pajlada.tmi.twitch.tv PRIVMSG #pajlada : Links that should NOT pass: )" + getInvalidLinks().join(' '));