diff --git a/src/common/CompletionModel.cpp b/src/common/CompletionModel.cpp index f6dfd5b0c..9e2f4d024 100644 --- a/src/common/CompletionModel.cpp +++ b/src/common/CompletionModel.cpp @@ -137,7 +137,7 @@ void CompletionModel::refresh() getApp() ->twitch2->getChannelOrEmptyByID(this->channelName_) .get())) { - auto bttv = channel->accessBttvEmotes(); + auto bttv = channel->bttvEmotes(); // auto it = bttv->begin(); // for (const auto &emote : *bttv) { // } @@ -148,7 +148,7 @@ void CompletionModel::refresh() // } // Channel-specific: FFZ Channel Emotes - for (const auto &emote : *channel->accessFfzEmotes()) { + for (const auto &emote : *channel->ffzEmotes()) { this->addString(emote.second->name.string, TaggedString::Type::FFZChannelEmote); } diff --git a/src/messages/Emote.cpp b/src/messages/Emote.cpp index c5ff83e12..318e2d300 100644 --- a/src/messages/Emote.cpp +++ b/src/messages/Emote.cpp @@ -24,4 +24,22 @@ EmotePtr cachedOrMakeEmotePtr(Emote &&emote, const EmoteMap &cache) return std::make_shared(std::move(emote)); } +EmotePtr cachedOrMakeEmotePtr( + Emote &&emote, + std::unordered_map> &cache, + std::mutex &mutex, const EmoteId &id) +{ + std::lock_guard guard(mutex); + + auto shared = cache[id].lock(); + if (shared && *shared == emote) { + // reuse old shared_ptr if nothing changed + return shared; + } else { + shared = std::make_shared(std::move(emote)); + cache[id] = shared; + return shared; + } +} + } // namespace chatterino diff --git a/src/messages/Emote.hpp b/src/messages/Emote.hpp index cfde58492..89cadc623 100644 --- a/src/messages/Emote.hpp +++ b/src/messages/Emote.hpp @@ -38,5 +38,9 @@ using WeakEmoteMap = std::unordered_map>; using WeakEmoteIdMap = std::unordered_map>; EmotePtr cachedOrMakeEmotePtr(Emote &&emote, const EmoteMap &cache); +EmotePtr cachedOrMakeEmotePtr( + Emote &&emote, + std::unordered_map> &cache, + std::mutex &mutex, const EmoteId &id); } // namespace chatterino diff --git a/src/messages/Image.cpp b/src/messages/Image.cpp index 56286cdfe..572432cdd 100644 --- a/src/messages/Image.cpp +++ b/src/messages/Image.cpp @@ -30,14 +30,27 @@ Frames::Frames() Frames::Frames(const QVector> &frames) : items_(frames) { + assertInGuiThread(); DebugCount::increase("images"); - if (this->animated()) DebugCount::increase("animated images"); + + if (this->animated()) { + DebugCount::increase("animated images"); + + this->gifTimerConnection_ = getApp()->emotes->gifTimer.signal.connect( + [this] { this->advance(); }); + } } Frames::~Frames() { + assertInGuiThread(); DebugCount::decrease("images"); - if (this->animated()) DebugCount::decrease("animated images"); + + if (this->animated()) { + DebugCount::decrease("animated images"); + } + + this->gifTimerConnection_.disconnect(); } void Frames::advance() @@ -46,6 +59,11 @@ void Frames::advance() while (true) { this->index_ %= this->items_.size(); + + if (this->index_ >= this->items_.size()) { + this->index_ = this->index_; + } + if (this->durationOffset_ > this->items_[this->index_].duration) { this->durationOffset_ -= this->items_[this->index_].duration; this->index_ = (this->index_ + 1) % this->items_.size(); @@ -195,12 +213,14 @@ Image::Image(const Url &url, qreal scale) : url_(url) , scale_(scale) , shouldLoad_(true) + , frames_(std::make_unique()) { } Image::Image(const QPixmap &pixmap, qreal scale) : scale_(scale) - , frames_(QVector>{Frame{pixmap, 1}}) + , frames_(std::make_unique( + QVector>{Frame{pixmap, 1}})) { } @@ -218,7 +238,7 @@ boost::optional Image::pixmap() const const_cast(this)->load(); } - return this->frames_.current(); + return this->frames_->current(); } qreal Image::scale() const @@ -235,14 +255,14 @@ bool Image::animated() const { assertInGuiThread(); - return this->frames_.animated(); + return this->frames_->animated(); } int Image::width() const { assertInGuiThread(); - if (auto pixmap = this->frames_.first()) + if (auto pixmap = this->frames_->first()) return pixmap->width() * this->scale_; else return 16; @@ -252,7 +272,7 @@ int Image::height() const { assertInGuiThread(); - if (auto pixmap = this->frames_.first()) + if (auto pixmap = this->frames_->first()) return pixmap->height() * this->scale_; else return 16; @@ -277,7 +297,8 @@ void Image::load() auto parsed = readFrames(reader, that->url()); postToThread(makeConvertCallback(parsed, [weak](auto frames) { - if (auto shared = weak.lock()) shared->frames_ = frames; + if (auto shared = weak.lock()) + shared->frames_ = std::make_unique(frames); })); return Success; @@ -290,7 +311,7 @@ bool Image::operator==(const Image &other) const { if (this->isEmpty() && other.isEmpty()) return true; if (!this->url_.string.isEmpty() && this->url_ == other.url_) return true; - if (this->frames_.first() == other.frames_.first()) return true; + if (this->frames_->first() == other.frames_->first()) return true; return false; } diff --git a/src/messages/Image.hpp b/src/messages/Image.hpp index 511ae9c48..3d869f798 100644 --- a/src/messages/Image.hpp +++ b/src/messages/Image.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "common/NullablePtr.hpp" @@ -21,14 +22,12 @@ struct Frame { Image image; int duration; }; -class Frames +class Frames : boost::noncopyable { public: Frames(); Frames(const QVector> &frames); ~Frames(); - Frames(Frames &&other) = default; - Frames &operator=(Frames &&other) = default; bool animated() const; void advance(); @@ -39,6 +38,7 @@ private: QVector> items_; int index_{0}; int durationOffset_{0}; + pajlada::Signals::Connection gifTimerConnection_; }; } // namespace @@ -74,7 +74,7 @@ private: qreal scale_{1}; bool empty_{false}; bool shouldLoad_{false}; - Frames frames_{}; + std::unique_ptr frames_{}; QObject object_{}; }; } // namespace chatterino diff --git a/src/providers/bttv/BttvEmotes.cpp b/src/providers/bttv/BttvEmotes.cpp index 072212c21..1d9115c09 100644 --- a/src/providers/bttv/BttvEmotes.cpp +++ b/src/providers/bttv/BttvEmotes.cpp @@ -44,8 +44,45 @@ std::pair parseGlobalEmotes(const QJsonObject &jsonRoot, return {Success, std::move(emotes)}; } +EmotePtr cachedOrMake(Emote &&emote, const EmoteId &id) +{ + static std::unordered_map> cache; + static std::mutex mutex; + + return cachedOrMakeEmotePtr(std::move(emote), cache, mutex, id); +} +std::pair parseChannelEmotes(const QJsonObject &jsonRoot) +{ + auto emotes = EmoteMap(); + auto jsonEmotes = jsonRoot.value("emotes").toArray(); + auto urlTemplate = "https:" + jsonRoot.value("urlTemplate").toString(); + + for (auto jsonEmote_ : jsonEmotes) { + auto jsonEmote = jsonEmote_.toObject(); + + auto id = EmoteId{jsonEmote.value("id").toString()}; + auto name = EmoteName{jsonEmote.value("code").toString()}; + // emoteObject.value("imageType").toString(); + + auto emote = Emote( + {name, + ImageSet{ + Image::fromUrl(getEmoteLink(urlTemplate, id, "1x"), 1), + Image::fromUrl(getEmoteLink(urlTemplate, id, "2x"), 0.5), + Image::fromUrl(getEmoteLink(urlTemplate, id, "3x"), 0.25)}, + Tooltip{name.string + "
Channel Bttv Emote"}, + Url{"https://manage.betterttv.net/emotes/" + id.string}}); + + emotes[name] = cachedOrMake(std::move(emote), id); + } + + return {Success, std::move(emotes)}; +} } // namespace +// +// BttvEmotes +// BttvEmotes::BttvEmotes() : global_(std::make_shared()) { @@ -84,4 +121,31 @@ void BttvEmotes::loadGlobal() request.execute(); } +void BttvEmotes::loadChannel(const QString &channelName, + std::function callback) +{ + auto request = + NetworkRequest(QString(bttvChannelEmoteApiUrl) + channelName); + + request.setCaller(QThread::currentThread()); + request.setTimeout(3000); + + request.onSuccess([callback = std::move(callback)](auto result) -> Outcome { + auto pair = parseChannelEmotes(result.parseJson()); + if (pair.first) callback(std::move(pair.second)); + return pair.first; + }); + + request.execute(); +} + +static Url getEmoteLink(QString urlTemplate, const EmoteId &id, + const QString &emoteScale) +{ + urlTemplate.detach(); + + return {urlTemplate.replace("{{id}}", id.string) + .replace("{{image}}", emoteScale)}; +} + } // namespace chatterino diff --git a/src/providers/bttv/BttvEmotes.hpp b/src/providers/bttv/BttvEmotes.hpp index 510f873fc..7392cd6bc 100644 --- a/src/providers/bttv/BttvEmotes.hpp +++ b/src/providers/bttv/BttvEmotes.hpp @@ -10,6 +10,8 @@ class BttvEmotes final { static constexpr const char *globalEmoteApiUrl = "https://api.betterttv.net/2/emotes"; + static constexpr const char *bttvChannelEmoteApiUrl = + "https://api.betterttv.net/2/channels/"; public: BttvEmotes(); @@ -17,6 +19,8 @@ public: std::shared_ptr global() const; boost::optional global(const EmoteName &name) const; void loadGlobal(); + static void loadChannel(const QString &channelName, + std::function callback); private: Atomic> global_; diff --git a/src/providers/bttv/LoadBttvChannelEmote.cpp b/src/providers/bttv/LoadBttvChannelEmote.cpp index ccc52a130..f5116cb02 100644 --- a/src/providers/bttv/LoadBttvChannelEmote.cpp +++ b/src/providers/bttv/LoadBttvChannelEmote.cpp @@ -11,78 +11,4 @@ namespace chatterino { -static Url getEmoteLink(QString urlTemplate, const EmoteId &id, - const QString &emoteScale); -static std::pair bttvParseChannelEmotes( - const QJsonObject &jsonRoot); - -void loadBttvChannelEmotes(const QString &channelName, - std::function callback) -{ - auto request = - NetworkRequest(QString(bttvChannelEmoteApiUrl) + channelName); - - request.setCaller(QThread::currentThread()); - request.setTimeout(3000); - request.onSuccess([callback = std::move(callback)](auto result) -> Outcome { - auto pair = bttvParseChannelEmotes(result.parseJson()); - - if (pair.first == Success) callback(std::move(pair.second)); - - return pair.first; - }); - - request.execute(); -} - -static std::pair bttvParseChannelEmotes( - const QJsonObject &jsonRoot) -{ - static UniqueAccess>> - cache_; - - auto cache = cache_.access(); - auto emotes = EmoteMap(); - auto jsonEmotes = jsonRoot.value("emotes").toArray(); - auto urlTemplate = - QString("https:" + jsonRoot.value("urlTemplate").toString()); - - for (auto jsonEmote_ : jsonEmotes) { - auto jsonEmote = jsonEmote_.toObject(); - - auto id = EmoteId{jsonEmote.value("id").toString()}; - auto name = EmoteName{jsonEmote.value("code").toString()}; - // emoteObject.value("imageType").toString(); - - auto emote = Emote( - {name, - ImageSet{ - Image::fromUrl(getEmoteLink(urlTemplate, id, "1x"), 1), - Image::fromUrl(getEmoteLink(urlTemplate, id, "2x"), 0.5), - Image::fromUrl(getEmoteLink(urlTemplate, id, "3x"), 0.25)}, - Tooltip{name.string + "
Channel Bttv Emote"}, - Url{"https://manage.betterttv.net/emotes/" + id.string}}); - - auto shared = (*cache)[id].lock(); - if (shared && *shared == emote) { - // reuse old shared_ptr if nothing changed - emotes[name] = shared; - } else { - (*cache)[id] = emotes[name] = - std::make_shared(std::move(emote)); - } - } - - return {Success, std::move(emotes)}; -} - -static Url getEmoteLink(QString urlTemplate, const EmoteId &id, - const QString &emoteScale) -{ - urlTemplate.detach(); - - return {urlTemplate.replace("{{id}}", id.string) - .replace("{{image}}", emoteScale)}; -} - } // namespace chatterino diff --git a/src/providers/bttv/LoadBttvChannelEmote.hpp b/src/providers/bttv/LoadBttvChannelEmote.hpp index ffbb50469..d0fa5cc32 100644 --- a/src/providers/bttv/LoadBttvChannelEmote.hpp +++ b/src/providers/bttv/LoadBttvChannelEmote.hpp @@ -6,11 +6,4 @@ class QString; namespace chatterino { -class EmoteMap; -constexpr const char *bttvChannelEmoteApiUrl = - "https://api.betterttv.net/2/channels/"; - -void loadBttvChannelEmotes(const QString &channelName, - std::function callback); - } // namespace chatterino diff --git a/src/providers/ffz/FfzEmotes.cpp b/src/providers/ffz/FfzEmotes.cpp index be6d776c6..24341a817 100644 --- a/src/providers/ffz/FfzEmotes.cpp +++ b/src/providers/ffz/FfzEmotes.cpp @@ -33,6 +33,13 @@ void fillInEmoteData(const QJsonObject &urls, const EmoteName &name, Image::fromUrl(url3x, 0.25)}; emoteData.tooltip = {tooltip}; } +EmotePtr cachedOrMake(Emote &&emote, const EmoteId &id) +{ + static std::unordered_map> cache; + static std::mutex mutex; + + return cachedOrMakeEmotePtr(std::move(emote), cache, mutex, id); +} std::pair parseGlobalEmotes(const QJsonObject &jsonRoot, const EmoteMap ¤tEmotes) { @@ -64,6 +71,36 @@ std::pair parseGlobalEmotes(const QJsonObject &jsonRoot, return {Success, std::move(emotes)}; } +std::pair parseChannelEmotes(const QJsonObject &jsonRoot) +{ + auto jsonSets = jsonRoot.value("sets").toObject(); + auto emotes = EmoteMap(); + + for (auto jsonSet : jsonSets) { + auto jsonEmotes = jsonSet.toObject().value("emoticons").toArray(); + + for (auto _jsonEmote : jsonEmotes) { + auto jsonEmote = _jsonEmote.toObject(); + + // margins + auto id = EmoteId{QString::number(jsonEmote.value("id").toInt())}; + auto name = EmoteName{jsonEmote.value("name").toString()}; + auto urls = jsonEmote.value("urls").toObject(); + + Emote emote; + fillInEmoteData(urls, name, name.string + "
Channel FFZ Emote", + emote); + emote.homePage = + Url{QString("https://www.frankerfacez.com/emoticon/%1-%2") + .arg(id.string) + .arg(name.string)}; + + emotes[name] = cachedOrMake(std::move(emote), id); + } + } + + return {Success, std::move(emotes)}; +} } // namespace FfzEmotes::FfzEmotes() @@ -107,66 +144,20 @@ void FfzEmotes::loadGlobal() void FfzEmotes::loadChannel(const QString &channelName, std::function callback) { - // printf("[FFZEmotes] Reload FFZ Channel Emotes for channel %s\n", - // qPrintable(channelName)); + log("[FFZEmotes] Reload FFZ Channel Emotes for channel %s\n", channelName); - // QString url("https://api.frankerfacez.com/v1/room/" + channelName); + NetworkRequest request("https://api.frankerfacez.com/v1/room/" + + channelName); + request.setCaller(QThread::currentThread()); + request.setTimeout(3000); - // NetworkRequest request(url); - // request.setCaller(QThread::currentThread()); - // request.setTimeout(3000); - // request.onSuccess([this, channelName, _map](auto result) -> Outcome { - // return this->parseChannelEmotes(result.parseJson()); - //}); + request.onSuccess([callback = std::move(callback)](auto result) -> Outcome { + auto pair = parseChannelEmotes(result.parseJson()); + if (pair.first) callback(std::move(pair.second)); + return pair.first; + }); - // request.execute(); -} - -Outcome parseChannelEmotes(const QJsonObject &jsonRoot) -{ - // auto rootNode = result.parseJson(); - // auto map = _map.lock(); - - // if (_map.expired()) { - // return false; - //} - - // map->clear(); - - // auto setsNode = rootNode.value("sets").toObject(); - - // std::vector codes; - // for (const QJsonValue &setNode : setsNode) { - // auto emotesNode = setNode.toObject().value("emoticons").toArray(); - - // for (const QJsonValue &emoteNode : emotesNode) { - // QJsonObject emoteObject = emoteNode.toObject(); - - // // margins - // int id = emoteObject.value("id").toInt(); - // QString code = emoteObject.value("name").toString(); - - // QJsonObject urls = emoteObject.value("urls").toObject(); - - // auto emote = this->channelEmoteCache_.getOrAdd(id, [id, &code, - // &urls] { - // EmoteData emoteData; - // fillInEmoteData(urls, code, code + "
Channel FFZ Emote", - // emoteData); emoteData.pageLink = - // QString("https://www.frankerfacez.com/emoticon/%1-%2").arg(id).arg(code); - - // return emoteData; - // }); - - // this->channelEmotes.insert(code, emote); - // map->insert(code, emote); - // codes.push_back(code); - // } - - // this->channelEmoteCodes[channelName] = codes; - //} - - return Success; + request.execute(); } } // namespace chatterino diff --git a/src/providers/ffz/FfzEmotes.hpp b/src/providers/ffz/FfzEmotes.hpp index ccc850bff..cc75b09bd 100644 --- a/src/providers/ffz/FfzEmotes.hpp +++ b/src/providers/ffz/FfzEmotes.hpp @@ -1,14 +1,12 @@ #pragma once #include - #include "common/Atomic.hpp" #include "messages/Emote.hpp" -#include "messages/EmoteCache.hpp" namespace chatterino { -class FfzEmotes final : std::enable_shared_from_this +class FfzEmotes final { static constexpr const char *globalEmoteApiUrl = "https://api.frankerfacez.com/v1/set/global"; @@ -20,10 +18,9 @@ public: std::shared_ptr global() const; boost::optional global(const EmoteName &name) const; - void loadGlobal(); - void loadChannel(const QString &channelName, - std::function callback); + static void loadChannel(const QString &channelName, + std::function callback); private: Atomic> global_; diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index 4d9445c75..7e0a1efc1 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -5,6 +5,7 @@ #include "controllers/accounts/AccountController.hpp" #include "debug/Log.hpp" #include "messages/Message.hpp" +#include "providers/bttv/BttvEmotes.hpp" #include "providers/bttv/LoadBttvChannelEmote.hpp" #include "providers/twitch/PubsubClient.hpp" #include "providers/twitch/TwitchCommon.hpp" @@ -51,6 +52,8 @@ auto parseRecentMessages(const QJsonObject &jsonRoot, TwitchChannel &channel) TwitchChannel::TwitchChannel(const QString &name) : Channel(name, Channel::Type::Twitch) + , bttvEmotes_(std::make_shared()) + , ffzEmotes_(std::make_shared()) , subscriptionUrl_("https://www.twitch.tv/subs/" + name) , channelUrl_("https://twitch.tv/" + name) , popoutPlayerUrl_("https://player.twitch.tv/?channel=" + name) @@ -111,15 +114,17 @@ bool TwitchChannel::canSendMessage() const void TwitchChannel::refreshChannelEmotes() { - loadBttvChannelEmotes( - this->getName(), [this, weak = weakOf(this)](auto &&emoteMap) { - if (auto shared = weak.lock()) // - *this->bttvEmotes_.access() = emoteMap; - }); - getApp()->emotes->ffz.loadChannel( + BttvEmotes::loadChannel( this->getName(), [this, weak = weakOf(this)](auto &&emoteMap) { if (auto shared = weak.lock()) - *this->ffzEmotes_.access() = emoteMap; + this->bttvEmotes_.set( + std::make_shared(std::move(emoteMap))); + }); + FfzEmotes::loadChannel( + this->getName(), [this, weak = weakOf(this)](auto &&emoteMap) { + if (auto shared = weak.lock()) + this->ffzEmotes_.set( + std::make_shared(std::move(emoteMap))); }); } @@ -248,7 +253,7 @@ void TwitchChannel::addPartedUser(const QString &user) } } -QString TwitchChannel::getRoomId() const +QString TwitchChannel::roomId() const { return *this->roomID_.access(); } @@ -284,47 +289,45 @@ TwitchChannel::accessStreamStatus() const return this->streamStatus_.accessConst(); } -boost::optional TwitchChannel::getBttvEmote( - const EmoteName &name) const +boost::optional TwitchChannel::bttvEmote(const EmoteName &name) const { - auto emotes = this->bttvEmotes_.access(); + auto emotes = this->bttvEmotes_.get(); auto it = emotes->find(name); if (it == emotes->end()) return boost::none; return it->second; } -boost::optional TwitchChannel::getFfzEmote( - const EmoteName &name) const +boost::optional TwitchChannel::ffzEmote(const EmoteName &name) const { - auto emotes = this->bttvEmotes_.access(); + auto emotes = this->bttvEmotes_.get(); auto it = emotes->find(name); if (it == emotes->end()) return boost::none; return it->second; } -AccessGuard TwitchChannel::accessBttvEmotes() const +std::shared_ptr TwitchChannel::bttvEmotes() const { - return this->bttvEmotes_.accessConst(); + return this->bttvEmotes_.get(); } -AccessGuard TwitchChannel::accessFfzEmotes() const +std::shared_ptr TwitchChannel::ffzEmotes() const { - return this->ffzEmotes_.accessConst(); + return this->ffzEmotes_.get(); } -const QString &TwitchChannel::getSubscriptionUrl() +const QString &TwitchChannel::subscriptionUrl() { return this->subscriptionUrl_; } -const QString &TwitchChannel::getChannelUrl() +const QString &TwitchChannel::channelUrl() { return this->channelUrl_; } -const QString &TwitchChannel::getPopoutPlayerUrl() +const QString &TwitchChannel::popoutPlayerUrl() { return this->popoutPlayerUrl_; } @@ -347,7 +350,7 @@ void TwitchChannel::setLive(bool newLiveStatus) void TwitchChannel::refreshLiveStatus() { - auto roomID = this->getRoomId(); + auto roomID = this->roomId(); if (roomID.isEmpty()) { log("[TwitchChannel:{}] Refreshing live status (Missing ID)", @@ -459,7 +462,7 @@ void TwitchChannel::loadRecentMessages() "https://tmi.twitch.tv/api/rooms/%1/recent_messages?client_id=" + getDefaultClientID(); - NetworkRequest request(genericURL.arg(this->getRoomId())); + NetworkRequest request(genericURL.arg(this->roomId())); request.makeAuthorizedV5(getDefaultClientID()); request.setCaller(QThread::currentThread()); // can't be concurrent right now due to SignalVector @@ -483,7 +486,7 @@ void TwitchChannel::refreshPubsub() { // listen to moderation actions if (!this->hasModRights()) return; - auto roomId = this->getRoomId(); + auto roomId = this->roomId(); if (roomId.isEmpty()) return; auto account = getApp()->accounts->twitch.getCurrent(); @@ -541,7 +544,7 @@ Outcome TwitchChannel::parseViewerList(const QJsonObject &jsonRoot) void TwitchChannel::loadBadges() { auto url = Url{"https://badges.twitch.tv/v1/badges/channels/" + - this->getRoomId() + "/display?language=en"}; + this->roomId() + "/display?language=en"}; NetworkRequest req(url.string); req.setCaller(QThread::currentThread()); diff --git a/src/providers/twitch/TwitchChannel.hpp b/src/providers/twitch/TwitchChannel.hpp index e41120cda..8b2edeab4 100644 --- a/src/providers/twitch/TwitchChannel.hpp +++ b/src/providers/twitch/TwitchChannel.hpp @@ -2,9 +2,9 @@ #include +#include "common/Atomic.hpp" #include "common/Channel.hpp" #include "common/Common.hpp" -#include "common/Atomic.hpp" #include "common/UniqueAccess.hpp" #include "messages/Emote.hpp" #include "singletons/Emotes.hpp" @@ -64,19 +64,19 @@ public: void setMod(bool value); virtual bool isBroadcaster() const override; - QString getRoomId() const; + QString roomId() const; void setRoomId(const QString &id); AccessGuard accessRoomModes() const; void setRoomModes(const RoomModes &roomModes_); AccessGuard accessStreamStatus() const; - boost::optional getBttvEmote(const EmoteName &name) const; - boost::optional getFfzEmote(const EmoteName &name) const; - AccessGuard accessBttvEmotes() const; - AccessGuard accessFfzEmotes() const; - const QString &getSubscriptionUrl(); - const QString &getChannelUrl(); - const QString &getPopoutPlayerUrl(); + boost::optional bttvEmote(const EmoteName &name) const; + boost::optional ffzEmote(const EmoteName &name) const; + std::shared_ptr bttvEmotes() const; + std::shared_ptr ffzEmotes() const; + const QString &subscriptionUrl(); + const QString &channelUrl(); + const QString &popoutPlayerUrl(); boost::optional getTwitchBadge(const QString &set, const QString &version) const; @@ -127,8 +127,8 @@ private: UniqueAccess userState_; UniqueAccess roomModes_; - UniqueAccess bttvEmotes_; - UniqueAccess ffzEmotes_; + Atomic> bttvEmotes_; + Atomic> ffzEmotes_; const QString subscriptionUrl_; const QString channelUrl_; const QString popoutPlayerUrl_; diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index fef0e4a23..7bc5ce0f2 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -341,7 +341,7 @@ void TwitchMessageBuilder::parseRoomID() if (iterator != std::end(this->tags)) { this->roomID_ = iterator.value().toString(); - if (this->twitchChannel->getRoomId().isEmpty()) { + if (this->twitchChannel->roomId().isEmpty()) { this->twitchChannel->setRoomId(this->roomID_); } } @@ -641,12 +641,12 @@ Outcome TwitchMessageBuilder::tryAppendEmote(const EmoteName &name) if ((emote = getApp()->emotes->bttv.global(name))) { flags = MessageElementFlag::BttvEmote; } else if (twitchChannel && - (emote = this->twitchChannel->getBttvEmote(name))) { + (emote = this->twitchChannel->bttvEmote(name))) { flags = MessageElementFlag::BttvEmote; } else if ((emote = getApp()->emotes->ffz.global(name))) { flags = MessageElementFlag::FfzEmote; } else if (twitchChannel && - (emote = this->twitchChannel->getFfzEmote(name))) { + (emote = this->twitchChannel->ffzEmote(name))) { flags = MessageElementFlag::FfzEmote; } diff --git a/src/providers/twitch/TwitchServer.cpp b/src/providers/twitch/TwitchServer.cpp index 8a5e48cab..4e3bcddad 100644 --- a/src/providers/twitch/TwitchServer.cpp +++ b/src/providers/twitch/TwitchServer.cpp @@ -79,10 +79,12 @@ void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead, std::shared_ptr TwitchServer::createChannel(const QString &channelName) { - TwitchChannel *channel = new TwitchChannel(channelName); + auto channel = + std::shared_ptr(new TwitchChannel(channelName)); + channel->refreshChannelEmotes(); channel->sendMessageSignal.connect( - [this, channel](auto &chan, auto &msg, bool &sent) { + [this, channel = channel.get()](auto &chan, auto &msg, bool &sent) { this->onMessageSendRequested(channel, msg, sent); }); @@ -175,7 +177,7 @@ std::shared_ptr TwitchServer::getChannelOrEmptyByID( auto twitchChannel = std::dynamic_pointer_cast(channel); if (!twitchChannel) continue; - if (twitchChannel->getRoomId() == channelId) { + if (twitchChannel->roomId() == channelId) { return twitchChannel; } } diff --git a/src/widgets/dialogs/EmotePopup.cpp b/src/widgets/dialogs/EmotePopup.cpp index b5dbb965b..8c3fc87e1 100644 --- a/src/widgets/dialogs/EmotePopup.cpp +++ b/src/widgets/dialogs/EmotePopup.cpp @@ -74,6 +74,8 @@ EmotePopup::EmotePopup() layout->addWidget(notebook); layout->setMargin(0); + auto clicked = [this](const Link &link) { this->linkClicked.invoke(link); }; + auto makeView = [&](QString tabTitle) { auto view = new ChannelView(); @@ -82,6 +84,7 @@ EmotePopup::EmotePopup() MessageElementFlag::EmoteImages}); view->setEnableScrollingToBottom(false); notebook->addPage(view, tabTitle); + view->linkClicked.connect(clicked); return view; }; @@ -92,11 +95,6 @@ EmotePopup::EmotePopup() this->viewEmojis_ = makeView("Emojis"); this->loadEmojis(); - - this->globalEmotesView_->linkClicked.connect( - [this](const Link &link) { this->linkClicked.invoke(link); }); - this->viewEmojis_->linkClicked.connect( - [this](const Link &link) { this->linkClicked.invoke(link); }); } void EmotePopup::loadChannel(ChannelPtr _channel) @@ -128,10 +126,8 @@ void EmotePopup::loadChannel(ChannelPtr _channel) addEmotes(*globalChannel, *getApp()->emotes->ffz.global(), "FrankerFaceZ"); // channel - // addEmotes(*channel->accessBttvEmotes(), "BetterTTV Channel Emotes", - // "BetterTTV Channel Emote"); - // addEmotes(*channel->accessFfzEmotes(), "FrankerFaceZ Channel Emotes", - // "FrankerFaceZ Channel Emote"); + addEmotes(*channelChannel, *twitchChannel->bttvEmotes(), "BetterTTV"); + addEmotes(*channelChannel, *twitchChannel->ffzEmotes(), "FrankerFaceZ"); this->globalEmotesView_->setChannel(globalChannel); this->subEmotesView_->setChannel(subChannel);