From 1a9588b709319b3d31821549c203b232c3931803 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 29 Apr 2024 00:08:37 +0200 Subject: [PATCH 01/39] Clean up ModerationAction - remove commented out code, - remove useless if --- .../moderationactions/ModerationAction.cpp | 43 +++---------------- 1 file changed, 7 insertions(+), 36 deletions(-) diff --git a/src/controllers/moderationactions/ModerationAction.cpp b/src/controllers/moderationactions/ModerationAction.cpp index 2b3a95b0642..f7faab771ba 100644 --- a/src/controllers/moderationactions/ModerationAction.cpp +++ b/src/controllers/moderationactions/ModerationAction.cpp @@ -9,24 +9,6 @@ namespace chatterino { -// ModerationAction::ModerationAction(Image *_image, const QString &_action) -// : _isImage(true) -// , image(_image) -// , action(_action) -//{ -//} - -// ModerationAction::ModerationAction(const QString &_line1, const QString -// &_line2, -// const QString &_action) -// : _isImage(false) -// , image(nullptr) -// , line1(_line1) -// , line2(_line2) -// , action(_action) -//{ -//} - ModerationAction::ModerationAction(const QString &action) : action_(action) { @@ -99,13 +81,6 @@ ModerationAction::ModerationAction(const QString &action) } this->line2_ = "w"; } - - // line1 = this->line1_; - // line2 = this->line2_; - // } else { - // this->_moderationActions.emplace_back(getResources().buttonTimeout, - // str); - // } } else if (action.startsWith("/ban ")) { @@ -140,18 +115,14 @@ const std::optional &ModerationAction::getImage() const { assertInGuiThread(); - if (this->imageToLoad_ != 0) + if (this->imageToLoad_ == 1) { - if (this->imageToLoad_ == 1) - { - this->image_ = - Image::fromResourcePixmap(getResources().buttons.ban); - } - else if (this->imageToLoad_ == 2) - { - this->image_ = - Image::fromResourcePixmap(getResources().buttons.trashCan); - } + this->image_ = Image::fromResourcePixmap(getResources().buttons.ban); + } + else if (this->imageToLoad_ == 2) + { + this->image_ = + Image::fromResourcePixmap(getResources().buttons.trashCan); } return this->image_; From 27d3e0a1dafe4b22ce8fc7ffb95652b661c0d2a1 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 29 Apr 2024 00:20:39 +0200 Subject: [PATCH 02/39] Add image path to ModerationAction --- .../moderationactions/ModerationAction.cpp | 21 ++++++++++++---- .../moderationactions/ModerationAction.hpp | 24 +++++++++++++++---- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/controllers/moderationactions/ModerationAction.cpp b/src/controllers/moderationactions/ModerationAction.cpp index f7faab771ba..82cb7f4b0d8 100644 --- a/src/controllers/moderationactions/ModerationAction.cpp +++ b/src/controllers/moderationactions/ModerationAction.cpp @@ -6,10 +6,11 @@ #include "singletons/Resources.hpp" #include +#include namespace chatterino { -ModerationAction::ModerationAction(const QString &action) +ModerationAction::ModerationAction(const QString &action, const QUrl &iconPath) : action_(action) { static QRegularExpression replaceRegex("[!/.]"); @@ -84,11 +85,11 @@ ModerationAction::ModerationAction(const QString &action) } else if (action.startsWith("/ban ")) { - this->imageToLoad_ = 1; + this->builtInImageToLoad_ = BuiltInImage::Ban; } else if (action.startsWith("/delete ")) { - this->imageToLoad_ = 2; + this->builtInImageToLoad_ = BuiltInImage::TrashCan; } else { @@ -99,6 +100,12 @@ ModerationAction::ModerationAction(const QString &action) this->line1_ = xD.mid(0, 2); this->line2_ = xD.mid(2, 2); } + + if (!iconPath.isEmpty()) + { + this->builtInImageToLoad_ = BuiltInImage::None; + this->iconPath_ = iconPath; + } } bool ModerationAction::operator==(const ModerationAction &other) const @@ -115,15 +122,19 @@ const std::optional &ModerationAction::getImage() const { assertInGuiThread(); - if (this->imageToLoad_ == 1) + if (this->builtInImageToLoad_ == BuiltInImage::Ban) { this->image_ = Image::fromResourcePixmap(getResources().buttons.ban); } - else if (this->imageToLoad_ == 2) + else if (this->builtInImageToLoad_ == BuiltInImage::TrashCan) { this->image_ = Image::fromResourcePixmap(getResources().buttons.trashCan); } + else if (!this->iconPath_.isEmpty()) + { + this->image_ = Image::fromUrl({this->iconPath_.toString()}); + } return this->image_; } diff --git a/src/controllers/moderationactions/ModerationAction.hpp b/src/controllers/moderationactions/ModerationAction.hpp index 8fa4c9be8a2..24aa7215bf5 100644 --- a/src/controllers/moderationactions/ModerationAction.hpp +++ b/src/controllers/moderationactions/ModerationAction.hpp @@ -16,7 +16,7 @@ using ImagePtr = std::shared_ptr; class ModerationAction { public: - ModerationAction(const QString &action); + ModerationAction(const QString &action, const QUrl &iconPath = {}); bool operator==(const ModerationAction &other) const; @@ -26,12 +26,25 @@ class ModerationAction const QString &getLine2() const; const QString &getAction() const; + const QUrl &iconPath() const + { + return this->iconPath_; + }; + private: mutable std::optional image_; QString line1_; QString line2_; QString action_; - int imageToLoad_{}; + + enum class BuiltInImage { + None, + Ban, + TrashCan, + }; + BuiltInImage builtInImageToLoad_{}; + + QUrl iconPath_; }; } // namespace chatterino @@ -46,6 +59,7 @@ struct Serialize { rapidjson::Value ret(rapidjson::kObjectType); chatterino::rj::set(ret, "pattern", value.getAction(), a); + chatterino::rj::set(ret, "icon", value.iconPath().toString(), a); return ret; } @@ -63,10 +77,12 @@ struct Deserialize { } QString pattern; - chatterino::rj::getSafe(value, "pattern", pattern); - return chatterino::ModerationAction(pattern); + QString icon; + chatterino::rj::getSafe(value, "icon", icon); + + return chatterino::ModerationAction(pattern, QUrl(icon)); } }; From f10481a3cb55e9bb71e903a149e5489e829bd665 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 29 Apr 2024 00:49:08 +0200 Subject: [PATCH 03/39] Allow editing the icon path --- .../moderationactions/ModerationActionModel.cpp | 9 ++++++--- .../moderationactions/ModerationActionModel.hpp | 5 +++++ src/widgets/settingspages/ModerationPage.cpp | 13 +++++++++++++ src/widgets/settingspages/ModerationPage.hpp | 3 +++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/controllers/moderationactions/ModerationActionModel.cpp b/src/controllers/moderationactions/ModerationActionModel.cpp index d6595556d51..730c7305b50 100644 --- a/src/controllers/moderationactions/ModerationActionModel.cpp +++ b/src/controllers/moderationactions/ModerationActionModel.cpp @@ -7,7 +7,7 @@ namespace chatterino { // commandmodel ModerationActionModel ::ModerationActionModel(QObject *parent) - : SignalVectorModel(1, parent) + : SignalVectorModel(2, parent) { } @@ -15,14 +15,17 @@ ModerationActionModel ::ModerationActionModel(QObject *parent) ModerationAction ModerationActionModel::getItemFromRow( std::vector &row, const ModerationAction &original) { - return ModerationAction(row[0]->data(Qt::DisplayRole).toString()); + return ModerationAction( + row[Column::Command]->data(Qt::DisplayRole).toString(), + row[Column::Icon]->data(Qt::UserRole).toString()); } // turns a row in the model into a vector item void ModerationActionModel::getRowFromItem(const ModerationAction &item, std::vector &row) { - setStringItem(row[0], item.getAction()); + setStringItem(row[Column::Command], item.getAction()); + setFilePathItem(row[Column::Icon], item.iconPath()); } } // namespace chatterino diff --git a/src/controllers/moderationactions/ModerationActionModel.hpp b/src/controllers/moderationactions/ModerationActionModel.hpp index e8e51db037c..6695c135d0f 100644 --- a/src/controllers/moderationactions/ModerationActionModel.hpp +++ b/src/controllers/moderationactions/ModerationActionModel.hpp @@ -13,6 +13,11 @@ class ModerationActionModel : public SignalVectorModel public: explicit ModerationActionModel(QObject *parent); + enum Column { + Command, + Icon, + }; + protected: // turn a vector item into a model row ModerationAction getItemFromRow(std::vector &row, diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index fce69eff00e..e4e18b9d15d 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -212,6 +212,19 @@ ModerationPage::ModerationPage() QHeaderView::Fixed); view->getTableView()->horizontalHeader()->setSectionResizeMode( 0, QHeaderView::Stretch); + QObject::connect( + view->getTableView(), &QTableView::clicked, + [this, view](const QModelIndex &clicked) { + if (clicked.column() == ModerationActionModel::Column::Icon) + { + auto fileUrl = QFileDialog::getOpenFileUrl( + this, tr("Open Image"), QUrl(), + tr("Image Files (*.png)")); + view->getModel()->setData(clicked, fileUrl, Qt::UserRole); + view->getModel()->setData(clicked, fileUrl.fileName(), + Qt::DisplayRole); + } + }); // We can safely ignore this signal connection since we own the view std::ignore = view->addButtonPressed.connect([] { diff --git a/src/widgets/settingspages/ModerationPage.hpp b/src/widgets/settingspages/ModerationPage.hpp index 24662ec0d7b..30597965352 100644 --- a/src/widgets/settingspages/ModerationPage.hpp +++ b/src/widgets/settingspages/ModerationPage.hpp @@ -11,6 +11,7 @@ namespace chatterino { template class LayoutCreator; +class EditableModelView; class ModerationPage : public SettingsPage { @@ -27,6 +28,8 @@ class ModerationPage : public SettingsPage std::vector durationInputs_; std::vector unitInputs_; + + void openImageDialog(const QModelIndex &clicked, EditableModelView *view); }; } // namespace chatterino From 43b5257b58df03fa7d61b1e3f244cde4f5c9bac3 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 29 Apr 2024 01:11:55 +0200 Subject: [PATCH 04/39] Render the emote image in settings dialog --- .../ModerationActionModel.cpp | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/controllers/moderationactions/ModerationActionModel.cpp b/src/controllers/moderationactions/ModerationActionModel.cpp index 730c7305b50..08d5f6feaad 100644 --- a/src/controllers/moderationactions/ModerationActionModel.cpp +++ b/src/controllers/moderationactions/ModerationActionModel.cpp @@ -1,8 +1,60 @@ #include "controllers/moderationactions/ModerationActionModel.hpp" +#include "Application.hpp" +#include "common/network/NetworkRequest.hpp" +#include "common/network/NetworkResult.hpp" +#include "common/QLogging.hpp" #include "controllers/moderationactions/ModerationAction.hpp" +#include "messages/Image.hpp" #include "util/StandardItemHelper.hpp" +#include +#include +#include +namespace { + +using namespace chatterino; + +// This was copied from TwitchBadges::loadEmoteImage and modified. +void loadEmoteImage(const ImagePtr &image, + std::function &&callback) +{ + auto url = image->url().string; + NetworkRequest(url) + .concurrent() + .cache() + .onSuccess( + [callback = std::move(callback), url](const NetworkResult &result) { + auto data = result.getData(); + + // const cast since we are only reading from it + QBuffer buffer(const_cast(&data)); + buffer.open(QIODevice::ReadOnly); + QImageReader reader(&buffer); + + if (!reader.canRead() || reader.size().isEmpty()) + { + qCWarning(chatterinoSettings) + << "Can't read mod action image at" << url << ":" + << reader.errorString(); + return; + } + + QImage image = reader.read(); + if (image.isNull()) + { + qCWarning(chatterinoSettings) + << "Failed reading mod action image at" << url << ":" + << reader.errorString(); + return; + } + + callback(QPixmap::fromImage(image)); + }) + .execute(); +} +} // namespace + namespace chatterino { // commandmodel @@ -26,6 +78,12 @@ void ModerationActionModel::getRowFromItem(const ModerationAction &item, { setStringItem(row[Column::Command], item.getAction()); setFilePathItem(row[Column::Icon], item.iconPath()); + if (!item.iconPath().isEmpty()) + { + loadEmoteImage(*item.getImage(), [row](const QPixmap &pixmap) { + row[Column::Icon]->setData(pixmap, Qt::DecorationRole); + }); + } } } // namespace chatterino From f2940b190bb45c9cf9cc33be177de9a76e97cb17 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 29 Apr 2024 01:29:46 +0200 Subject: [PATCH 05/39] Move the image loading code to its own file --- src/CMakeLists.txt | 2 + .../ModerationActionModel.cpp | 56 ++----------------- src/util/LoadPixmapLazy.cpp | 46 +++++++++++++++ src/util/LoadPixmapLazy.hpp | 7 +++ 4 files changed, 60 insertions(+), 51 deletions(-) create mode 100644 src/util/LoadPixmapLazy.cpp create mode 100644 src/util/LoadPixmapLazy.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index eb64bcf31f0..474df6be023 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -500,6 +500,8 @@ set(SOURCE_FILES util/IpcQueue.hpp util/LayoutHelper.cpp util/LayoutHelper.hpp + util/LoadPixmapLazy.cpp + util/LoadPixmapLazy.hpp util/RapidjsonHelpers.cpp util/RapidjsonHelpers.hpp util/RatelimitBucket.cpp diff --git a/src/controllers/moderationactions/ModerationActionModel.cpp b/src/controllers/moderationactions/ModerationActionModel.cpp index 08d5f6feaad..6977b89cc82 100644 --- a/src/controllers/moderationactions/ModerationActionModel.cpp +++ b/src/controllers/moderationactions/ModerationActionModel.cpp @@ -1,59 +1,12 @@ #include "controllers/moderationactions/ModerationActionModel.hpp" -#include "Application.hpp" -#include "common/network/NetworkRequest.hpp" -#include "common/network/NetworkResult.hpp" -#include "common/QLogging.hpp" #include "controllers/moderationactions/ModerationAction.hpp" #include "messages/Image.hpp" +#include "util/LoadPixmapLazy.hpp" #include "util/StandardItemHelper.hpp" #include -#include #include -namespace { - -using namespace chatterino; - -// This was copied from TwitchBadges::loadEmoteImage and modified. -void loadEmoteImage(const ImagePtr &image, - std::function &&callback) -{ - auto url = image->url().string; - NetworkRequest(url) - .concurrent() - .cache() - .onSuccess( - [callback = std::move(callback), url](const NetworkResult &result) { - auto data = result.getData(); - - // const cast since we are only reading from it - QBuffer buffer(const_cast(&data)); - buffer.open(QIODevice::ReadOnly); - QImageReader reader(&buffer); - - if (!reader.canRead() || reader.size().isEmpty()) - { - qCWarning(chatterinoSettings) - << "Can't read mod action image at" << url << ":" - << reader.errorString(); - return; - } - - QImage image = reader.read(); - if (image.isNull()) - { - qCWarning(chatterinoSettings) - << "Failed reading mod action image at" << url << ":" - << reader.errorString(); - return; - } - - callback(QPixmap::fromImage(image)); - }) - .execute(); -} -} // namespace namespace chatterino { @@ -80,9 +33,10 @@ void ModerationActionModel::getRowFromItem(const ModerationAction &item, setFilePathItem(row[Column::Icon], item.iconPath()); if (!item.iconPath().isEmpty()) { - loadEmoteImage(*item.getImage(), [row](const QPixmap &pixmap) { - row[Column::Icon]->setData(pixmap, Qt::DecorationRole); - }); + loadPixmapFromUrlLazy( + (*item.getImage())->url(), [row](const QPixmap &pixmap) { + row[Column::Icon]->setData(pixmap, Qt::DecorationRole); + }); } } diff --git a/src/util/LoadPixmapLazy.cpp b/src/util/LoadPixmapLazy.cpp new file mode 100644 index 00000000000..32410c24132 --- /dev/null +++ b/src/util/LoadPixmapLazy.cpp @@ -0,0 +1,46 @@ +#include "common/network/NetworkRequest.hpp" +#include "common/network/NetworkResult.hpp" +#include "common/QLogging.hpp" +#include "messages/Image.hpp" + +#include +namespace chatterino { + +// This was copied from TwitchBadges::loadEmoteImage and modified. +void loadPixmapFromUrlLazy(const Url &url, + std::function &&callback) +{ + NetworkRequest(url.string) + .concurrent() + .cache() + .onSuccess( + [callback = std::move(callback), url](const NetworkResult &result) { + auto data = result.getData(); + + // const cast since we are only reading from it + QBuffer buffer(const_cast(&data)); + buffer.open(QIODevice::ReadOnly); + QImageReader reader(&buffer); + + if (!reader.canRead() || reader.size().isEmpty()) + { + qCWarning(chatterinoSettings) + << "Can't read mod action image at" << url.string << ":" + << reader.errorString(); + return; + } + + QImage image = reader.read(); + if (image.isNull()) + { + qCWarning(chatterinoSettings) + << "Failed reading mod action image at" << url.string + << ":" << reader.errorString(); + return; + } + + callback(QPixmap::fromImage(image)); + }) + .execute(); +} +} // namespace chatterino diff --git a/src/util/LoadPixmapLazy.hpp b/src/util/LoadPixmapLazy.hpp new file mode 100644 index 00000000000..378eafe98f4 --- /dev/null +++ b/src/util/LoadPixmapLazy.hpp @@ -0,0 +1,7 @@ +#pragma once +#include "common/Aliases.hpp" + +namespace chatterino { +void loadPixmapFromUrlLazy(const Url &url, + std::function &&callback); +} // namespace chatterino From 8b9cfb38f78c428f3564ba7c65ef58a2a803594f Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 29 Apr 2024 01:29:59 +0200 Subject: [PATCH 06/39] Update the image when selecting a new file --- src/widgets/settingspages/ModerationPage.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index e4e18b9d15d..96e7f2ee8c7 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -9,12 +9,14 @@ #include "singletons/Settings.hpp" #include "util/Helpers.hpp" #include "util/LayoutCreator.hpp" +#include "util/LoadPixmapLazy.hpp" #include "widgets/helper/EditableModelView.hpp" #include #include #include #include +#include #include #include #include @@ -223,6 +225,21 @@ ModerationPage::ModerationPage() view->getModel()->setData(clicked, fileUrl, Qt::UserRole); view->getModel()->setData(clicked, fileUrl.fileName(), Qt::DisplayRole); + // Clear the icon if the user canceled the dialog + if (fileUrl.isEmpty()) + { + view->getModel()->setData(clicked, QVariant(), + Qt::DecorationRole); + } + else + { + loadPixmapFromUrlLazy( + {fileUrl.toString()}, + [clicked, view](const QPixmap &pixmap) { + view->getModel()->setData(clicked, pixmap, + Qt::DecorationRole); + }); + } } }); From 5450161de066f55e539506374f2a30ec452f67d6 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 29 Apr 2024 01:30:36 +0200 Subject: [PATCH 07/39] Allow for more file types --- src/widgets/settingspages/ModerationPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index 96e7f2ee8c7..bcf6da11ac6 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -221,7 +221,7 @@ ModerationPage::ModerationPage() { auto fileUrl = QFileDialog::getOpenFileUrl( this, tr("Open Image"), QUrl(), - tr("Image Files (*.png)")); + tr("Image Files (*.png *.jpg *.jpeg)")); view->getModel()->setData(clicked, fileUrl, Qt::UserRole); view->getModel()->setData(clicked, fileUrl.fileName(), Qt::DisplayRole); From ec695b4058f1358b7fbfbc62db3301e44aa6c823 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 29 Apr 2024 01:33:37 +0200 Subject: [PATCH 08/39] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d335d8274f6..1c1f257b299 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unversioned - Bugfix: Fixed links without a protocol not being clickable. (#5345) +- Minor: Add option to customise Moderation buttons with images. (#5369) ## 2.5.0 From 2b6bac61475310c51a9f52efeefda09182701959 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 29 Apr 2024 12:46:12 +0200 Subject: [PATCH 09/39] Include QUrl in ModerationAction.cpp --- src/controllers/moderationactions/ModerationAction.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controllers/moderationactions/ModerationAction.hpp b/src/controllers/moderationactions/ModerationAction.hpp index 24aa7215bf5..9cb65f49c8a 100644 --- a/src/controllers/moderationactions/ModerationAction.hpp +++ b/src/controllers/moderationactions/ModerationAction.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include From 1de63068d598b4ac7e4bc6496043c8da76539f0d Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 29 Apr 2024 12:48:16 +0200 Subject: [PATCH 10/39] Add a whole ton more includes --- src/util/LoadPixmapLazy.cpp | 4 ++++ src/util/LoadPixmapLazy.hpp | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/util/LoadPixmapLazy.cpp b/src/util/LoadPixmapLazy.cpp index 32410c24132..4ff5d06713f 100644 --- a/src/util/LoadPixmapLazy.cpp +++ b/src/util/LoadPixmapLazy.cpp @@ -3,7 +3,11 @@ #include "common/QLogging.hpp" #include "messages/Image.hpp" +#include +#include #include +#include + namespace chatterino { // This was copied from TwitchBadges::loadEmoteImage and modified. diff --git a/src/util/LoadPixmapLazy.hpp b/src/util/LoadPixmapLazy.hpp index 378eafe98f4..5e21011b9fd 100644 --- a/src/util/LoadPixmapLazy.hpp +++ b/src/util/LoadPixmapLazy.hpp @@ -1,6 +1,8 @@ #pragma once #include "common/Aliases.hpp" +#include + namespace chatterino { void loadPixmapFromUrlLazy(const Url &url, std::function &&callback); From be3b595ec699f83cb58d5e01024a8dfd8a6ee3f5 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Wed, 1 May 2024 12:40:16 +0200 Subject: [PATCH 11/39] Move changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c1f257b299..4ea99607e06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,8 @@ ## Unversioned -- Bugfix: Fixed links without a protocol not being clickable. (#5345) - Minor: Add option to customise Moderation buttons with images. (#5369) +- Bugfix: Fixed links without a protocol not being clickable. (#5345) ## 2.5.0 From 43b40577aa476697f1df0b7b2267c78483705479 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Wed, 1 May 2024 12:40:23 +0200 Subject: [PATCH 12/39] Add table header for mod action icon --- src/widgets/settingspages/ModerationPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index bcf6da11ac6..829ade1b999 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -209,7 +209,7 @@ ModerationPage::ModerationPage() ->initialized(&getSettings()->moderationActions)) .getElement(); - view->setTitles({"Actions"}); + view->setTitles({"Action", "Icon"}); view->getTableView()->horizontalHeader()->setSectionResizeMode( QHeaderView::Fixed); view->getTableView()->horizontalHeader()->setSectionResizeMode( From 569b24e2b93358a68e935d566ffd0fe78e734cfa Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Wed, 1 May 2024 12:58:55 +0200 Subject: [PATCH 13/39] Specify ModerationActionModel::Column values --- src/controllers/moderationactions/ModerationActionModel.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/moderationactions/ModerationActionModel.hpp b/src/controllers/moderationactions/ModerationActionModel.hpp index 6695c135d0f..3382b437803 100644 --- a/src/controllers/moderationactions/ModerationActionModel.hpp +++ b/src/controllers/moderationactions/ModerationActionModel.hpp @@ -14,8 +14,8 @@ class ModerationActionModel : public SignalVectorModel explicit ModerationActionModel(QObject *parent); enum Column { - Command, - Icon, + Command = 0, + Icon = 1, }; protected: From 0c9b6c3242a974e2819595b80f085ad1a8bbaf89 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Wed, 1 May 2024 13:00:41 +0200 Subject: [PATCH 14/39] Document loadPixmapFromUrlLazy --- src/util/LoadPixmapLazy.cpp | 1 - src/util/LoadPixmapLazy.hpp | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/util/LoadPixmapLazy.cpp b/src/util/LoadPixmapLazy.cpp index 4ff5d06713f..f973fbee2d7 100644 --- a/src/util/LoadPixmapLazy.cpp +++ b/src/util/LoadPixmapLazy.cpp @@ -10,7 +10,6 @@ namespace chatterino { -// This was copied from TwitchBadges::loadEmoteImage and modified. void loadPixmapFromUrlLazy(const Url &url, std::function &&callback) { diff --git a/src/util/LoadPixmapLazy.hpp b/src/util/LoadPixmapLazy.hpp index 5e21011b9fd..90dc5e9d897 100644 --- a/src/util/LoadPixmapLazy.hpp +++ b/src/util/LoadPixmapLazy.hpp @@ -4,6 +4,10 @@ #include namespace chatterino { + +/** + * Loads an image from url into a QPixmap. Allows for file:// protocol links. Uses cacheing. + */ void loadPixmapFromUrlLazy(const Url &url, std::function &&callback); } // namespace chatterino From 2235418ccee7d43e84584aed8086e8cb56e8bb6b Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Wed, 1 May 2024 13:02:12 +0200 Subject: [PATCH 15/39] Use loadPixmapFromUrlLazy in TwitchBadges --- src/providers/twitch/TwitchBadges.cpp | 45 ++++++--------------------- 1 file changed, 9 insertions(+), 36 deletions(-) diff --git a/src/providers/twitch/TwitchBadges.cpp b/src/providers/twitch/TwitchBadges.cpp index 14c7475e0b9..7f93780f14b 100644 --- a/src/providers/twitch/TwitchBadges.cpp +++ b/src/providers/twitch/TwitchBadges.cpp @@ -7,6 +7,7 @@ #include "messages/Image.hpp" #include "providers/twitch/api/Helix.hpp" #include "util/DisplayBadge.hpp" +#include "util/LoadPixmapLazy.hpp" #include #include @@ -243,44 +244,16 @@ void TwitchBadges::loadEmoteImage(const QString &name, ImagePtr image, BadgeIconCallback &&callback) { auto url = image->url().string; - NetworkRequest(url) - .concurrent() - .cache() - .onSuccess([this, name, callback, url](auto result) { - auto data = result.getData(); - - // const cast since we are only reading from it - QBuffer buffer(const_cast(&data)); - buffer.open(QIODevice::ReadOnly); - QImageReader reader(&buffer); - - if (!reader.canRead() || reader.size().isEmpty()) - { - qCWarning(chatterinoTwitch) - << "Can't read badge image at" << url << "for" << name - << reader.errorString(); - return; - } - - QImage image = reader.read(); - if (image.isNull()) - { - qCWarning(chatterinoTwitch) - << "Failed reading badge image at" << url << "for" << name - << reader.errorString(); - return; - } + loadPixmapFromUrlLazy({url}, [this, name, callback, url](auto pixmap) { + auto icon = std::make_shared(pixmap); - auto icon = std::make_shared(QPixmap::fromImage(image)); - - { - std::unique_lock lock(this->badgesMutex_); - this->badgesMap_[name] = icon; - } + { + std::unique_lock lock(this->badgesMutex_); + this->badgesMap_[name] = icon; + } - callback(name, icon); - }) - .execute(); + callback(name, icon); + }); } } // namespace chatterino From baa4296ac37def73d024d0bc8d77f0ca44f10d62 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Wed, 1 May 2024 13:02:32 +0200 Subject: [PATCH 16/39] Return image if we already have it in ModerationAction --- src/controllers/moderationactions/ModerationAction.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/controllers/moderationactions/ModerationAction.cpp b/src/controllers/moderationactions/ModerationAction.cpp index 82cb7f4b0d8..5e995ca7bda 100644 --- a/src/controllers/moderationactions/ModerationAction.cpp +++ b/src/controllers/moderationactions/ModerationAction.cpp @@ -121,6 +121,10 @@ bool ModerationAction::isImage() const const std::optional &ModerationAction::getImage() const { assertInGuiThread(); + if (this->image_.has_value()) + { + return this->image_; + } if (this->builtInImageToLoad_ == BuiltInImage::Ban) { From a2981e847fa87d1921c23fa44cdcfda322863a45 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Wed, 1 May 2024 13:05:47 +0200 Subject: [PATCH 17/39] Don't translate --- src/widgets/settingspages/ModerationPage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index 829ade1b999..53d4d001b87 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -220,8 +220,8 @@ ModerationPage::ModerationPage() if (clicked.column() == ModerationActionModel::Column::Icon) { auto fileUrl = QFileDialog::getOpenFileUrl( - this, tr("Open Image"), QUrl(), - tr("Image Files (*.png *.jpg *.jpeg)")); + this, "Open Image", QUrl(), + "Image Files (*.png *.jpg *.jpeg)"); view->getModel()->setData(clicked, fileUrl, Qt::UserRole); view->getModel()->setData(clicked, fileUrl.fileName(), Qt::DisplayRole); From 0aad9010c307924f4e745c7489dcff1d09ba344c Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Wed, 1 May 2024 13:07:35 +0200 Subject: [PATCH 18/39] Change error texts and reformat in LoadPixmapLazy.cpp --- src/util/LoadPixmapLazy.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/util/LoadPixmapLazy.cpp b/src/util/LoadPixmapLazy.cpp index f973fbee2d7..db9602a2428 100644 --- a/src/util/LoadPixmapLazy.cpp +++ b/src/util/LoadPixmapLazy.cpp @@ -28,7 +28,7 @@ void loadPixmapFromUrlLazy(const Url &url, if (!reader.canRead() || reader.size().isEmpty()) { qCWarning(chatterinoSettings) - << "Can't read mod action image at" << url.string << ":" + << "Can't read image file at" << url.string << ":" << reader.errorString(); return; } @@ -37,8 +37,8 @@ void loadPixmapFromUrlLazy(const Url &url, if (image.isNull()) { qCWarning(chatterinoSettings) - << "Failed reading mod action image at" << url.string - << ":" << reader.errorString(); + << "Failed reading image at" << url.string << ":" + << reader.errorString(); return; } @@ -46,4 +46,5 @@ void loadPixmapFromUrlLazy(const Url &url, }) .execute(); } + } // namespace chatterino From f511abb9426341215efc74b3b622b025991a131d Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Wed, 1 May 2024 13:07:54 +0200 Subject: [PATCH 19/39] Send pixmap to main thread --- src/controllers/moderationactions/ModerationActionModel.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/controllers/moderationactions/ModerationActionModel.cpp b/src/controllers/moderationactions/ModerationActionModel.cpp index 6977b89cc82..f8daf9d4477 100644 --- a/src/controllers/moderationactions/ModerationActionModel.cpp +++ b/src/controllers/moderationactions/ModerationActionModel.cpp @@ -3,6 +3,7 @@ #include "controllers/moderationactions/ModerationAction.hpp" #include "messages/Image.hpp" #include "util/LoadPixmapLazy.hpp" +#include "util/PostToThread.hpp" #include "util/StandardItemHelper.hpp" #include @@ -35,7 +36,9 @@ void ModerationActionModel::getRowFromItem(const ModerationAction &item, { loadPixmapFromUrlLazy( (*item.getImage())->url(), [row](const QPixmap &pixmap) { - row[Column::Icon]->setData(pixmap, Qt::DecorationRole); + postToThread([row, pixmap]() { + row[Column::Icon]->setData(pixmap, Qt::DecorationRole); + }); }); } } From 673d4276a08806b319e8de3590b8fe20bfe554de Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Wed, 1 May 2024 20:18:23 +0200 Subject: [PATCH 20/39] Rename loadPixmapFromUrl{Lazy,} --- src/CMakeLists.txt | 4 ++-- src/controllers/moderationactions/ModerationActionModel.cpp | 4 ++-- src/providers/twitch/TwitchBadges.cpp | 4 ++-- src/util/{LoadPixmapLazy.cpp => LoadPixmap.cpp} | 3 +-- src/util/{LoadPixmapLazy.hpp => LoadPixmap.hpp} | 3 +-- src/widgets/settingspages/ModerationPage.cpp | 4 ++-- 6 files changed, 10 insertions(+), 12 deletions(-) rename src/util/{LoadPixmapLazy.cpp => LoadPixmap.cpp} (92%) rename src/util/{LoadPixmapLazy.hpp => LoadPixmap.hpp} (65%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 474df6be023..9fc78c9234f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -500,8 +500,8 @@ set(SOURCE_FILES util/IpcQueue.hpp util/LayoutHelper.cpp util/LayoutHelper.hpp - util/LoadPixmapLazy.cpp - util/LoadPixmapLazy.hpp + util/LoadPixmap.cpp + util/LoadPixmap.hpp util/RapidjsonHelpers.cpp util/RapidjsonHelpers.hpp util/RatelimitBucket.cpp diff --git a/src/controllers/moderationactions/ModerationActionModel.cpp b/src/controllers/moderationactions/ModerationActionModel.cpp index f8daf9d4477..a1e0e4e5632 100644 --- a/src/controllers/moderationactions/ModerationActionModel.cpp +++ b/src/controllers/moderationactions/ModerationActionModel.cpp @@ -2,7 +2,7 @@ #include "controllers/moderationactions/ModerationAction.hpp" #include "messages/Image.hpp" -#include "util/LoadPixmapLazy.hpp" +#include "util/LoadPixmap.hpp" #include "util/PostToThread.hpp" #include "util/StandardItemHelper.hpp" @@ -34,7 +34,7 @@ void ModerationActionModel::getRowFromItem(const ModerationAction &item, setFilePathItem(row[Column::Icon], item.iconPath()); if (!item.iconPath().isEmpty()) { - loadPixmapFromUrlLazy( + loadPixmapFromUrl( (*item.getImage())->url(), [row](const QPixmap &pixmap) { postToThread([row, pixmap]() { row[Column::Icon]->setData(pixmap, Qt::DecorationRole); diff --git a/src/providers/twitch/TwitchBadges.cpp b/src/providers/twitch/TwitchBadges.cpp index 7f93780f14b..d35e49be02f 100644 --- a/src/providers/twitch/TwitchBadges.cpp +++ b/src/providers/twitch/TwitchBadges.cpp @@ -7,7 +7,7 @@ #include "messages/Image.hpp" #include "providers/twitch/api/Helix.hpp" #include "util/DisplayBadge.hpp" -#include "util/LoadPixmapLazy.hpp" +#include "util/LoadPixmap.hpp" #include #include @@ -244,7 +244,7 @@ void TwitchBadges::loadEmoteImage(const QString &name, ImagePtr image, BadgeIconCallback &&callback) { auto url = image->url().string; - loadPixmapFromUrlLazy({url}, [this, name, callback, url](auto pixmap) { + loadPixmapFromUrl({url}, [this, name, callback, url](auto pixmap) { auto icon = std::make_shared(pixmap); { diff --git a/src/util/LoadPixmapLazy.cpp b/src/util/LoadPixmap.cpp similarity index 92% rename from src/util/LoadPixmapLazy.cpp rename to src/util/LoadPixmap.cpp index db9602a2428..ac17a28055b 100644 --- a/src/util/LoadPixmapLazy.cpp +++ b/src/util/LoadPixmap.cpp @@ -10,8 +10,7 @@ namespace chatterino { -void loadPixmapFromUrlLazy(const Url &url, - std::function &&callback) +void loadPixmapFromUrl(const Url &url, std::function &&callback) { NetworkRequest(url.string) .concurrent() diff --git a/src/util/LoadPixmapLazy.hpp b/src/util/LoadPixmap.hpp similarity index 65% rename from src/util/LoadPixmapLazy.hpp rename to src/util/LoadPixmap.hpp index 90dc5e9d897..68f0abf5b47 100644 --- a/src/util/LoadPixmapLazy.hpp +++ b/src/util/LoadPixmap.hpp @@ -8,6 +8,5 @@ namespace chatterino { /** * Loads an image from url into a QPixmap. Allows for file:// protocol links. Uses cacheing. */ -void loadPixmapFromUrlLazy(const Url &url, - std::function &&callback); +void loadPixmapFromUrl(const Url &url, std::function &&callback); } // namespace chatterino diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index 53d4d001b87..1c877975ff8 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -9,7 +9,7 @@ #include "singletons/Settings.hpp" #include "util/Helpers.hpp" #include "util/LayoutCreator.hpp" -#include "util/LoadPixmapLazy.hpp" +#include "util/LoadPixmap.hpp" #include "widgets/helper/EditableModelView.hpp" #include @@ -233,7 +233,7 @@ ModerationPage::ModerationPage() } else { - loadPixmapFromUrlLazy( + loadPixmapFromUrl( {fileUrl.toString()}, [clicked, view](const QPixmap &pixmap) { view->getModel()->setData(clicked, pixmap, From d9ff6ca62ccb438a599f5b015f38fe5cf9a2e921 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Wed, 1 May 2024 20:25:35 +0200 Subject: [PATCH 21/39] Document callback param --- src/util/LoadPixmap.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/util/LoadPixmap.hpp b/src/util/LoadPixmap.hpp index 68f0abf5b47..81fb1192144 100644 --- a/src/util/LoadPixmap.hpp +++ b/src/util/LoadPixmap.hpp @@ -7,6 +7,9 @@ namespace chatterino { /** * Loads an image from url into a QPixmap. Allows for file:// protocol links. Uses cacheing. + * + * @param callback The callback you will get the pixmap by. It will be invoked concurrently with no guarantees on which thread. */ void loadPixmapFromUrl(const Url &url, std::function &&callback); + } // namespace chatterino From ca5b62d35de42d72c873953861b2c05464ec6781 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Wed, 1 May 2024 21:11:19 +0200 Subject: [PATCH 22/39] Correct callback in ModerationPage - now uses QPointer to ensure the view is valid - now calls the code on the main thread to never hit an assertion --- src/widgets/settingspages/ModerationPage.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index 1c877975ff8..30d32d7bca2 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -10,6 +10,7 @@ #include "util/Helpers.hpp" #include "util/LayoutCreator.hpp" #include "util/LoadPixmap.hpp" +#include "util/PostToThread.hpp" #include "widgets/helper/EditableModelView.hpp" #include @@ -233,11 +234,21 @@ ModerationPage::ModerationPage() } else { + // QPointer will be cleared when view is destroyed + QPointer viewtemp = view; + loadPixmapFromUrl( {fileUrl.toString()}, - [clicked, view](const QPixmap &pixmap) { - view->getModel()->setData(clicked, pixmap, - Qt::DecorationRole); + [clicked, view = viewtemp](const QPixmap &pixmap) { + postToThread([clicked, view, pixmap]() { + if (view.isNull()) + { + return; + } + + view->getModel()->setData( + clicked, pixmap, Qt::DecorationRole); + }); }); } } From 6e4722f4fe6eb8cd10c6bf1e19c620d4eae7afbd Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Fri, 3 May 2024 13:45:30 +0200 Subject: [PATCH 23/39] Remove useless const_cast --- src/util/LoadPixmap.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/util/LoadPixmap.cpp b/src/util/LoadPixmap.cpp index ac17a28055b..f2d2aafb318 100644 --- a/src/util/LoadPixmap.cpp +++ b/src/util/LoadPixmap.cpp @@ -18,9 +18,7 @@ void loadPixmapFromUrl(const Url &url, std::function &&callback) .onSuccess( [callback = std::move(callback), url](const NetworkResult &result) { auto data = result.getData(); - - // const cast since we are only reading from it - QBuffer buffer(const_cast(&data)); + QBuffer buffer(&data); buffer.open(QIODevice::ReadOnly); QImageReader reader(&buffer); From 918076db842d97e50c3eca508cdac998446b3f9a Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sat, 4 May 2024 13:01:30 +0200 Subject: [PATCH 24/39] Get rid of None enum value --- .../moderationactions/ModerationAction.cpp | 13 ++++++------- .../moderationactions/ModerationAction.hpp | 1 - 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/controllers/moderationactions/ModerationAction.cpp b/src/controllers/moderationactions/ModerationAction.cpp index 5e995ca7bda..a1c5b7b983c 100644 --- a/src/controllers/moderationactions/ModerationAction.cpp +++ b/src/controllers/moderationactions/ModerationAction.cpp @@ -101,9 +101,8 @@ ModerationAction::ModerationAction(const QString &action, const QUrl &iconPath) this->line2_ = xD.mid(2, 2); } - if (!iconPath.isEmpty()) + if (iconPath.isValid()) { - this->builtInImageToLoad_ = BuiltInImage::None; this->iconPath_ = iconPath; } } @@ -126,7 +125,11 @@ const std::optional &ModerationAction::getImage() const return this->image_; } - if (this->builtInImageToLoad_ == BuiltInImage::Ban) + if (this->iconPath_.isValid()) + { + this->image_ = Image::fromUrl({this->iconPath_.toString()}); + } + else if (this->builtInImageToLoad_ == BuiltInImage::Ban) { this->image_ = Image::fromResourcePixmap(getResources().buttons.ban); } @@ -135,10 +138,6 @@ const std::optional &ModerationAction::getImage() const this->image_ = Image::fromResourcePixmap(getResources().buttons.trashCan); } - else if (!this->iconPath_.isEmpty()) - { - this->image_ = Image::fromUrl({this->iconPath_.toString()}); - } return this->image_; } diff --git a/src/controllers/moderationactions/ModerationAction.hpp b/src/controllers/moderationactions/ModerationAction.hpp index 9cb65f49c8a..50eb7a0c9f2 100644 --- a/src/controllers/moderationactions/ModerationAction.hpp +++ b/src/controllers/moderationactions/ModerationAction.hpp @@ -39,7 +39,6 @@ class ModerationAction QString action_; enum class BuiltInImage { - None, Ban, TrashCan, }; From 0d27299fd9209ba9229994260aba9a5fe8f562ee Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sat, 4 May 2024 13:06:03 +0200 Subject: [PATCH 25/39] rename BuiltInImage to ActionIconType and TrashCan to Delete i hate naming things --- src/controllers/moderationactions/ModerationAction.cpp | 8 ++++---- src/controllers/moderationactions/ModerationAction.hpp | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/controllers/moderationactions/ModerationAction.cpp b/src/controllers/moderationactions/ModerationAction.cpp index a1c5b7b983c..019c09897db 100644 --- a/src/controllers/moderationactions/ModerationAction.cpp +++ b/src/controllers/moderationactions/ModerationAction.cpp @@ -85,11 +85,11 @@ ModerationAction::ModerationAction(const QString &action, const QUrl &iconPath) } else if (action.startsWith("/ban ")) { - this->builtInImageToLoad_ = BuiltInImage::Ban; + this->builtInImageToLoad_ = ActionIconType::Ban; } else if (action.startsWith("/delete ")) { - this->builtInImageToLoad_ = BuiltInImage::TrashCan; + this->builtInImageToLoad_ = ActionIconType::Delete; } else { @@ -129,11 +129,11 @@ const std::optional &ModerationAction::getImage() const { this->image_ = Image::fromUrl({this->iconPath_.toString()}); } - else if (this->builtInImageToLoad_ == BuiltInImage::Ban) + else if (this->builtInImageToLoad_ == ActionIconType::Ban) { this->image_ = Image::fromResourcePixmap(getResources().buttons.ban); } - else if (this->builtInImageToLoad_ == BuiltInImage::TrashCan) + else if (this->builtInImageToLoad_ == ActionIconType::Delete) { this->image_ = Image::fromResourcePixmap(getResources().buttons.trashCan); diff --git a/src/controllers/moderationactions/ModerationAction.hpp b/src/controllers/moderationactions/ModerationAction.hpp index 50eb7a0c9f2..4e759556dd9 100644 --- a/src/controllers/moderationactions/ModerationAction.hpp +++ b/src/controllers/moderationactions/ModerationAction.hpp @@ -38,11 +38,12 @@ class ModerationAction QString line2_; QString action_; - enum class BuiltInImage { + enum class ActionIconType { Ban, - TrashCan, + Delete, + // Note: timeouts use text (line1_ and line2_), they aren't rendered with an image }; - BuiltInImage builtInImageToLoad_{}; + ActionIconType builtInImageToLoad_{}; QUrl iconPath_; }; From af30aacf3d2bc7a5a080dbf369b61f2e73ab2c3e Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sat, 4 May 2024 14:10:06 +0200 Subject: [PATCH 26/39] Add tests ensuring moderationaction parses ban/delete/timeouts properly with their icon & line --- tests/CMakeLists.txt | 1 + tests/src/ModerationAction.cpp | 145 +++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 tests/src/ModerationAction.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fb5730048b8..1dfb33b5912 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -41,6 +41,7 @@ set(test_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/LinkInfo.cpp ${CMAKE_CURRENT_LIST_DIR}/src/MessageLayout.cpp ${CMAKE_CURRENT_LIST_DIR}/src/QMagicEnum.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/ModerationAction.cpp # Add your new file above this line! ) diff --git a/tests/src/ModerationAction.cpp b/tests/src/ModerationAction.cpp new file mode 100644 index 00000000000..78f6d1f3559 --- /dev/null +++ b/tests/src/ModerationAction.cpp @@ -0,0 +1,145 @@ +#include "controllers/moderationactions/ModerationAction.hpp" + +#include "common/Literals.hpp" +#include "controllers/accounts/AccountController.hpp" +#include "controllers/commands/Command.hpp" +#include "controllers/commands/CommandController.hpp" +#include "controllers/hotkeys/HotkeyController.hpp" +#include "messages/Image.hpp" +#include "mocks/EmptyApplication.hpp" +#include "singletons/Emotes.hpp" +#include "singletons/Fonts.hpp" +#include "singletons/Paths.hpp" +#include "singletons/Resources.hpp" +#include "singletons/Settings.hpp" +#include "singletons/Theme.hpp" +#include "singletons/WindowManager.hpp" +#include "widgets/Notebook.hpp" +#include "widgets/splits/Split.hpp" + +#include +#include + +using namespace chatterino; + +using namespace std::chrono_literals; + +namespace { + +class MockApplication : mock::EmptyApplication +{ +public: + MockApplication() + : settings(this->settingsDir.filePath("settings.json")) + , fonts(this->settings) + , windowManager(this->paths) + { + } + Theme *getThemes() override + { + return &this->theme; + } + + HotkeyController *getHotkeys() override + { + return &this->hotkeys; + } + + Fonts *getFonts() override + { + return &this->fonts; + } + + WindowManager *getWindows() override + { + return &this->windowManager; + } + + AccountController *getAccounts() override + { + return &this->accounts; + } + + CommandController *getCommands() override + { + return &this->commands; + } + + IEmotes *getEmotes() override + { + return &this->emotes; + } + + Settings settings; + Theme theme; + HotkeyController hotkeys; + Fonts fonts; + Paths paths; + WindowManager windowManager; + AccountController accounts; + CommandController commands; + Emotes emotes; +}; + +class ModerationActionTest : public ::testing::Test +{ +public: + MockApplication mockApplication; +}; + +} // namespace + +TEST_F(ModerationActionTest, Parse) +{ + struct TestCase { + QString action; + + QString expectedLine1; + QString expectedLine2; + + std::optional expectedImage; + }; + + std::vector tests{ + { + .action = "/ban forsen", + .expectedImage = + Image::fromResourcePixmap(getResources().buttons.ban), + }, + { + .action = "/delete {message.id}", + .expectedImage = + Image::fromResourcePixmap(getResources().buttons.trashCan), + }, + { + .action = "/timeout {user.name} 1d", + .expectedLine1 = "1", + .expectedLine2 = "d", + }, + { + .action = ".timeout {user.name} 300", + .expectedLine1 = "5", + .expectedLine2 = "m", + }, + { + .action = "forsen", + .expectedLine1 = "fo", + .expectedLine2 = "rs", + }, + }; + + for (const auto &test : tests) + { + ModerationAction moderationAction(test.action); + + EXPECT_EQ(moderationAction.getAction(), test.action); + + EXPECT_EQ(moderationAction.getLine1(), test.expectedLine1); + EXPECT_EQ(moderationAction.getLine2(), test.expectedLine2); + + if (test.expectedImage.has_value()) + { + EXPECT_EQ(moderationAction.getImage(), test.expectedImage); + } + } +} From f28c97c42cd4cce6a1170667b61f876f6654dd3d Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sat, 4 May 2024 14:18:18 +0200 Subject: [PATCH 27/39] Make the icon type a full action type --- .../moderationactions/ModerationAction.cpp | 17 +++++++--- .../moderationactions/ModerationAction.hpp | 33 +++++++++++++++---- tests/src/ModerationAction.cpp | 9 +++++ 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/controllers/moderationactions/ModerationAction.cpp b/src/controllers/moderationactions/ModerationAction.cpp index 019c09897db..5a30566fc0b 100644 --- a/src/controllers/moderationactions/ModerationAction.cpp +++ b/src/controllers/moderationactions/ModerationAction.cpp @@ -20,6 +20,8 @@ ModerationAction::ModerationAction(const QString &action, const QUrl &iconPath) if (timeoutMatch.hasMatch()) { + this->type_ = Type::Timeout; + // if (multipleTimeouts > 1) { // QString line1; // QString line2; @@ -85,14 +87,16 @@ ModerationAction::ModerationAction(const QString &action, const QUrl &iconPath) } else if (action.startsWith("/ban ")) { - this->builtInImageToLoad_ = ActionIconType::Ban; + this->type_ = Type::Ban; } else if (action.startsWith("/delete ")) { - this->builtInImageToLoad_ = ActionIconType::Delete; + this->type_ = Type::Delete; } else { + this->type_ = Type::Custom; + QString xD = action; xD.replace(replaceRegex, ""); @@ -129,11 +133,11 @@ const std::optional &ModerationAction::getImage() const { this->image_ = Image::fromUrl({this->iconPath_.toString()}); } - else if (this->builtInImageToLoad_ == ActionIconType::Ban) + else if (this->type_ == Type::Ban) { this->image_ = Image::fromResourcePixmap(getResources().buttons.ban); } - else if (this->builtInImageToLoad_ == ActionIconType::Delete) + else if (this->type_ == Type::Delete) { this->image_ = Image::fromResourcePixmap(getResources().buttons.trashCan); @@ -157,4 +161,9 @@ const QString &ModerationAction::getAction() const return this->action_; } +ModerationAction::Type ModerationAction::getType() const +{ + return this->type_; +} + } // namespace chatterino diff --git a/src/controllers/moderationactions/ModerationAction.hpp b/src/controllers/moderationactions/ModerationAction.hpp index 4e759556dd9..340681121cf 100644 --- a/src/controllers/moderationactions/ModerationAction.hpp +++ b/src/controllers/moderationactions/ModerationAction.hpp @@ -17,6 +17,31 @@ using ImagePtr = std::shared_ptr; class ModerationAction { public: + /** + * Type of the action, parsed from the input `action` + */ + enum class Type { + /** + * /ban + */ + Ban, + + /** + * /delete + */ + Delete, + + /** + * /timeout + */ + Timeout, + + /** + * Anything not matching the action types above + */ + Custom, + }; + ModerationAction(const QString &action, const QUrl &iconPath = {}); bool operator==(const ModerationAction &other) const; @@ -26,6 +51,7 @@ class ModerationAction const QString &getLine1() const; const QString &getLine2() const; const QString &getAction() const; + Type getType() const; const QUrl &iconPath() const { @@ -38,12 +64,7 @@ class ModerationAction QString line2_; QString action_; - enum class ActionIconType { - Ban, - Delete, - // Note: timeouts use text (line1_ and line2_), they aren't rendered with an image - }; - ActionIconType builtInImageToLoad_{}; + Type type_{}; QUrl iconPath_; }; diff --git a/tests/src/ModerationAction.cpp b/tests/src/ModerationAction.cpp index 78f6d1f3559..7117722f553 100644 --- a/tests/src/ModerationAction.cpp +++ b/tests/src/ModerationAction.cpp @@ -98,6 +98,8 @@ TEST_F(ModerationActionTest, Parse) QString expectedLine2; std::optional expectedImage; + + ModerationAction::Type expectedType; }; std::vector tests{ @@ -105,26 +107,31 @@ TEST_F(ModerationActionTest, Parse) .action = "/ban forsen", .expectedImage = Image::fromResourcePixmap(getResources().buttons.ban), + .expectedType = ModerationAction::Type::Ban, }, { .action = "/delete {message.id}", .expectedImage = Image::fromResourcePixmap(getResources().buttons.trashCan), + .expectedType = ModerationAction::Type::Delete, }, { .action = "/timeout {user.name} 1d", .expectedLine1 = "1", .expectedLine2 = "d", + .expectedType = ModerationAction::Type::Timeout, }, { .action = ".timeout {user.name} 300", .expectedLine1 = "5", .expectedLine2 = "m", + .expectedType = ModerationAction::Type::Timeout, }, { .action = "forsen", .expectedLine1 = "fo", .expectedLine2 = "rs", + .expectedType = ModerationAction::Type::Custom, }, }; @@ -141,5 +148,7 @@ TEST_F(ModerationActionTest, Parse) { EXPECT_EQ(moderationAction.getImage(), test.expectedImage); } + + EXPECT_EQ(moderationAction.getType(), test.expectedType); } } From 62730db4619834e181f842ff7abf759e7b31a8cd Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sat, 4 May 2024 14:46:43 +0200 Subject: [PATCH 28/39] Remove unused stuff from tests --- tests/src/ModerationAction.cpp | 49 ---------------------------------- 1 file changed, 49 deletions(-) diff --git a/tests/src/ModerationAction.cpp b/tests/src/ModerationAction.cpp index 7117722f553..856ad75753c 100644 --- a/tests/src/ModerationAction.cpp +++ b/tests/src/ModerationAction.cpp @@ -1,21 +1,10 @@ #include "controllers/moderationactions/ModerationAction.hpp" -#include "common/Literals.hpp" -#include "controllers/accounts/AccountController.hpp" -#include "controllers/commands/Command.hpp" -#include "controllers/commands/CommandController.hpp" -#include "controllers/hotkeys/HotkeyController.hpp" #include "messages/Image.hpp" #include "mocks/EmptyApplication.hpp" #include "singletons/Emotes.hpp" -#include "singletons/Fonts.hpp" -#include "singletons/Paths.hpp" #include "singletons/Resources.hpp" #include "singletons/Settings.hpp" -#include "singletons/Theme.hpp" -#include "singletons/WindowManager.hpp" -#include "widgets/Notebook.hpp" -#include "widgets/splits/Split.hpp" #include #include @@ -31,39 +20,8 @@ class MockApplication : mock::EmptyApplication public: MockApplication() : settings(this->settingsDir.filePath("settings.json")) - , fonts(this->settings) - , windowManager(this->paths) { } - Theme *getThemes() override - { - return &this->theme; - } - - HotkeyController *getHotkeys() override - { - return &this->hotkeys; - } - - Fonts *getFonts() override - { - return &this->fonts; - } - - WindowManager *getWindows() override - { - return &this->windowManager; - } - - AccountController *getAccounts() override - { - return &this->accounts; - } - - CommandController *getCommands() override - { - return &this->commands; - } IEmotes *getEmotes() override { @@ -71,13 +29,6 @@ class MockApplication : mock::EmptyApplication } Settings settings; - Theme theme; - HotkeyController hotkeys; - Fonts fonts; - Paths paths; - WindowManager windowManager; - AccountController accounts; - CommandController commands; Emotes emotes; }; From 23a141ee87ecb15fe5d2301b84dfeebbd53264ba Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sat, 4 May 2024 14:46:54 +0200 Subject: [PATCH 29/39] Test custom icon path parsing --- tests/src/ModerationAction.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/src/ModerationAction.cpp b/tests/src/ModerationAction.cpp index 856ad75753c..e2235acbace 100644 --- a/tests/src/ModerationAction.cpp +++ b/tests/src/ModerationAction.cpp @@ -44,6 +44,7 @@ TEST_F(ModerationActionTest, Parse) { struct TestCase { QString action; + QString iconPath; QString expectedLine1; QString expectedLine2; @@ -84,21 +85,27 @@ TEST_F(ModerationActionTest, Parse) .expectedLine2 = "rs", .expectedType = ModerationAction::Type::Custom, }, + { + .action = "forsen", + .iconPath = "file:///this-is-the-path-to-the-icon.png", + .expectedLine1 = "fo", + .expectedLine2 = "rs", + .expectedImage = + Image::fromUrl(Url("file:///this-is-the-path-to-the-icon.png")), + .expectedType = ModerationAction::Type::Custom, + }, }; for (const auto &test : tests) { - ModerationAction moderationAction(test.action); + ModerationAction moderationAction(test.action, test.iconPath); EXPECT_EQ(moderationAction.getAction(), test.action); EXPECT_EQ(moderationAction.getLine1(), test.expectedLine1); EXPECT_EQ(moderationAction.getLine2(), test.expectedLine2); - if (test.expectedImage.has_value()) - { - EXPECT_EQ(moderationAction.getImage(), test.expectedImage); - } + EXPECT_EQ(moderationAction.getImage(), test.expectedImage); EXPECT_EQ(moderationAction.getType(), test.expectedType); } From c22ccd4c0d525461173ccb61cda98cce2e460c2c Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 5 May 2024 12:23:19 +0200 Subject: [PATCH 30/39] nit: add self-include to LoadPixmap --- src/util/LoadPixmap.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/util/LoadPixmap.cpp b/src/util/LoadPixmap.cpp index f2d2aafb318..810e80dfe82 100644 --- a/src/util/LoadPixmap.cpp +++ b/src/util/LoadPixmap.cpp @@ -1,3 +1,5 @@ +#include "util/LoadPixmap.hpp" + #include "common/network/NetworkRequest.hpp" #include "common/network/NetworkResult.hpp" #include "common/QLogging.hpp" From 01c9b0309b877ba9042dcd4fcb027d950b171a90 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 5 May 2024 12:23:28 +0200 Subject: [PATCH 31/39] nit: change LoadPixmap logging from chatterinoSettings to chatterinoImage --- src/util/LoadPixmap.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/LoadPixmap.cpp b/src/util/LoadPixmap.cpp index 810e80dfe82..474416a08f2 100644 --- a/src/util/LoadPixmap.cpp +++ b/src/util/LoadPixmap.cpp @@ -26,7 +26,7 @@ void loadPixmapFromUrl(const Url &url, std::function &&callback) if (!reader.canRead() || reader.size().isEmpty()) { - qCWarning(chatterinoSettings) + qCWarning(chatterinoImage) << "Can't read image file at" << url.string << ":" << reader.errorString(); return; @@ -35,7 +35,7 @@ void loadPixmapFromUrl(const Url &url, std::function &&callback) QImage image = reader.read(); if (image.isNull()) { - qCWarning(chatterinoSettings) + qCWarning(chatterinoImage) << "Failed reading image at" << url.string << ":" << reader.errorString(); return; From ae669a5cf47687fa005e912644bdeefbc4b2c29b Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 5 May 2024 12:23:40 +0200 Subject: [PATCH 32/39] nit: remove unused Image include from LoadPixmap --- src/util/LoadPixmap.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/util/LoadPixmap.cpp b/src/util/LoadPixmap.cpp index 474416a08f2..99fdf95f369 100644 --- a/src/util/LoadPixmap.cpp +++ b/src/util/LoadPixmap.cpp @@ -3,7 +3,6 @@ #include "common/network/NetworkRequest.hpp" #include "common/network/NetworkResult.hpp" #include "common/QLogging.hpp" -#include "messages/Image.hpp" #include #include From 85d679890a218a9556d8870bcca9d0daa0702c58 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 5 May 2024 12:27:08 +0200 Subject: [PATCH 33/39] use Url{} instead of Url() in tests --- tests/src/ModerationAction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/ModerationAction.cpp b/tests/src/ModerationAction.cpp index e2235acbace..ce32fb39d02 100644 --- a/tests/src/ModerationAction.cpp +++ b/tests/src/ModerationAction.cpp @@ -91,7 +91,7 @@ TEST_F(ModerationActionTest, Parse) .expectedLine1 = "fo", .expectedLine2 = "rs", .expectedImage = - Image::fromUrl(Url("file:///this-is-the-path-to-the-icon.png")), + Image::fromUrl(Url{"file:///this-is-the-path-to-the-icon.png"}), .expectedType = ModerationAction::Type::Custom, }, }; From ff7e3826498bd1cf02cbd950ec3285f5193acece Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 5 May 2024 12:31:33 +0200 Subject: [PATCH 34/39] Remove unused `openImageDialog` definition in ModerationPage --- src/widgets/settingspages/ModerationPage.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/widgets/settingspages/ModerationPage.hpp b/src/widgets/settingspages/ModerationPage.hpp index 30597965352..24662ec0d7b 100644 --- a/src/widgets/settingspages/ModerationPage.hpp +++ b/src/widgets/settingspages/ModerationPage.hpp @@ -11,7 +11,6 @@ namespace chatterino { template class LayoutCreator; -class EditableModelView; class ModerationPage : public SettingsPage { @@ -28,8 +27,6 @@ class ModerationPage : public SettingsPage std::vector durationInputs_; std::vector unitInputs_; - - void openImageDialog(const QModelIndex &clicked, EditableModelView *view); }; } // namespace chatterino From 81a477f8e39f043390d99d78e4aa1e33c1eaf313 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 5 May 2024 13:33:44 +0200 Subject: [PATCH 35/39] Scale icons down (or up) to fit within the row --- src/CMakeLists.txt | 2 ++ src/widgets/helper/IconDelegate.cpp | 29 ++++++++++++++++++++ src/widgets/helper/IconDelegate.hpp | 19 +++++++++++++ src/widgets/settingspages/ModerationPage.cpp | 3 ++ 4 files changed, 53 insertions(+) create mode 100644 src/widgets/helper/IconDelegate.cpp create mode 100644 src/widgets/helper/IconDelegate.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d542590b32c..a506744e1cf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -633,6 +633,8 @@ set(SOURCE_FILES widgets/helper/EditableModelView.hpp widgets/helper/EffectLabel.cpp widgets/helper/EffectLabel.hpp + widgets/helper/IconDelegate.cpp + widgets/helper/IconDelegate.hpp widgets/helper/InvisibleSizeGrip.cpp widgets/helper/InvisibleSizeGrip.hpp widgets/helper/NotebookButton.cpp diff --git a/src/widgets/helper/IconDelegate.cpp b/src/widgets/helper/IconDelegate.cpp new file mode 100644 index 00000000000..c89037eea68 --- /dev/null +++ b/src/widgets/helper/IconDelegate.cpp @@ -0,0 +1,29 @@ +#include "widgets/helper/IconDelegate.hpp" + +#include +#include + +namespace chatterino { + +IconDelegate::IconDelegate(QObject *parent) + : QStyledItemDelegate(parent) +{ +} + +void IconDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + auto data = index.data(Qt::DecorationRole); + + if (data.type() != QVariant::Pixmap) + { + return QStyledItemDelegate::paint(painter, option, index); + } + + auto scaledRect = option.rect; + scaledRect.setWidth(scaledRect.height()); + + painter->drawPixmap(scaledRect, data.value()); +} + +} // namespace chatterino diff --git a/src/widgets/helper/IconDelegate.hpp b/src/widgets/helper/IconDelegate.hpp new file mode 100644 index 00000000000..6afd5183ae6 --- /dev/null +++ b/src/widgets/helper/IconDelegate.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace chatterino { + +/** + * IconDelegate draws the decoration role pixmap scaled down to a square icon + */ +class IconDelegate : public QStyledItemDelegate +{ +public: + explicit IconDelegate(QObject *parent = nullptr); + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const override; +}; + +} // namespace chatterino diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index 30d32d7bca2..65ba577b1ae 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -12,6 +12,7 @@ #include "util/LoadPixmap.hpp" #include "util/PostToThread.hpp" #include "widgets/helper/EditableModelView.hpp" +#include "widgets/helper/IconDelegate.hpp" #include #include @@ -215,6 +216,8 @@ ModerationPage::ModerationPage() QHeaderView::Fixed); view->getTableView()->horizontalHeader()->setSectionResizeMode( 0, QHeaderView::Stretch); + view->getTableView()->setItemDelegateForColumn( + ModerationActionModel::Column::Icon, new IconDelegate(view)); QObject::connect( view->getTableView(), &QTableView::clicked, [this, view](const QModelIndex &clicked) { From 6da9cf1e0c26eb9b7fdedbd2030ebaf462f30828 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 5 May 2024 13:34:04 +0200 Subject: [PATCH 36/39] Sort CMakeLists.txt sources --- src/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a506744e1cf..15806aae102 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -643,8 +643,6 @@ set(SOURCE_FILES widgets/helper/NotebookTab.hpp widgets/helper/RegExpItemDelegate.cpp widgets/helper/RegExpItemDelegate.hpp - widgets/helper/TrimRegExpValidator.cpp - widgets/helper/TrimRegExpValidator.hpp widgets/helper/ResizingTextEdit.cpp widgets/helper/ResizingTextEdit.hpp widgets/helper/ScrollbarHighlight.cpp @@ -659,6 +657,8 @@ set(SOURCE_FILES widgets/helper/TitlebarButton.hpp widgets/helper/TitlebarButtons.cpp widgets/helper/TitlebarButtons.hpp + widgets/helper/TrimRegExpValidator.cpp + widgets/helper/TrimRegExpValidator.hpp widgets/layout/FlowLayout.cpp widgets/layout/FlowLayout.hpp From e2ad5e273d3de76f7fcad1e88f748352940c9bfb Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 5 May 2024 13:38:12 +0200 Subject: [PATCH 37/39] Move iconPath to source file --- src/controllers/moderationactions/ModerationAction.cpp | 5 +++++ src/controllers/moderationactions/ModerationAction.hpp | 6 +----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/controllers/moderationactions/ModerationAction.cpp b/src/controllers/moderationactions/ModerationAction.cpp index 5a30566fc0b..a82d1848ccb 100644 --- a/src/controllers/moderationactions/ModerationAction.cpp +++ b/src/controllers/moderationactions/ModerationAction.cpp @@ -161,6 +161,11 @@ const QString &ModerationAction::getAction() const return this->action_; } +const QUrl &ModerationAction::iconPath() const +{ + return this->iconPath_; +} + ModerationAction::Type ModerationAction::getType() const { return this->type_; diff --git a/src/controllers/moderationactions/ModerationAction.hpp b/src/controllers/moderationactions/ModerationAction.hpp index 340681121cf..643eaf06d62 100644 --- a/src/controllers/moderationactions/ModerationAction.hpp +++ b/src/controllers/moderationactions/ModerationAction.hpp @@ -51,13 +51,9 @@ class ModerationAction const QString &getLine1() const; const QString &getLine2() const; const QString &getAction() const; + const QUrl &iconPath() const; Type getType() const; - const QUrl &iconPath() const - { - return this->iconPath_; - }; - private: mutable std::optional image_; QString line1_; From 786d1a6e8151e45c429af5fde0ed1b6a4f470b3b Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 5 May 2024 13:44:01 +0200 Subject: [PATCH 38/39] Validate getImage return value before accessing it --- .../moderationactions/ModerationActionModel.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/controllers/moderationactions/ModerationActionModel.cpp b/src/controllers/moderationactions/ModerationActionModel.cpp index a1e0e4e5632..f7160b5896a 100644 --- a/src/controllers/moderationactions/ModerationActionModel.cpp +++ b/src/controllers/moderationactions/ModerationActionModel.cpp @@ -34,12 +34,17 @@ void ModerationActionModel::getRowFromItem(const ModerationAction &item, setFilePathItem(row[Column::Icon], item.iconPath()); if (!item.iconPath().isEmpty()) { - loadPixmapFromUrl( - (*item.getImage())->url(), [row](const QPixmap &pixmap) { + auto oImage = item.getImage(); + assert(oImage.has_value()); + if (oImage.has_value()) + { + auto url = oImage->get()->url(); + loadPixmapFromUrl(url, [row](const QPixmap &pixmap) { postToThread([row, pixmap]() { row[Column::Icon]->setData(pixmap, Qt::DecorationRole); }); }); + } } } From 44dc2e3a49d0f75f26b1d323457ecf5b1bd33791 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 5 May 2024 13:50:37 +0200 Subject: [PATCH 39/39] clean up twitchbadges loademoteimage --- src/providers/twitch/TwitchBadges.cpp | 20 ++++++++++---------- src/providers/twitch/TwitchBadges.hpp | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/providers/twitch/TwitchBadges.cpp b/src/providers/twitch/TwitchBadges.cpp index d35e49be02f..6e2b4c4aad8 100644 --- a/src/providers/twitch/TwitchBadges.cpp +++ b/src/providers/twitch/TwitchBadges.cpp @@ -240,20 +240,20 @@ void TwitchBadges::getBadgeIcons(const QList &badges, } } -void TwitchBadges::loadEmoteImage(const QString &name, ImagePtr image, +void TwitchBadges::loadEmoteImage(const QString &name, const ImagePtr &image, BadgeIconCallback &&callback) { - auto url = image->url().string; - loadPixmapFromUrl({url}, [this, name, callback, url](auto pixmap) { - auto icon = std::make_shared(pixmap); + loadPixmapFromUrl(image->url(), + [this, name, callback{std::move(callback)}](auto pixmap) { + auto icon = std::make_shared(pixmap); - { - std::unique_lock lock(this->badgesMutex_); - this->badgesMap_[name] = icon; - } + { + std::unique_lock lock(this->badgesMutex_); + this->badgesMap_[name] = icon; + } - callback(name, icon); - }); + callback(name, icon); + }); } } // namespace chatterino diff --git a/src/providers/twitch/TwitchBadges.hpp b/src/providers/twitch/TwitchBadges.hpp index 9964030f079..fff0f5aff0b 100644 --- a/src/providers/twitch/TwitchBadges.hpp +++ b/src/providers/twitch/TwitchBadges.hpp @@ -48,7 +48,7 @@ class TwitchBadges private: void parseTwitchBadges(QJsonObject root); void loaded(); - void loadEmoteImage(const QString &name, ImagePtr image, + void loadEmoteImage(const QString &name, const ImagePtr &image, BadgeIconCallback &&callback); std::shared_mutex badgesMutex_;