fixed image animations

This commit is contained in:
fourtf 2018-08-11 17:15:17 +02:00
parent c719bb6b74
commit c768bd9bd9
16 changed files with 229 additions and 210 deletions

View file

@ -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);
}

View file

@ -24,4 +24,22 @@ EmotePtr cachedOrMakeEmotePtr(Emote &&emote, const EmoteMap &cache)
return std::make_shared<Emote>(std::move(emote));
}
EmotePtr cachedOrMakeEmotePtr(
Emote &&emote,
std::unordered_map<EmoteId, std::weak_ptr<const Emote>> &cache,
std::mutex &mutex, const EmoteId &id)
{
std::lock_guard<std::mutex> 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<Emote>(std::move(emote));
cache[id] = shared;
return shared;
}
}
} // namespace chatterino

View file

@ -38,5 +38,9 @@ using WeakEmoteMap = std::unordered_map<EmoteName, std::weak_ptr<const Emote>>;
using WeakEmoteIdMap = std::unordered_map<EmoteId, std::weak_ptr<const Emote>>;
EmotePtr cachedOrMakeEmotePtr(Emote &&emote, const EmoteMap &cache);
EmotePtr cachedOrMakeEmotePtr(
Emote &&emote,
std::unordered_map<EmoteId, std::weak_ptr<const Emote>> &cache,
std::mutex &mutex, const EmoteId &id);
} // namespace chatterino

View file

@ -30,14 +30,27 @@ Frames::Frames()
Frames::Frames(const QVector<Frame<QPixmap>> &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<Frames>())
{
}
Image::Image(const QPixmap &pixmap, qreal scale)
: scale_(scale)
, frames_(QVector<Frame<QPixmap>>{Frame<QPixmap>{pixmap, 1}})
, frames_(std::make_unique<Frames>(
QVector<Frame<QPixmap>>{Frame<QPixmap>{pixmap, 1}}))
{
}
@ -218,7 +238,7 @@ boost::optional<QPixmap> Image::pixmap() const
const_cast<Image *>(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>(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;
}

View file

@ -11,6 +11,7 @@
#include <boost/variant.hpp>
#include <memory>
#include <mutex>
#include <pajlada/signals/signal.hpp>
#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<Frame<QPixmap>> &frames);
~Frames();
Frames(Frames &&other) = default;
Frames &operator=(Frames &&other) = default;
bool animated() const;
void advance();
@ -39,6 +38,7 @@ private:
QVector<Frame<QPixmap>> 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> frames_{};
QObject object_{};
};
} // namespace chatterino

View file

@ -44,8 +44,45 @@ std::pair<Outcome, EmoteMap> parseGlobalEmotes(const QJsonObject &jsonRoot,
return {Success, std::move(emotes)};
}
EmotePtr cachedOrMake(Emote &&emote, const EmoteId &id)
{
static std::unordered_map<EmoteId, std::weak_ptr<const Emote>> cache;
static std::mutex mutex;
return cachedOrMakeEmotePtr(std::move(emote), cache, mutex, id);
}
std::pair<Outcome, EmoteMap> 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 + "<br />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<EmoteMap>())
{
@ -84,4 +121,31 @@ void BttvEmotes::loadGlobal()
request.execute();
}
void BttvEmotes::loadChannel(const QString &channelName,
std::function<void(EmoteMap &&)> 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

View file

@ -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<const EmoteMap> global() const;
boost::optional<EmotePtr> global(const EmoteName &name) const;
void loadGlobal();
static void loadChannel(const QString &channelName,
std::function<void(EmoteMap &&)> callback);
private:
Atomic<std::shared_ptr<const EmoteMap>> global_;

View file

@ -11,78 +11,4 @@
namespace chatterino {
static Url getEmoteLink(QString urlTemplate, const EmoteId &id,
const QString &emoteScale);
static std::pair<Outcome, EmoteMap> bttvParseChannelEmotes(
const QJsonObject &jsonRoot);
void loadBttvChannelEmotes(const QString &channelName,
std::function<void(EmoteMap &&)> 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<Outcome, EmoteMap> bttvParseChannelEmotes(
const QJsonObject &jsonRoot)
{
static UniqueAccess<std::unordered_map<EmoteId, std::weak_ptr<const Emote>>>
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 + "<br />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<Emote>(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

View file

@ -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<void(EmoteMap &&)> callback);
} // namespace chatterino

View file

@ -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<EmoteId, std::weak_ptr<const Emote>> cache;
static std::mutex mutex;
return cachedOrMakeEmotePtr(std::move(emote), cache, mutex, id);
}
std::pair<Outcome, EmoteMap> parseGlobalEmotes(const QJsonObject &jsonRoot,
const EmoteMap &currentEmotes)
{
@ -64,6 +71,36 @@ std::pair<Outcome, EmoteMap> parseGlobalEmotes(const QJsonObject &jsonRoot,
return {Success, std::move(emotes)};
}
std::pair<Outcome, EmoteMap> 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 + "<br/>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<void(EmoteMap &&)> 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<QString> 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 + "<br/>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

View file

@ -1,14 +1,12 @@
#pragma once
#include <memory>
#include "common/Atomic.hpp"
#include "messages/Emote.hpp"
#include "messages/EmoteCache.hpp"
namespace chatterino {
class FfzEmotes final : std::enable_shared_from_this<FfzEmotes>
class FfzEmotes final
{
static constexpr const char *globalEmoteApiUrl =
"https://api.frankerfacez.com/v1/set/global";
@ -20,9 +18,8 @@ public:
std::shared_ptr<const EmoteMap> global() const;
boost::optional<EmotePtr> global(const EmoteName &name) const;
void loadGlobal();
void loadChannel(const QString &channelName,
static void loadChannel(const QString &channelName,
std::function<void(EmoteMap &&)> callback);
private:

View file

@ -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<EmoteMap>())
, ffzEmotes_(std::make_shared<EmoteMap>())
, 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<Channel>(this)](auto &&emoteMap) {
if (auto shared = weak.lock()) //
*this->bttvEmotes_.access() = emoteMap;
});
getApp()->emotes->ffz.loadChannel(
BttvEmotes::loadChannel(
this->getName(), [this, weak = weakOf<Channel>(this)](auto &&emoteMap) {
if (auto shared = weak.lock())
*this->ffzEmotes_.access() = emoteMap;
this->bttvEmotes_.set(
std::make_shared<EmoteMap>(std::move(emoteMap)));
});
FfzEmotes::loadChannel(
this->getName(), [this, weak = weakOf<Channel>(this)](auto &&emoteMap) {
if (auto shared = weak.lock())
this->ffzEmotes_.set(
std::make_shared<EmoteMap>(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<EmotePtr> TwitchChannel::getBttvEmote(
const EmoteName &name) const
boost::optional<EmotePtr> 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<EmotePtr> TwitchChannel::getFfzEmote(
const EmoteName &name) const
boost::optional<EmotePtr> 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<const EmoteMap> TwitchChannel::accessBttvEmotes() const
std::shared_ptr<const EmoteMap> TwitchChannel::bttvEmotes() const
{
return this->bttvEmotes_.accessConst();
return this->bttvEmotes_.get();
}
AccessGuard<const EmoteMap> TwitchChannel::accessFfzEmotes() const
std::shared_ptr<const EmoteMap> 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());

View file

@ -2,9 +2,9 @@
#include <IrcConnection>
#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<const RoomModes> accessRoomModes() const;
void setRoomModes(const RoomModes &roomModes_);
AccessGuard<const StreamStatus> accessStreamStatus() const;
boost::optional<EmotePtr> getBttvEmote(const EmoteName &name) const;
boost::optional<EmotePtr> getFfzEmote(const EmoteName &name) const;
AccessGuard<const EmoteMap> accessBttvEmotes() const;
AccessGuard<const EmoteMap> accessFfzEmotes() const;
const QString &getSubscriptionUrl();
const QString &getChannelUrl();
const QString &getPopoutPlayerUrl();
boost::optional<EmotePtr> bttvEmote(const EmoteName &name) const;
boost::optional<EmotePtr> ffzEmote(const EmoteName &name) const;
std::shared_ptr<const EmoteMap> bttvEmotes() const;
std::shared_ptr<const EmoteMap> ffzEmotes() const;
const QString &subscriptionUrl();
const QString &channelUrl();
const QString &popoutPlayerUrl();
boost::optional<EmotePtr> getTwitchBadge(const QString &set,
const QString &version) const;
@ -127,8 +127,8 @@ private:
UniqueAccess<UserState> userState_;
UniqueAccess<RoomModes> roomModes_;
UniqueAccess<EmoteMap> bttvEmotes_;
UniqueAccess<EmoteMap> ffzEmotes_;
Atomic<std::shared_ptr<const EmoteMap>> bttvEmotes_;
Atomic<std::shared_ptr<const EmoteMap>> ffzEmotes_;
const QString subscriptionUrl_;
const QString channelUrl_;
const QString popoutPlayerUrl_;

View file

@ -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;
}

View file

@ -79,10 +79,12 @@ void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead,
std::shared_ptr<Channel> TwitchServer::createChannel(const QString &channelName)
{
TwitchChannel *channel = new TwitchChannel(channelName);
auto channel =
std::shared_ptr<TwitchChannel>(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<Channel> TwitchServer::getChannelOrEmptyByID(
auto twitchChannel = std::dynamic_pointer_cast<TwitchChannel>(channel);
if (!twitchChannel) continue;
if (twitchChannel->getRoomId() == channelId) {
if (twitchChannel->roomId() == channelId) {
return twitchChannel;
}
}

View file

@ -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);