diff --git a/CHANGELOG.md b/CHANGELOG.md index c95aa2df7..7497e46dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ - Major: Improve high-DPI support on Windows. (#4868, #5391, #5664, #5666) - Major: Added transparent overlay window (default keybind: CTRL + ALT + N). (#4746, #5643, #5659) - Minor: Removed the Ctrl+Shift+L hotkey for toggling the "live only" tab visibility state. (#5530) -- Minor: Add support for Shared Chat messages. Shared chat messages can be filtered with the `flags.shared` filter variable, or with search using `is:shared`. Some messages like subscriptions are filtered on purpose to avoid confusion for the broadcaster. If you have both channels participating in Shared Chat open, only one of the message triggering your highlight will trigger. (#5606, #5625) +- Minor: Add support for Shared Chat messages. Shared chat messages can be filtered with the `flags.shared` filter variable, or with search using `is:shared`. Some messages like subscriptions are filtered on purpose to avoid confusion for the broadcaster. If you have both channels participating in Shared Chat open, only one of the message triggering your highlight will trigger. (#5606, #5625, #5661) - Minor: Moved tab visibility control to a submenu, without any toggle actions. (#5530) - Minor: Add option to customise Moderation buttons with images. (#5369) - Minor: Colored usernames now update on the fly when changing the "Color @usernames" setting. (#5300) diff --git a/mocks/include/mocks/BaseApplication.hpp b/mocks/include/mocks/BaseApplication.hpp index 2ba9f949c..619203afc 100644 --- a/mocks/include/mocks/BaseApplication.hpp +++ b/mocks/include/mocks/BaseApplication.hpp @@ -3,6 +3,7 @@ #include "common/Args.hpp" #include "mocks/DisabledStreamerMode.hpp" #include "mocks/EmptyApplication.hpp" +#include "mocks/TwitchUsers.hpp" #include "providers/bttv/BttvLiveUpdates.hpp" #include "singletons/Fonts.hpp" #include "singletons/Settings.hpp" @@ -55,6 +56,11 @@ public: return &this->fonts; } + ITwitchUsers *getTwitchUsers() override + { + return &this->twitchUsers; + } + BttvLiveUpdates *getBttvLiveUpdates() override { return nullptr; @@ -71,6 +77,7 @@ public: DisabledStreamerMode streamerMode; Theme theme; Fonts fonts; + TwitchUsers twitchUsers; }; } // namespace chatterino::mock diff --git a/mocks/include/mocks/TwitchUsers.hpp b/mocks/include/mocks/TwitchUsers.hpp new file mode 100644 index 000000000..14f6bf7e6 --- /dev/null +++ b/mocks/include/mocks/TwitchUsers.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "providers/twitch/TwitchUser.hpp" +#include "providers/twitch/TwitchUsers.hpp" + +namespace chatterino::mock { + +class TwitchUsers : public ITwitchUsers +{ +public: + TwitchUsers() = default; + + std::shared_ptr resolveID(const UserId &id) + { + TwitchUser u = { + .id = id.string, + .name = {}, + .displayName = {}, + }; + return std::make_shared(u); + } +}; + +} // namespace chatterino::mock diff --git a/resources/twitch/sharedChat.png b/resources/twitch/sharedChat.png new file mode 100644 index 000000000..f9a66b17c Binary files /dev/null and b/resources/twitch/sharedChat.png differ diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index a17f955a9..601ba8ec9 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -32,6 +32,7 @@ #include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchIrc.hpp" #include "providers/twitch/TwitchIrcServer.hpp" +#include "providers/twitch/TwitchUsers.hpp" #include "singletons/Emotes.hpp" #include "singletons/Resources.hpp" #include "singletons/Settings.hpp" @@ -380,6 +381,18 @@ EmotePtr makeAutoModBadge() Url{"https://dashboard.twitch.tv/settings/moderation/automod"}}); } +EmotePtr makeSharedChatBadge(const QString &sourceName) +{ + return std::make_shared(Emote{ + .name = EmoteName{}, + .images = ImageSet{Image::fromResourcePixmap( + getResources().twitch.sharedChat, 0.25)}, + .tooltip = Tooltip{"Shared Message" + + (sourceName.isEmpty() ? "" : " from " + sourceName)}, + .homePage = Url{"https://link.twitch.tv/SharedChatViewer"}, + }); +} + std::tuple, MessageElementFlags, bool> parseEmote( TwitchChannel *twitchChannel, const EmoteName &name) { @@ -2751,6 +2764,28 @@ void MessageBuilder::appendTwitchBadges(const QVariantMap &tags, return; } + if (this->message().flags.has(MessageFlag::SharedMessage)) + { + const QString sourceId = tags["source-room-id"].toString(); + QString sourceName; + if (sourceId.isEmpty()) + { + sourceName = ""; + } + else if (twitchChannel->roomId() == sourceId) + { + sourceName = twitchChannel->getName(); + } + else + { + sourceName = + getApp()->getTwitchUsers()->resolveID({sourceId})->displayName; + } + + this->emplace(makeSharedChatBadge(sourceName), + MessageElementFlag::BadgeSharedChannel); + } + auto badgeInfos = parseBadgeInfoTag(tags); auto badges = parseBadgeTag(tags); appendBadges(this, badges, badgeInfos, twitchChannel); diff --git a/src/messages/MessageElement.hpp b/src/messages/MessageElement.hpp index 0e6c07b93..c19ed5c0c 100644 --- a/src/messages/MessageElement.hpp +++ b/src/messages/MessageElement.hpp @@ -66,6 +66,10 @@ enum class MessageElementFlag : int64_t { BitsStatic = (1LL << 11), BitsAnimated = (1LL << 12), + // Slot 0: Twitch + // - Shared Channel indicator badge + BadgeSharedChannel = (1LL << 37), + // Slot 1: Twitch // - Staff badge // - Admin badge @@ -119,7 +123,7 @@ enum class MessageElementFlag : int64_t { Badges = BadgeGlobalAuthority | BadgePredictions | BadgeChannelAuthority | BadgeSubscription | BadgeVanity | BadgeChatterino | BadgeSevenTV | - BadgeFfz, + BadgeFfz | BadgeSharedChannel, ChannelName = (1LL << 20), diff --git a/src/singletons/WindowManager.cpp b/src/singletons/WindowManager.cpp index f64eb47e7..88d5ec565 100644 --- a/src/singletons/WindowManager.cpp +++ b/src/singletons/WindowManager.cpp @@ -195,6 +195,7 @@ void WindowManager::updateWordTypeMask() flags.set(settings->animateEmotes ? MEF::BitsAnimated : MEF::BitsStatic); // badges + flags.set(MEF::BadgeSharedChannel); flags.set(settings->showBadgesGlobalAuthority ? MEF::BadgeGlobalAuthority : MEF::None); flags.set(settings->showBadgesPredictions ? MEF::BadgePredictions diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index 066085030..50f6d1acc 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -2408,6 +2408,11 @@ void ChannelView::handleMouseClick(QMouseEvent *event, return; } + if (link.value.startsWith("id:")) + { + return; + } + // Insert @username into split input const bool commaMention = getSettings()->mentionUsersWithComma; diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json b/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json index 9c6fa2f65..a6877c365 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json @@ -64,6 +64,24 @@ "trailingSpace": true, "type": "TwitchModerationElement" }, + { + "emote": { + "homePage": "https://link.twitch.tv/SharedChatViewer", + "images": { + "1x": "" + }, + "name": "", + "tooltip": "Shared Message" + }, + "flags": "BadgeSharedChannel", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "Shared Message", + "trailingSpace": true, + "type": "BadgeElement" + }, { "emote": { "homePage": "https://www.twitch.tv/jobs?ref=chat_badge", diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-emotes.json b/tests/snapshots/IrcMessageHandler/shared-chat-emotes.json index b7f9256d4..ba6485c21 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-emotes.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-emotes.json @@ -64,6 +64,24 @@ "trailingSpace": true, "type": "TwitchModerationElement" }, + { + "emote": { + "homePage": "https://link.twitch.tv/SharedChatViewer", + "images": { + "1x": "" + }, + "name": "", + "tooltip": "Shared Message from twitchdev" + }, + "flags": "BadgeSharedChannel", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "Shared Message from twitchdev", + "trailingSpace": true, + "type": "BadgeElement" + }, { "color": "#ffff0000", "flags": "Username", diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-known.json b/tests/snapshots/IrcMessageHandler/shared-chat-known.json index 6a13adcb4..c4a3f3e2f 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-known.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-known.json @@ -64,6 +64,24 @@ "trailingSpace": true, "type": "TwitchModerationElement" }, + { + "emote": { + "homePage": "https://link.twitch.tv/SharedChatViewer", + "images": { + "1x": "" + }, + "name": "", + "tooltip": "Shared Message from twitchdev" + }, + "flags": "BadgeSharedChannel", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "Shared Message from twitchdev", + "trailingSpace": true, + "type": "BadgeElement" + }, { "emote": { "homePage": "https://www.twitch.tv/jobs?ref=chat_badge", diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json b/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json index accbb76b0..6ea80b2ce 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json @@ -64,6 +64,24 @@ "trailingSpace": true, "type": "TwitchModerationElement" }, + { + "emote": { + "homePage": "https://link.twitch.tv/SharedChatViewer", + "images": { + "1x": "" + }, + "name": "", + "tooltip": "Shared Message" + }, + "flags": "BadgeSharedChannel", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "Shared Message", + "trailingSpace": true, + "type": "BadgeElement" + }, { "emote": { "homePage": "https://www.twitch.tv/jobs?ref=chat_badge",