mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
work on dynamic badge-loading
This commit is contained in:
parent
e7282b5097
commit
7525dae768
11 changed files with 263 additions and 15 deletions
|
@ -49,6 +49,7 @@ SOURCES += \
|
||||||
src/main.cpp \
|
src/main.cpp \
|
||||||
src/application.cpp \
|
src/application.cpp \
|
||||||
src/channel.cpp \
|
src/channel.cpp \
|
||||||
|
src/channeldata.cpp \
|
||||||
src/colorscheme.cpp \
|
src/colorscheme.cpp \
|
||||||
src/emojis.cpp \
|
src/emojis.cpp \
|
||||||
src/ircmanager.cpp \
|
src/ircmanager.cpp \
|
||||||
|
|
5
src/channeldata.cpp
Normal file
5
src/channeldata.cpp
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#include "channeldata.hpp"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
} // namespace chatterino
|
20
src/channeldata.hpp
Normal file
20
src/channeldata.hpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "lockedobject.hpp"
|
||||||
|
#include "messages/lazyloadedimage.hpp"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
class ChannelData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ChannelData() = default;
|
||||||
|
|
||||||
|
LockedObject<std::string> username;
|
||||||
|
LockedObject<std::string> id;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// std::map<std::string, BadgeData> subscriptionBadges;
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // namespace chatterino
|
|
@ -120,4 +120,17 @@ void ChannelManager::removeChannel(const QString &channel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string &ChannelManager::getUserID(const std::string &username)
|
||||||
|
{
|
||||||
|
auto it = this->usernameToID.find(username);
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (it != std::end(this->usernameToID)) {
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
return "xd";
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "channel.hpp"
|
#include "channel.hpp"
|
||||||
|
#include "channeldata.hpp"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
|
@ -24,11 +27,16 @@ public:
|
||||||
std::shared_ptr<Channel> getChannel(const QString &channel);
|
std::shared_ptr<Channel> getChannel(const QString &channel);
|
||||||
void removeChannel(const QString &channel);
|
void removeChannel(const QString &channel);
|
||||||
|
|
||||||
|
const std::string &getUserID(const std::string &username);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WindowManager &windowManager;
|
WindowManager &windowManager;
|
||||||
EmoteManager &emoteManager;
|
EmoteManager &emoteManager;
|
||||||
IrcManager &ircManager;
|
IrcManager &ircManager;
|
||||||
|
|
||||||
|
std::map<std::string, std::string> usernameToID;
|
||||||
|
std::map<std::string, ChannelData> channelDatas;
|
||||||
|
|
||||||
QMap<QString, std::tuple<std::shared_ptr<Channel>, int>> _channels;
|
QMap<QString, std::tuple<std::shared_ptr<Channel>, int>> _channels;
|
||||||
QMutex _channelsMutex;
|
QMutex _channelsMutex;
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <QMutexLocker>
|
#include <QMutexLocker>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
|
@ -64,4 +65,55 @@ private:
|
||||||
QMap<TKey, TValue> _map;
|
QMap<TKey, TValue> _map;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename TKey, typename TValue>
|
||||||
|
class ConcurrentStdMap
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool tryGet(const TKey &name, TValue &value) const
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&_mutex);
|
||||||
|
|
||||||
|
auto a = _map.find(name);
|
||||||
|
if (a == _map.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = a.value();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TValue getOrAdd(const TKey &name, std::function<TValue()> addLambda)
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&_mutex);
|
||||||
|
|
||||||
|
auto a = _map.find(name);
|
||||||
|
if (a == _map.end()) {
|
||||||
|
TValue value = addLambda();
|
||||||
|
_map.insert(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&_mutex);
|
||||||
|
|
||||||
|
_map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(const TKey &name, const TValue &value)
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&_mutex);
|
||||||
|
|
||||||
|
_map.insert(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable QMutex _mutex;
|
||||||
|
std::map<TKey, TValue> _map;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
38
src/lockedobject.hpp
Normal file
38
src/lockedobject.hpp
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
template <typename Type>
|
||||||
|
class LockedObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LockedObject &operator=(const LockedObject<Type> &other)
|
||||||
|
{
|
||||||
|
this->mutex.lock();
|
||||||
|
|
||||||
|
this->data = other.getValue();
|
||||||
|
|
||||||
|
this->mutex.unlock();
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
LockedObject &operator=(const Type &other)
|
||||||
|
{
|
||||||
|
this->mutex.lock();
|
||||||
|
|
||||||
|
this->data = other;
|
||||||
|
|
||||||
|
this->mutex.unlock();
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Type value;
|
||||||
|
std::mutex mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace chatterino
|
|
@ -1,5 +1,6 @@
|
||||||
#include "resources.hpp"
|
#include "resources.hpp"
|
||||||
#include "emotemanager.hpp"
|
#include "emotemanager.hpp"
|
||||||
|
#include "util/urlfetch.hpp"
|
||||||
#include "windowmanager.hpp"
|
#include "windowmanager.hpp"
|
||||||
|
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
|
@ -25,7 +26,7 @@ Resources::Resources(EmoteManager &emoteManager, WindowManager &windowManager)
|
||||||
, badgeTurbo(lli(emoteManager, windowManager, ":/images/turbo_bg.png"))
|
, badgeTurbo(lli(emoteManager, windowManager, ":/images/turbo_bg.png"))
|
||||||
, badgeBroadcaster(lli(emoteManager, windowManager, ":/images/broadcaster_bg.png"))
|
, badgeBroadcaster(lli(emoteManager, windowManager, ":/images/broadcaster_bg.png"))
|
||||||
, badgePremium(lli(emoteManager, windowManager, ":/images/twitchprime_bg.png"))
|
, badgePremium(lli(emoteManager, windowManager, ":/images/twitchprime_bg.png"))
|
||||||
, badgeVerified(lli(emoteManager, windowManager, ":/images/verified.png"))
|
, badgeVerified(lli(emoteManager, windowManager, ":/images/verified.png", 0.25))
|
||||||
, cheerBadge100000(lli(emoteManager, windowManager, ":/images/cheer100000"))
|
, cheerBadge100000(lli(emoteManager, windowManager, ":/images/cheer100000"))
|
||||||
, cheerBadge10000(lli(emoteManager, windowManager, ":/images/cheer10000"))
|
, cheerBadge10000(lli(emoteManager, windowManager, ":/images/cheer10000"))
|
||||||
, cheerBadge5000(lli(emoteManager, windowManager, ":/images/cheer5000"))
|
, cheerBadge5000(lli(emoteManager, windowManager, ":/images/cheer5000"))
|
||||||
|
@ -35,6 +36,49 @@ Resources::Resources(EmoteManager &emoteManager, WindowManager &windowManager)
|
||||||
, buttonBan(lli(emoteManager, windowManager, ":/images/button_ban.png", 0.25))
|
, buttonBan(lli(emoteManager, windowManager, ":/images/button_ban.png", 0.25))
|
||||||
, buttonTimeout(lli(emoteManager, windowManager, ":/images/button_timeout.png", 0.25))
|
, buttonTimeout(lli(emoteManager, windowManager, ":/images/button_timeout.png", 0.25))
|
||||||
{
|
{
|
||||||
|
QString badgesUrl("https://badges.twitch.tv/v1/badges/global/display?language=en");
|
||||||
|
|
||||||
|
util::urlJsonFetch(badgesUrl, [this](QJsonObject &root) {
|
||||||
|
QJsonObject sets = root.value("badge_sets").toObject();
|
||||||
|
|
||||||
|
for (auto it = std::begin(sets); it != std::end(sets); ++it) {
|
||||||
|
printf("%s\n", qPrintable(it.key()));
|
||||||
|
|
||||||
|
auto &badgeSet = this->badgeSets[it.key().toStdString()];
|
||||||
|
|
||||||
|
std::map<std::string, BadgeVersion> &versionsMap = badgeSet.versions;
|
||||||
|
|
||||||
|
QJsonObject versions = sets.value("versions").toObject();
|
||||||
|
|
||||||
|
for (auto versionIt = std::begin(versions); versionIt != std::end(versions);
|
||||||
|
++versionIt) {
|
||||||
|
/*
|
||||||
|
std::string kkey = versionIt.key().toStdString();
|
||||||
|
QJsonObject versionObj = versionIt.value().toObject();
|
||||||
|
versionsMap.emplace(std::make_pair(kkey, versionObj));
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Resources::BadgeVersion::BadgeVersion(QJsonObject &&root)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resources::Channel::loadData()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
if (this->loaded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->loaded = true;
|
||||||
|
|
||||||
|
if (this->id.empty()) {
|
||||||
|
//util::urlJsonFetch()
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
#include "messages/lazyloadedimage.hpp"
|
#include "messages/lazyloadedimage.hpp"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
class EmoteManager;
|
class EmoteManager;
|
||||||
|
@ -28,8 +31,46 @@ public:
|
||||||
messages::LazyLoadedImage *cheerBadge100;
|
messages::LazyLoadedImage *cheerBadge100;
|
||||||
messages::LazyLoadedImage *cheerBadge1;
|
messages::LazyLoadedImage *cheerBadge1;
|
||||||
|
|
||||||
|
std::map<std::string, messages::LazyLoadedImage *> cheerBadges;
|
||||||
|
|
||||||
|
struct BadgeVersion {
|
||||||
|
BadgeVersion() = delete;
|
||||||
|
|
||||||
|
explicit BadgeVersion(QJsonObject &&root);
|
||||||
|
|
||||||
|
messages::LazyLoadedImage *badgeImage1x;
|
||||||
|
messages::LazyLoadedImage *badgeImage2x;
|
||||||
|
messages::LazyLoadedImage *badgeImage4x;
|
||||||
|
std::string description;
|
||||||
|
std::string title;
|
||||||
|
std::string clickAction;
|
||||||
|
std::string clickUrl;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BadgeSet {
|
||||||
|
std::map<std::string, BadgeVersion> versions;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::map<std::string, BadgeSet> badgeSets;
|
||||||
|
|
||||||
|
bool bitBadgesLoaded = false;
|
||||||
|
bool dynamicBadgesLoaded = false;
|
||||||
|
|
||||||
messages::LazyLoadedImage *buttonBan;
|
messages::LazyLoadedImage *buttonBan;
|
||||||
messages::LazyLoadedImage *buttonTimeout;
|
messages::LazyLoadedImage *buttonTimeout;
|
||||||
|
|
||||||
|
struct Channel {
|
||||||
|
std::string id;
|
||||||
|
|
||||||
|
std::mutex globalMapMutex;
|
||||||
|
|
||||||
|
void loadData();
|
||||||
|
|
||||||
|
// std::atomic<bool> loaded = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// channelId
|
||||||
|
std::map<std::string, Channel> channels;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -18,9 +18,17 @@ TwitchMessageBuilder::TwitchMessageBuilder()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedMessage TwitchMessageBuilder::parse(const Communi::IrcPrivateMessage *ircMessage,
|
// When a twitch message is being parsed it makes sense for the builder to have access to:
|
||||||
Channel *channel, const MessageParseArgs &args,
|
// - The IRC Message
|
||||||
const Resources &resources, EmoteManager &emoteManager,
|
// - Message-specific parsing arguments (i.e. if ping sounds are disabled)
|
||||||
|
// - The Channel that the message originated from
|
||||||
|
// - The resources class for Badges, Moderation buttons
|
||||||
|
// - The Emote Manager for all different kinds of emotes
|
||||||
|
SharedMessage TwitchMessageBuilder::parse(const Communi::IrcPrivateMessage *ircMessage, //
|
||||||
|
Channel *channel, //
|
||||||
|
const MessageParseArgs &args, //
|
||||||
|
Resources &resources, //
|
||||||
|
EmoteManager &emoteManager, //
|
||||||
WindowManager &windowManager)
|
WindowManager &windowManager)
|
||||||
{
|
{
|
||||||
TwitchMessageBuilder b;
|
TwitchMessageBuilder b;
|
||||||
|
@ -37,6 +45,13 @@ SharedMessage TwitchMessageBuilder::parse(const Communi::IrcPrivateMessage *ircM
|
||||||
b.messageId = iterator.value().toString();
|
b.messageId = iterator.value().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// room id
|
||||||
|
iterator = tags.find("room-id");
|
||||||
|
std::string roomID;
|
||||||
|
if (iterator != std::end(tags)) {
|
||||||
|
roomID = iterator.value().toString().toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
// timestamps
|
// timestamps
|
||||||
iterator = tags.find("tmi-sent-ts");
|
iterator = tags.find("tmi-sent-ts");
|
||||||
|
|
||||||
|
@ -45,10 +60,12 @@ SharedMessage TwitchMessageBuilder::parse(const Communi::IrcPrivateMessage *ircM
|
||||||
// badges
|
// badges
|
||||||
iterator = tags.find("badges");
|
iterator = tags.find("badges");
|
||||||
|
|
||||||
|
const auto &channelResources = resources.channels[roomID];
|
||||||
|
|
||||||
if (iterator != tags.end()) {
|
if (iterator != tags.end()) {
|
||||||
auto badges = iterator.value().toString().split(',');
|
auto badges = iterator.value().toString().split(',');
|
||||||
|
|
||||||
b.appendTwitchBadges(badges, resources, emoteManager);
|
b.appendTwitchBadges(badges, resources, emoteManager, channelResources);
|
||||||
}
|
}
|
||||||
|
|
||||||
// color
|
// color
|
||||||
|
@ -331,8 +348,9 @@ void TwitchMessageBuilder::appendTwitchEmote(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwitchMessageBuilder::appendTwitchBadges(const QStringList &badges, const Resources &resources,
|
void TwitchMessageBuilder::appendTwitchBadges(const QStringList &badges, Resources &resources,
|
||||||
EmoteManager &emoteManager)
|
EmoteManager &emoteManager,
|
||||||
|
const Resources::Channel &channelResources)
|
||||||
{
|
{
|
||||||
for (QString badge : badges) {
|
for (QString badge : badges) {
|
||||||
if (badge.isEmpty()) {
|
if (badge.isEmpty()) {
|
||||||
|
@ -340,9 +358,16 @@ void TwitchMessageBuilder::appendTwitchBadges(const QStringList &badges, const R
|
||||||
}
|
}
|
||||||
|
|
||||||
if (badge.startsWith("bits/")) {
|
if (badge.startsWith("bits/")) {
|
||||||
long long int cheer = std::strtoll(badge.mid(5).toStdString().c_str(), nullptr, 10);
|
if (!resources.bitBadgesLoaded) {
|
||||||
appendWord(Word(emoteManager.getCheerBadge(cheer), Word::BadgeCheer, QString(),
|
// Do nothing
|
||||||
QString("Twitch Cheer" + QString::number(cheer))));
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString cheerAmountQS = badge.mid(5);
|
||||||
|
std::string cheerAmountString = cheerAmountQS.toStdString();
|
||||||
|
|
||||||
|
appendWord(Word(resources.cheerBadges[cheerAmountString], Word::BadgeCheer, QString(),
|
||||||
|
QString("Twitch Cheer" + cheerAmountQS)));
|
||||||
} else if (badge == "staff/1") {
|
} else if (badge == "staff/1") {
|
||||||
appendWord(
|
appendWord(
|
||||||
Word(resources.badgeStaff, Word::BadgeStaff, QString(), QString("Twitch Staff")));
|
Word(resources.badgeStaff, Word::BadgeStaff, QString(), QString("Twitch Staff")));
|
||||||
|
@ -382,7 +407,8 @@ void TwitchMessageBuilder::appendTwitchBadges(const QStringList &badges, const R
|
||||||
// TODO: Implement subscriber badges here
|
// TODO: Implement subscriber badges here
|
||||||
switch (index) {
|
switch (index) {
|
||||||
default: {
|
default: {
|
||||||
// printf("[TwitchMessageBuilder] Unhandled subscriber badge index: %d\n", index);
|
// printf("[TwitchMessageBuilder] Unhandled subscriber badge index: %d\n",
|
||||||
|
// index);
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
#include "channel.hpp"
|
#include "channel.hpp"
|
||||||
#include "messages/messagebuilder.hpp"
|
#include "messages/messagebuilder.hpp"
|
||||||
|
#include "resources.hpp"
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
class Resources;
|
|
||||||
class EmoteManager;
|
class EmoteManager;
|
||||||
class WindowManager;
|
class WindowManager;
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ public:
|
||||||
|
|
||||||
static messages::SharedMessage parse(const Communi::IrcPrivateMessage *ircMessage,
|
static messages::SharedMessage parse(const Communi::IrcPrivateMessage *ircMessage,
|
||||||
Channel *channel, const messages::MessageParseArgs &args,
|
Channel *channel, const messages::MessageParseArgs &args,
|
||||||
const Resources &resources, EmoteManager &emoteManager,
|
Resources &resources, EmoteManager &emoteManager,
|
||||||
WindowManager &windowManager);
|
WindowManager &windowManager);
|
||||||
|
|
||||||
// static bool sortTwitchEmotes(
|
// static bool sortTwitchEmotes(
|
||||||
|
@ -37,8 +37,8 @@ private:
|
||||||
void appendTwitchEmote(const Communi::IrcPrivateMessage *ircMessage, const QString &emote,
|
void appendTwitchEmote(const Communi::IrcPrivateMessage *ircMessage, const QString &emote,
|
||||||
std::vector<std::pair<long int, messages::LazyLoadedImage *>> &vec,
|
std::vector<std::pair<long int, messages::LazyLoadedImage *>> &vec,
|
||||||
EmoteManager &emoteManager);
|
EmoteManager &emoteManager);
|
||||||
void appendTwitchBadges(const QStringList &badges, const Resources &resources,
|
void appendTwitchBadges(const QStringList &badges, Resources &resources,
|
||||||
EmoteManager &emoteManager);
|
EmoteManager &emoteManager, const Resources::Channel &channelResources);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace twitch
|
} // namespace twitch
|
||||||
|
|
Loading…
Reference in a new issue