this commit is too big

This commit is contained in:
fourtf
2018-08-02 14:23:27 +02:00
parent 3b3c5d8d75
commit c2e2dfb577
186 changed files with 3626 additions and 2656 deletions
+9
View File
@@ -0,0 +1,9 @@
#include "Badges.hpp"
namespace chatterino {
Badges::Badges()
{
}
} // namespace chatterino
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include <unordered_map>
#include "common/Singleton.hpp"
#include "messages/Emote.hpp"
namespace chatterino {
class Badges : public Singleton
{
public:
Badges();
};
} // namespace chatterino
+7 -8
View File
@@ -5,15 +5,14 @@
namespace chatterino {
void Emotes::initialize(Application &app)
Emotes::Emotes()
{
const auto refreshTwitchEmotes = [this, &app] {
auto currentUser = app.accounts->twitch.getCurrent();
assert(currentUser);
this->twitch.refresh(currentUser);
};
app.accounts->twitch.currentUserChanged.connect(refreshTwitchEmotes);
refreshTwitchEmotes();
}
void Emotes::initialize(Settings &settings, Paths &paths)
{
getApp()->accounts->twitch.currentUserChanged.connect(
[] { getApp()->accounts->twitch.getCurrent()->loadEmotes(); });
this->emojis.load();
this->bttv.loadGlobalEmotes();
+8 -3
View File
@@ -12,16 +12,21 @@
namespace chatterino {
class Settings;
class Paths;
class Emotes final : public Singleton
{
public:
virtual void initialize(Application &app) override;
Emotes();
virtual void initialize(Settings &settings, Paths &paths) override;
bool isIgnoredEmote(const QString &emote);
TwitchEmotes twitch;
BTTVEmotes bttv;
FFZEmotes ffz;
BttvEmotes bttv;
FfzEmotes ffz;
Emojis emojis;
GIFTimer gifTimer;
+3 -11
View File
@@ -29,28 +29,20 @@ Fonts::Fonts()
this->fontsByType_.resize(size_t(EndType));
}
void Fonts::initialize(Application &app)
void Fonts::initialize(Settings &, Paths &)
{
this->chatFontFamily.connect([this, &app](const std::string &, auto) {
this->chatFontFamily.connect([this](const std::string &, auto) {
assertInGuiThread();
if (app.windows) {
app.windows->incGeneration();
}
for (auto &map : this->fontsByType_) {
map.clear();
}
this->fontChanged.invoke();
});
this->chatFontSize.connect([this, &app](const int &, auto) {
this->chatFontSize.connect([this](const int &, auto) {
assertInGuiThread();
if (app.windows) {
app.windows->incGeneration();
}
for (auto &map : this->fontsByType_) {
map.clear();
}
+4 -1
View File
@@ -13,12 +13,15 @@
namespace chatterino {
class Settings;
class Paths;
class Fonts final : public Singleton
{
public:
Fonts();
virtual void initialize(Application &app) override;
virtual void initialize(Settings &settings, Paths &paths) override;
// font data gets set in createFontData(...)
enum Type : uint8_t {
+1 -1
View File
@@ -12,7 +12,7 @@
namespace chatterino {
void Logging::initialize(Application &app)
void Logging::initialize(Settings &settings, Paths &paths)
{
}
+1 -1
View File
@@ -18,7 +18,7 @@ class Logging : public Singleton
public:
Logging() = default;
virtual void initialize(Application &app) override;
virtual void initialize(Settings &settings, Paths &paths) override;
void addMessage(const QString &channelName, MessagePtr message);
+51 -58
View File
@@ -31,29 +31,12 @@ namespace ipc = boost::interprocess;
namespace chatterino {
// fourtf: don't add this class to the application class
NativeMessaging::NativeMessaging()
{
qDebug() << "init NativeMessagingManager";
}
void registerNmManifest(Paths &paths, const QString &manifestFilename,
const QString &registryKeyName, const QJsonDocument &document);
void NativeMessaging::writeByteArray(QByteArray a)
void registerNmHost(Paths &paths)
{
char *data = a.data();
uint32_t size;
size = a.size();
std::cout.write(reinterpret_cast<char *>(&size), 4);
std::cout.write(data, a.size());
std::cout.flush();
}
void NativeMessaging::registerHost()
{
auto app = getApp();
if (app->paths->isPortable()) {
return;
}
if (paths.isPortable()) return;
auto getBaseDocument = [&] {
QJsonObject obj;
@@ -65,22 +48,6 @@ void NativeMessaging::registerHost()
return obj;
};
auto registerManifest = [&](const QString &manifestFilename, const QString &registryKeyName,
const QJsonDocument &document) {
// save the manifest
QString manifestPath = app->paths->miscDirectory + manifestFilename;
QFile file(manifestPath);
file.open(QIODevice::WriteOnly | QIODevice::Truncate);
file.write(document.toJson());
file.flush();
#ifdef Q_OS_WIN
// clang-format off
QProcess::execute("REG ADD \"" + registryKeyName + "\" /ve /t REG_SZ /d \"" + manifestPath + "\" /f");
// clang-format on
#endif
};
// chrome
{
QJsonDocument document;
@@ -90,8 +57,8 @@ void NativeMessaging::registerHost()
obj.insert("allowed_origins", allowed_origins_arr);
document.setObject(obj);
registerManifest(
"/native-messaging-manifest-chrome.json",
registerNmManifest(
paths, "/native-messaging-manifest-chrome.json",
"HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\com.chatterino.chatterino",
document);
}
@@ -105,24 +72,40 @@ void NativeMessaging::registerHost()
obj.insert("allowed_extensions", allowed_extensions);
document.setObject(obj);
registerManifest("/native-messaging-manifest-firefox.json",
"HKCU\\Software\\Mozilla\\NativeMessagingHosts\\com.chatterino.chatterino",
document);
registerNmManifest(
paths, "/native-messaging-manifest-firefox.json",
"HKCU\\Software\\Mozilla\\NativeMessagingHosts\\com.chatterino.chatterino", document);
}
}
void NativeMessaging::openGuiMessageQueue()
void registerNmManifest(Paths &paths, const QString &manifestFilename,
const QString &registryKeyName, const QJsonDocument &document)
{
static ReceiverThread thread;
(void)registryKeyName;
if (thread.isRunning()) {
thread.exit();
}
// save the manifest
QString manifestPath = paths.miscDirectory + manifestFilename;
QFile file(manifestPath);
file.open(QIODevice::WriteOnly | QIODevice::Truncate);
file.write(document.toJson());
file.flush();
thread.start();
#ifdef Q_OS_WIN
// clang-format off
QProcess::execute("REG ADD \"" + registryKeyName + "\" /ve /t REG_SZ /d \"" + manifestPath + "\" /f");
// clang-format on
#endif
}
void NativeMessaging::sendToGuiProcess(const QByteArray &array)
std::string &getNmQueueName(Paths &paths)
{
static std::string name = "chatterino_gui" + paths.applicationFilePathHash.toStdString();
return name;
}
// CLIENT
void NativeMessagingClient::sendMessage(const QByteArray &array)
{
try {
ipc::message_queue messageQueue(ipc::open_only, "chatterino_gui");
@@ -133,7 +116,24 @@ void NativeMessaging::sendToGuiProcess(const QByteArray &array)
}
}
void NativeMessaging::ReceiverThread::run()
void NativeMessagingClient::writeToCout(const QByteArray &array)
{
auto *data = array.data();
auto size = uint32_t(array.size());
std::cout.write(reinterpret_cast<char *>(&size), 4);
std::cout.write(data, size);
std::cout.flush();
}
// SERVER
void NativeMessagingServer::start()
{
this->thread.start();
}
void NativeMessagingServer::ReceiverThread::run()
{
ipc::message_queue::remove("chatterino_gui");
@@ -157,7 +157,7 @@ void NativeMessaging::ReceiverThread::run()
}
}
void NativeMessaging::ReceiverThread::handleMessage(const QJsonObject &root)
void NativeMessagingServer::ReceiverThread::handleMessage(const QJsonObject &root)
{
auto app = getApp();
@@ -231,11 +231,4 @@ void NativeMessaging::ReceiverThread::handleMessage(const QJsonObject &root)
}
}
std::string &NativeMessaging::getGuiMessageQueueName()
{
static std::string name =
"chatterino_gui" + Paths::getInstance()->applicationFilePathHash.toStdString();
return name;
}
} // namespace chatterino
+18 -11
View File
@@ -1,16 +1,28 @@
#pragma once
#include "common/Singleton.hpp"
#include <QThread>
class Application;
class Paths;
namespace chatterino {
class NativeMessaging final
void registerNmHost(Application &app);
std::string &getNmQueueName(Paths &paths);
class NativeMessagingClient final
{
public:
// fourtf: don't add this class to the application class
NativeMessaging();
void sendMessage(const QByteArray &array);
void writeToCout(const QByteArray &array);
};
class NativeMessagingServer final
{
public:
void start();
private:
class ReceiverThread : public QThread
{
public:
@@ -20,12 +32,7 @@ public:
void handleMessage(const QJsonObject &root);
};
void writeByteArray(QByteArray a);
void registerHost();
void openGuiMessageQueue();
void sendToGuiProcess(const QByteArray &array);
static std::string &getGuiMessageQueueName();
ReceiverThread thread;
};
} // namespace chatterino
+3 -15
View File
@@ -14,6 +14,8 @@ Paths *Paths::instance = nullptr;
Paths::Paths()
{
this->instance = this;
this->initAppFilePathHash();
this->initCheckPortable();
@@ -21,20 +23,6 @@ Paths::Paths()
this->initSubDirectories();
}
void Paths::initInstance()
{
assert(!instance);
instance = new Paths();
}
Paths *Paths::getInstance()
{
assert(instance);
return instance;
}
bool Paths::createFolder(const QString &folderPath)
{
return QDir().mkpath(folderPath);
@@ -116,7 +104,7 @@ void Paths::initSubDirectories()
Paths *getPaths()
{
return Paths::getInstance();
return Paths::instance;
}
} // namespace chatterino
+4 -6
View File
@@ -7,11 +7,10 @@ namespace chatterino {
class Paths
{
Paths();
public:
static void initInstance();
static Paths *getInstance();
static Paths *instance;
Paths();
// Root directory for the configuration files. %APPDATA%/chatterino or ExecutablePath for
// portable mode
@@ -41,10 +40,9 @@ private:
void initAppDataDirectory();
void initSubDirectories();
static Paths *instance;
boost::optional<bool> portable_;
};
Paths *getPaths();
[[deprecated]] Paths *getPaths();
} // namespace chatterino
-480
View File
@@ -1,481 +1 @@
#include "singletons/Resources.hpp"
#include "common/NetworkRequest.hpp"
#include <QDebug>
#include <QIcon>
#include <QJsonArray>
#include <QPixmap>
#include <QThread>
namespace chatterino {
namespace {
inline Image *lli(const char *pixmapPath, qreal scale = 1)
{
return new Image(new QPixmap(pixmapPath), scale);
}
template <typename Type>
inline bool ReadValue(const rapidjson::Value &object, const char *key, Type &out)
{
if (!object.HasMember(key)) {
return false;
}
const auto &value = object[key];
if (!value.Is<Type>()) {
return false;
}
out = value.Get<Type>();
return true;
}
template <>
inline bool ReadValue<QString>(const rapidjson::Value &object, const char *key, QString &out)
{
if (!object.HasMember(key)) {
return false;
}
const auto &value = object[key];
if (!value.IsString()) {
return false;
}
out = value.GetString();
return true;
}
template <>
inline bool ReadValue<std::vector<QString>>(const rapidjson::Value &object, const char *key,
std::vector<QString> &out)
{
if (!object.HasMember(key)) {
return false;
}
const auto &value = object[key];
if (!value.IsArray()) {
return false;
}
for (const rapidjson::Value &innerValue : value.GetArray()) {
if (!innerValue.IsString()) {
return false;
}
out.emplace_back(innerValue.GetString());
}
return true;
}
// Parse a single cheermote set (or "action") from the twitch api
inline bool ParseSingleCheermoteSet(Resources::JSONCheermoteSet &set,
const rapidjson::Value &action)
{
if (!action.IsObject()) {
return false;
}
if (!ReadValue(action, "prefix", set.prefix)) {
return false;
}
if (!ReadValue(action, "scales", set.scales)) {
return false;
}
if (!ReadValue(action, "backgrounds", set.backgrounds)) {
return false;
}
if (!ReadValue(action, "states", set.states)) {
return false;
}
if (!ReadValue(action, "type", set.type)) {
return false;
}
if (!ReadValue(action, "updated_at", set.updatedAt)) {
return false;
}
if (!ReadValue(action, "priority", set.priority)) {
return false;
}
// Tiers
if (!action.HasMember("tiers")) {
return false;
}
const auto &tiersValue = action["tiers"];
if (!tiersValue.IsArray()) {
return false;
}
for (const rapidjson::Value &tierValue : tiersValue.GetArray()) {
Resources::JSONCheermoteSet::CheermoteTier tier;
if (!tierValue.IsObject()) {
return false;
}
if (!ReadValue(tierValue, "min_bits", tier.minBits)) {
return false;
}
if (!ReadValue(tierValue, "id", tier.id)) {
return false;
}
if (!ReadValue(tierValue, "color", tier.color)) {
return false;
}
// Images
if (!tierValue.HasMember("images")) {
return false;
}
const auto &imagesValue = tierValue["images"];
if (!imagesValue.IsObject()) {
return false;
}
// Read images object
for (const auto &imageBackgroundValue : imagesValue.GetObject()) {
QString background = imageBackgroundValue.name.GetString();
bool backgroundExists = false;
for (const auto &bg : set.backgrounds) {
if (background == bg) {
backgroundExists = true;
break;
}
}
if (!backgroundExists) {
continue;
}
const rapidjson::Value &imageBackgroundStates = imageBackgroundValue.value;
if (!imageBackgroundStates.IsObject()) {
continue;
}
// Read each key which represents a background
for (const auto &imageBackgroundState : imageBackgroundStates.GetObject()) {
QString state = imageBackgroundState.name.GetString();
bool stateExists = false;
for (const auto &_state : set.states) {
if (state == _state) {
stateExists = true;
break;
}
}
if (!stateExists) {
continue;
}
const rapidjson::Value &imageScalesValue = imageBackgroundState.value;
if (!imageScalesValue.IsObject()) {
continue;
}
// Read each key which represents a scale
for (const auto &imageScaleValue : imageScalesValue.GetObject()) {
QString scale = imageScaleValue.name.GetString();
bool scaleExists = false;
for (const auto &_scale : set.scales) {
if (scale == _scale) {
scaleExists = true;
break;
}
}
if (!scaleExists) {
continue;
}
const rapidjson::Value &imageScaleURLValue = imageScaleValue.value;
if (!imageScaleURLValue.IsString()) {
continue;
}
QString url = imageScaleURLValue.GetString();
bool ok = false;
qreal scaleNumber = scale.toFloat(&ok);
if (!ok) {
continue;
}
qreal chatterinoScale = 1 / scaleNumber;
auto image = new Image(url, chatterinoScale);
// TODO(pajlada): Fill in name and tooltip
tier.images[background][state][scale] = image;
}
}
}
set.tiers.emplace_back(tier);
}
return true;
}
// Look through the results of https://api.twitch.tv/kraken/bits/actions?channel_id=11148817 for
// cheermote sets or "Actions" as they are called in the API
inline void ParseCheermoteSets(std::vector<Resources::JSONCheermoteSet> &sets,
const rapidjson::Document &d)
{
if (!d.IsObject()) {
return;
}
if (!d.HasMember("actions")) {
return;
}
const auto &actionsValue = d["actions"];
if (!actionsValue.IsArray()) {
return;
}
for (const auto &action : actionsValue.GetArray()) {
Resources::JSONCheermoteSet set;
bool res = ParseSingleCheermoteSet(set, action);
if (res) {
sets.emplace_back(set);
}
}
}
} // namespace
Resources::Resources()
: badgeStaff(lli(":/images/staff_bg.png"))
, badgeAdmin(lli(":/images/admin_bg.png"))
, badgeGlobalModerator(lli(":/images/globalmod_bg.png"))
, badgeModerator(lli(":/images/moderator_bg.png"))
, badgeTurbo(lli(":/images/turbo_bg.png"))
, badgeBroadcaster(lli(":/images/broadcaster_bg.png"))
, badgePremium(lli(":/images/twitchprime_bg.png"))
, badgeVerified(lli(":/images/verified.png", 0.25))
, badgeSubscriber(lli(":/images/subscriber.png", 0.25))
, badgeCollapsed(lli(":/images/collapse.png"))
, cheerBadge100000(lli(":/images/cheer100000"))
, cheerBadge10000(lli(":/images/cheer10000"))
, cheerBadge5000(lli(":/images/cheer5000"))
, cheerBadge1000(lli(":/images/cheer1000"))
, cheerBadge100(lli(":/images/cheer100"))
, cheerBadge1(lli(":/images/cheer1"))
, moderationmode_enabled(lli(":/images/moderatormode_enabled"))
, moderationmode_disabled(lli(":/images/moderatormode_disabled"))
, splitHeaderContext(lli(":/images/tool_moreCollapser_off16.png"))
, buttonBan(lli(":/images/button_ban.png", 0.25))
, buttonTimeout(lli(":/images/button_timeout.png", 0.25))
, pajaDank(lli(":/images/pajaDank.png", 0.25))
, ppHop(new Image("https://fourtf.com/ppHop.gif", 0.25))
{
this->split.left = QIcon(":/images/split/splitleft.png");
this->split.right = QIcon(":/images/split/splitright.png");
this->split.up = QIcon(":/images/split/splitup.png");
this->split.down = QIcon(":/images/split/splitdown.png");
this->split.move = QIcon(":/images/split/splitmove.png");
this->buttons.ban = QPixmap(":/images/buttons/ban.png");
this->buttons.unban = QPixmap(":/images/buttons/unban.png");
this->buttons.mod = QPixmap(":/images/buttons/mod.png");
this->buttons.unmod = QPixmap(":/images/buttons/unmod.png");
qDebug() << "init ResourceManager";
}
void Resources::initialize(Application &app)
{
this->loadDynamicTwitchBadges();
this->loadChatterinoBadges();
}
Resources::BadgeVersion::BadgeVersion(QJsonObject &&root)
: badgeImage1x(new Image(root.value("image_url_1x").toString()))
, badgeImage2x(new Image(root.value("image_url_2x").toString()))
, badgeImage4x(new Image(root.value("image_url_4x").toString()))
, description(root.value("description").toString().toStdString())
, title(root.value("title").toString().toStdString())
, clickAction(root.value("clickAction").toString().toStdString())
, clickURL(root.value("clickURL").toString().toStdString())
{
}
void Resources::loadChannelData(const QString &roomID, bool bypassCache)
{
QString url = "https://badges.twitch.tv/v1/badges/channels/" + roomID + "/display?language=en";
NetworkRequest req(url);
req.setCaller(QThread::currentThread());
req.onSuccess([this, roomID](auto result) {
auto root = result.parseJson();
QJsonObject sets = root.value("badge_sets").toObject();
Resources::Channel &ch = this->channels[roomID];
for (QJsonObject::iterator it = sets.begin(); it != sets.end(); ++it) {
QJsonObject versions = it.value().toObject().value("versions").toObject();
auto &badgeSet = ch.badgeSets[it.key().toStdString()];
auto &versionsMap = badgeSet.versions;
for (auto versionIt = std::begin(versions); versionIt != std::end(versions);
++versionIt) {
std::string kkey = versionIt.key().toStdString();
QJsonObject versionObj = versionIt.value().toObject();
BadgeVersion v(std::move(versionObj));
versionsMap.emplace(kkey, v);
}
}
ch.loaded = true;
return true;
});
req.execute();
QString cheermoteUrl = "https://api.twitch.tv/kraken/bits/actions?channel_id=" + roomID;
auto request = NetworkRequest::twitchRequest(cheermoteUrl);
request.setCaller(QThread::currentThread());
request.onSuccess([this, roomID](auto result) {
auto d = result.parseRapidJson();
Resources::Channel &ch = this->channels[roomID];
ParseCheermoteSets(ch.jsonCheermoteSets, d);
for (auto &set : ch.jsonCheermoteSets) {
CheermoteSet cheermoteSet;
cheermoteSet.regex = QRegularExpression("^" + set.prefix.toLower() + "([1-9][0-9]*)$");
for (auto &tier : set.tiers) {
Cheermote cheermote;
cheermote.color = QColor(tier.color);
cheermote.minBits = tier.minBits;
// TODO(pajlada): We currently hardcode dark here :|
// We will continue to do so for now since we haven't had to
// solve that anywhere else
cheermote.emoteDataAnimated.image1x = tier.images["dark"]["animated"]["1"];
cheermote.emoteDataAnimated.image2x = tier.images["dark"]["animated"]["2"];
cheermote.emoteDataAnimated.image3x = tier.images["dark"]["animated"]["4"];
cheermote.emoteDataStatic.image1x = tier.images["dark"]["static"]["1"];
cheermote.emoteDataStatic.image2x = tier.images["dark"]["static"]["2"];
cheermote.emoteDataStatic.image3x = tier.images["dark"]["static"]["4"];
cheermoteSet.cheermotes.emplace_back(cheermote);
}
std::sort(cheermoteSet.cheermotes.begin(), cheermoteSet.cheermotes.end(),
[](const auto &lhs, const auto &rhs) {
return lhs.minBits < rhs.minBits; //
});
ch.cheermoteSets.emplace_back(cheermoteSet);
}
return true;
});
request.execute();
}
void Resources::loadDynamicTwitchBadges()
{
static QString url("https://badges.twitch.tv/v1/badges/global/display?language=en");
NetworkRequest req(url);
req.setCaller(QThread::currentThread());
req.onSuccess([this](auto result) {
auto root = result.parseJson();
QJsonObject sets = root.value("badge_sets").toObject();
for (QJsonObject::iterator it = sets.begin(); it != sets.end(); ++it) {
QJsonObject versions = it.value().toObject().value("versions").toObject();
auto &badgeSet = this->badgeSets[it.key().toStdString()];
auto &versionsMap = badgeSet.versions;
for (auto versionIt = std::begin(versions); versionIt != std::end(versions);
++versionIt) {
std::string kkey = versionIt.key().toStdString();
QJsonObject versionObj = versionIt.value().toObject();
BadgeVersion v(std::move(versionObj));
versionsMap.emplace(kkey, v);
}
}
this->dynamicBadgesLoaded = true;
return true;
});
req.execute();
}
void Resources::loadChatterinoBadges()
{
this->chatterinoBadges.clear();
static QString url("https://fourtf.com/chatterino/badges.json");
NetworkRequest req(url);
req.setCaller(QThread::currentThread());
req.onSuccess([this](auto result) {
auto root = result.parseJson();
QJsonArray badgeVariants = root.value("badges").toArray();
for (QJsonArray::iterator it = badgeVariants.begin(); it != badgeVariants.end(); ++it) {
QJsonObject badgeVariant = it->toObject();
const std::string badgeVariantTooltip =
badgeVariant.value("tooltip").toString().toStdString();
const QString &badgeVariantImageURL = badgeVariant.value("image").toString();
auto badgeVariantPtr = std::make_shared<ChatterinoBadge>(
badgeVariantTooltip, new Image(badgeVariantImageURL));
QJsonArray badgeVariantUsers = badgeVariant.value("users").toArray();
for (QJsonArray::iterator it = badgeVariantUsers.begin(); it != badgeVariantUsers.end();
++it) {
const std::string username = it->toString().toStdString();
this->chatterinoBadges[username] =
std::shared_ptr<ChatterinoBadge>(badgeVariantPtr);
}
}
return true;
});
req.execute();
}
} // namespace chatterino
+1 -158
View File
@@ -1,160 +1,3 @@
#pragma once
#include "common/Singleton.hpp"
#include "common/Emotemap.hpp"
#include <QIcon>
#include <QRegularExpression>
#include <map>
#include <memory>
#include <mutex>
namespace chatterino {
class Resources : public Singleton
{
public:
Resources();
~Resources() = delete;
virtual void initialize(Application &app) override;
struct {
QIcon left;
QIcon right;
QIcon up;
QIcon down;
QIcon move;
} split;
struct {
QPixmap ban;
QPixmap unban;
QPixmap mod;
QPixmap unmod;
} buttons;
Image *badgeStaff;
Image *badgeAdmin;
Image *badgeGlobalModerator;
Image *badgeModerator;
Image *badgeTurbo;
Image *badgeBroadcaster;
Image *badgePremium;
Image *badgeVerified;
Image *badgeSubscriber;
Image *badgeCollapsed;
Image *cheerBadge100000;
Image *cheerBadge10000;
Image *cheerBadge5000;
Image *cheerBadge1000;
Image *cheerBadge100;
Image *cheerBadge1;
Image *moderationmode_enabled;
Image *moderationmode_disabled;
Image *splitHeaderContext;
std::map<std::string, Image *> cheerBadges;
struct BadgeVersion {
BadgeVersion() = delete;
explicit BadgeVersion(QJsonObject &&root);
Image *badgeImage1x;
Image *badgeImage2x;
Image *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 dynamicBadgesLoaded = false;
Image *buttonBan;
Image *buttonTimeout;
Image *pajaDank;
Image *ppHop;
struct JSONCheermoteSet {
QString prefix;
std::vector<QString> scales;
std::vector<QString> backgrounds;
std::vector<QString> states;
QString type;
QString updatedAt;
int priority;
struct CheermoteTier {
int minBits;
QString id;
QString color;
// Background State Scale
std::map<QString, std::map<QString, std::map<QString, Image *>>> images;
};
std::vector<CheermoteTier> tiers;
};
struct Cheermote {
// a Cheermote indicates one tier
QColor color;
int minBits;
EmoteData emoteDataAnimated;
EmoteData emoteDataStatic;
};
struct CheermoteSet {
QRegularExpression regex;
std::vector<Cheermote> cheermotes;
};
struct Channel {
std::map<std::string, BadgeSet> badgeSets;
std::vector<JSONCheermoteSet> jsonCheermoteSets;
std::vector<CheermoteSet> cheermoteSets;
bool loaded = false;
};
// channelId
std::map<QString, Channel> channels;
// Chatterino badges
struct ChatterinoBadge {
ChatterinoBadge(const std::string &_tooltip, Image *_image)
: tooltip(_tooltip)
, image(_image)
{
}
std::string tooltip;
Image *image;
};
// username
std::map<std::string, std::shared_ptr<ChatterinoBadge>> chatterinoBadges;
void loadChannelData(const QString &roomID, bool bypassCache = false);
void loadDynamicTwitchBadges();
void loadChatterinoBadges();
};
} // namespace chatterino
#include "autogenerated/ResourcesAutogen.hpp"
+9 -16
View File
@@ -10,32 +10,25 @@ namespace chatterino {
std::vector<std::weak_ptr<pajlada::Settings::ISettingData>> _settings;
Settings *Settings::instance = nullptr;
void _actuallyRegisterSetting(std::weak_ptr<pajlada::Settings::ISettingData> setting)
{
_settings.push_back(setting);
}
Settings::Settings()
Settings::Settings(Paths &paths)
{
qDebug() << "init SettingManager";
instance = this;
QString settingsPath = paths.settingsDirectory + "/settings.json";
pajlada::Settings::SettingManager::gLoad(qPrintable(settingsPath));
}
Settings &Settings::getInstance()
{
static Settings instance;
return instance;
}
void Settings::initialize()
{
}
void Settings::load()
{
QString settingsPath = getPaths()->settingsDirectory + "/settings.json";
pajlada::Settings::SettingManager::gLoad(qPrintable(settingsPath));
return *instance;
}
void Settings::saveSnapshot()
+6 -5
View File
@@ -1,5 +1,7 @@
#pragma once
#include "Paths.hpp"
#include "common/ChatterinoSetting.hpp"
#include "controllers/highlights/HighlightPhrase.hpp"
#include "controllers/moderationactions/ModerationAction.hpp"
@@ -14,13 +16,12 @@ void _actuallyRegisterSetting(std::weak_ptr<pajlada::Settings::ISettingData> set
class Settings
{
Settings();
static Settings *instance;
public:
static Settings &getInstance();
Settings(Paths &paths);
void initialize();
void load();
static Settings &getInstance();
/// Appearance
BoolSetting showTimestamps = {"/appearance/messages/showTimestamps", true};
@@ -131,6 +132,6 @@ private:
std::unique_ptr<rapidjson::Document> snapshot_;
};
Settings *getSettings();
[[deprecated]] Settings *getSettings();
} // namespace chatterino
+10 -14
View File
@@ -207,11 +207,12 @@ Window *WindowManager::windowAt(int index)
return this->windows_.at(index);
}
void WindowManager::initialize(Application &app)
void WindowManager::initialize(Settings &settings, Paths &paths)
{
assertInGuiThread();
app.themes->repaintVisibleChatWidgets_.connect([this] { this->repaintVisibleChatWidgets(); });
getApp()->themes->repaintVisibleChatWidgets_.connect(
[this] { this->repaintVisibleChatWidgets(); });
assert(!this->initialized_);
@@ -301,20 +302,15 @@ void WindowManager::initialize(Application &app)
mainWindow_->getNotebook().addPage(true);
}
auto settings = getSettings();
settings.timestampFormat.connect([this](auto, auto) { this->layoutChannelViews(); });
settings->timestampFormat.connect([this](auto, auto) {
auto app = getApp();
this->layoutChannelViews();
});
settings.emoteScale.connect([this](auto, auto) { this->forceLayoutChannelViews(); });
settings->emoteScale.connect([this](auto, auto) { this->forceLayoutChannelViews(); });
settings->timestampFormat.connect([this](auto, auto) { this->forceLayoutChannelViews(); });
settings->alternateMessageBackground.connect(
settings.timestampFormat.connect([this](auto, auto) { this->forceLayoutChannelViews(); });
settings.alternateMessageBackground.connect(
[this](auto, auto) { this->forceLayoutChannelViews(); });
settings->separateMessages.connect([this](auto, auto) { this->forceLayoutChannelViews(); });
settings->collpseMessagesMinLines.connect(
settings.separateMessages.connect([this](auto, auto) { this->forceLayoutChannelViews(); });
settings.collpseMessagesMinLines.connect(
[this](auto, auto) { this->forceLayoutChannelViews(); });
this->initialized_ = true;
@@ -438,7 +434,7 @@ void WindowManager::encodeChannel(IndirectChannel channel, QJsonObject &obj)
switch (channel.getType()) {
case Channel::Type::Twitch: {
obj.insert("type", "twitch");
obj.insert("name", channel.get()->name);
obj.insert("name", channel.get()->getName());
} break;
case Channel::Type::TwitchMentions: {
obj.insert("type", "mentions");
+5 -2
View File
@@ -6,7 +6,10 @@
namespace chatterino {
class WindowManager : public Singleton
class Settings;
class Paths;
class WindowManager final : public Singleton
{
public:
WindowManager();
@@ -36,7 +39,7 @@ public:
int windowCount();
Window *windowAt(int index);
virtual void initialize(Application &app) override;
virtual void initialize(Settings &settings, Paths &paths) override;
virtual void save() override;
void closeAll();