mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
fixed image animations
This commit is contained in:
parent
c719bb6b74
commit
c768bd9bd9
16 changed files with 229 additions and 210 deletions
|
@ -137,7 +137,7 @@ void CompletionModel::refresh()
|
||||||
getApp()
|
getApp()
|
||||||
->twitch2->getChannelOrEmptyByID(this->channelName_)
|
->twitch2->getChannelOrEmptyByID(this->channelName_)
|
||||||
.get())) {
|
.get())) {
|
||||||
auto bttv = channel->accessBttvEmotes();
|
auto bttv = channel->bttvEmotes();
|
||||||
// auto it = bttv->begin();
|
// auto it = bttv->begin();
|
||||||
// for (const auto &emote : *bttv) {
|
// for (const auto &emote : *bttv) {
|
||||||
// }
|
// }
|
||||||
|
@ -148,7 +148,7 @@ void CompletionModel::refresh()
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Channel-specific: FFZ Channel Emotes
|
// Channel-specific: FFZ Channel Emotes
|
||||||
for (const auto &emote : *channel->accessFfzEmotes()) {
|
for (const auto &emote : *channel->ffzEmotes()) {
|
||||||
this->addString(emote.second->name.string,
|
this->addString(emote.second->name.string,
|
||||||
TaggedString::Type::FFZChannelEmote);
|
TaggedString::Type::FFZChannelEmote);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,4 +24,22 @@ EmotePtr cachedOrMakeEmotePtr(Emote &&emote, const EmoteMap &cache)
|
||||||
return std::make_shared<Emote>(std::move(emote));
|
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
|
} // namespace chatterino
|
||||||
|
|
|
@ -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>>;
|
using WeakEmoteIdMap = std::unordered_map<EmoteId, std::weak_ptr<const Emote>>;
|
||||||
|
|
||||||
EmotePtr cachedOrMakeEmotePtr(Emote &&emote, const EmoteMap &cache);
|
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
|
} // namespace chatterino
|
||||||
|
|
|
@ -30,14 +30,27 @@ Frames::Frames()
|
||||||
Frames::Frames(const QVector<Frame<QPixmap>> &frames)
|
Frames::Frames(const QVector<Frame<QPixmap>> &frames)
|
||||||
: items_(frames)
|
: items_(frames)
|
||||||
{
|
{
|
||||||
|
assertInGuiThread();
|
||||||
DebugCount::increase("images");
|
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()
|
Frames::~Frames()
|
||||||
{
|
{
|
||||||
|
assertInGuiThread();
|
||||||
DebugCount::decrease("images");
|
DebugCount::decrease("images");
|
||||||
if (this->animated()) DebugCount::decrease("animated images");
|
|
||||||
|
if (this->animated()) {
|
||||||
|
DebugCount::decrease("animated images");
|
||||||
|
}
|
||||||
|
|
||||||
|
this->gifTimerConnection_.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Frames::advance()
|
void Frames::advance()
|
||||||
|
@ -46,6 +59,11 @@ void Frames::advance()
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
this->index_ %= this->items_.size();
|
this->index_ %= this->items_.size();
|
||||||
|
|
||||||
|
if (this->index_ >= this->items_.size()) {
|
||||||
|
this->index_ = this->index_;
|
||||||
|
}
|
||||||
|
|
||||||
if (this->durationOffset_ > this->items_[this->index_].duration) {
|
if (this->durationOffset_ > this->items_[this->index_].duration) {
|
||||||
this->durationOffset_ -= this->items_[this->index_].duration;
|
this->durationOffset_ -= this->items_[this->index_].duration;
|
||||||
this->index_ = (this->index_ + 1) % this->items_.size();
|
this->index_ = (this->index_ + 1) % this->items_.size();
|
||||||
|
@ -195,12 +213,14 @@ Image::Image(const Url &url, qreal scale)
|
||||||
: url_(url)
|
: url_(url)
|
||||||
, scale_(scale)
|
, scale_(scale)
|
||||||
, shouldLoad_(true)
|
, shouldLoad_(true)
|
||||||
|
, frames_(std::make_unique<Frames>())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::Image(const QPixmap &pixmap, qreal scale)
|
Image::Image(const QPixmap &pixmap, qreal scale)
|
||||||
: scale_(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();
|
const_cast<Image *>(this)->load();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this->frames_.current();
|
return this->frames_->current();
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal Image::scale() const
|
qreal Image::scale() const
|
||||||
|
@ -235,14 +255,14 @@ bool Image::animated() const
|
||||||
{
|
{
|
||||||
assertInGuiThread();
|
assertInGuiThread();
|
||||||
|
|
||||||
return this->frames_.animated();
|
return this->frames_->animated();
|
||||||
}
|
}
|
||||||
|
|
||||||
int Image::width() const
|
int Image::width() const
|
||||||
{
|
{
|
||||||
assertInGuiThread();
|
assertInGuiThread();
|
||||||
|
|
||||||
if (auto pixmap = this->frames_.first())
|
if (auto pixmap = this->frames_->first())
|
||||||
return pixmap->width() * this->scale_;
|
return pixmap->width() * this->scale_;
|
||||||
else
|
else
|
||||||
return 16;
|
return 16;
|
||||||
|
@ -252,7 +272,7 @@ int Image::height() const
|
||||||
{
|
{
|
||||||
assertInGuiThread();
|
assertInGuiThread();
|
||||||
|
|
||||||
if (auto pixmap = this->frames_.first())
|
if (auto pixmap = this->frames_->first())
|
||||||
return pixmap->height() * this->scale_;
|
return pixmap->height() * this->scale_;
|
||||||
else
|
else
|
||||||
return 16;
|
return 16;
|
||||||
|
@ -277,7 +297,8 @@ void Image::load()
|
||||||
auto parsed = readFrames(reader, that->url());
|
auto parsed = readFrames(reader, that->url());
|
||||||
|
|
||||||
postToThread(makeConvertCallback(parsed, [weak](auto frames) {
|
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;
|
return Success;
|
||||||
|
@ -290,7 +311,7 @@ bool Image::operator==(const Image &other) const
|
||||||
{
|
{
|
||||||
if (this->isEmpty() && other.isEmpty()) return true;
|
if (this->isEmpty() && other.isEmpty()) return true;
|
||||||
if (!this->url_.string.isEmpty() && this->url_ == other.url_) 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <boost/variant.hpp>
|
#include <boost/variant.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <pajlada/signals/signal.hpp>
|
||||||
|
|
||||||
#include "common/NullablePtr.hpp"
|
#include "common/NullablePtr.hpp"
|
||||||
|
|
||||||
|
@ -21,14 +22,12 @@ struct Frame {
|
||||||
Image image;
|
Image image;
|
||||||
int duration;
|
int duration;
|
||||||
};
|
};
|
||||||
class Frames
|
class Frames : boost::noncopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Frames();
|
Frames();
|
||||||
Frames(const QVector<Frame<QPixmap>> &frames);
|
Frames(const QVector<Frame<QPixmap>> &frames);
|
||||||
~Frames();
|
~Frames();
|
||||||
Frames(Frames &&other) = default;
|
|
||||||
Frames &operator=(Frames &&other) = default;
|
|
||||||
|
|
||||||
bool animated() const;
|
bool animated() const;
|
||||||
void advance();
|
void advance();
|
||||||
|
@ -39,6 +38,7 @@ private:
|
||||||
QVector<Frame<QPixmap>> items_;
|
QVector<Frame<QPixmap>> items_;
|
||||||
int index_{0};
|
int index_{0};
|
||||||
int durationOffset_{0};
|
int durationOffset_{0};
|
||||||
|
pajlada::Signals::Connection gifTimerConnection_;
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ private:
|
||||||
qreal scale_{1};
|
qreal scale_{1};
|
||||||
bool empty_{false};
|
bool empty_{false};
|
||||||
bool shouldLoad_{false};
|
bool shouldLoad_{false};
|
||||||
Frames frames_{};
|
std::unique_ptr<Frames> frames_{};
|
||||||
QObject object_{};
|
QObject object_{};
|
||||||
};
|
};
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -44,8 +44,45 @@ std::pair<Outcome, EmoteMap> parseGlobalEmotes(const QJsonObject &jsonRoot,
|
||||||
|
|
||||||
return {Success, std::move(emotes)};
|
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
|
} // namespace
|
||||||
|
|
||||||
|
//
|
||||||
|
// BttvEmotes
|
||||||
|
//
|
||||||
BttvEmotes::BttvEmotes()
|
BttvEmotes::BttvEmotes()
|
||||||
: global_(std::make_shared<EmoteMap>())
|
: global_(std::make_shared<EmoteMap>())
|
||||||
{
|
{
|
||||||
|
@ -84,4 +121,31 @@ void BttvEmotes::loadGlobal()
|
||||||
request.execute();
|
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
|
} // namespace chatterino
|
||||||
|
|
|
@ -10,6 +10,8 @@ class BttvEmotes final
|
||||||
{
|
{
|
||||||
static constexpr const char *globalEmoteApiUrl =
|
static constexpr const char *globalEmoteApiUrl =
|
||||||
"https://api.betterttv.net/2/emotes";
|
"https://api.betterttv.net/2/emotes";
|
||||||
|
static constexpr const char *bttvChannelEmoteApiUrl =
|
||||||
|
"https://api.betterttv.net/2/channels/";
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BttvEmotes();
|
BttvEmotes();
|
||||||
|
@ -17,6 +19,8 @@ public:
|
||||||
std::shared_ptr<const EmoteMap> global() const;
|
std::shared_ptr<const EmoteMap> global() const;
|
||||||
boost::optional<EmotePtr> global(const EmoteName &name) const;
|
boost::optional<EmotePtr> global(const EmoteName &name) const;
|
||||||
void loadGlobal();
|
void loadGlobal();
|
||||||
|
static void loadChannel(const QString &channelName,
|
||||||
|
std::function<void(EmoteMap &&)> callback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Atomic<std::shared_ptr<const EmoteMap>> global_;
|
Atomic<std::shared_ptr<const EmoteMap>> global_;
|
||||||
|
|
|
@ -11,78 +11,4 @@
|
||||||
|
|
||||||
namespace chatterino {
|
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
|
} // namespace chatterino
|
||||||
|
|
|
@ -6,11 +6,4 @@ class QString;
|
||||||
|
|
||||||
namespace chatterino {
|
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
|
} // namespace chatterino
|
||||||
|
|
|
@ -33,6 +33,13 @@ void fillInEmoteData(const QJsonObject &urls, const EmoteName &name,
|
||||||
Image::fromUrl(url3x, 0.25)};
|
Image::fromUrl(url3x, 0.25)};
|
||||||
emoteData.tooltip = {tooltip};
|
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,
|
std::pair<Outcome, EmoteMap> parseGlobalEmotes(const QJsonObject &jsonRoot,
|
||||||
const EmoteMap ¤tEmotes)
|
const EmoteMap ¤tEmotes)
|
||||||
{
|
{
|
||||||
|
@ -64,6 +71,36 @@ std::pair<Outcome, EmoteMap> parseGlobalEmotes(const QJsonObject &jsonRoot,
|
||||||
|
|
||||||
return {Success, std::move(emotes)};
|
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
|
} // namespace
|
||||||
|
|
||||||
FfzEmotes::FfzEmotes()
|
FfzEmotes::FfzEmotes()
|
||||||
|
@ -107,66 +144,20 @@ void FfzEmotes::loadGlobal()
|
||||||
void FfzEmotes::loadChannel(const QString &channelName,
|
void FfzEmotes::loadChannel(const QString &channelName,
|
||||||
std::function<void(EmoteMap &&)> callback)
|
std::function<void(EmoteMap &&)> callback)
|
||||||
{
|
{
|
||||||
// printf("[FFZEmotes] Reload FFZ Channel Emotes for channel %s\n",
|
log("[FFZEmotes] Reload FFZ Channel Emotes for channel %s\n", channelName);
|
||||||
// qPrintable(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.onSuccess([callback = std::move(callback)](auto result) -> Outcome {
|
||||||
// request.setCaller(QThread::currentThread());
|
auto pair = parseChannelEmotes(result.parseJson());
|
||||||
// request.setTimeout(3000);
|
if (pair.first) callback(std::move(pair.second));
|
||||||
// request.onSuccess([this, channelName, _map](auto result) -> Outcome {
|
return pair.first;
|
||||||
// return this->parseChannelEmotes(result.parseJson());
|
});
|
||||||
//});
|
|
||||||
|
|
||||||
// request.execute();
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "common/Atomic.hpp"
|
#include "common/Atomic.hpp"
|
||||||
#include "messages/Emote.hpp"
|
#include "messages/Emote.hpp"
|
||||||
#include "messages/EmoteCache.hpp"
|
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
class FfzEmotes final : std::enable_shared_from_this<FfzEmotes>
|
class FfzEmotes final
|
||||||
{
|
{
|
||||||
static constexpr const char *globalEmoteApiUrl =
|
static constexpr const char *globalEmoteApiUrl =
|
||||||
"https://api.frankerfacez.com/v1/set/global";
|
"https://api.frankerfacez.com/v1/set/global";
|
||||||
|
@ -20,10 +18,9 @@ public:
|
||||||
|
|
||||||
std::shared_ptr<const EmoteMap> global() const;
|
std::shared_ptr<const EmoteMap> global() const;
|
||||||
boost::optional<EmotePtr> global(const EmoteName &name) const;
|
boost::optional<EmotePtr> global(const EmoteName &name) const;
|
||||||
|
|
||||||
void loadGlobal();
|
void loadGlobal();
|
||||||
void loadChannel(const QString &channelName,
|
static void loadChannel(const QString &channelName,
|
||||||
std::function<void(EmoteMap &&)> callback);
|
std::function<void(EmoteMap &&)> callback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Atomic<std::shared_ptr<const EmoteMap>> global_;
|
Atomic<std::shared_ptr<const EmoteMap>> global_;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "controllers/accounts/AccountController.hpp"
|
#include "controllers/accounts/AccountController.hpp"
|
||||||
#include "debug/Log.hpp"
|
#include "debug/Log.hpp"
|
||||||
#include "messages/Message.hpp"
|
#include "messages/Message.hpp"
|
||||||
|
#include "providers/bttv/BttvEmotes.hpp"
|
||||||
#include "providers/bttv/LoadBttvChannelEmote.hpp"
|
#include "providers/bttv/LoadBttvChannelEmote.hpp"
|
||||||
#include "providers/twitch/PubsubClient.hpp"
|
#include "providers/twitch/PubsubClient.hpp"
|
||||||
#include "providers/twitch/TwitchCommon.hpp"
|
#include "providers/twitch/TwitchCommon.hpp"
|
||||||
|
@ -51,6 +52,8 @@ auto parseRecentMessages(const QJsonObject &jsonRoot, TwitchChannel &channel)
|
||||||
|
|
||||||
TwitchChannel::TwitchChannel(const QString &name)
|
TwitchChannel::TwitchChannel(const QString &name)
|
||||||
: Channel(name, Channel::Type::Twitch)
|
: Channel(name, Channel::Type::Twitch)
|
||||||
|
, bttvEmotes_(std::make_shared<EmoteMap>())
|
||||||
|
, ffzEmotes_(std::make_shared<EmoteMap>())
|
||||||
, subscriptionUrl_("https://www.twitch.tv/subs/" + name)
|
, subscriptionUrl_("https://www.twitch.tv/subs/" + name)
|
||||||
, channelUrl_("https://twitch.tv/" + name)
|
, channelUrl_("https://twitch.tv/" + name)
|
||||||
, popoutPlayerUrl_("https://player.twitch.tv/?channel=" + name)
|
, popoutPlayerUrl_("https://player.twitch.tv/?channel=" + name)
|
||||||
|
@ -111,15 +114,17 @@ bool TwitchChannel::canSendMessage() const
|
||||||
|
|
||||||
void TwitchChannel::refreshChannelEmotes()
|
void TwitchChannel::refreshChannelEmotes()
|
||||||
{
|
{
|
||||||
loadBttvChannelEmotes(
|
BttvEmotes::loadChannel(
|
||||||
this->getName(), [this, weak = weakOf<Channel>(this)](auto &&emoteMap) {
|
|
||||||
if (auto shared = weak.lock()) //
|
|
||||||
*this->bttvEmotes_.access() = emoteMap;
|
|
||||||
});
|
|
||||||
getApp()->emotes->ffz.loadChannel(
|
|
||||||
this->getName(), [this, weak = weakOf<Channel>(this)](auto &&emoteMap) {
|
this->getName(), [this, weak = weakOf<Channel>(this)](auto &&emoteMap) {
|
||||||
if (auto shared = weak.lock())
|
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();
|
return *this->roomID_.access();
|
||||||
}
|
}
|
||||||
|
@ -284,47 +289,45 @@ TwitchChannel::accessStreamStatus() const
|
||||||
return this->streamStatus_.accessConst();
|
return this->streamStatus_.accessConst();
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<EmotePtr> TwitchChannel::getBttvEmote(
|
boost::optional<EmotePtr> TwitchChannel::bttvEmote(const EmoteName &name) const
|
||||||
const EmoteName &name) const
|
|
||||||
{
|
{
|
||||||
auto emotes = this->bttvEmotes_.access();
|
auto emotes = this->bttvEmotes_.get();
|
||||||
auto it = emotes->find(name);
|
auto it = emotes->find(name);
|
||||||
|
|
||||||
if (it == emotes->end()) return boost::none;
|
if (it == emotes->end()) return boost::none;
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<EmotePtr> TwitchChannel::getFfzEmote(
|
boost::optional<EmotePtr> TwitchChannel::ffzEmote(const EmoteName &name) const
|
||||||
const EmoteName &name) const
|
|
||||||
{
|
{
|
||||||
auto emotes = this->bttvEmotes_.access();
|
auto emotes = this->bttvEmotes_.get();
|
||||||
auto it = emotes->find(name);
|
auto it = emotes->find(name);
|
||||||
|
|
||||||
if (it == emotes->end()) return boost::none;
|
if (it == emotes->end()) return boost::none;
|
||||||
return it->second;
|
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_;
|
return this->subscriptionUrl_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString &TwitchChannel::getChannelUrl()
|
const QString &TwitchChannel::channelUrl()
|
||||||
{
|
{
|
||||||
return this->channelUrl_;
|
return this->channelUrl_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString &TwitchChannel::getPopoutPlayerUrl()
|
const QString &TwitchChannel::popoutPlayerUrl()
|
||||||
{
|
{
|
||||||
return this->popoutPlayerUrl_;
|
return this->popoutPlayerUrl_;
|
||||||
}
|
}
|
||||||
|
@ -347,7 +350,7 @@ void TwitchChannel::setLive(bool newLiveStatus)
|
||||||
|
|
||||||
void TwitchChannel::refreshLiveStatus()
|
void TwitchChannel::refreshLiveStatus()
|
||||||
{
|
{
|
||||||
auto roomID = this->getRoomId();
|
auto roomID = this->roomId();
|
||||||
|
|
||||||
if (roomID.isEmpty()) {
|
if (roomID.isEmpty()) {
|
||||||
log("[TwitchChannel:{}] Refreshing live status (Missing ID)",
|
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=" +
|
"https://tmi.twitch.tv/api/rooms/%1/recent_messages?client_id=" +
|
||||||
getDefaultClientID();
|
getDefaultClientID();
|
||||||
|
|
||||||
NetworkRequest request(genericURL.arg(this->getRoomId()));
|
NetworkRequest request(genericURL.arg(this->roomId()));
|
||||||
request.makeAuthorizedV5(getDefaultClientID());
|
request.makeAuthorizedV5(getDefaultClientID());
|
||||||
request.setCaller(QThread::currentThread());
|
request.setCaller(QThread::currentThread());
|
||||||
// can't be concurrent right now due to SignalVector
|
// can't be concurrent right now due to SignalVector
|
||||||
|
@ -483,7 +486,7 @@ void TwitchChannel::refreshPubsub()
|
||||||
{
|
{
|
||||||
// listen to moderation actions
|
// listen to moderation actions
|
||||||
if (!this->hasModRights()) return;
|
if (!this->hasModRights()) return;
|
||||||
auto roomId = this->getRoomId();
|
auto roomId = this->roomId();
|
||||||
if (roomId.isEmpty()) return;
|
if (roomId.isEmpty()) return;
|
||||||
|
|
||||||
auto account = getApp()->accounts->twitch.getCurrent();
|
auto account = getApp()->accounts->twitch.getCurrent();
|
||||||
|
@ -541,7 +544,7 @@ Outcome TwitchChannel::parseViewerList(const QJsonObject &jsonRoot)
|
||||||
void TwitchChannel::loadBadges()
|
void TwitchChannel::loadBadges()
|
||||||
{
|
{
|
||||||
auto url = Url{"https://badges.twitch.tv/v1/badges/channels/" +
|
auto url = Url{"https://badges.twitch.tv/v1/badges/channels/" +
|
||||||
this->getRoomId() + "/display?language=en"};
|
this->roomId() + "/display?language=en"};
|
||||||
NetworkRequest req(url.string);
|
NetworkRequest req(url.string);
|
||||||
req.setCaller(QThread::currentThread());
|
req.setCaller(QThread::currentThread());
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
#include <IrcConnection>
|
#include <IrcConnection>
|
||||||
|
|
||||||
|
#include "common/Atomic.hpp"
|
||||||
#include "common/Channel.hpp"
|
#include "common/Channel.hpp"
|
||||||
#include "common/Common.hpp"
|
#include "common/Common.hpp"
|
||||||
#include "common/Atomic.hpp"
|
|
||||||
#include "common/UniqueAccess.hpp"
|
#include "common/UniqueAccess.hpp"
|
||||||
#include "messages/Emote.hpp"
|
#include "messages/Emote.hpp"
|
||||||
#include "singletons/Emotes.hpp"
|
#include "singletons/Emotes.hpp"
|
||||||
|
@ -64,19 +64,19 @@ public:
|
||||||
void setMod(bool value);
|
void setMod(bool value);
|
||||||
virtual bool isBroadcaster() const override;
|
virtual bool isBroadcaster() const override;
|
||||||
|
|
||||||
QString getRoomId() const;
|
QString roomId() const;
|
||||||
void setRoomId(const QString &id);
|
void setRoomId(const QString &id);
|
||||||
AccessGuard<const RoomModes> accessRoomModes() const;
|
AccessGuard<const RoomModes> accessRoomModes() const;
|
||||||
void setRoomModes(const RoomModes &roomModes_);
|
void setRoomModes(const RoomModes &roomModes_);
|
||||||
AccessGuard<const StreamStatus> accessStreamStatus() const;
|
AccessGuard<const StreamStatus> accessStreamStatus() const;
|
||||||
|
|
||||||
boost::optional<EmotePtr> getBttvEmote(const EmoteName &name) const;
|
boost::optional<EmotePtr> bttvEmote(const EmoteName &name) const;
|
||||||
boost::optional<EmotePtr> getFfzEmote(const EmoteName &name) const;
|
boost::optional<EmotePtr> ffzEmote(const EmoteName &name) const;
|
||||||
AccessGuard<const EmoteMap> accessBttvEmotes() const;
|
std::shared_ptr<const EmoteMap> bttvEmotes() const;
|
||||||
AccessGuard<const EmoteMap> accessFfzEmotes() const;
|
std::shared_ptr<const EmoteMap> ffzEmotes() const;
|
||||||
const QString &getSubscriptionUrl();
|
const QString &subscriptionUrl();
|
||||||
const QString &getChannelUrl();
|
const QString &channelUrl();
|
||||||
const QString &getPopoutPlayerUrl();
|
const QString &popoutPlayerUrl();
|
||||||
|
|
||||||
boost::optional<EmotePtr> getTwitchBadge(const QString &set,
|
boost::optional<EmotePtr> getTwitchBadge(const QString &set,
|
||||||
const QString &version) const;
|
const QString &version) const;
|
||||||
|
@ -127,8 +127,8 @@ private:
|
||||||
UniqueAccess<UserState> userState_;
|
UniqueAccess<UserState> userState_;
|
||||||
UniqueAccess<RoomModes> roomModes_;
|
UniqueAccess<RoomModes> roomModes_;
|
||||||
|
|
||||||
UniqueAccess<EmoteMap> bttvEmotes_;
|
Atomic<std::shared_ptr<const EmoteMap>> bttvEmotes_;
|
||||||
UniqueAccess<EmoteMap> ffzEmotes_;
|
Atomic<std::shared_ptr<const EmoteMap>> ffzEmotes_;
|
||||||
const QString subscriptionUrl_;
|
const QString subscriptionUrl_;
|
||||||
const QString channelUrl_;
|
const QString channelUrl_;
|
||||||
const QString popoutPlayerUrl_;
|
const QString popoutPlayerUrl_;
|
||||||
|
|
|
@ -341,7 +341,7 @@ void TwitchMessageBuilder::parseRoomID()
|
||||||
if (iterator != std::end(this->tags)) {
|
if (iterator != std::end(this->tags)) {
|
||||||
this->roomID_ = iterator.value().toString();
|
this->roomID_ = iterator.value().toString();
|
||||||
|
|
||||||
if (this->twitchChannel->getRoomId().isEmpty()) {
|
if (this->twitchChannel->roomId().isEmpty()) {
|
||||||
this->twitchChannel->setRoomId(this->roomID_);
|
this->twitchChannel->setRoomId(this->roomID_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -641,12 +641,12 @@ Outcome TwitchMessageBuilder::tryAppendEmote(const EmoteName &name)
|
||||||
if ((emote = getApp()->emotes->bttv.global(name))) {
|
if ((emote = getApp()->emotes->bttv.global(name))) {
|
||||||
flags = MessageElementFlag::BttvEmote;
|
flags = MessageElementFlag::BttvEmote;
|
||||||
} else if (twitchChannel &&
|
} else if (twitchChannel &&
|
||||||
(emote = this->twitchChannel->getBttvEmote(name))) {
|
(emote = this->twitchChannel->bttvEmote(name))) {
|
||||||
flags = MessageElementFlag::BttvEmote;
|
flags = MessageElementFlag::BttvEmote;
|
||||||
} else if ((emote = getApp()->emotes->ffz.global(name))) {
|
} else if ((emote = getApp()->emotes->ffz.global(name))) {
|
||||||
flags = MessageElementFlag::FfzEmote;
|
flags = MessageElementFlag::FfzEmote;
|
||||||
} else if (twitchChannel &&
|
} else if (twitchChannel &&
|
||||||
(emote = this->twitchChannel->getFfzEmote(name))) {
|
(emote = this->twitchChannel->ffzEmote(name))) {
|
||||||
flags = MessageElementFlag::FfzEmote;
|
flags = MessageElementFlag::FfzEmote;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,10 +79,12 @@ void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead,
|
||||||
|
|
||||||
std::shared_ptr<Channel> TwitchServer::createChannel(const QString &channelName)
|
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(
|
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);
|
this->onMessageSendRequested(channel, msg, sent);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -175,7 +177,7 @@ std::shared_ptr<Channel> TwitchServer::getChannelOrEmptyByID(
|
||||||
auto twitchChannel = std::dynamic_pointer_cast<TwitchChannel>(channel);
|
auto twitchChannel = std::dynamic_pointer_cast<TwitchChannel>(channel);
|
||||||
if (!twitchChannel) continue;
|
if (!twitchChannel) continue;
|
||||||
|
|
||||||
if (twitchChannel->getRoomId() == channelId) {
|
if (twitchChannel->roomId() == channelId) {
|
||||||
return twitchChannel;
|
return twitchChannel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,6 +74,8 @@ EmotePopup::EmotePopup()
|
||||||
layout->addWidget(notebook);
|
layout->addWidget(notebook);
|
||||||
layout->setMargin(0);
|
layout->setMargin(0);
|
||||||
|
|
||||||
|
auto clicked = [this](const Link &link) { this->linkClicked.invoke(link); };
|
||||||
|
|
||||||
auto makeView = [&](QString tabTitle) {
|
auto makeView = [&](QString tabTitle) {
|
||||||
auto view = new ChannelView();
|
auto view = new ChannelView();
|
||||||
|
|
||||||
|
@ -82,6 +84,7 @@ EmotePopup::EmotePopup()
|
||||||
MessageElementFlag::EmoteImages});
|
MessageElementFlag::EmoteImages});
|
||||||
view->setEnableScrollingToBottom(false);
|
view->setEnableScrollingToBottom(false);
|
||||||
notebook->addPage(view, tabTitle);
|
notebook->addPage(view, tabTitle);
|
||||||
|
view->linkClicked.connect(clicked);
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
};
|
};
|
||||||
|
@ -92,11 +95,6 @@ EmotePopup::EmotePopup()
|
||||||
this->viewEmojis_ = makeView("Emojis");
|
this->viewEmojis_ = makeView("Emojis");
|
||||||
|
|
||||||
this->loadEmojis();
|
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)
|
void EmotePopup::loadChannel(ChannelPtr _channel)
|
||||||
|
@ -128,10 +126,8 @@ void EmotePopup::loadChannel(ChannelPtr _channel)
|
||||||
addEmotes(*globalChannel, *getApp()->emotes->ffz.global(), "FrankerFaceZ");
|
addEmotes(*globalChannel, *getApp()->emotes->ffz.global(), "FrankerFaceZ");
|
||||||
|
|
||||||
// channel
|
// channel
|
||||||
// addEmotes(*channel->accessBttvEmotes(), "BetterTTV Channel Emotes",
|
addEmotes(*channelChannel, *twitchChannel->bttvEmotes(), "BetterTTV");
|
||||||
// "BetterTTV Channel Emote");
|
addEmotes(*channelChannel, *twitchChannel->ffzEmotes(), "FrankerFaceZ");
|
||||||
// addEmotes(*channel->accessFfzEmotes(), "FrankerFaceZ Channel Emotes",
|
|
||||||
// "FrankerFaceZ Channel Emote");
|
|
||||||
|
|
||||||
this->globalEmotesView_->setChannel(globalChannel);
|
this->globalEmotesView_->setChannel(globalChannel);
|
||||||
this->subEmotesView_->setChannel(subChannel);
|
this->subEmotesView_->setChannel(subChannel);
|
||||||
|
|
Loading…
Reference in a new issue