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/application.cpp \
|
||||
src/channel.cpp \
|
||||
src/channeldata.cpp \
|
||||
src/colorscheme.cpp \
|
||||
src/emojis.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
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "channel.hpp"
|
||||
#include "channeldata.hpp"
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
|
@ -24,11 +27,16 @@ public:
|
|||
std::shared_ptr<Channel> getChannel(const QString &channel);
|
||||
void removeChannel(const QString &channel);
|
||||
|
||||
const std::string &getUserID(const std::string &username);
|
||||
|
||||
private:
|
||||
WindowManager &windowManager;
|
||||
EmoteManager &emoteManager;
|
||||
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;
|
||||
QMutex _channelsMutex;
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <QMutexLocker>
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
|
@ -64,4 +65,55 @@ private:
|
|||
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
|
||||
|
|
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 "emotemanager.hpp"
|
||||
#include "util/urlfetch.hpp"
|
||||
#include "windowmanager.hpp"
|
||||
|
||||
#include <QPixmap>
|
||||
|
@ -25,7 +26,7 @@ Resources::Resources(EmoteManager &emoteManager, WindowManager &windowManager)
|
|||
, badgeTurbo(lli(emoteManager, windowManager, ":/images/turbo_bg.png"))
|
||||
, badgeBroadcaster(lli(emoteManager, windowManager, ":/images/broadcaster_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"))
|
||||
, cheerBadge10000(lli(emoteManager, windowManager, ":/images/cheer10000"))
|
||||
, 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))
|
||||
, 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
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
#include "messages/lazyloadedimage.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class EmoteManager;
|
||||
|
@ -28,8 +31,46 @@ public:
|
|||
messages::LazyLoadedImage *cheerBadge100;
|
||||
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 *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
|
||||
|
|
|
@ -18,9 +18,17 @@ TwitchMessageBuilder::TwitchMessageBuilder()
|
|||
{
|
||||
}
|
||||
|
||||
SharedMessage TwitchMessageBuilder::parse(const Communi::IrcPrivateMessage *ircMessage,
|
||||
Channel *channel, const MessageParseArgs &args,
|
||||
const Resources &resources, EmoteManager &emoteManager,
|
||||
// When a twitch message is being parsed it makes sense for the builder to have access to:
|
||||
// - The IRC Message
|
||||
// - 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)
|
||||
{
|
||||
TwitchMessageBuilder b;
|
||||
|
@ -37,6 +45,13 @@ SharedMessage TwitchMessageBuilder::parse(const Communi::IrcPrivateMessage *ircM
|
|||
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
|
||||
iterator = tags.find("tmi-sent-ts");
|
||||
|
||||
|
@ -45,10 +60,12 @@ SharedMessage TwitchMessageBuilder::parse(const Communi::IrcPrivateMessage *ircM
|
|||
// badges
|
||||
iterator = tags.find("badges");
|
||||
|
||||
const auto &channelResources = resources.channels[roomID];
|
||||
|
||||
if (iterator != tags.end()) {
|
||||
auto badges = iterator.value().toString().split(',');
|
||||
|
||||
b.appendTwitchBadges(badges, resources, emoteManager);
|
||||
b.appendTwitchBadges(badges, resources, emoteManager, channelResources);
|
||||
}
|
||||
|
||||
// color
|
||||
|
@ -331,8 +348,9 @@ void TwitchMessageBuilder::appendTwitchEmote(
|
|||
}
|
||||
}
|
||||
|
||||
void TwitchMessageBuilder::appendTwitchBadges(const QStringList &badges, const Resources &resources,
|
||||
EmoteManager &emoteManager)
|
||||
void TwitchMessageBuilder::appendTwitchBadges(const QStringList &badges, Resources &resources,
|
||||
EmoteManager &emoteManager,
|
||||
const Resources::Channel &channelResources)
|
||||
{
|
||||
for (QString badge : badges) {
|
||||
if (badge.isEmpty()) {
|
||||
|
@ -340,9 +358,16 @@ void TwitchMessageBuilder::appendTwitchBadges(const QStringList &badges, const R
|
|||
}
|
||||
|
||||
if (badge.startsWith("bits/")) {
|
||||
long long int cheer = std::strtoll(badge.mid(5).toStdString().c_str(), nullptr, 10);
|
||||
appendWord(Word(emoteManager.getCheerBadge(cheer), Word::BadgeCheer, QString(),
|
||||
QString("Twitch Cheer" + QString::number(cheer))));
|
||||
if (!resources.bitBadgesLoaded) {
|
||||
// Do nothing
|
||||
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") {
|
||||
appendWord(
|
||||
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
|
||||
switch (index) {
|
||||
default: {
|
||||
// printf("[TwitchMessageBuilder] Unhandled subscriber badge index: %d\n", index);
|
||||
// printf("[TwitchMessageBuilder] Unhandled subscriber badge index: %d\n",
|
||||
// index);
|
||||
} break;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
#include "channel.hpp"
|
||||
#include "messages/messagebuilder.hpp"
|
||||
#include "resources.hpp"
|
||||
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class Resources;
|
||||
class EmoteManager;
|
||||
class WindowManager;
|
||||
|
||||
|
@ -24,7 +24,7 @@ public:
|
|||
|
||||
static messages::SharedMessage parse(const Communi::IrcPrivateMessage *ircMessage,
|
||||
Channel *channel, const messages::MessageParseArgs &args,
|
||||
const Resources &resources, EmoteManager &emoteManager,
|
||||
Resources &resources, EmoteManager &emoteManager,
|
||||
WindowManager &windowManager);
|
||||
|
||||
// static bool sortTwitchEmotes(
|
||||
|
@ -37,8 +37,8 @@ private:
|
|||
void appendTwitchEmote(const Communi::IrcPrivateMessage *ircMessage, const QString &emote,
|
||||
std::vector<std::pair<long int, messages::LazyLoadedImage *>> &vec,
|
||||
EmoteManager &emoteManager);
|
||||
void appendTwitchBadges(const QStringList &badges, const Resources &resources,
|
||||
EmoteManager &emoteManager);
|
||||
void appendTwitchBadges(const QStringList &badges, Resources &resources,
|
||||
EmoteManager &emoteManager, const Resources::Channel &channelResources);
|
||||
};
|
||||
|
||||
} // namespace twitch
|
||||
|
|
Loading…
Reference in a new issue