refactor: Un-singletonize Paths & Updates (#5092)

This commit is contained in:
pajlada 2024-01-16 21:56:43 +01:00 committed by GitHub
parent 7f935665f9
commit 718696db53
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
60 changed files with 237 additions and 165 deletions

View file

@ -117,6 +117,7 @@
- Dev: Channels without any animated elements on screen will skip updates from the GIF timer. (#5042, #5043, #5045)
- Dev: Autogenerate docs/plugin-meta.lua. (#5055)
- Dev: Refactor `NetworkPrivate`. (#5063)
- Dev: Refactor `Paths` & `Updates`, focusing on reducing their singletoniability. (#5092)
- Dev: Removed duplicate scale in settings dialog. (#5069)
- Dev: Fix `NotebookTab` emitting updates for every message. (#5068)
- Dev: Added benchmark for parsing and building recent messages. (#5071)

View file

@ -2,14 +2,26 @@
#include "Application.hpp"
#include "common/Args.hpp"
#include "singletons/Paths.hpp"
#include "singletons/Updates.hpp"
namespace chatterino::mock {
class EmptyApplication : public IApplication
{
public:
EmptyApplication()
: updates_(this->paths_)
{
}
virtual ~EmptyApplication() = default;
const Paths &getPaths() override
{
return this->paths_;
}
const Args &getArgs() override
{
return this->args_;
@ -168,8 +180,15 @@ public:
return nullptr;
}
Updates &getUpdates() override
{
return this->updates_;
}
private:
Paths paths_;
Args args_;
Updates updates_;
};
} // namespace chatterino::mock

View file

@ -107,18 +107,20 @@ IApplication::IApplication()
// It will create the instances of the major classes, and connect their signals
// to each other
Application::Application(Settings &_settings, Paths &_paths, const Args &_args)
: args_(_args)
Application::Application(Settings &_settings, const Paths &paths,
const Args &_args, Updates &_updates)
: paths_(paths)
, args_(_args)
, themes(&this->emplace<Theme>())
, fonts(&this->emplace<Fonts>())
, emotes(&this->emplace<Emotes>())
, accounts(&this->emplace<AccountController>())
, hotkeys(&this->emplace<HotkeyController>())
, windows(&this->emplace<WindowManager>())
, windows(&this->emplace(new WindowManager(paths)))
, toasts(&this->emplace<Toasts>())
, imageUploader(&this->emplace<ImageUploader>())
, seventvAPI(&this->emplace<SeventvAPI>())
, crashHandler(&this->emplace<CrashHandler>())
, crashHandler(&this->emplace(new CrashHandler(paths)))
, commands(&this->emplace<CommandController>())
, notifications(&this->emplace<NotificationController>())
@ -127,14 +129,15 @@ Application::Application(Settings &_settings, Paths &_paths, const Args &_args)
, chatterinoBadges(&this->emplace<ChatterinoBadges>())
, ffzBadges(&this->emplace<FfzBadges>())
, seventvBadges(&this->emplace<SeventvBadges>())
, userData(&this->emplace<UserDataController>())
, userData(&this->emplace(new UserDataController(paths)))
, sound(&this->emplace<ISoundController>(makeSoundController(_settings)))
, twitchLiveController(&this->emplace<TwitchLiveController>())
, twitchPubSub(new PubSub(TWITCH_PUBSUB_URL))
, logging(new Logging(_settings))
#ifdef CHATTERINO_HAVE_PLUGINS
, plugins(&this->emplace<PluginController>())
, plugins(&this->emplace(new PluginController(paths)))
#endif
, updates(_updates)
{
Application::instance = this;
@ -152,7 +155,7 @@ void Application::fakeDtor()
this->twitchPubSub.reset();
}
void Application::initialize(Settings &settings, Paths &paths)
void Application::initialize(Settings &settings, const Paths &paths)
{
assert(isAppInitialized == false);
isAppInitialized = true;
@ -237,8 +240,8 @@ int Application::run(QApplication &qtApp)
}
getSettings()->betaUpdates.connect(
[] {
Updates::instance().checkForUpdates();
[this] {
this->updates.checkForUpdates();
},
false);
@ -340,7 +343,7 @@ void Application::save()
}
}
void Application::initNm(Paths &paths)
void Application::initNm(const Paths &paths)
{
(void)paths;

View file

@ -15,6 +15,7 @@ class Args;
class TwitchIrcServer;
class ITwitchIrcServer;
class PubSub;
class Updates;
class CommandController;
class AccountController;
@ -55,6 +56,7 @@ public:
static IApplication *instance;
virtual const Paths &getPaths() = 0;
virtual const Args &getArgs() = 0;
virtual Theme *getThemes() = 0;
virtual Fonts *getFonts() = 0;
@ -78,10 +80,12 @@ public:
virtual ITwitchLiveController *getTwitchLiveController() = 0;
virtual ImageUploader *getImageUploader() = 0;
virtual SeventvAPI *getSeventvAPI() = 0;
virtual Updates &getUpdates() = 0;
};
class Application : public IApplication
{
const Paths &paths_;
const Args &args_;
std::vector<std::unique_ptr<Singleton>> singletons_;
int argc_{};
@ -90,7 +94,8 @@ class Application : public IApplication
public:
static Application *instance;
Application(Settings &_settings, Paths &_paths, const Args &_args);
Application(Settings &_settings, const Paths &paths, const Args &_args,
Updates &_updates);
~Application() override;
Application(const Application &) = delete;
@ -104,7 +109,7 @@ public:
*/
void fakeDtor();
void initialize(Settings &settings, Paths &paths);
void initialize(Settings &settings, const Paths &paths);
void load();
void save();
@ -143,6 +148,10 @@ public:
PluginController *const plugins{};
#endif
const Paths &getPaths() override
{
return this->paths_;
}
const Args &getArgs() override
{
return this->args_;
@ -214,6 +223,10 @@ public:
{
return this->seventvAPI;
}
Updates &getUpdates() override
{
return this->updates;
}
pajlada::Signals::NoArgSignal streamerModeChanged;
@ -222,7 +235,7 @@ private:
void initPubSub();
void initBttvLiveUpdates();
void initSeventvEventAPI();
void initNm(Paths &paths);
void initNm(const Paths &paths);
template <typename T,
typename = std::enable_if_t<std::is_base_of<Singleton, T>::value>>
@ -242,6 +255,7 @@ private:
}
NativeMessagingServer nmServer{};
Updates &updates;
};
Application *getApp();

View file

@ -98,9 +98,9 @@ namespace {
installCustomPalette();
}
void showLastCrashDialog(const Args &args)
void showLastCrashDialog(const Args &args, const Paths &paths)
{
auto *dialog = new LastRunCrashDialog(args);
auto *dialog = new LastRunCrashDialog(args, paths);
// Use exec() over open() to block the app from being loaded
// and to be able to set the safe mode.
dialog->exec();
@ -223,7 +223,8 @@ namespace {
}
} // namespace
void runGui(QApplication &a, Paths &paths, Settings &settings, const Args &args)
void runGui(QApplication &a, const Paths &paths, Settings &settings,
const Args &args, Updates &updates)
{
initQt();
initResources();
@ -232,7 +233,7 @@ void runGui(QApplication &a, Paths &paths, Settings &settings, const Args &args)
#ifdef Q_OS_WIN
if (args.crashRecovery)
{
showLastCrashDialog(args);
showLastCrashDialog(args, paths);
}
#endif
@ -269,9 +270,9 @@ void runGui(QApplication &a, Paths &paths, Settings &settings, const Args &args)
});
chatterino::NetworkManager::init();
chatterino::Updates::instance().checkForUpdates();
updates.checkForUpdates();
Application app(settings, paths, args);
Application app(settings, paths, args, updates);
app.initialize(settings, paths);
app.run(a);
app.save();

View file

@ -7,8 +7,9 @@ namespace chatterino {
class Args;
class Paths;
class Settings;
class Updates;
void runGui(QApplication &a, Paths &paths, Settings &settings,
const Args &args);
void runGui(QApplication &a, const Paths &paths, Settings &settings,
const Args &args, Updates &updates);
} // namespace chatterino

View file

@ -66,7 +66,7 @@ QStringList extractCommandLine(
namespace chatterino {
Args::Args(const QApplication &app)
Args::Args(const QApplication &app, const Paths &paths)
{
QCommandLineParser parser;
parser.setApplicationDescription("Chatterino 2 Client for Twitch Chat");
@ -132,7 +132,7 @@ Args::Args(const QApplication &app)
if (parser.isSet(channelLayout))
{
this->applyCustomChannelLayout(parser.value(channelLayout));
this->applyCustomChannelLayout(parser.value(channelLayout), paths);
}
this->verbose = parser.isSet(verboseOption);
@ -175,7 +175,7 @@ QStringList Args::currentArguments() const
return this->currentArguments_;
}
void Args::applyCustomChannelLayout(const QString &argValue)
void Args::applyCustomChannelLayout(const QString &argValue, const Paths &paths)
{
WindowLayout layout;
WindowDescriptor window;
@ -187,10 +187,9 @@ void Args::applyCustomChannelLayout(const QString &argValue)
window.type_ = WindowType::Main;
// Load main window layout from config file so we can use the same geometry
const QRect configMainLayout = [] {
const QString windowLayoutFile =
combinePath(getPaths()->settingsDirectory,
WindowManager::WINDOW_LAYOUT_FILENAME);
const QRect configMainLayout = [paths] {
const QString windowLayoutFile = combinePath(
paths.settingsDirectory, WindowManager::WINDOW_LAYOUT_FILENAME);
const WindowLayout configLayout =
WindowLayout::loadFromFile(windowLayoutFile);

View file

@ -8,6 +8,8 @@
namespace chatterino {
class Paths;
/// Command line arguments passed to Chatterino.
///
/// All accepted arguments:
@ -31,7 +33,7 @@ class Args
{
public:
Args() = default;
Args(const QApplication &app);
Args(const QApplication &app, const Paths &paths);
bool printVersion{};
@ -56,7 +58,7 @@ public:
QStringList currentArguments() const;
private:
void applyCustomChannelLayout(const QString &argValue);
void applyCustomChannelLayout(const QString &argValue, const Paths &paths);
QStringList currentArguments_;
};

View file

@ -1,5 +1,6 @@
#include "common/Credentials.hpp"
#include "Application.hpp"
#include "debug/AssertInGuiThread.hpp"
#include "singletons/Paths.hpp"
#include "singletons/Settings.hpp"
@ -40,7 +41,7 @@ bool useKeyring()
#ifdef NO_QTKEYCHAIN
return false;
#endif
if (getPaths()->isPortable())
if (getIApp()->getPaths().isPortable())
{
return false;
}
@ -55,7 +56,8 @@ bool useKeyring()
// Insecure storage:
QString insecurePath()
{
return combinePath(getPaths()->settingsDirectory, "credentials.json");
return combinePath(getIApp()->getPaths().settingsDirectory,
"credentials.json");
}
QJsonDocument loadInsecure()

View file

@ -17,7 +17,7 @@ public:
Singleton(Singleton &&) = delete;
Singleton &operator=(Singleton &&) = delete;
virtual void initialize(Settings &settings, Paths &paths)
virtual void initialize(Settings &settings, const Paths &paths)
{
(void)(settings);
(void)(paths);

View file

@ -1,5 +1,6 @@
#include "common/network/NetworkPrivate.hpp"
#include "Application.hpp"
#include "common/network/NetworkManager.hpp"
#include "common/network/NetworkResult.hpp"
#include "common/network/NetworkTask.hpp"
@ -57,7 +58,8 @@ void loadUncached(std::shared_ptr<NetworkData> &&data)
void loadCached(std::shared_ptr<NetworkData> &&data)
{
QFile cachedFile(getPaths()->cacheDirectory() + "/" + data->getHash());
QFile cachedFile(getIApp()->getPaths().cacheDirectory() + "/" +
data->getHash());
if (!cachedFile.exists() || !cachedFile.open(QIODevice::ReadOnly))
{

View file

@ -1,5 +1,6 @@
#include "common/network/NetworkTask.hpp"
#include "Application.hpp"
#include "common/network/NetworkManager.hpp"
#include "common/network/NetworkPrivate.hpp"
#include "common/network/NetworkResult.hpp"
@ -117,7 +118,8 @@ void NetworkTask::logReply()
void NetworkTask::writeToCache(const QByteArray &bytes) const
{
std::ignore = QtConcurrent::run([data = this->data_, bytes] {
QFile cachedFile(getPaths()->cacheDirectory() + "/" + data->getHash());
QFile cachedFile(getIApp()->getPaths().cacheDirectory() + "/" +
data->getHash());
if (cachedFile.open(QIODevice::WriteOnly))
{

View file

@ -47,7 +47,7 @@ AccountController::AccountController()
});
}
void AccountController::initialize(Settings &settings, Paths &paths)
void AccountController::initialize(Settings &settings, const Paths &paths)
{
this->twitch.load();
}

View file

@ -21,7 +21,7 @@ public:
AccountModel *createModel(QObject *parent);
void initialize(Settings &settings, Paths &paths) override;
void initialize(Settings &settings, const Paths &paths) override;
TwitchAccountManager twitch;

View file

@ -261,7 +261,7 @@ const std::unordered_map<QString, VariableReplacer> COMMAND_VARS{
namespace chatterino {
void CommandController::initialize(Settings &, Paths &paths)
void CommandController::initialize(Settings &, const Paths &paths)
{
// Update commands map when the vector of commands has been updated
auto addFirstMatchToMap = [this](auto args) {

View file

@ -33,7 +33,7 @@ public:
bool dryRun);
QStringList getDefaultChatterinoCommandList();
void initialize(Settings &, Paths &paths) override;
void initialize(Settings &, const Paths &paths) override;
void save() override;
CommandModel *createModel(QObject *parent);

View file

@ -440,7 +440,8 @@ std::ostream &operator<<(std::ostream &os, const HighlightResult &result)
return os;
}
void HighlightController::initialize(Settings &settings, Paths & /*paths*/)
void HighlightController::initialize(Settings &settings,
const Paths & /*paths*/)
{
this->rebuildListener_.addSetting(settings.enableSelfHighlight);
this->rebuildListener_.addSetting(settings.enableSelfHighlightSound);

View file

@ -86,7 +86,7 @@ struct HighlightCheck {
class HighlightController final : public Singleton
{
public:
void initialize(Settings &settings, Paths &paths) override;
void initialize(Settings &settings, const Paths &paths) override;
/**
* @brief Checks the given message parameters if it matches our internal checks, and returns a result

View file

@ -26,7 +26,7 @@
namespace chatterino {
void NotificationController::initialize(Settings &settings, Paths &paths)
void NotificationController::initialize(Settings &settings, const Paths &paths)
{
this->initialized_ = true;
for (const QString &channelName : this->twitchSetting_.getValue())

View file

@ -20,7 +20,7 @@ enum class Platform : uint8_t {
class NotificationController final : public Singleton, private QObject
{
public:
void initialize(Settings &settings, Paths &paths) override;
void initialize(Settings &settings, const Paths &paths) override;
bool isChannelNotified(const QString &channelName, Platform p);
void updateChannelNotification(const QString &channelName, Platform p);

View file

@ -23,7 +23,12 @@
namespace chatterino {
void PluginController::initialize(Settings &settings, Paths &paths)
PluginController::PluginController(const Paths &paths_)
: paths(paths_)
{
}
void PluginController::initialize(Settings &settings, const Paths &paths)
{
(void)paths;
@ -44,7 +49,7 @@ void PluginController::initialize(Settings &settings, Paths &paths)
void PluginController::loadPlugins()
{
this->plugins_.clear();
auto dir = QDir(getPaths()->pluginsDirectory);
auto dir = QDir(this->paths.pluginsDirectory);
qCDebug(chatterinoLua) << "Loading plugins in" << dir.path();
for (const auto &info :
dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot))

View file

@ -26,8 +26,12 @@ class Paths;
class PluginController : public Singleton
{
const Paths &paths;
public:
void initialize(Settings &settings, Paths &paths) override;
explicit PluginController(const Paths &paths_);
void initialize(Settings &settings, const Paths &paths) override;
QString tryExecPluginCommand(const QString &commandName,
const CommandContext &ctx);

View file

@ -68,7 +68,7 @@ namespace chatterino {
// NUM_SOUNDS specifies how many simultaneous default ping sounds & decoders to create
constexpr const auto NUM_SOUNDS = 4;
void MiniaudioBackend::initialize(Settings &settings, Paths &paths)
void MiniaudioBackend::initialize(Settings &settings, const Paths &paths)
{
(void)(settings);
(void)(paths);

View file

@ -25,7 +25,7 @@ namespace chatterino {
**/
class MiniaudioBackend : public ISoundController
{
void initialize(Settings &settings, Paths &paths) override;
void initialize(Settings &settings, const Paths &paths) override;
public:
MiniaudioBackend();

View file

@ -8,13 +8,12 @@ namespace {
using namespace chatterino;
std::shared_ptr<pajlada::Settings::SettingManager> initSettingsInstance()
std::shared_ptr<pajlada::Settings::SettingManager> initSettingsInstance(
const Paths &paths)
{
auto sm = std::make_shared<pajlada::Settings::SettingManager>();
auto *paths = getPaths();
auto path = combinePath(paths->settingsDirectory, "user-data.json");
auto path = combinePath(paths.settingsDirectory, "user-data.json");
sm->setPath(path.toUtf8().toStdString());
@ -30,8 +29,8 @@ std::shared_ptr<pajlada::Settings::SettingManager> initSettingsInstance()
namespace chatterino {
UserDataController::UserDataController()
: sm(initSettingsInstance())
UserDataController::UserDataController(const Paths &paths)
: sm(initSettingsInstance(paths))
, setting("/users", this->sm)
{
this->sm->load();

View file

@ -17,6 +17,8 @@
namespace chatterino {
class Paths;
class IUserDataController
{
public:
@ -31,7 +33,7 @@ public:
class UserDataController : public IUserDataController, public Singleton
{
public:
UserDataController();
explicit UserDataController(const Paths &paths);
// Get extra data about a user
// If the user does not have any extra data, return none

View file

@ -11,6 +11,7 @@
#include "singletons/CrashHandler.hpp"
#include "singletons/Paths.hpp"
#include "singletons/Settings.hpp"
#include "singletons/Updates.hpp"
#include "util/AttachToConsole.hpp"
#include <QApplication>
@ -35,11 +36,11 @@ int main(int argc, char **argv)
QCoreApplication::setApplicationVersion(CHATTERINO_VERSION);
QCoreApplication::setOrganizationDomain("chatterino.com");
Paths *paths{};
std::unique_ptr<Paths> paths;
try
{
paths = new Paths;
paths = std::make_unique<Paths>();
}
catch (std::runtime_error &error)
{
@ -62,10 +63,10 @@ int main(int argc, char **argv)
return 1;
}
const Args args(a);
const Args args(a, *paths);
#ifdef CHATTERINO_WITH_CRASHPAD
const auto crashpadHandler = installCrashHandler(args);
const auto crashpadHandler = installCrashHandler(args, *paths);
#endif
// run in gui mode or browser extension host mode
@ -92,6 +93,8 @@ int main(int argc, char **argv)
attachToConsole();
}
Updates updates(*paths);
NetworkConfigurationProvider::applyFromEnv(Env::get());
IvrApi::initialize();
@ -99,7 +102,7 @@ int main(int argc, char **argv)
Settings settings(paths->settingsDirectory);
runGui(a, *paths, settings, args);
runGui(a, *paths, settings, args, updates);
}
return 0;
}

View file

@ -11,7 +11,7 @@
#include <QUrl>
namespace chatterino {
void ChatterinoBadges::initialize(Settings &settings, Paths &paths)
void ChatterinoBadges::initialize(Settings &settings, const Paths &paths)
{
this->loadChatterinoBadges();
}

View file

@ -18,7 +18,7 @@ using EmotePtr = std::shared_ptr<const Emote>;
class ChatterinoBadges : public Singleton
{
public:
void initialize(Settings &settings, Paths &paths) override;
void initialize(Settings &settings, const Paths &paths) override;
ChatterinoBadges();
std::optional<EmotePtr> getBadge(const UserId &id);

View file

@ -16,7 +16,7 @@
namespace chatterino {
void FfzBadges::initialize(Settings &settings, Paths &paths)
void FfzBadges::initialize(Settings &settings, const Paths &paths)
{
this->load();
}

View file

@ -21,7 +21,7 @@ using EmotePtr = std::shared_ptr<const Emote>;
class FfzBadges : public Singleton
{
public:
void initialize(Settings &settings, Paths &paths) override;
void initialize(Settings &settings, const Paths &paths) override;
FfzBadges() = default;
struct Badge {

View file

@ -1,5 +1,6 @@
#include "Irc2.hpp"
#include "Application.hpp"
#include "common/Credentials.hpp"
#include "common/SignalVectorModel.hpp"
#include "providers/irc/IrcChannel2.hpp"
@ -21,7 +22,7 @@ namespace {
QString configPath()
{
return combinePath(getPaths()->settingsDirectory, "irc.json");
return combinePath(getIApp()->getPaths().settingsDirectory, "irc.json");
}
class Model : public SignalVectorModel<IrcServerData>

View file

@ -64,7 +64,7 @@ TwitchIrcServer::TwitchIrcServer()
// false);
}
void TwitchIrcServer::initialize(Settings &settings, Paths &paths)
void TwitchIrcServer::initialize(Settings &settings, const Paths &paths)
{
getApp()->accounts->twitch.currentUserChanged.connect([this]() {
postToThread([this] {

View file

@ -43,7 +43,7 @@ public:
TwitchIrcServer();
~TwitchIrcServer() override = default;
void initialize(Settings &settings, Paths &paths) override;
void initialize(Settings &settings, const Paths &paths) override;
void forEachChannelAndSpecialChannels(std::function<void(ChannelPtr)> func);

View file

@ -128,7 +128,12 @@ namespace chatterino {
using namespace std::string_literals;
void CrashHandler::initialize(Settings & /*settings*/, Paths &paths)
CrashHandler::CrashHandler(const Paths &paths_)
: paths(paths_)
{
}
void CrashHandler::initialize(Settings & /*settings*/, const Paths &paths_)
{
auto optSettings = readRecoverySettings(paths);
if (optSettings)
@ -146,7 +151,7 @@ void CrashHandler::saveShouldRecover(bool value)
{
this->shouldRecover_ = value;
QFile file(QDir(getPaths()->crashdumpDirectory).filePath(RECOVERY_FILE));
QFile file(QDir(this->paths.crashdumpDirectory).filePath(RECOVERY_FILE));
if (!file.open(QFile::WriteOnly | QFile::Truncate))
{
qCWarning(chatterinoCrashhandler)
@ -160,7 +165,8 @@ void CrashHandler::saveShouldRecover(bool value)
}
#ifdef CHATTERINO_WITH_CRASHPAD
std::unique_ptr<crashpad::CrashpadClient> installCrashHandler(const Args &args)
std::unique_ptr<crashpad::CrashpadClient> installCrashHandler(
const Args &args, const Paths &paths)
{
// Currently, the following directory layout is assumed:
// [applicationDirPath]
@ -188,15 +194,14 @@ std::unique_ptr<crashpad::CrashpadClient> installCrashHandler(const Args &args)
// Argument passed in --database
// > Crash reports are written to this database, and if uploads are enabled,
// uploaded from this database to a crash report collection server.
auto databaseDir =
base::FilePath(nativeString(getPaths()->crashdumpDirectory));
auto databaseDir = base::FilePath(nativeString(paths.crashdumpDirectory));
auto client = std::make_unique<crashpad::CrashpadClient>();
std::map<std::string, std::string> annotations{
{
"canRestart"s,
canRestart(*getPaths(), args) ? "true"s : "false"s,
canRestart(paths, args) ? "true"s : "false"s,
},
{
"exePath"s,

View file

@ -13,10 +13,15 @@
namespace chatterino {
class Args;
class Paths;
class CrashHandler : public Singleton
{
const Paths &paths;
public:
explicit CrashHandler(const Paths &paths_);
bool shouldRecover() const
{
return this->shouldRecover_;
@ -25,14 +30,15 @@ public:
/// Sets and saves whether Chatterino should restart on a crash
void saveShouldRecover(bool value);
void initialize(Settings &settings, Paths &paths) override;
void initialize(Settings &settings, const Paths &paths) override;
private:
bool shouldRecover_ = false;
};
#ifdef CHATTERINO_WITH_CRASHPAD
std::unique_ptr<crashpad::CrashpadClient> installCrashHandler(const Args &args);
std::unique_ptr<crashpad::CrashpadClient> installCrashHandler(
const Args &args, const Paths &paths);
#endif
} // namespace chatterino

View file

@ -6,7 +6,7 @@ Emotes::Emotes()
{
}
void Emotes::initialize(Settings &settings, Paths &paths)
void Emotes::initialize(Settings &settings, const Paths &paths)
{
this->emojis.load();

View file

@ -25,7 +25,7 @@ class Emotes final : public IEmotes, public Singleton
public:
Emotes();
void initialize(Settings &settings, Paths &paths) override;
void initialize(Settings &settings, const Paths &paths) override;
bool isIgnoredEmote(const QString &emote);

View file

@ -79,7 +79,7 @@ Fonts::Fonts()
this->fontsByType_.resize(size_t(FontStyle::EndType));
}
void Fonts::initialize(Settings &, Paths &)
void Fonts::initialize(Settings &, const Paths &)
{
this->chatFontFamily.connect(
[this]() {

View file

@ -43,7 +43,7 @@ class Fonts final : public Singleton
public:
Fonts();
void initialize(Settings &settings, Paths &paths) override;
void initialize(Settings &settings, const Paths &paths) override;
// font data gets set in createFontData(...)

View file

@ -1,5 +1,6 @@
#include "singletons/ImageUploader.hpp"
#include "Application.hpp"
#include "common/Env.hpp"
#include "common/network/NetworkRequest.hpp"
#include "common/network/NetworkResult.hpp"
@ -50,7 +51,7 @@ void ImageUploader::logToFile(const QString &originalFilePath,
{
const QString logFileName =
combinePath((getSettings()->logPath.getValue().isEmpty()
? getPaths()->messageLogDirectory
? getIApp()->getPaths().messageLogDirectory
: getSettings()->logPath),
"ImageUploader.json");

View file

@ -34,11 +34,11 @@ namespace chatterino {
using namespace literals;
void registerNmManifest(Paths &paths, const QString &manifestFilename,
void registerNmManifest(const Paths &paths, const QString &manifestFilename,
const QString &registryKeyName,
const QJsonDocument &document);
void registerNmHost(Paths &paths)
void registerNmHost(const Paths &paths)
{
if (paths.isPortable())
{
@ -80,7 +80,7 @@ void registerNmHost(Paths &paths)
}
}
void registerNmManifest(Paths &paths, const QString &manifestFilename,
void registerNmManifest(const Paths &paths, const QString &manifestFilename,
const QString &registryKeyName,
const QJsonDocument &document)
{
@ -99,7 +99,7 @@ void registerNmManifest(Paths &paths, const QString &manifestFilename,
#endif
}
std::string &getNmQueueName(Paths &paths)
std::string &getNmQueueName(const Paths &paths)
{
static std::string name =
"chatterino_gui" + paths.applicationFilePathHash.toStdString();

View file

@ -16,8 +16,8 @@ class Channel;
using ChannelPtr = std::shared_ptr<Channel>;
void registerNmHost(Paths &paths);
std::string &getNmQueueName(Paths &paths);
void registerNmHost(const Paths &paths);
std::string &getNmQueueName(const Paths &paths);
Atomic<std::optional<QString>> &nmIpcError();

View file

@ -15,12 +15,8 @@ using namespace std::literals;
namespace chatterino {
Paths *Paths::instance = nullptr;
Paths::Paths()
{
this->instance = this;
this->initAppFilePathHash();
this->initCheckPortable();
@ -33,12 +29,12 @@ bool Paths::createFolder(const QString &folderPath)
return QDir().mkpath(folderPath);
}
bool Paths::isPortable()
bool Paths::isPortable() const
{
return Modes::instance().isPortable;
}
QString Paths::cacheDirectory()
QString Paths::cacheDirectory() const
{
static const auto pathSetting = [] {
QStringSetting cachePathSetting("/cache/path");
@ -146,9 +142,4 @@ void Paths::initSubDirectories()
this->crashdumpDirectory = makePath("Crashes");
}
Paths *getPaths()
{
return Paths::instance;
}
} // namespace chatterino

View file

@ -9,8 +9,6 @@ namespace chatterino {
class Paths
{
public:
static Paths *instance;
Paths();
// Root directory for the configuration files. %APPDATA%/chatterino or
@ -42,9 +40,10 @@ public:
QString themesDirectory;
bool createFolder(const QString &folderPath);
bool isPortable();
[[deprecated("use Modes::instance().portable instead")]] bool isPortable()
const;
QString cacheDirectory();
QString cacheDirectory() const;
private:
void initAppFilePathHash();
@ -58,6 +57,4 @@ private:
QString cacheDirectory_;
};
Paths *getPaths();
} // namespace chatterino

View file

@ -219,7 +219,7 @@ bool Theme::isLightTheme() const
return this->isLight_;
}
void Theme::initialize(Settings &settings, Paths &paths)
void Theme::initialize(Settings &settings, const Paths &paths)
{
this->themeName.connect(
[this](auto themeName) {
@ -228,7 +228,7 @@ void Theme::initialize(Settings &settings, Paths &paths)
},
false);
this->loadAvailableThemes();
this->loadAvailableThemes(paths);
this->update();
}
@ -328,11 +328,11 @@ std::vector<std::pair<QString, QVariant>> Theme::availableThemes() const
return packagedThemes;
}
void Theme::loadAvailableThemes()
void Theme::loadAvailableThemes(const Paths &paths)
{
this->availableThemes_ = Theme::builtInThemes;
auto dir = QDir(getPaths()->themesDirectory);
auto dir = QDir(paths.themesDirectory);
for (const auto &info :
dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name))
{

View file

@ -43,7 +43,7 @@ public:
static const int AUTO_RELOAD_INTERVAL_MS = 500;
void initialize(Settings &settings, Paths &paths) final;
void initialize(Settings &settings, const Paths &paths) final;
bool isLightTheme() const;
@ -169,7 +169,7 @@ private:
*
* NOTE: This is currently not built to be reloadable
**/
void loadAvailableThemes();
void loadAvailableThemes(const Paths &paths);
std::optional<ThemeDescriptor> findThemeByKey(const QString &key);

View file

@ -33,7 +33,8 @@ using namespace literals;
QString avatarFilePath(const QString &channelName)
{
// TODO: cleanup channel (to be used as a file) and use combinePath
return getPaths()->twitchProfileAvatars % '/' % channelName % u".png";
return getIApp()->getPaths().twitchProfileAvatars % '/' % channelName %
u".png";
}
bool hasAvatarForChannel(const QString &channelName)

View file

@ -26,21 +26,14 @@ namespace {
} // namespace
Updates::Updates()
: currentVersion_(CHATTERINO_VERSION)
Updates::Updates(const Paths &paths_)
: paths(paths_)
, currentVersion_(CHATTERINO_VERSION)
, updateGuideLink_("https://chatterino.com")
{
qCDebug(chatterinoUpdate) << "init UpdateManager";
}
Updates &Updates::instance()
{
// fourtf: don't add this class to the application class
static Updates instance;
return instance;
}
/// Checks if the online version is newer or older than the current version.
bool Updates::isDowngradeOf(const QString &online, const QString &current)
{
@ -97,7 +90,7 @@ void Updates::installUpdates()
box->exec();
QDesktopServices::openUrl(this->updateGuideLink_);
#elif defined Q_OS_WIN
if (getPaths()->isPortable())
if (this->paths.isPortable())
{
QMessageBox *box =
new QMessageBox(QMessageBox::Information, "Chatterino Update",
@ -136,7 +129,7 @@ void Updates::installUpdates()
QByteArray object = result.getData();
auto filename =
combinePath(getPaths()->miscDirectory, "update.zip");
combinePath(this->paths.miscDirectory, "update.zip");
QFile file(filename);
file.open(QIODevice::Truncate | QIODevice::WriteOnly);
@ -196,7 +189,7 @@ void Updates::installUpdates()
QByteArray object = result.getData();
auto filePath =
combinePath(getPaths()->miscDirectory, "Update.exe");
combinePath(this->paths.miscDirectory, "Update.exe");
QFile file(filePath);
file.open(QIODevice::Truncate | QIODevice::WriteOnly);

View file

@ -5,11 +5,19 @@
namespace chatterino {
class Paths;
/**
* To check for updates, use the `checkForUpdates` method.
* The class by itself does not start any automatic updates.
*/
class Updates
{
Updates();
const Paths &paths;
public:
explicit Updates(const Paths &paths_);
enum Status {
None,
Searching,
@ -21,9 +29,6 @@ public:
WriteFileFailed,
};
// fourtf: don't add this class to the application class
static Updates &instance();
static bool isDowngradeOf(const QString &online, const QString &current);
void checkForUpdates();

View file

@ -91,8 +91,8 @@ void WindowManager::showAccountSelectPopup(QPoint point)
w->setFocus();
}
WindowManager::WindowManager()
: windowLayoutFilePath(combinePath(getPaths()->settingsDirectory,
WindowManager::WindowManager(const Paths &paths)
: windowLayoutFilePath(combinePath(paths.settingsDirectory,
WindowManager::WINDOW_LAYOUT_FILENAME))
{
qCDebug(chatterinoWindowmanager) << "init WindowManager";
@ -338,7 +338,7 @@ void WindowManager::setEmotePopupPos(QPoint pos)
this->emotePopupPos_ = pos;
}
void WindowManager::initialize(Settings &settings, Paths &paths)
void WindowManager::initialize(Settings &settings, const Paths &paths)
{
(void)paths;
assertInGuiThread();

View file

@ -37,9 +37,14 @@ class WindowManager final : public Singleton
public:
static const QString WINDOW_LAYOUT_FILENAME;
WindowManager();
explicit WindowManager(const Paths &paths);
~WindowManager() override;
WindowManager(const WindowManager &) = delete;
WindowManager(WindowManager &&) = delete;
WindowManager &operator=(const WindowManager &) = delete;
WindowManager &operator=(WindowManager &&) = delete;
static void encodeTab(SplitContainer *tab, bool isSelected,
QJsonObject &obj);
static void encodeChannel(IndirectChannel channel, QJsonObject &obj);
@ -93,7 +98,7 @@ public:
QPoint emotePopupPos();
void setEmotePopupPos(QPoint pos);
void initialize(Settings &settings, Paths &paths) override;
void initialize(Settings &settings, const Paths &paths) override;
void save() override;
void closeAll();

View file

@ -1,5 +1,6 @@
#include "LoggingChannel.hpp"
#include "Application.hpp"
#include "common/QLogging.hpp"
#include "messages/Message.hpp"
#include "messages/MessageThread.hpp"
@ -44,8 +45,9 @@ LoggingChannel::LoggingChannel(const QString &_channelName,
QDir::separator() + this->subDirectory;
getSettings()->logPath.connect([this](const QString &logPath, auto) {
this->baseDirectory =
logPath.isEmpty() ? getPaths()->messageLogDirectory : logPath;
this->baseDirectory = logPath.isEmpty()
? getIApp()->getPaths().messageLogDirectory
: logPath;
this->openLogFile();
});
}

View file

@ -1,5 +1,6 @@
#include "util/InitUpdateButton.hpp"
#include "Application.hpp"
#include "widgets/dialogs/UpdateDialog.hpp"
#include "widgets/helper/Button.hpp"
@ -40,7 +41,7 @@ void initUpdateButton(Button &button,
}
break;
case UpdateDialog::Install: {
Updates::instance().installUpdates();
getIApp()->getUpdates().installUpdates();
}
break;
}
@ -52,17 +53,17 @@ void initUpdateButton(Button &button,
// update image when state changes
auto updateChange = [&button](auto) {
button.setVisible(Updates::instance().shouldShowUpdateButton());
button.setVisible(getIApp()->getUpdates().shouldShowUpdateButton());
const auto *imageUrl = Updates::instance().isError()
const auto *imageUrl = getIApp()->getUpdates().isError()
? ":/buttons/updateError.png"
: ":/buttons/update.png";
button.setPixmap(QPixmap(imageUrl));
};
updateChange(Updates::instance().getStatus());
updateChange(getIApp()->getUpdates().getStatus());
signalHolder.managedConnect(Updates::instance().statusUpdated,
signalHolder.managedConnect(getIApp()->getUpdates().statusUpdated,
[updateChange](auto status) {
updateChange(status);
});

View file

@ -43,7 +43,7 @@ namespace chatterino {
using namespace literals;
LastRunCrashDialog::LastRunCrashDialog(const Args &args)
LastRunCrashDialog::LastRunCrashDialog(const Args &args, const Paths &paths)
{
this->setWindowFlag(Qt::WindowContextHelpButtonHint, false);
this->setWindowTitle(u"Chatterino - " % randomMessage());
@ -56,8 +56,7 @@ LastRunCrashDialog::LastRunCrashDialog(const Args &args)
"<i>You can disable automatic restarts in the settings.</i><br><br>";
#ifdef CHATTERINO_WITH_CRASHPAD
auto reportsDir =
QDir(getPaths()->crashdumpDirectory).filePath(u"reports"_s);
auto reportsDir = QDir(paths.crashdumpDirectory).filePath(u"reports"_s);
text += u"A <b>crash report</b> has been saved to "
"<a href=\"file:///" %
reportsDir % u"\">" % reportsDir % u"</a>.<br>";

View file

@ -5,11 +5,12 @@
namespace chatterino {
class Args;
class Paths;
class LastRunCrashDialog : public QDialog
{
public:
explicit LastRunCrashDialog(const Args &args);
explicit LastRunCrashDialog(const Args &args, const Paths &paths);
};
} // namespace chatterino

View file

@ -1,5 +1,6 @@
#include "UpdateDialog.hpp"
#include "widgets/dialogs/UpdateDialog.hpp"
#include "Application.hpp"
#include "singletons/Updates.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/Label.hpp"
@ -26,7 +27,7 @@ UpdateDialog::UpdateDialog()
auto *dismiss = buttons->addButton("Dismiss", QDialogButtonBox::RejectRole);
QObject::connect(install, &QPushButton::clicked, this, [this] {
Updates::instance().installUpdates();
getIApp()->getUpdates().installUpdates();
this->close();
});
QObject::connect(dismiss, &QPushButton::clicked, this, [this] {
@ -34,8 +35,8 @@ UpdateDialog::UpdateDialog()
this->close();
});
this->updateStatusChanged(Updates::instance().getStatus());
this->connections_.managedConnect(Updates::instance().statusUpdated,
this->updateStatusChanged(getIApp()->getUpdates().getStatus());
this->connections_.managedConnect(getIApp()->getUpdates().statusUpdated,
[this](auto status) {
this->updateStatusChanged(status);
});
@ -52,17 +53,17 @@ void UpdateDialog::updateStatusChanged(Updates::Status status)
{
case Updates::UpdateAvailable: {
this->ui_.label->setText((
Updates::instance().isDowngrade()
getIApp()->getUpdates().isDowngrade()
? QString(
"The version online (%1) seems to be\nlower than the "
"current (%2).\nEither a version was reverted or "
"you are\nrunning a newer build.\n\nDo you want to "
"download and install it?")
.arg(Updates::instance().getOnlineVersion(),
Updates::instance().getCurrentVersion())
.arg(getIApp()->getUpdates().getOnlineVersion(),
getIApp()->getUpdates().getCurrentVersion())
: QString("An update (%1) is available.\n\nDo you want to "
"download and install it?")
.arg(Updates::instance().getOnlineVersion())));
.arg(getIApp()->getUpdates().getOnlineVersion())));
this->updateGeometry();
}
break;

View file

@ -791,9 +791,10 @@ void GeneralPage::initLayout(GeneralPageView &layout)
"store in this directory.");
layout.addButton("Open AppData directory", [] {
#ifdef Q_OS_DARWIN
QDesktopServices::openUrl("file://" + getPaths()->rootAppDataDirectory);
QDesktopServices::openUrl("file://" +
getIApp()->getPaths().rootAppDataDirectory);
#else
QDesktopServices::openUrl(getPaths()->rootAppDataDirectory);
QDesktopServices::openUrl(getIApp()->getPaths().rootAppDataDirectory);
#endif
});
@ -805,7 +806,7 @@ void GeneralPage::initLayout(GeneralPageView &layout)
auto *cachePathLabel = layout.addDescription("placeholder :D");
getSettings()->cachePath.connect([cachePathLabel](const auto &,
auto) mutable {
QString newPath = getPaths()->cacheDirectory();
QString newPath = getIApp()->getPaths().cacheDirectory();
QString pathShortened = "Cache saved at <a href=\"file:///" + newPath +
"\"><span style=\"color: white;\">" +
@ -833,9 +834,9 @@ void GeneralPage::initLayout(GeneralPageView &layout)
if (reply == QMessageBox::Yes)
{
auto cacheDir = QDir(getPaths()->cacheDirectory());
auto cacheDir = QDir(getIApp()->getPaths().cacheDirectory());
cacheDir.removeRecursively();
cacheDir.mkdir(getPaths()->cacheDirectory());
cacheDir.mkdir(getIApp()->getPaths().cacheDirectory());
}
}));
box->addStretch(1);
@ -947,7 +948,7 @@ void GeneralPage::initLayout(GeneralPageView &layout)
"When possible, restart Chatterino if the program crashes");
#if defined(Q_OS_LINUX) && !defined(NO_QTKEYCHAIN)
if (!getPaths()->isPortable())
if (!getIApp()->getPaths().isPortable())
{
layout.addCheckbox(
"Use libsecret/KWallet/Gnome keychain to secure passwords",
@ -1232,7 +1233,7 @@ void GeneralPage::initExtra()
{
getSettings()->cachePath.connect(
[cachePath = this->cachePath_](const auto &, auto) mutable {
QString newPath = getPaths()->cacheDirectory();
QString newPath = getIApp()->getPaths().cacheDirectory();
QString pathShortened = "Current location: <a href=\"file:///" +
newPath + "\">" +

View file

@ -54,7 +54,7 @@ QString formatSize(qint64 size)
QString fetchLogDirectorySize()
{
QString logsDirectoryPath = getSettings()->logPath.getValue().isEmpty()
? getPaths()->messageLogDirectory
? getIApp()->getPaths().messageLogDirectory
: getSettings()->logPath;
auto logsSize = dirSize(logsDirectoryPath);
@ -82,7 +82,8 @@ ModerationPage::ModerationPage()
getSettings()->logPath.connect([logsPathLabel](const QString &logPath,
auto) mutable {
QString pathOriginal =
logPath.isEmpty() ? getPaths()->messageLogDirectory : logPath;
logPath.isEmpty() ? getIApp()->getPaths().messageLogDirectory
: logPath;
QString pathShortened =
"Logs are saved at <a href=\"file:///" + pathOriginal +

View file

@ -37,11 +37,12 @@ PluginsPage::PluginsPage()
auto group = layout.emplace<QGroupBox>("General plugin settings");
this->generalGroup = group.getElement();
auto groupLayout = group.setLayoutType<QFormLayout>();
auto *description = new QLabel(
"You can load plugins by putting them into " +
formatRichNamedLink("file:///" + getPaths()->pluginsDirectory,
"the Plugins directory") +
". Each one is a new directory.");
auto *description =
new QLabel("You can load plugins by putting them into " +
formatRichNamedLink(
"file:///" + getIApp()->getPaths().pluginsDirectory,
"the Plugins directory") +
". Each one is a new directory.");
description->setOpenExternalLinks(true);
description->setWordWrap(true);
description->setStyleSheet("color: #bbb");