mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
Move settings into a separate JSON file.
This will unfortunately mean losing your commands, but they can be restored by converting the old commands.txt format into the commands.json file Fix #372
This commit is contained in:
parent
221ec4f1e8
commit
a4fd7b5366
5 changed files with 110 additions and 64 deletions
|
@ -163,7 +163,7 @@ void CompletionModel::refresh(const QString &prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commands
|
// Commands
|
||||||
for (auto &command : getApp()->commands->items.getVector())
|
for (auto &command : getApp()->commands->items_.getVector())
|
||||||
{
|
{
|
||||||
addString(command.name, TaggedString::Command);
|
addString(command.name, TaggedString::Command);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "util/RapidjsonHelpers.hpp"
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <pajlada/serialize.hpp>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
|
@ -16,3 +19,49 @@ struct Command {
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
||||||
|
namespace pajlada {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Serialize<chatterino::Command> {
|
||||||
|
static rapidjson::Value get(const chatterino::Command &value,
|
||||||
|
rapidjson::Document::AllocatorType &a)
|
||||||
|
{
|
||||||
|
rapidjson::Value ret(rapidjson::kObjectType);
|
||||||
|
|
||||||
|
chatterino::rj::set(ret, "name", value.name, a);
|
||||||
|
chatterino::rj::set(ret, "func", value.func, a);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Deserialize<chatterino::Command> {
|
||||||
|
static chatterino::Command get(const rapidjson::Value &value,
|
||||||
|
bool *error = nullptr)
|
||||||
|
{
|
||||||
|
chatterino::Command command;
|
||||||
|
|
||||||
|
if (!value.IsObject())
|
||||||
|
{
|
||||||
|
PAJLADA_REPORT_ERROR(error);
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chatterino::rj::getSafe(value, "name", command.name))
|
||||||
|
{
|
||||||
|
PAJLADA_REPORT_ERROR(error);
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
if (!chatterino::rj::getSafe(value, "func", command.func))
|
||||||
|
{
|
||||||
|
PAJLADA_REPORT_ERROR(error);
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pajlada
|
||||||
|
|
|
@ -33,12 +33,13 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
CommandController::CommandController()
|
void CommandController::initialize(Settings &, Paths &paths)
|
||||||
{
|
{
|
||||||
|
// Update commands map when the vector of commands has been updated
|
||||||
auto addFirstMatchToMap = [this](auto args) {
|
auto addFirstMatchToMap = [this](auto args) {
|
||||||
this->commandsMap_.remove(args.item.name);
|
this->commandsMap_.remove(args.item.name);
|
||||||
|
|
||||||
for (const Command &cmd : this->items.getVector())
|
for (const Command &cmd : this->items_.getVector())
|
||||||
{
|
{
|
||||||
if (cmd.name == args.item.name)
|
if (cmd.name == args.item.name)
|
||||||
{
|
{
|
||||||
|
@ -47,70 +48,51 @@ CommandController::CommandController()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
this->items_.itemInserted.connect(addFirstMatchToMap);
|
||||||
|
this->items_.itemRemoved.connect(addFirstMatchToMap);
|
||||||
|
|
||||||
this->items.itemInserted.connect(addFirstMatchToMap);
|
// Initialize setting manager for commands.json
|
||||||
this->items.itemRemoved.connect(addFirstMatchToMap);
|
auto path = combinePath(paths.settingsDirectory, "commands.json");
|
||||||
}
|
this->sm_ = std::make_shared<pajlada::Settings::SettingManager>();
|
||||||
|
this->sm_->setPath(path.toStdString());
|
||||||
|
|
||||||
void CommandController::initialize(Settings &, Paths &paths)
|
// Delayed initialization of the setting storing all commands
|
||||||
{
|
this->commandsSetting_.reset(
|
||||||
this->load(paths);
|
new pajlada::Settings::Setting<std::vector<Command>>("/commands",
|
||||||
}
|
this->sm_));
|
||||||
|
|
||||||
void CommandController::load(Paths &paths)
|
// Update the setting when the vector of commands has been updated (most
|
||||||
{
|
// likely from the settings dialog)
|
||||||
this->filePath_ = combinePath(paths.settingsDirectory, "commands.txt");
|
this->items_.delayedItemsChanged.connect([this] { //
|
||||||
|
this->commandsSetting_->setValue(this->items_.getVector());
|
||||||
|
});
|
||||||
|
|
||||||
QFile textFile(this->filePath_);
|
// Load commands from commands.json
|
||||||
if (!textFile.open(QIODevice::ReadOnly))
|
this->sm_->load();
|
||||||
|
|
||||||
|
// Add loaded commands to our vector of commands (which will update the map
|
||||||
|
// of commands)
|
||||||
|
for (const auto &command : this->commandsSetting_->getValue())
|
||||||
{
|
{
|
||||||
// No commands file created yet
|
this->items_.appendItem(command);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QByteArray> test = textFile.readAll().split('\n');
|
|
||||||
|
|
||||||
for (const auto &command : test)
|
|
||||||
{
|
|
||||||
if (command.isEmpty())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->items.appendItem(Command(command));
|
|
||||||
}
|
|
||||||
|
|
||||||
textFile.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandController::save()
|
void CommandController::save()
|
||||||
{
|
{
|
||||||
QFile textFile(this->filePath_);
|
this->sm_->save();
|
||||||
if (!textFile.open(QIODevice::WriteOnly))
|
|
||||||
{
|
|
||||||
log("[CommandController::saveCommands] Unable to open {} for writing",
|
|
||||||
this->filePath_);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const Command &cmd : this->items.getVector())
|
|
||||||
{
|
|
||||||
textFile.write((cmd.toString() + "\n").toUtf8());
|
|
||||||
}
|
|
||||||
|
|
||||||
textFile.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandModel *CommandController::createModel(QObject *parent)
|
CommandModel *CommandController::createModel(QObject *parent)
|
||||||
{
|
{
|
||||||
CommandModel *model = new CommandModel(parent);
|
CommandModel *model = new CommandModel(parent);
|
||||||
model->init(&this->items);
|
model->init(&this->items_);
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CommandController::execCommand(const QString &textNoEmoji, ChannelPtr channel,
|
QString CommandController::execCommand(const QString &textNoEmoji,
|
||||||
bool dryRun)
|
ChannelPtr channel, bool dryRun)
|
||||||
{
|
{
|
||||||
QString text = getApp()->emotes->emojis.replaceShortCodes(textNoEmoji);
|
QString text = getApp()->emotes->emojis.replaceShortCodes(textNoEmoji);
|
||||||
QStringList words = text.split(' ', QString::SkipEmptyParts);
|
QStringList words = text.split(' ', QString::SkipEmptyParts);
|
||||||
|
@ -156,10 +138,12 @@ QString CommandController::execCommand(const QString &textNoEmoji, ChannelPtr ch
|
||||||
const auto &ffzemotes = app->twitch.server->getFfzEmotes();
|
const auto &ffzemotes = app->twitch.server->getFfzEmotes();
|
||||||
auto flags = MessageElementFlags();
|
auto flags = MessageElementFlags();
|
||||||
auto emote = boost::optional<EmotePtr>{};
|
auto emote = boost::optional<EmotePtr>{};
|
||||||
for (int i = 2; i < words.length(); i++) {
|
for (int i = 2; i < words.length(); i++)
|
||||||
|
{
|
||||||
{ // twitch emote
|
{ // twitch emote
|
||||||
auto it = accemotes.emotes.find({words[i]});
|
auto it = accemotes.emotes.find({words[i]});
|
||||||
if (it != accemotes.emotes.end()) {
|
if (it != accemotes.emotes.end())
|
||||||
|
{
|
||||||
b.emplace<EmoteElement>(
|
b.emplace<EmoteElement>(
|
||||||
it->second, MessageElementFlag::TwitchEmote);
|
it->second, MessageElementFlag::TwitchEmote);
|
||||||
continue;
|
continue;
|
||||||
|
@ -167,19 +151,24 @@ QString CommandController::execCommand(const QString &textNoEmoji, ChannelPtr ch
|
||||||
} // twitch emote
|
} // twitch emote
|
||||||
|
|
||||||
{ // bttv/ffz emote
|
{ // bttv/ffz emote
|
||||||
if ((emote = bttvemotes.emote({words[i]}))) {
|
if ((emote = bttvemotes.emote({words[i]})))
|
||||||
|
{
|
||||||
flags = MessageElementFlag::BttvEmote;
|
flags = MessageElementFlag::BttvEmote;
|
||||||
} else if ((emote = ffzemotes.emote({words[i]}))) {
|
}
|
||||||
|
else if ((emote = ffzemotes.emote({words[i]})))
|
||||||
|
{
|
||||||
flags = MessageElementFlag::FfzEmote;
|
flags = MessageElementFlag::FfzEmote;
|
||||||
}
|
}
|
||||||
if (emote) {
|
if (emote)
|
||||||
|
{
|
||||||
b.emplace<EmoteElement>(emote.get(), flags);
|
b.emplace<EmoteElement>(emote.get(), flags);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} // bttv/ffz emote
|
} // bttv/ffz emote
|
||||||
{ // emoji/text
|
{ // emoji/text
|
||||||
for (auto &variant :
|
for (auto &variant :
|
||||||
app->emotes->emojis.parse(words[i])) {
|
app->emotes->emojis.parse(words[i]))
|
||||||
|
{
|
||||||
constexpr const static struct {
|
constexpr const static struct {
|
||||||
void operator()(EmotePtr emote,
|
void operator()(EmotePtr emote,
|
||||||
MessageBuilder &b) const
|
MessageBuilder &b) const
|
||||||
|
@ -207,7 +196,8 @@ QString CommandController::execCommand(const QString &textNoEmoji, ChannelPtr ch
|
||||||
|
|
||||||
app->twitch.server->sendMessage("jtv", text);
|
app->twitch.server->sendMessage("jtv", text);
|
||||||
|
|
||||||
if (getSettings()->inlineWhispers) {
|
if (getSettings()->inlineWhispers)
|
||||||
|
{
|
||||||
app->twitch.server->forEachChannel(
|
app->twitch.server->forEachChannel(
|
||||||
[&messagexD](ChannelPtr _channel) {
|
[&messagexD](ChannelPtr _channel) {
|
||||||
_channel->addMessage(messagexD);
|
_channel->addMessage(messagexD);
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/ChatterinoSetting.hpp"
|
||||||
|
#include "common/SignalVector.hpp"
|
||||||
#include "common/Singleton.hpp"
|
#include "common/Singleton.hpp"
|
||||||
|
#include "controllers/commands/Command.hpp"
|
||||||
|
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
|
#include <pajlada/settings.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include "common/SignalVector.hpp"
|
|
||||||
#include "controllers/commands/Command.hpp"
|
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
class Settings;
|
class Settings;
|
||||||
|
@ -20,26 +22,31 @@ class CommandModel;
|
||||||
class CommandController final : public Singleton
|
class CommandController final : public Singleton
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CommandController();
|
UnsortedSignalVector<Command> items_;
|
||||||
|
|
||||||
QString execCommand(const QString &text, std::shared_ptr<Channel> channel,
|
QString execCommand(const QString &text, std::shared_ptr<Channel> channel,
|
||||||
bool dryRun);
|
bool dryRun);
|
||||||
QStringList getDefaultTwitchCommandList();
|
QStringList getDefaultTwitchCommandList();
|
||||||
|
|
||||||
virtual void initialize(Settings &settings, Paths &paths) override;
|
virtual void initialize(Settings &, Paths &paths) override;
|
||||||
virtual void save() override;
|
virtual void save() override;
|
||||||
|
|
||||||
CommandModel *createModel(QObject *parent);
|
CommandModel *createModel(QObject *parent);
|
||||||
|
|
||||||
UnsortedSignalVector<Command> items;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void load(Paths &paths);
|
void load(Paths &paths);
|
||||||
|
|
||||||
QMap<QString, Command> commandsMap_;
|
QMap<QString, Command> commandsMap_;
|
||||||
|
|
||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
QString filePath_;
|
|
||||||
|
std::shared_ptr<pajlada::Settings::SettingManager> sm_;
|
||||||
|
// Because the setting manager is not initialized until the initialize
|
||||||
|
// function is called (and not in the constructor), we have to
|
||||||
|
// late-initialize the setting, which is why we're storing it as a
|
||||||
|
// unique_ptr
|
||||||
|
std::unique_ptr<pajlada::Settings::Setting<std::vector<Command>>>
|
||||||
|
commandsSetting_;
|
||||||
|
|
||||||
QString execCustomCommand(const QStringList &words, const Command &command);
|
QString execCustomCommand(const QStringList &words, const Command &command);
|
||||||
};
|
};
|
||||||
|
|
|
@ -41,7 +41,7 @@ CommandPage::CommandPage()
|
||||||
view->setTitles({"Trigger", "Command"});
|
view->setTitles({"Trigger", "Command"});
|
||||||
view->getTableView()->horizontalHeader()->setStretchLastSection(true);
|
view->getTableView()->horizontalHeader()->setStretchLastSection(true);
|
||||||
view->addButtonPressed.connect([] {
|
view->addButtonPressed.connect([] {
|
||||||
getApp()->commands->items.appendItem(
|
getApp()->commands->items_.appendItem(
|
||||||
Command{"/command", "I made a new command HeyGuys"});
|
Command{"/command", "I made a new command HeyGuys"});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue