From 4c782ce90c6b0dd6b99a8780c34212d43a228243 Mon Sep 17 00:00:00 2001 From: askepticaldreamer <106888785+askepticaldreamer@users.noreply.github.com> Date: Sun, 15 Jan 2023 03:47:22 -0800 Subject: [PATCH] Add feature to select channels to log (#4302) * Add checkbox for custom logging and table with channels to log on Logs page * Add checkbox to enable and disable logging per channel * Return from addMessage before logging if custom logging enabled and channel does not have logging enabled * Use clang-format to fix formatting * Add CHANGELOG.md entry * Resolve PR comments * Remove toggle for channels so any channel listed will be logged * Move Only log channels listed below checkbox to just above table * Fix formatting * Re-order changelog * ChannelLog constructor: Copy & move instead of const ref & copy * ChannelLog::createEmpty: Curly brace initialize instead of repeating name * ChannelLog toString & createEmpty: nodiscard * Use COUNT paradigm in model column * Remove ChanneLoggingModel source file comments * Use Column::Channel in getRowFromItem * Rename `getItemFromRow` parameter and mark it as unused * Curly brace initialize ChannelLog * private & friend class the model * Filter out channels to log using a set instead of iterating over a vector every time a message comes in * Rename `ChannelLog::channel` member to `ChannelLog::channelName` Also made it private * mini comment on ChannelLog Co-authored-by: Felanbird <41973452+Felanbird@users.noreply.github.com> Co-authored-by: Rasmus Karlsson --- CHANGELOG.md | 1 + src/CMakeLists.txt | 5 ++ src/controllers/logging/ChannelLog.cpp | 25 +++++++ src/controllers/logging/ChannelLog.hpp | 69 +++++++++++++++++++ .../logging/ChannelLoggingModel.cpp | 25 +++++++ .../logging/ChannelLoggingModel.hpp | 31 +++++++++ src/singletons/Logging.cpp | 24 ++++++- src/singletons/Logging.hpp | 7 ++ src/singletons/Settings.cpp | 2 + src/singletons/Settings.hpp | 4 ++ src/widgets/settingspages/ModerationPage.cpp | 38 +++++++++- 11 files changed, 225 insertions(+), 6 deletions(-) create mode 100644 src/controllers/logging/ChannelLog.cpp create mode 100644 src/controllers/logging/ChannelLog.hpp create mode 100644 src/controllers/logging/ChannelLoggingModel.cpp create mode 100644 src/controllers/logging/ChannelLoggingModel.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index f775be91d2f..aeac9082ec0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Minor: Tables in settings window will now scroll to newly added rows. (#4216) - Minor: Added link to streamlink docs for easier user setup. (#4217) - Minor: Added setting to turn off rendering of reply context. (#4224) +- Minor: Added setting to select which channels to log. (#4302) - Bugfix: Fixed crash that would occur when performing certain actions after removing all tabs. (#4271) - Bugfix: Fixed highlight sounds not reloading on change properly. (#4194) - Bugfix: Fixed CTRL + C not working in reply thread popups. (#4209) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b058761fb43..5302129251b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -119,6 +119,11 @@ set(SOURCE_FILES controllers/moderationactions/ModerationActionModel.cpp controllers/moderationactions/ModerationActionModel.hpp + controllers/logging/ChannelLog.cpp + controllers/logging/ChannelLog.hpp + controllers/logging/ChannelLoggingModel.cpp + controllers/logging/ChannelLoggingModel.hpp + controllers/nicknames/NicknamesModel.cpp controllers/nicknames/NicknamesModel.hpp controllers/nicknames/Nickname.hpp diff --git a/src/controllers/logging/ChannelLog.cpp b/src/controllers/logging/ChannelLog.cpp new file mode 100644 index 00000000000..a7d8ea04c04 --- /dev/null +++ b/src/controllers/logging/ChannelLog.cpp @@ -0,0 +1,25 @@ +#include "controllers/logging/ChannelLog.hpp" + +namespace chatterino { + +ChannelLog::ChannelLog(QString channelName) + : channelName_(std::move(channelName)) +{ +} + +QString ChannelLog::channelName() const +{ + return this->channelName_; +} + +QString ChannelLog::toString() const +{ + return this->channelName_; +} + +ChannelLog ChannelLog::createEmpty() +{ + return {""}; +} + +} // namespace chatterino diff --git a/src/controllers/logging/ChannelLog.hpp b/src/controllers/logging/ChannelLog.hpp new file mode 100644 index 00000000000..b04cc0f0655 --- /dev/null +++ b/src/controllers/logging/ChannelLog.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include "util/RapidjsonHelpers.hpp" + +#include +#include + +namespace chatterino { + +/** + * @brief Contains the description of a channel that will be logged + **/ +class ChannelLog +{ + QString channelName_; + +public: + ChannelLog(QString channelName); + + bool operator==(const ChannelLog &other) const; + + [[nodiscard]] QString channelName() const; + + [[nodiscard]] QString toString() const; + + [[nodiscard]] static ChannelLog createEmpty(); +}; + +} // namespace chatterino + +namespace pajlada { + +template <> +struct Serialize { + static rapidjson::Value get(const chatterino::ChannelLog &value, + rapidjson::Document::AllocatorType &a) + { + rapidjson::Value ret(rapidjson::kObjectType); + + chatterino::rj::set(ret, "channelName", value.channelName(), a); + + return ret; + } +}; + +template <> +struct Deserialize { + static chatterino::ChannelLog get(const rapidjson::Value &value, + bool *error = nullptr) + { + if (!value.IsObject()) + { + PAJLADA_REPORT_ERROR(error); + return chatterino::ChannelLog::createEmpty(); + } + + QString channelName; + + if (!chatterino::rj::getSafe(value, "channelName", channelName)) + { + PAJLADA_REPORT_ERROR(error); + return chatterino::ChannelLog::createEmpty(); + } + + return {channelName}; + } +}; + +} // namespace pajlada diff --git a/src/controllers/logging/ChannelLoggingModel.cpp b/src/controllers/logging/ChannelLoggingModel.cpp new file mode 100644 index 00000000000..5c49a1f03e4 --- /dev/null +++ b/src/controllers/logging/ChannelLoggingModel.cpp @@ -0,0 +1,25 @@ +#include "controllers/logging/ChannelLoggingModel.hpp" + +#include "util/StandardItemHelper.hpp" + +namespace chatterino { + +ChannelLoggingModel ::ChannelLoggingModel(QObject *parent) + : SignalVectorModel(Column::COUNT, parent) +{ +} + +ChannelLog ChannelLoggingModel::getItemFromRow( + std::vector &row, const ChannelLog & /*original*/) +{ + auto channelName = row[Column::Channel]->data(Qt::DisplayRole).toString(); + return {channelName}; +} + +void ChannelLoggingModel::getRowFromItem(const ChannelLog &item, + std::vector &row) +{ + setStringItem(row[Column::Channel], item.channelName()); +} + +} // namespace chatterino diff --git a/src/controllers/logging/ChannelLoggingModel.hpp b/src/controllers/logging/ChannelLoggingModel.hpp new file mode 100644 index 00000000000..dddebab4ba1 --- /dev/null +++ b/src/controllers/logging/ChannelLoggingModel.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include "common/SignalVectorModel.hpp" +#include "controllers/logging/ChannelLog.hpp" + +#include + +namespace chatterino { + +class ChannelLoggingModel : public SignalVectorModel +{ + explicit ChannelLoggingModel(QObject *parent); + + enum Column { + Channel, + COUNT, + }; + +protected: + // turn a vector item into a model row + ChannelLog getItemFromRow(std::vector &row, + const ChannelLog &original) override; + + // turns a row in the model into a vector item + void getRowFromItem(const ChannelLog &item, + std::vector &row) override; + + friend class ModerationPage; +}; + +} // namespace chatterino diff --git a/src/singletons/Logging.cpp b/src/singletons/Logging.cpp index 4c4810e91be..cdf85b5ae9e 100644 --- a/src/singletons/Logging.cpp +++ b/src/singletons/Logging.cpp @@ -1,6 +1,5 @@ #include "singletons/Logging.hpp" -#include "Application.hpp" #include "singletons/helper/LoggingChannel.hpp" #include "singletons/Paths.hpp" #include "singletons/Settings.hpp" @@ -9,23 +8,42 @@ #include #include -#include #include namespace chatterino { -void Logging::initialize(Settings &settings, Paths &paths) +void Logging::initialize(Settings &settings, Paths & /*paths*/) { + settings.loggedChannels.delayedItemsChanged.connect([this, &settings]() { + this->threadGuard.guard(); + + this->onlyLogListedChannels.clear(); + + for (const auto &loggedChannel : *settings.loggedChannels.readOnly()) + { + this->onlyLogListedChannels.insert(loggedChannel.channelName()); + } + }); } void Logging::addMessage(const QString &channelName, MessagePtr message, const QString &platformName) { + this->threadGuard.guard(); + if (!getSettings()->enableLogging) { return; } + if (getSettings()->onlyLogListedChannels) + { + if (!this->onlyLogListedChannels.contains(channelName)) + { + return; + } + } + auto platIt = this->loggingChannels_.find(platformName); if (platIt == this->loggingChannels_.end()) { diff --git a/src/singletons/Logging.hpp b/src/singletons/Logging.hpp index 46d3782b011..19fd2bacd72 100644 --- a/src/singletons/Logging.hpp +++ b/src/singletons/Logging.hpp @@ -1,11 +1,14 @@ #pragma once #include "common/Singleton.hpp" +#include "util/QStringHash.hpp" +#include "util/ThreadGuard.hpp" #include #include #include +#include namespace chatterino { @@ -32,6 +35,10 @@ class Logging : public Singleton std::map>> loggingChannels_; + + // Keeps the value of the `loggedChannels` settings + std::unordered_set onlyLogListedChannels; + ThreadGuard threadGuard; }; } // namespace chatterino diff --git a/src/singletons/Settings.cpp b/src/singletons/Settings.cpp index 495fe1d3578..a4cf20f9632 100644 --- a/src/singletons/Settings.cpp +++ b/src/singletons/Settings.cpp @@ -25,6 +25,7 @@ ConcurrentSettings::ConcurrentSettings() , filterRecords(*new SignalVector()) , nicknames(*new SignalVector()) , moderationActions(*new SignalVector) + , loggedChannels(*new SignalVector) { persist(this->highlightedMessages, "/highlighting/highlights"); persist(this->blacklistedUsers, "/highlighting/blacklist"); @@ -36,6 +37,7 @@ ConcurrentSettings::ConcurrentSettings() persist(this->nicknames, "/nicknames"); // tagged users? persist(this->moderationActions, "/moderation/actions"); + persist(this->loggedChannels, "/logging/channels"); } bool ConcurrentSettings::isHighlightedUser(const QString &username) diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index 1a671085d73..992e387b4c5 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -3,6 +3,7 @@ #include "BaseSettings.hpp" #include "common/Channel.hpp" #include "common/SignalVector.hpp" +#include "controllers/logging/ChannelLog.hpp" #include "singletons/Toasts.hpp" #include "util/RapidJsonSerializeQString.hpp" #include "util/StreamerMode.hpp" @@ -40,6 +41,7 @@ class ConcurrentSettings SignalVector &filterRecords; SignalVector &nicknames; SignalVector &moderationActions; + SignalVector &loggedChannels; bool isHighlightedUser(const QString &username); bool isBlacklistedUser(const QString &username); @@ -364,6 +366,8 @@ class Settings : public ABSettings, public ConcurrentSettings /// Logging BoolSetting enableLogging = {"/logging/enabled", false}; + BoolSetting onlyLogListedChannels = {"/logging/onlyLogListedChannels", + false}; QStringSetting logPath = {"/logging/path", ""}; diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index 64b340d0de0..e306d2734ca 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -1,6 +1,7 @@ #include "ModerationPage.hpp" #include "Application.hpp" +#include "controllers/logging/ChannelLoggingModel.hpp" #include "controllers/moderationactions/ModerationAction.hpp" #include "controllers/moderationactions/ModerationActionModel.hpp" #include "singletons/Logging.hpp" @@ -69,8 +70,10 @@ ModerationPage::ModerationPage() auto logs = tabs.appendTab(new QVBoxLayout, "Logs"); { - logs.append(this->createCheckBox("Enable logging", - getSettings()->enableLogging)); + QCheckBox *enableLogging = this->createCheckBox( + "Enable logging", getSettings()->enableLogging); + logs.append(enableLogging); + auto logsPathLabel = logs.emplace(); // Logs (copied from LoggingMananger) @@ -105,7 +108,6 @@ ModerationPage::ModerationPage() }); buttons->addStretch(); - logs->addStretch(1); // Show how big (size-wise) the logs are auto logsPathSizeLabel = logs.emplace(); @@ -140,6 +142,36 @@ ModerationPage::ModerationPage() })); }); + QCheckBox *onlyLogListedChannels = + this->createCheckBox("Only log channels listed below", + getSettings()->onlyLogListedChannels); + + onlyLogListedChannels->setEnabled(getSettings()->enableLogging); + logs.append(onlyLogListedChannels); + + // Select event + QObject::connect( + enableLogging, &QCheckBox::stateChanged, this, + [enableLogging, onlyLogListedChannels]() mutable { + onlyLogListedChannels->setEnabled(enableLogging->isChecked()); + }); + + EditableModelView *view = + logs.emplace( + (new ChannelLoggingModel(nullptr)) + ->initialized(&getSettings()->loggedChannels)) + .getElement(); + + view->setTitles({"Twitch channels"}); + view->getTableView()->horizontalHeader()->setSectionResizeMode( + QHeaderView::Fixed); + view->getTableView()->horizontalHeader()->setSectionResizeMode( + 0, QHeaderView::Stretch); + + view->addButtonPressed.connect([] { + getSettings()->loggedChannels.append(ChannelLog("channel")); + }); + } // logs end auto modMode = tabs.appendTab(new QVBoxLayout, "Moderation buttons");