Emote popup channelname (#475)

Fix #464
This commit is contained in:
pajlada 2018-06-24 14:42:40 +02:00 committed by GitHub
parent ebe0f0c87f
commit eae2c2c521
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 163 additions and 32 deletions

View file

@ -2,6 +2,8 @@
#include "debug/log.hpp" #include "debug/log.hpp"
#include "messages/image.hpp" #include "messages/image.hpp"
#include "util/benchmark.hpp"
#include "util/rapidjson-helpers.hpp"
#include "util/urlfetch.hpp" #include "util/urlfetch.hpp"
#define TWITCH_EMOTE_TEMPLATE "https://static-cdn.jtvnw.net/emoticons/v1/{id}/{scale}" #define TWITCH_EMOTE_TEMPLATE "https://static-cdn.jtvnw.net/emoticons/v1/{id}/{scale}"
@ -41,8 +43,58 @@ QString cleanUpCode(const QString &dirtyEmoteCode)
return dirtyEmoteCode; return dirtyEmoteCode;
} }
void loadSetData(std::shared_ptr<TwitchEmotes::EmoteSet> emoteSet)
{
debug::Log("Load twitch emote set data for {}", emoteSet->key);
util::NetworkRequest req("https://braize.pajlada.com/chatterino/twitchemotes/set/" +
emoteSet->key + "/");
req.setRequestType(util::NetworkRequest::GetRequest);
req.onError([](int errorCode) -> bool {
debug::Log("Emote sets on ERROR {}", errorCode);
return true;
});
req.onSuccess([emoteSet](const rapidjson::Document &root) -> bool {
debug::Log("Emote sets on success");
if (!root.IsObject()) {
return false;
}
std::string emoteSetID;
QString channelName;
if (!rj::getSafe(root, "channel_name", channelName)) {
return false;
}
emoteSet->channelName = channelName;
return true;
});
req.execute();
}
} // namespace } // namespace
TwitchEmotes::TwitchEmotes()
{
{
EmoteSet emoteSet;
emoteSet.key = "19194";
emoteSet.text = "Twitch Prime Emotes";
this->staticEmoteSets[emoteSet.key] = std::move(emoteSet);
}
{
EmoteSet emoteSet;
emoteSet.key = "0";
emoteSet.text = "Twitch Global Emotes";
this->staticEmoteSets[emoteSet.key] = std::move(emoteSet);
}
}
// id is used for lookup // id is used for lookup
// emoteName is used for giving a name to the emote in case it doesn't exist // emoteName is used for giving a name to the emote in case it doesn't exist
util::EmoteData TwitchEmotes::getEmoteById(const QString &id, const QString &emoteName) util::EmoteData TwitchEmotes::getEmoteById(const QString &id, const QString &emoteName)
@ -107,23 +159,26 @@ void TwitchEmotes::refresh(const std::shared_ptr<TwitchAccount> &user)
auto emoticonSets = root.value("emoticon_sets").toObject(); auto emoticonSets = root.value("emoticon_sets").toObject();
for (QJsonObject::iterator it = emoticonSets.begin(); it != emoticonSets.end(); ++it) { for (QJsonObject::iterator it = emoticonSets.begin(); it != emoticonSets.end(); ++it) {
EmoteSet emoteSet; auto emoteSet = std::make_shared<EmoteSet>();
emoteSet.key = it.key(); emoteSet->key = it.key();
loadSetData(emoteSet);
for (QJsonValue emoteValue : it.value().toArray()) { for (QJsonValue emoteValue : it.value().toArray()) {
QJsonObject emoticon = emoteValue.toObject(); QJsonObject emoticon = emoteValue.toObject();
QString id = QString::number(emoticon["id"].toInt()); QString id = QString::number(emoticon["id"].toInt());
QString code = emoticon["code"].toString(); QString code = emoticon["code"].toString();
auto cleanCode = cleanUpCode(code); auto cleanCode = cleanUpCode(code);
emoteSet.emotes.emplace_back(id, cleanCode); emoteSet->emotes.emplace_back(id, cleanCode);
emoteData.emoteCodes.push_back(cleanCode); emoteData.emoteCodes.push_back(cleanCode);
util::EmoteData emote = this->getEmoteById(id, code); util::EmoteData emote = this->getEmoteById(id, code);
emoteData.emotes.insert(code, emote); emoteData.emotes.insert(code, emote);
} }
emoteData.emoteSets.emplace_back(std::move(emoteSet)); emoteData.emoteSets.emplace_back(emoteSet);
} }
emoteData.filled = true; emoteData.filled = true;
@ -132,6 +187,64 @@ void TwitchEmotes::refresh(const std::shared_ptr<TwitchAccount> &user)
util::twitch::getAuthorized(url, clientID, oauthToken, QThread::currentThread(), loadEmotes); util::twitch::getAuthorized(url, clientID, oauthToken, QThread::currentThread(), loadEmotes);
} }
void TwitchEmotes::loadSetData(std::shared_ptr<TwitchEmotes::EmoteSet> emoteSet)
{
if (!emoteSet) {
debug::Log("null emote set sent");
return;
}
auto staticSetIt = this->staticEmoteSets.find(emoteSet->key);
if (staticSetIt != this->staticEmoteSets.end()) {
const auto &staticSet = staticSetIt->second;
emoteSet->channelName = staticSet.channelName;
emoteSet->text = staticSet.text;
return;
}
debug::Log("Load twitch emote set data for {}..", emoteSet->key);
util::NetworkRequest req("https://braize.pajlada.com/chatterino/twitchemotes/set/" +
emoteSet->key + "/");
req.setRequestType(util::NetworkRequest::GetRequest);
req.onError([](int errorCode) -> bool {
debug::Log("Emote sets on ERROR {}", errorCode);
return true;
});
req.onSuccess([emoteSet](const rapidjson::Document &root) -> bool {
if (!root.IsObject()) {
return false;
}
std::string emoteSetID;
QString channelName;
QString type;
if (!rj::getSafe(root, "channel_name", channelName)) {
return false;
}
if (!rj::getSafe(root, "type", type)) {
return false;
}
debug::Log("Loaded twitch emote set data for {}!", emoteSet->key);
if (type == "sub") {
emoteSet->text = QString("Twitch Subscriber Emote (%1)").arg(channelName);
} else {
emoteSet->text = QString("Twitch Account Emote (%1)").arg(channelName);
}
emoteSet->channelName = channelName;
return true;
});
req.execute();
}
} // namespace twitch } // namespace twitch
} // namespace providers } // namespace providers
} // namespace chatterino } // namespace chatterino

View file

@ -8,6 +8,8 @@
#include <map> #include <map>
#include <QString>
namespace chatterino { namespace chatterino {
namespace providers { namespace providers {
namespace twitch { namespace twitch {
@ -15,6 +17,8 @@ namespace twitch {
class TwitchEmotes class TwitchEmotes
{ {
public: public:
TwitchEmotes();
util::EmoteData getEmoteById(const QString &id, const QString &emoteName); util::EmoteData getEmoteById(const QString &id, const QString &emoteName);
/// Twitch emotes /// Twitch emotes
@ -36,11 +40,15 @@ public:
struct EmoteSet { struct EmoteSet {
QString key; QString key;
QString channelName;
QString text;
std::vector<TwitchEmote> emotes; std::vector<TwitchEmote> emotes;
}; };
std::map<QString, EmoteSet> staticEmoteSets;
struct TwitchAccountEmoteData { struct TwitchAccountEmoteData {
std::vector<EmoteSet> emoteSets; std::vector<std::shared_ptr<EmoteSet>> emoteSets;
std::vector<QString> emoteCodes; std::vector<QString> emoteCodes;
@ -53,6 +61,8 @@ public:
std::map<QString, TwitchAccountEmoteData> emotes; std::map<QString, TwitchAccountEmoteData> emotes;
private: private:
void loadSetData(std::shared_ptr<TwitchEmotes::EmoteSet> emoteSet);
// emote code // emote code
util::ConcurrentMap<QString, providers::twitch::EmoteValue *> _twitchEmotes; util::ConcurrentMap<QString, providers::twitch::EmoteValue *> _twitchEmotes;

View file

@ -216,7 +216,7 @@ public:
if (this->data.caller != nullptr) { if (this->data.caller != nullptr) {
QObject::connect(worker, &NetworkWorker::doneUrl, this->data.caller, QObject::connect(worker, &NetworkWorker::doneUrl, this->data.caller,
[ onFinished, data = this->data ](auto reply) mutable { [onFinished, data = this->data](auto reply) mutable {
if (reply->error() != QNetworkReply::NetworkError::NoError) { if (reply->error() != QNetworkReply::NetworkError::NoError) {
// TODO: We might want to call an onError callback here // TODO: We might want to call an onError callback here
return; return;
@ -238,7 +238,7 @@ public:
QObject::connect( QObject::connect(
&requester, &NetworkRequester::requestUrl, worker, &requester, &NetworkRequester::requestUrl, worker,
[ timer, data = std::move(this->data), worker, onFinished{std::move(onFinished)} ]() { [timer, data = std::move(this->data), worker, onFinished{std::move(onFinished)}]() {
QNetworkReply *reply = NetworkManager::NaM.get(data.request); QNetworkReply *reply = NetworkManager::NaM.get(data.request);
if (timer != nullptr) { if (timer != nullptr) {
@ -253,9 +253,9 @@ public:
data.onReplyCreated(reply); data.onReplyCreated(reply);
} }
QObject::connect(reply, &QNetworkReply::finished, worker, [ QObject::connect(reply, &QNetworkReply::finished, worker,
data = std::move(data), worker, reply, onFinished = std::move(onFinished) [data = std::move(data), worker, reply,
]() mutable { onFinished = std::move(onFinished)]() mutable {
if (data.caller == nullptr) { if (data.caller == nullptr) {
QByteArray bytes = reply->readAll(); QByteArray bytes = reply->readAll();
data.writeToCache(bytes); data.writeToCache(bytes);
@ -276,7 +276,7 @@ public:
template <typename FinishedCallback> template <typename FinishedCallback>
void getJSON(FinishedCallback onFinished) void getJSON(FinishedCallback onFinished)
{ {
this->get([onFinished{std::move(onFinished)}](const QByteArray &bytes)->bool { this->get([onFinished{std::move(onFinished)}](const QByteArray &bytes) -> bool {
auto object = parseJSONFromData(bytes); auto object = parseJSONFromData(bytes);
onFinished(object); onFinished(object);
@ -289,7 +289,7 @@ public:
template <typename FinishedCallback> template <typename FinishedCallback>
void getJSON2(FinishedCallback onFinished) void getJSON2(FinishedCallback onFinished)
{ {
this->get([onFinished{std::move(onFinished)}](const QByteArray &bytes)->bool { this->get([onFinished{std::move(onFinished)}](const QByteArray &bytes) -> bool {
auto object = parseJSONFromData2(bytes); auto object = parseJSONFromData2(bytes);
onFinished(object); onFinished(object);
@ -303,22 +303,19 @@ public:
{ {
switch (this->data.requestType) { switch (this->data.requestType) {
case GetRequest: { case GetRequest: {
debug::Log("Call GET request!");
this->executeGet(); this->executeGet();
} break; } break;
case PutRequest: { case PutRequest: {
debug::Log("Call PUT request!");
this->executePut(); this->executePut();
} break; } break;
case DeleteRequest: { case DeleteRequest: {
debug::Log("Call DELETE request!");
this->executeDelete(); this->executeDelete();
} break; } break;
default: { default: {
debug::Log("Unhandled request type {}", (int)this->data.requestType); debug::Log("[Execute] Unhandled request type {}", (int)this->data.requestType);
} break; } break;
} }
} }
@ -370,8 +367,8 @@ private:
worker->moveToThread(&NetworkManager::workerThread); worker->moveToThread(&NetworkManager::workerThread);
if (this->data.caller != nullptr) { if (this->data.caller != nullptr) {
QObject::connect(worker, &NetworkWorker::doneUrl, QObject::connect(worker, &NetworkWorker::doneUrl, this->data.caller,
this->data.caller, [data = this->data](auto reply) mutable { [data = this->data](auto reply) mutable {
if (reply->error() != QNetworkReply::NetworkError::NoError) { if (reply->error() != QNetworkReply::NetworkError::NoError) {
if (data.onError) { if (data.onError) {
data.onError(reply->error()); data.onError(reply->error());
@ -394,7 +391,7 @@ private:
} }
QObject::connect(&requester, &NetworkRequester::requestUrl, worker, QObject::connect(&requester, &NetworkRequester::requestUrl, worker,
[ timer, data = std::move(this->data), worker ]() { [timer, data = std::move(this->data), worker]() {
QNetworkReply *reply = nullptr; QNetworkReply *reply = nullptr;
switch (data.requestType) { switch (data.requestType) {
case GetRequest: { case GetRequest: {
@ -430,7 +427,7 @@ private:
} }
QObject::connect(reply, &QNetworkReply::finished, worker, QObject::connect(reply, &QNetworkReply::finished, worker,
[ data = std::move(data), worker, reply ]() mutable { [data = std::move(data), worker, reply]() mutable {
if (data.caller == nullptr) { if (data.caller == nullptr) {
QByteArray bytes = reply->readAll(); QByteArray bytes = reply->readAll();
data.writeToCache(bytes); data.writeToCache(bytes);

View file

@ -92,7 +92,18 @@ void EmotePopup::loadChannel(ChannelPtr _channel)
// TITLE // TITLE
messages::MessageBuilder builder1; messages::MessageBuilder builder1;
builder1.append(new TextElement("Twitch Account Emotes", MessageElement::Text)); QString setText;
if (set->text.isEmpty()) {
if (set->channelName.isEmpty()) {
setText = "Twitch Account Emotes";
} else {
setText = "Twitch Account Emotes (" + set->channelName + ")";
}
} else {
setText = set->text;
}
builder1.append(new TextElement(setText, MessageElement::Text));
builder1.getMessage()->flags |= Message::Centered; builder1.getMessage()->flags |= Message::Centered;
emoteChannel->addMessage(builder1.getMessage()); emoteChannel->addMessage(builder1.getMessage());
@ -102,7 +113,7 @@ void EmotePopup::loadChannel(ChannelPtr _channel)
builder2.getMessage()->flags |= Message::Centered; builder2.getMessage()->flags |= Message::Centered;
builder2.getMessage()->flags |= Message::DisableCompactEmotes; builder2.getMessage()->flags |= Message::DisableCompactEmotes;
for (const auto &emote : set.emotes) { for (const auto &emote : set->emotes) {
[&](const QString &key, const util::EmoteData &value) { [&](const QString &key, const util::EmoteData &value) {
builder2.append((new EmoteElement(value, MessageElement::Flags::AlwaysShow)) builder2.append((new EmoteElement(value, MessageElement::Flags::AlwaysShow))
->setLink(Link(Link::InsertText, key))); ->setLink(Link(Link::InsertText, key)));