diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cfa3bee851..e310e203c8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unversioned +- Major: Allow use of Twitch follower emotes in other channels if subscribed. (#4922) - Minor: Migrate to the new Get Channel Followers Helix endpoint, fixing follower count not showing up in usercards. (#4809) - Minor: The account switcher is now styled to match your theme. (#4817) - Minor: Add an invisible resize handle to the bottom of frameless user info popups and reply thread popups. (#4795) diff --git a/src/providers/twitch/TwitchAccount.cpp b/src/providers/twitch/TwitchAccount.cpp index 6f597c46664..dc435fef47f 100644 --- a/src/providers/twitch/TwitchAccount.cpp +++ b/src/providers/twitch/TwitchAccount.cpp @@ -264,11 +264,32 @@ void TwitchAccount::loadUserstateEmotes(std::weak_ptr weakChannel) [this, weakChannel](QJsonArray emoteSetArray) { auto emoteData = this->emotes_.access(); auto localEmoteData = this->localEmotes_.access(); - for (auto emoteSet_ : emoteSetArray) + + std::unordered_set subscriberChannelIDs; + std::vector ivrEmoteSets; + ivrEmoteSets.reserve(emoteSetArray.size()); + + for (auto emoteSet : emoteSetArray) { - auto emoteSet = std::make_shared(); + IvrEmoteSet ivrEmoteSet(emoteSet.toObject()); + if (!ivrEmoteSet.tier.isNull()) + { + subscriberChannelIDs.insert(ivrEmoteSet.channelId); + } + ivrEmoteSets.emplace_back(ivrEmoteSet); + } + + for (const auto &emoteSet : emoteData->emoteSets) + { + if (emoteSet->subscriber) + { + subscriberChannelIDs.insert(emoteSet->channelID); + } + } - IvrEmoteSet ivrEmoteSet(emoteSet_.toObject()); + for (const auto &ivrEmoteSet : ivrEmoteSets) + { + auto emoteSet = std::make_shared(); QString setKey = ivrEmoteSet.setId; emoteSet->key = setKey; @@ -285,8 +306,15 @@ void TwitchAccount::loadUserstateEmotes(std::weak_ptr weakChannel) continue; } + emoteSet->channelID = ivrEmoteSet.channelId; emoteSet->channelName = ivrEmoteSet.login; emoteSet->text = ivrEmoteSet.displayName; + emoteSet->subscriber = !ivrEmoteSet.tier.isNull(); + + // NOTE: If a user does not have a subscriber emote set, but a follower emote set, this logic will be wrong + // However, that's not a realistic problem. + bool haveSubscriberSetForChannel = + subscriberChannelIDs.contains(ivrEmoteSet.channelId); for (const auto &emoteObj : ivrEmoteSet.emotes) { @@ -302,7 +330,9 @@ void TwitchAccount::loadUserstateEmotes(std::weak_ptr weakChannel) getApp()->emotes->twitch.getOrCreateEmote(id, code); // Follower emotes can be only used in their origin channel - if (ivrEmote.emoteType == "FOLLOWER") + // unless the user is subscribed, then they can be used anywhere. + if (ivrEmote.emoteType == "FOLLOWER" && + !haveSubscriberSetForChannel) { emoteSet->local = true; diff --git a/src/providers/twitch/TwitchAccount.hpp b/src/providers/twitch/TwitchAccount.hpp index e68624d0d9c..69032b80c9c 100644 --- a/src/providers/twitch/TwitchAccount.hpp +++ b/src/providers/twitch/TwitchAccount.hpp @@ -36,7 +36,9 @@ class TwitchAccount : public Account struct EmoteSet { QString key; QString channelName; + QString channelID; QString text; + bool subscriber{false}; bool local{false}; std::vector emotes; };