From f4036b269bc9dfa789d6bb97278e6a3e5ddc680e Mon Sep 17 00:00:00 2001 From: iProdigy Date: Sat, 19 Oct 2024 03:40:06 -0700 Subject: [PATCH 01/15] feat: add shared chat badge --- lib/lrucache/lrucache/lrucache.hpp | 11 ++++++++ resources/twitch/sharedChat.png | Bin 0 -> 2006 bytes src/messages/MessageBuilder.cpp | 30 +++++++++++++++++++-- src/messages/MessageBuilder.hpp | 1 + src/messages/MessageElement.hpp | 6 ++++- src/providers/twitch/TwitchIrcServer.cpp | 33 +++++++++++++++++++++++ src/providers/twitch/TwitchIrcServer.hpp | 11 ++++++++ src/singletons/WindowManager.cpp | 1 + 8 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 resources/twitch/sharedChat.png diff --git a/lib/lrucache/lrucache/lrucache.hpp b/lib/lrucache/lrucache/lrucache.hpp index ef7d031db..183bc3b02 100644 --- a/lib/lrucache/lrucache/lrucache.hpp +++ b/lib/lrucache/lrucache/lrucache.hpp @@ -71,6 +71,17 @@ public: } } + void remove(const key_t &key) + { + auto it = _cache_items_map.find(key); + if (it == _cache_items_map.end()) + { + throw std::range_error("There is no such key in cache"); + } + _cache_items_list.erase(it->second); + _cache_items_map.erase(it); + } + const value_t &get(const key_t &key) { auto it = _cache_items_map.find(key); diff --git a/resources/twitch/sharedChat.png b/resources/twitch/sharedChat.png new file mode 100644 index 0000000000000000000000000000000000000000..116108c77032a75551cea742eae343bc3423c151 GIT binary patch literal 2006 zcmV;{2Pyc8P)Ev-aA1uf5Jb za5U$itial5t^fM=f7b2$*4i~mbdZfh4z>a9j*dVD=m=DRjz9(I2vmTMKn3UsRDg~^ z1(@0b&MsgH@GIa{;CP@9*ahqW-U0pr{1e#Qg7zy#t-_sjFK`oZJ#ctH`LBUhz#YKu zs*I=_RSC2l_#4m@QTI#WT3~$@hE$Da=b##J8}KA?;(+zMwZO_cUt6KsIp|j4mW1n^WI>PWf4Qd zoiqLwIkepLqQkq%Q^q&v?3A=jQq3t_lXQ`!Ay1tRp?1Z*$abf(2P+cjA%}OH^ZQRS z#3`D52RtH8NIEUVu9!Kf&<K;P7F{$sg_HmwD=32Ygt-`vLG<=UFX-FNGL^TtQsH zV&Mwn3bxqE4+0wkxz_?)3;4W<5oo)UABf=aVO~Kyvz`3&K{1AV}m&a-ztWs-1vSP{N(@@|jyB1WK%PJSA&B7!H0>qtAWZaePKKBiNjFRC&8XKW>5obetA^pf&S~?h43U=0zmvYi zBaS4k3w_UKW6~?x9|?4^kNpwFFEM{>9=2L#MKW&z{*u$)$(S$J?fGphwv-QHE7UJZ z4=b=Z)9Jv+IqmF66FsQ$S6Z3TlgeO1(w{uNz?aUGv@f!qeUi=%Xrq!LmP||KcM3x= zr2Jv!OH1Sr+2&!lN{XWj1wztMvPhu21<1mfq}%16kVDdsWnOtv@12m%Idfv#sq)~k zftgsat8ub=0{i2{+p!(;M&Mb}OL+`V!l17#Uk|($OWq2Beo*LKUMN6E zpaOIRDnLh|0(1l_K!`wx0XG8w#_sg!0H!hE)q4AfC2Z;i{u9YLTIamM;q;?mqtq|y z?ZAy+tFc9sPj8o2z`0okC&GFApu_7*&o5N@z+nq;2JpXn{uSWoO>(|j=lsU^W8M(E zmi7_{Y}b5EA|HCHk%t8i=OpA^pt{V-3-~*x_`$^IiT|%9F!-Lp3D|X+BE(+u#e#!M(qpKO(DOEAC0R z0zU#S#qL?J#-f#Fa0nK-@5iQzPp~QD24JH8uOB%}oQqrYmctgNg_;~3dn|(E!9@Mx zI%ngznxs`(b=zH_g-YKf8<#Y-1V>Hj8*w8Zk-YWu<{)?Wo>H<~H;aYX=L+s+sX@vO zF_FHwaAci(VWNx|c>=l7zF8%VY8F}Ii~?6i@Lf&%eO!_J$myEzINkE4gE*`tX;jkL zP4F&~|154)(pg#c@(9;BMBhj{{(CA;v`BemmwSlTe2>M65-E>py8LHdH@85XpJs@2 zpfMt|B}gD7{Vs#(5yhWp`O+IzzGaixtb}bv$39bl7qBmMTzm%H13V1u30q7uF^vDb z{a|32#ErS4h~l@HL+A6u`Z0k%!@TONFqa_ao3BSB>a+^0lbO(i!(*`P|M`0c!(Qz4 zbQ`df$zSaphk4SouvkvxCdV}331F#*Zw&jIv8?}V+2!yC{}f4&XOvy1a_&V|nj-V%eApm+$KX{gE6tH~pQ>4Pc1SwKd4X1m zR9YNG3e-qll2%B1R@NJ0v0befDNmgB$`D5Zt5_%w#e(NQ!@d@ri`}&7#y)N8!senq z^)!rqLhwFzob(!#zoDN7JdXd8=H0;WOWOBW?C8ivdd_js&bK?VMA}LE&T}4Wn4{`O z4GYDA_C8r1$D)Yt_w?=!{33|-XAME(SncX|!>vkFCEX))dtQ<>H-cl4q>p5`-&W<# oIjx$5ez0igA=Vb?2vmUY0DcCkx0}UhmH+?%07*qoM6N<$g1u_(00000 literal 0 HcmV?d00001 diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index e470b01ce..9b6cfe6fa 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -384,6 +384,17 @@ EmotePtr makeAutoModBadge() Url{"https://dashboard.twitch.tv/settings/moderation/automod"}}); } +EmotePtr makeSharedChatBadge(const QString &sourceName) +{ + return std::make_shared( + Emote{"SharedChat_" + sourceName, + ImageSet{Image::fromResourcePixmap( + getResources().twitch.sharedChat, 0.25)}, + Tooltip{"Shared Message" + + (sourceName.isEmpty() ? "" : " from " + sourceName)}, + Url{"https://link.twitch.tv/SharedChatViewer"}}); +} + } // namespace namespace chatterino { @@ -1156,6 +1167,14 @@ MessagePtr MessageBuilder::build() this->emplace(); } + if (this->sourceName.has_value()) + { + this->emplace( + makeSharedChatBadge(this->sourceName.value()), + MessageElementFlag::BadgeSharedChannel) + ->setLink({Link::UserInfo, sourceName.value()}); + } + this->appendTwitchBadges(); this->appendChatterinoBadges(); @@ -2242,16 +2261,23 @@ void MessageBuilder::parseRoomID() { this->message().flags.set(MessageFlag::SharedMessage); - auto sourceChan = - getApp()->getTwitch()->getChannelOrEmptyByID(sourceRoom); + auto *twitch = getApp()->getTwitch(); + auto sourceChan = twitch->getChannelOrEmptyByID(sourceRoom); if (sourceChan && !sourceChan->isEmpty()) { + this->sourceName = sourceChan->getName(); this->sourceChannel = dynamic_cast(sourceChan.get()); + // avoid duplicate pings this->message().flags.set( MessageFlag::DoNotTriggerNotification); } + else + { + this->sourceName = + twitch->getOrPopulateChannelCache(sourceRoom); + } } } } diff --git a/src/messages/MessageBuilder.hpp b/src/messages/MessageBuilder.hpp index aa2933b64..18e0bb286 100644 --- a/src/messages/MessageBuilder.hpp +++ b/src/messages/MessageBuilder.hpp @@ -157,6 +157,7 @@ public: TwitchChannel *twitchChannel = nullptr; /// The Twitch Channel the message was sent in, according to the Shared Chat feature TwitchChannel *sourceChannel = nullptr; + std::optional sourceName = std::nullopt; Message *operator->(); Message &message(); 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/providers/twitch/TwitchIrcServer.cpp b/src/providers/twitch/TwitchIrcServer.cpp index 9ca93f6fd..f738125da 100644 --- a/src/providers/twitch/TwitchIrcServer.cpp +++ b/src/providers/twitch/TwitchIrcServer.cpp @@ -153,6 +153,7 @@ TwitchIrcServer::TwitchIrcServer() , liveChannel(new Channel("/live", Channel::Type::TwitchLive)) , automodChannel(new Channel("/automod", Channel::Type::TwitchAutomod)) , watchingChannel(Channel::getEmpty(), Channel::Type::TwitchWatching) + , channelNamesById_(512) { // Initialize the connections // XXX: don't create write connection if there is no separate write connection. @@ -1128,6 +1129,38 @@ std::shared_ptr TwitchIrcServer::getChannelOrEmptyByID( return Channel::getEmpty(); } +std::optional TwitchIrcServer::getOrPopulateChannelCache( + const QString &channelId) +{ + { + const auto cache = this->channelNamesById_.access(); + if (cache->exists(channelId)) + { + return cache->get(channelId); + } + + // prevent multiple helix requests for single user + cache->put(channelId, ""); + } + + getHelix()->getUserById( + channelId, + [this](const HelixUser &user) { + const auto cache = this->channelNamesById_.access(); + cache->put(user.id, user.login); + }, + [this, &channelId] { + const auto cache = this->channelNamesById_.access(); + if (cache->exists(channelId) && cache->get(channelId).isEmpty()) + { + // invalidate cache so another helix request can be attempted + cache->remove(channelId); + } + }); + + return {}; +} + QString TwitchIrcServer::cleanChannelName(const QString &dirtyChannelName) { if (dirtyChannelName.startsWith('#')) diff --git a/src/providers/twitch/TwitchIrcServer.hpp b/src/providers/twitch/TwitchIrcServer.hpp index fc3b888ef..532e0afe2 100644 --- a/src/providers/twitch/TwitchIrcServer.hpp +++ b/src/providers/twitch/TwitchIrcServer.hpp @@ -3,10 +3,12 @@ #include "common/Atomic.hpp" #include "common/Channel.hpp" #include "common/Common.hpp" +#include "common/UniqueAccess.hpp" #include "providers/irc/IrcConnection2.hpp" #include "util/RatelimitBucket.hpp" #include +#include #include #include @@ -43,6 +45,9 @@ public: virtual ChannelPtr getOrAddChannel(const QString &dirtyChannelName) = 0; virtual ChannelPtr getChannelOrEmpty(const QString &dirtyChannelName) = 0; + virtual std::optional getOrPopulateChannelCache( + const QString &channelId) = 0; + virtual void addFakeMessage(const QString &data) = 0; virtual void addGlobalSystemMessage(const QString &messageText) = 0; @@ -95,6 +100,9 @@ public: std::shared_ptr getChannelOrEmptyByID( const QString &channelID) override; + std::optional getOrPopulateChannelCache( + const QString &channelId) override; + void reloadAllBTTVChannelEmotes(); void reloadAllFFZChannelEmotes(); void reloadAllSevenTVChannelEmotes(); @@ -190,6 +198,9 @@ private: // https://dev.twitch.tv/docs/irc/guide#rate-limits QObjectPtr joinBucket_; + // cached channel id => name for resolving Shared Chat members + UniqueAccess> channelNamesById_; + QTimer reconnectTimer_; int falloffCounter_ = 1; 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 From 7d0c4738286fef7ec7dfd7ea277454953aae8ad9 Mon Sep 17 00:00:00 2001 From: iProdigy <8106344+iProdigy@users.noreply.github.com> Date: Sat, 19 Oct 2024 03:48:56 -0700 Subject: [PATCH 02/15] chore: update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1aa0e5a5a..137a7126c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ - Major: Improve high-DPI support on Windows. (#4868, #5391) - 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) From c4faca50e0c8ffd54f5a125aff653c572690bed9 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Sat, 19 Oct 2024 04:06:20 -0700 Subject: [PATCH 03/15] chore: update mocks --- mocks/include/mocks/TwitchIrcServer.hpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mocks/include/mocks/TwitchIrcServer.hpp b/mocks/include/mocks/TwitchIrcServer.hpp index d218192b3..8f8de58a8 100644 --- a/mocks/include/mocks/TwitchIrcServer.hpp +++ b/mocks/include/mocks/TwitchIrcServer.hpp @@ -26,6 +26,7 @@ public: , mentionsChannel(std::shared_ptr(new MockChannel("forsen3"))) , liveChannel(std::shared_ptr(new MockChannel("forsen"))) , automodChannel(std::shared_ptr(new MockChannel("forsen2"))) + , channelNamesById_(1) { } @@ -49,6 +50,14 @@ public: return {}; } + std::optional getOrPopulateChannelCache( + const QString &channelId) override + { + assert(false && + "unimplemented getOrPopulateChannelCache in mock irc server"); + return {}; + } + void addFakeMessage(const QString &data) override { } @@ -148,6 +157,7 @@ public: ChannelPtr mentionsChannel; ChannelPtr liveChannel; ChannelPtr automodChannel; + UniqueAccess> channelNamesById_; QString lastUserThatWhisperedMe{"forsen"}; std::unordered_map> mockChannels; From c64523f23cdd50c6fce62a4ef8e5df41e3eb84b9 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Sat, 19 Oct 2024 16:12:25 -0700 Subject: [PATCH 04/15] chore: fix snapshot tests --- mocks/include/mocks/TwitchIrcServer.hpp | 8 ++++++-- .../shared-chat-announcement.json | 18 ++++++++++++++++++ .../IrcMessageHandler/shared-chat-emotes.json | 18 ++++++++++++++++++ .../IrcMessageHandler/shared-chat-known.json | 18 ++++++++++++++++++ .../IrcMessageHandler/shared-chat-unknown.json | 18 ++++++++++++++++++ 5 files changed, 78 insertions(+), 2 deletions(-) diff --git a/mocks/include/mocks/TwitchIrcServer.hpp b/mocks/include/mocks/TwitchIrcServer.hpp index 8f8de58a8..6b305e5ba 100644 --- a/mocks/include/mocks/TwitchIrcServer.hpp +++ b/mocks/include/mocks/TwitchIrcServer.hpp @@ -53,8 +53,12 @@ public: std::optional getOrPopulateChannelCache( const QString &channelId) override { - assert(false && - "unimplemented getOrPopulateChannelCache in mock irc server"); + if (channelId == "11148817") + return "pajlada"; + if (channelId == "141981764") + return "twitchdev"; + if (channelId == "1025594235") + return "shared_chat_test_01"; return {}; } diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json b/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json index 9c6fa2f65..4aa99b775 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": "SharedChat_shared_chat_test_01", + "tooltip": "Shared Message from shared_chat_test_01" + }, + "flags": "BadgeSharedChannel", + "link": { + "type": "UserInfo", + "value": "shared_chat_test_01" + }, + "tooltip": "Shared Message from shared_chat_test_01", + "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..ffac89ba1 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": "SharedChat_twitchdev", + "tooltip": "Shared Message from twitchdev" + }, + "flags": "BadgeSharedChannel", + "link": { + "type": "UserInfo", + "value": "twitchdev" + }, + "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..c01c4a8ae 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": "SharedChat_twitchdev", + "tooltip": "Shared Message from twitchdev" + }, + "flags": "BadgeSharedChannel", + "link": { + "type": "UserInfo", + "value": "twitchdev" + }, + "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..94914f72e 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": "SharedChat_shared_chat_test_01", + "tooltip": "Shared Message from shared_chat_test_01" + }, + "flags": "BadgeSharedChannel", + "link": { + "type": "UserInfo", + "value": "shared_chat_test_01" + }, + "tooltip": "Shared Message from shared_chat_test_01", + "trailingSpace": true, + "type": "BadgeElement" + }, { "emote": { "homePage": "https://www.twitch.tv/jobs?ref=chat_badge", From 5638346d7c9c879675437e15b8f98f62ed01a6fe Mon Sep 17 00:00:00 2001 From: iProdigy Date: Sat, 19 Oct 2024 17:27:09 -0700 Subject: [PATCH 05/15] chore: update shared chat icon --- resources/twitch/sharedChat.png | Bin 2006 -> 2407 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/twitch/sharedChat.png b/resources/twitch/sharedChat.png index 116108c77032a75551cea742eae343bc3423c151..ec9f3f51269cdfdc1939cd30315c11396a960fe4 100644 GIT binary patch delta 2397 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 bRDkaQeg>(xo5g3A00000NkvXXu0mjfe7o_D From 68361054083226f45b1a3a1a48b0a402d4f85ed5 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Sat, 19 Oct 2024 19:14:40 -0700 Subject: [PATCH 06/15] chore: crush png file --- resources/twitch/sharedChat.png | Bin 2407 -> 1026 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/twitch/sharedChat.png b/resources/twitch/sharedChat.png index ec9f3f51269cdfdc1939cd30315c11396a960fe4..f9a66b17c8f1307a811337d4095cfaf1d5133515 100644 GIT binary patch literal 1026 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAk#;Q*fy*NU|NG(!LX|Nr^(e@*uP zd$<4p{rms?ng1U@{9iif|L@=buU-Cs`TYM?i~qlU^Z)U~|I@nvSEm0zxaa@IRsSb6 z|9}4M|DnDA`x^h>xcYzj{QnbM|9}7X|M;Q*i)Q|xH}(Il{{MA(|DQei-(LCu#q<=zrK9*{>1C^clU0Xn)vbhy!sVAg*UfUI2;J^GsxzPd&a=P zbkWnrF(l&f+Z#8_RvCz}J>ckI4mh6Gp1eq8rcA-QsO;bW{7YvAziCqwl*&8j`oBAl z;kEY6ZQZ^9?nM4g+q+Op)#OKEfM=le*R<1Bdz{PpV=u4BdMY%jF<|AI&}yKVL)iAq z*Rn-j#e%dR$gQXeipB1Fb4wiI2 z)>(dif#tKbx}Ez~3nu2QcM!HS*|~?m)L~YaVmrU($G6wNJY6cGRMTu|=CWKQEhf`u($Zu_j(ksaa11KN_h^Z&GV=o?ZM?@%@Y-&FVjXfAll)^oi9wdK|>goDW=gNNsxji~kMF<|(h3uuOo{TaS6teGKZnoa~OJoN)-YzWg&zh?9*|LBGHGu!>011cstd z7EdO$@Uf^g+_5=l@$$e`5tTVg`cFGl8s@Q{yFH<>LdZdwQRMIJ?w$$E4%%nV&s^u1 z=G3rk#(d$s`ZE<7I*)yGDlbU;!&35OowTjU;fv2t);@Yte7&cQq4VFy>Lo%p^SJJK zI2~3Euc}K_xyIP>?9VjsB-v$7^Y-YR^mnqe@L74c%_h^RTXkDcgRw__*SV&Nv%^kk zG&CBdvfN`gls~>aVLzvA((NOh=D#0&F;%tW>_5rgulTY3gV~p2*(FT#Ute&T;CII% zBV%e((49*^vNnknYTa7fH_LYKGA#v9%d6GQTYZ?NqBi;M%~e{R`fb5cHO8qcFTY&u zzFO4$?}U`}H{bT%eHpdo^>xE9O0o8L4SrvH93SN4boPM9;wQjdz~JfX=d#Wzp$Pys C^A0Qk literal 2407 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 Z{}0aneoo8>n!W%4002ovPDHLkV1mX Date: Sun, 20 Oct 2024 21:40:32 -0700 Subject: [PATCH 07/15] docs: explain getOrPopulateChannelCache --- src/messages/MessageBuilder.cpp | 2 +- src/providers/twitch/TwitchIrcServer.hpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index efc885a49..35b360b00 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -2759,7 +2759,7 @@ void MessageBuilder::appendTwitchBadges(const QVariantMap &tags, if (this->message().flags.has(MessageFlag::SharedMessage)) { - QString sourceId = tags["source-room-id"].toString(); + const QString sourceId = tags["source-room-id"].toString(); std::optional sourceName; if (twitchChannel->roomId() == sourceId) { diff --git a/src/providers/twitch/TwitchIrcServer.hpp b/src/providers/twitch/TwitchIrcServer.hpp index 532e0afe2..a590a41f8 100644 --- a/src/providers/twitch/TwitchIrcServer.hpp +++ b/src/providers/twitch/TwitchIrcServer.hpp @@ -100,6 +100,11 @@ public: std::shared_ptr getChannelOrEmptyByID( const QString &channelID) override; + /** + * Obtains the channel login name associated with the passed ID, + * so that Shared Chat messages can provide source channel context. + * Can yield an empty string if a helix request is already in-flight. + */ std::optional getOrPopulateChannelCache( const QString &channelId) override; From 021f1a0bfc7c77c18aaa7f65a62f7263b7db6893 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Sun, 20 Oct 2024 21:48:05 -0700 Subject: [PATCH 08/15] fix: skip UserInfo link if name is empty --- src/messages/MessageBuilder.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index 35b360b00..68e304bf3 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -2773,9 +2773,14 @@ void MessageBuilder::appendTwitchBadges(const QVariantMap &tags, if (sourceName.has_value()) { - this->emplace(makeSharedChatBadge(sourceName.value()), - MessageElementFlag::BadgeSharedChannel) - ->setLink({Link::UserInfo, sourceName.value()}); + const auto &name = sourceName.value(); + auto *badge = this->emplace( + makeSharedChatBadge(name), + MessageElementFlag::BadgeSharedChannel); + if (!name.isEmpty()) + { + badge->setLink({Link::UserInfo, name}); + } } } From de40d42a6064928a44238ddb53a4c4f83ea44fe9 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Mon, 21 Oct 2024 11:22:05 -0700 Subject: [PATCH 09/15] chore: prevent mention on right click --- src/messages/MessageBuilder.cpp | 2 +- src/providers/twitch/TwitchIrcServer.cpp | 2 +- src/providers/twitch/TwitchIrcServer.hpp | 2 +- src/widgets/helper/ChannelView.cpp | 5 +++++ 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index 68e304bf3..873e34ed1 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -2779,7 +2779,7 @@ void MessageBuilder::appendTwitchBadges(const QVariantMap &tags, MessageElementFlag::BadgeSharedChannel); if (!name.isEmpty()) { - badge->setLink({Link::UserInfo, name}); + badge->setLink({Link::UserInfo, "id:" + sourceId}); } } } diff --git a/src/providers/twitch/TwitchIrcServer.cpp b/src/providers/twitch/TwitchIrcServer.cpp index f738125da..898ecc41d 100644 --- a/src/providers/twitch/TwitchIrcServer.cpp +++ b/src/providers/twitch/TwitchIrcServer.cpp @@ -1147,7 +1147,7 @@ std::optional TwitchIrcServer::getOrPopulateChannelCache( channelId, [this](const HelixUser &user) { const auto cache = this->channelNamesById_.access(); - cache->put(user.id, user.login); + cache->put(user.id, user.displayName); }, [this, &channelId] { const auto cache = this->channelNamesById_.access(); diff --git a/src/providers/twitch/TwitchIrcServer.hpp b/src/providers/twitch/TwitchIrcServer.hpp index a590a41f8..757f9448c 100644 --- a/src/providers/twitch/TwitchIrcServer.hpp +++ b/src/providers/twitch/TwitchIrcServer.hpp @@ -101,7 +101,7 @@ public: const QString &channelID) override; /** - * Obtains the channel login name associated with the passed ID, + * Obtains the channel display name associated with the passed ID, * so that Shared Chat messages can provide source channel context. * Can yield an empty string if a helix request is already in-flight. */ 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; From 59bb2fe2e9568c0aa99391b26e5586fb71580f03 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Mon, 21 Oct 2024 12:19:59 -0700 Subject: [PATCH 10/15] chore: remove userinfo link on badge --- src/messages/MessageBuilder.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index 250c3d708..87d4a1a2b 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -2778,14 +2778,8 @@ void MessageBuilder::appendTwitchBadges(const QVariantMap &tags, if (sourceName.has_value()) { - const auto &name = sourceName.value(); - auto *badge = this->emplace( - makeSharedChatBadge(name), - MessageElementFlag::BadgeSharedChannel); - if (!name.isEmpty()) - { - badge->setLink({Link::UserInfo, "id:" + sourceId}); - } + this->emplace(makeSharedChatBadge(sourceName.value()), + MessageElementFlag::BadgeSharedChannel); } } From e326ce428c3414204a5d5ea6b436dabf4956ccd4 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Mon, 21 Oct 2024 12:42:35 -0700 Subject: [PATCH 11/15] chore: update snapshot tests --- .../snapshots/IrcMessageHandler/shared-chat-announcement.json | 4 ++-- tests/snapshots/IrcMessageHandler/shared-chat-emotes.json | 4 ++-- tests/snapshots/IrcMessageHandler/shared-chat-known.json | 4 ++-- tests/snapshots/IrcMessageHandler/shared-chat-unknown.json | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json b/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json index 4aa99b775..e038fce57 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json @@ -75,8 +75,8 @@ }, "flags": "BadgeSharedChannel", "link": { - "type": "UserInfo", - "value": "shared_chat_test_01" + "type": "None", + "value": "" }, "tooltip": "Shared Message from shared_chat_test_01", "trailingSpace": true, diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-emotes.json b/tests/snapshots/IrcMessageHandler/shared-chat-emotes.json index ffac89ba1..f3ef24534 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-emotes.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-emotes.json @@ -75,8 +75,8 @@ }, "flags": "BadgeSharedChannel", "link": { - "type": "UserInfo", - "value": "twitchdev" + "type": "None", + "value": "" }, "tooltip": "Shared Message from twitchdev", "trailingSpace": true, diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-known.json b/tests/snapshots/IrcMessageHandler/shared-chat-known.json index c01c4a8ae..653ccb0ff 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-known.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-known.json @@ -75,8 +75,8 @@ }, "flags": "BadgeSharedChannel", "link": { - "type": "UserInfo", - "value": "twitchdev" + "type": "None", + "value": "" }, "tooltip": "Shared Message from twitchdev", "trailingSpace": true, diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json b/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json index 94914f72e..6eaf5e5be 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json @@ -75,8 +75,8 @@ }, "flags": "BadgeSharedChannel", "link": { - "type": "UserInfo", - "value": "shared_chat_test_01" + "type": "None", + "value": "" }, "tooltip": "Shared Message from shared_chat_test_01", "trailingSpace": true, From d2681a0b43875c13f8b7cab343467a4d4d2db315 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Mon, 21 Oct 2024 13:27:30 -0700 Subject: [PATCH 12/15] fix: capture channelId by value instead of ref --- src/providers/twitch/TwitchIrcServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/twitch/TwitchIrcServer.cpp b/src/providers/twitch/TwitchIrcServer.cpp index 898ecc41d..16a48f1b7 100644 --- a/src/providers/twitch/TwitchIrcServer.cpp +++ b/src/providers/twitch/TwitchIrcServer.cpp @@ -1149,7 +1149,7 @@ std::optional TwitchIrcServer::getOrPopulateChannelCache( const auto cache = this->channelNamesById_.access(); cache->put(user.id, user.displayName); }, - [this, &channelId] { + [this, channelId] { const auto cache = this->channelNamesById_.access(); if (cache->exists(channelId) && cache->get(channelId).isEmpty()) { From 0163474a9317db96b45ad67e943fbd892c667929 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Mon, 21 Oct 2024 15:11:04 -0700 Subject: [PATCH 13/15] refactor: utilize TwitchUsers::resolveID --- lib/lrucache/lrucache/lrucache.hpp | 11 ------- mocks/include/mocks/BaseApplication.hpp | 7 ++++ mocks/include/mocks/TwitchIrcServer.hpp | 14 -------- mocks/include/mocks/TwitchUsers.hpp | 24 ++++++++++++++ src/messages/MessageBuilder.cpp | 18 +++++----- src/providers/twitch/TwitchIrcServer.cpp | 33 ------------------- src/providers/twitch/TwitchIrcServer.hpp | 16 --------- .../shared-chat-announcement.json | 6 ++-- .../shared-chat-unknown.json | 6 ++-- 9 files changed, 47 insertions(+), 88 deletions(-) create mode 100644 mocks/include/mocks/TwitchUsers.hpp diff --git a/lib/lrucache/lrucache/lrucache.hpp b/lib/lrucache/lrucache/lrucache.hpp index 183bc3b02..ef7d031db 100644 --- a/lib/lrucache/lrucache/lrucache.hpp +++ b/lib/lrucache/lrucache/lrucache.hpp @@ -71,17 +71,6 @@ public: } } - void remove(const key_t &key) - { - auto it = _cache_items_map.find(key); - if (it == _cache_items_map.end()) - { - throw std::range_error("There is no such key in cache"); - } - _cache_items_list.erase(it->second); - _cache_items_map.erase(it); - } - const value_t &get(const key_t &key) { auto it = _cache_items_map.find(key); 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/TwitchIrcServer.hpp b/mocks/include/mocks/TwitchIrcServer.hpp index 6b305e5ba..d218192b3 100644 --- a/mocks/include/mocks/TwitchIrcServer.hpp +++ b/mocks/include/mocks/TwitchIrcServer.hpp @@ -26,7 +26,6 @@ public: , mentionsChannel(std::shared_ptr(new MockChannel("forsen3"))) , liveChannel(std::shared_ptr(new MockChannel("forsen"))) , automodChannel(std::shared_ptr(new MockChannel("forsen2"))) - , channelNamesById_(1) { } @@ -50,18 +49,6 @@ public: return {}; } - std::optional getOrPopulateChannelCache( - const QString &channelId) override - { - if (channelId == "11148817") - return "pajlada"; - if (channelId == "141981764") - return "twitchdev"; - if (channelId == "1025594235") - return "shared_chat_test_01"; - return {}; - } - void addFakeMessage(const QString &data) override { } @@ -161,7 +148,6 @@ public: ChannelPtr mentionsChannel; ChannelPtr liveChannel; ChannelPtr automodChannel; - UniqueAccess> channelNamesById_; QString lastUserThatWhisperedMe{"forsen"}; std::unordered_map> mockChannels; 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/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index 87d4a1a2b..49b6399cd 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" @@ -2765,22 +2766,23 @@ void MessageBuilder::appendTwitchBadges(const QVariantMap &tags, if (this->message().flags.has(MessageFlag::SharedMessage)) { const QString sourceId = tags["source-room-id"].toString(); - std::optional sourceName; - if (twitchChannel->roomId() == sourceId) + QString sourceName; + if (sourceId.isEmpty()) + { + sourceName = ""; + } + else if (twitchChannel->roomId() == sourceId) { sourceName = twitchChannel->getName(); } else { sourceName = - getApp()->getTwitch()->getOrPopulateChannelCache(sourceId); + getApp()->getTwitchUsers()->resolveID({sourceId})->displayName; } - if (sourceName.has_value()) - { - this->emplace(makeSharedChatBadge(sourceName.value()), - MessageElementFlag::BadgeSharedChannel); - } + this->emplace(makeSharedChatBadge(sourceName), + MessageElementFlag::BadgeSharedChannel); } auto badgeInfos = parseBadgeInfoTag(tags); diff --git a/src/providers/twitch/TwitchIrcServer.cpp b/src/providers/twitch/TwitchIrcServer.cpp index 16a48f1b7..9ca93f6fd 100644 --- a/src/providers/twitch/TwitchIrcServer.cpp +++ b/src/providers/twitch/TwitchIrcServer.cpp @@ -153,7 +153,6 @@ TwitchIrcServer::TwitchIrcServer() , liveChannel(new Channel("/live", Channel::Type::TwitchLive)) , automodChannel(new Channel("/automod", Channel::Type::TwitchAutomod)) , watchingChannel(Channel::getEmpty(), Channel::Type::TwitchWatching) - , channelNamesById_(512) { // Initialize the connections // XXX: don't create write connection if there is no separate write connection. @@ -1129,38 +1128,6 @@ std::shared_ptr TwitchIrcServer::getChannelOrEmptyByID( return Channel::getEmpty(); } -std::optional TwitchIrcServer::getOrPopulateChannelCache( - const QString &channelId) -{ - { - const auto cache = this->channelNamesById_.access(); - if (cache->exists(channelId)) - { - return cache->get(channelId); - } - - // prevent multiple helix requests for single user - cache->put(channelId, ""); - } - - getHelix()->getUserById( - channelId, - [this](const HelixUser &user) { - const auto cache = this->channelNamesById_.access(); - cache->put(user.id, user.displayName); - }, - [this, channelId] { - const auto cache = this->channelNamesById_.access(); - if (cache->exists(channelId) && cache->get(channelId).isEmpty()) - { - // invalidate cache so another helix request can be attempted - cache->remove(channelId); - } - }); - - return {}; -} - QString TwitchIrcServer::cleanChannelName(const QString &dirtyChannelName) { if (dirtyChannelName.startsWith('#')) diff --git a/src/providers/twitch/TwitchIrcServer.hpp b/src/providers/twitch/TwitchIrcServer.hpp index 757f9448c..fc3b888ef 100644 --- a/src/providers/twitch/TwitchIrcServer.hpp +++ b/src/providers/twitch/TwitchIrcServer.hpp @@ -3,12 +3,10 @@ #include "common/Atomic.hpp" #include "common/Channel.hpp" #include "common/Common.hpp" -#include "common/UniqueAccess.hpp" #include "providers/irc/IrcConnection2.hpp" #include "util/RatelimitBucket.hpp" #include -#include #include #include @@ -45,9 +43,6 @@ public: virtual ChannelPtr getOrAddChannel(const QString &dirtyChannelName) = 0; virtual ChannelPtr getChannelOrEmpty(const QString &dirtyChannelName) = 0; - virtual std::optional getOrPopulateChannelCache( - const QString &channelId) = 0; - virtual void addFakeMessage(const QString &data) = 0; virtual void addGlobalSystemMessage(const QString &messageText) = 0; @@ -100,14 +95,6 @@ public: std::shared_ptr getChannelOrEmptyByID( const QString &channelID) override; - /** - * Obtains the channel display name associated with the passed ID, - * so that Shared Chat messages can provide source channel context. - * Can yield an empty string if a helix request is already in-flight. - */ - std::optional getOrPopulateChannelCache( - const QString &channelId) override; - void reloadAllBTTVChannelEmotes(); void reloadAllFFZChannelEmotes(); void reloadAllSevenTVChannelEmotes(); @@ -203,9 +190,6 @@ private: // https://dev.twitch.tv/docs/irc/guide#rate-limits QObjectPtr joinBucket_; - // cached channel id => name for resolving Shared Chat members - UniqueAccess> channelNamesById_; - QTimer reconnectTimer_; int falloffCounter_ = 1; diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json b/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json index e038fce57..7e3ad232a 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json @@ -70,15 +70,15 @@ "images": { "1x": "" }, - "name": "SharedChat_shared_chat_test_01", - "tooltip": "Shared Message from shared_chat_test_01" + "name": "SharedChat_", + "tooltip": "Shared Message" }, "flags": "BadgeSharedChannel", "link": { "type": "None", "value": "" }, - "tooltip": "Shared Message from shared_chat_test_01", + "tooltip": "Shared Message", "trailingSpace": true, "type": "BadgeElement" }, diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json b/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json index 6eaf5e5be..a99e1b1e9 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json @@ -70,15 +70,15 @@ "images": { "1x": "" }, - "name": "SharedChat_shared_chat_test_01", - "tooltip": "Shared Message from shared_chat_test_01" + "name": "SharedChat_", + "tooltip": "Shared Message" }, "flags": "BadgeSharedChannel", "link": { "type": "None", "value": "" }, - "tooltip": "Shared Message from shared_chat_test_01", + "tooltip": "Shared Message", "trailingSpace": true, "type": "BadgeElement" }, From c7512dc73437524cfcef0df6042a36d4bc379a50 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Mon, 21 Oct 2024 15:20:15 -0700 Subject: [PATCH 14/15] chore: remove emote name for badge --- src/messages/MessageBuilder.cpp | 2 +- tests/snapshots/IrcMessageHandler/shared-chat-announcement.json | 2 +- tests/snapshots/IrcMessageHandler/shared-chat-emotes.json | 2 +- tests/snapshots/IrcMessageHandler/shared-chat-known.json | 2 +- tests/snapshots/IrcMessageHandler/shared-chat-unknown.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index 49b6399cd..411f170d7 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -384,7 +384,7 @@ EmotePtr makeAutoModBadge() EmotePtr makeSharedChatBadge(const QString &sourceName) { return std::make_shared( - Emote{"SharedChat_" + sourceName, + Emote{EmoteName{}, ImageSet{Image::fromResourcePixmap( getResources().twitch.sharedChat, 0.25)}, Tooltip{"Shared Message" + diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json b/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json index 7e3ad232a..a6877c365 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json @@ -70,7 +70,7 @@ "images": { "1x": "" }, - "name": "SharedChat_", + "name": "", "tooltip": "Shared Message" }, "flags": "BadgeSharedChannel", diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-emotes.json b/tests/snapshots/IrcMessageHandler/shared-chat-emotes.json index f3ef24534..ba6485c21 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-emotes.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-emotes.json @@ -70,7 +70,7 @@ "images": { "1x": "" }, - "name": "SharedChat_twitchdev", + "name": "", "tooltip": "Shared Message from twitchdev" }, "flags": "BadgeSharedChannel", diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-known.json b/tests/snapshots/IrcMessageHandler/shared-chat-known.json index 653ccb0ff..c4a3f3e2f 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-known.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-known.json @@ -70,7 +70,7 @@ "images": { "1x": "" }, - "name": "SharedChat_twitchdev", + "name": "", "tooltip": "Shared Message from twitchdev" }, "flags": "BadgeSharedChannel", diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json b/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json index a99e1b1e9..6ea80b2ce 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json @@ -70,7 +70,7 @@ "images": { "1x": "" }, - "name": "SharedChat_", + "name": "", "tooltip": "Shared Message" }, "flags": "BadgeSharedChannel", From 6d376b5416e0a1003989bd88274b1d0b1803ac18 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Mon, 21 Oct 2024 15:23:52 -0700 Subject: [PATCH 15/15] chore: reformat emote initializer --- src/messages/MessageBuilder.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index 411f170d7..601ba8ec9 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -383,13 +383,14 @@ EmotePtr makeAutoModBadge() EmotePtr makeSharedChatBadge(const QString &sourceName) { - return std::make_shared( - Emote{EmoteName{}, - ImageSet{Image::fromResourcePixmap( - getResources().twitch.sharedChat, 0.25)}, - Tooltip{"Shared Message" + - (sourceName.isEmpty() ? "" : " from " + sourceName)}, - Url{"https://link.twitch.tv/SharedChatViewer"}}); + 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(