From ad69eb691f3d1ac5a9f856305b4dcd29594c848a Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 2 Oct 2024 11:28:25 +0200 Subject: [PATCH 1/4] feat: show warning before blocking followed channel --- src/providers/twitch/TwitchChannel.cpp | 2 +- src/providers/twitch/api/Helix.cpp | 3 +- src/providers/twitch/api/Helix.hpp | 4 +- src/widgets/DraggablePopup.cpp | 11 +++ src/widgets/DraggablePopup.hpp | 8 ++ src/widgets/dialogs/UserInfoPopup.cpp | 127 ++++++++++++++++++------- 6 files changed, 117 insertions(+), 38 deletions(-) diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index c54e377413c..be0ea721faa 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -274,7 +274,7 @@ void TwitchChannel::refreshTwitchChannelEmotes(bool manualRefresh) getHelix()->getFollowedChannel( getApp()->getAccounts()->twitch.getCurrent()->getUserId(), - this->roomId(), + this->roomId(), nullptr, [weak{this->weak_from_this()}, makeEmotes](const auto &chan) { auto self = std::dynamic_pointer_cast(weak.lock()); if (!self || !chan) diff --git a/src/providers/twitch/api/Helix.cpp b/src/providers/twitch/api/Helix.cpp index a756a0f47a2..b16320dbb54 100644 --- a/src/providers/twitch/api/Helix.cpp +++ b/src/providers/twitch/api/Helix.cpp @@ -3138,7 +3138,7 @@ void Helix::getUserEmotes( } void Helix::getFollowedChannel( - QString userID, QString broadcasterID, + QString userID, QString broadcasterID, const QObject *caller, ResultCallback> successCallback, FailureCallback failureCallback) { @@ -3147,6 +3147,7 @@ void Helix::getFollowedChannel( {u"user_id"_s, userID}, {u"broadcaster_id"_s, broadcasterID}, }) + .caller(caller) .onSuccess([successCallback](auto result) { if (result.status() != 200) { diff --git a/src/providers/twitch/api/Helix.hpp b/src/providers/twitch/api/Helix.hpp index ec82f165441..38eebf3b3e2 100644 --- a/src/providers/twitch/api/Helix.hpp +++ b/src/providers/twitch/api/Helix.hpp @@ -1143,7 +1143,7 @@ class IHelix /// https://dev.twitch.tv/docs/api/reference/#get-followed-channels /// (non paginated) virtual void getFollowedChannel( - QString userID, QString broadcasterID, + QString userID, QString broadcasterID, const QObject *caller, ResultCallback> successCallback, FailureCallback failureCallback) = 0; @@ -1486,7 +1486,7 @@ class Helix final : public IHelix /// https://dev.twitch.tv/docs/api/reference/#get-followed-channels /// (non paginated) void getFollowedChannel( - QString userID, QString broadcasterID, + QString userID, QString broadcasterID, const QObject *caller, ResultCallback> successCallback, FailureCallback failureCallback) final; diff --git a/src/widgets/DraggablePopup.cpp b/src/widgets/DraggablePopup.cpp index 84a57c0f77d..b4ad4ff411f 100644 --- a/src/widgets/DraggablePopup.cpp +++ b/src/widgets/DraggablePopup.cpp @@ -41,6 +41,7 @@ DraggablePopup::DraggablePopup(bool closeAutomatically, QWidget *parent) : popupFlags | BaseWindow::DisableLayoutSave, parent) , lifetimeHack_(std::make_shared(false)) + , closeAutomatically_(closeAutomatically) , dragTimer_(this) { @@ -128,4 +129,14 @@ Button *DraggablePopup::createPinButton() return this->pinButton_; } +bool DraggablePopup::ensurePinned() +{ + if (this->closeAutomatically_ && !this->isPinned_) + { + this->togglePinned(); + return true; + } + return false; +} + } // namespace chatterino diff --git a/src/widgets/DraggablePopup.hpp b/src/widgets/DraggablePopup.hpp index bf82af0082c..97217c0df14 100644 --- a/src/widgets/DraggablePopup.hpp +++ b/src/widgets/DraggablePopup.hpp @@ -38,10 +38,18 @@ class DraggablePopup : public BaseWindow // button pixmap void togglePinned(); + /// Ensures that this popup is pinned (if it's expected to close automatically) + /// + /// @returns `true` if the popup was pinned as a result (i.e. if the popup + /// was unpinned and said to automatically close before) + bool ensurePinned(); + private: // isMoving_ is set to true if the user is holding the left mouse button down and has moved the mouse a small amount away from the original click point (startPosDrag_) bool isMoving_ = false; + bool closeAutomatically_ = false; + // startPosDrag_ is the coordinates where the user originally pressed the mouse button down to start dragging QPoint startPosDrag_; diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index fabae271d11..2623e4da641 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -2,6 +2,7 @@ #include "Application.hpp" #include "common/Channel.hpp" +#include "common/Literals.hpp" #include "common/network/NetworkRequest.hpp" #include "common/QLogging.hpp" #include "controllers/accounts/AccountController.hpp" @@ -37,6 +38,8 @@ #include #include +#include +#include #include #include #include @@ -141,6 +144,8 @@ int calculateTimeoutDuration(TimeoutButton timeout) namespace chatterino { +using namespace literals; + UserInfoPopup::UserInfoPopup(bool closeAutomatically, Split *split) : DraggablePopup(closeAutomatically, split) , split_(split) @@ -621,38 +626,33 @@ void UserInfoPopup::installEvents() return; } - switch (newState) + if (newState == Qt::Unchecked) { - case Qt::CheckState::Unchecked: { - this->ui_.block->setEnabled(false); - - getApp()->getAccounts()->twitch.getCurrent()->unblockUser( - this->userId_, this, - [this, reenableBlockCheckbox, currentUser] { - this->channel_->addSystemMessage( - QString("You successfully unblocked user %1") - .arg(this->userName_)); - reenableBlockCheckbox(); - }, - [this, reenableBlockCheckbox] { - this->channel_->addSystemMessage( - QString( - "User %1 couldn't be unblocked, an unknown " + this->ui_.block->setEnabled(false); + + getApp()->getAccounts()->twitch.getCurrent()->unblockUser( + this->userId_, this, + [this, reenableBlockCheckbox, currentUser] { + this->channel_->addSystemMessage( + QString("You successfully unblocked user %1") + .arg(this->userName_)); + reenableBlockCheckbox(); + }, + [this, reenableBlockCheckbox] { + this->channel_->addSystemMessage( + QString("User %1 couldn't be unblocked, an unknown " "error occurred!") - .arg(this->userName_)); - reenableBlockCheckbox(); - }); - } - break; - - case Qt::CheckState::PartiallyChecked: { - // We deliberately ignore this state - } - break; - - case Qt::CheckState::Checked: { - this->ui_.block->setEnabled(false); + .arg(this->userName_)); + reenableBlockCheckbox(); + }); + return; + } + if (newState == Qt::Checked) + { + this->ui_.block->setEnabled(false); + auto blockThisUser = [this, reenableBlockCheckbox, + currentUser] { getApp()->getAccounts()->twitch.getCurrent()->blockUser( this->userId_, this, [this, reenableBlockCheckbox, currentUser] { @@ -663,15 +663,74 @@ void UserInfoPopup::installEvents() }, [this, reenableBlockCheckbox] { this->channel_->addSystemMessage( - QString( - "User %1 couldn't be blocked, an unknown " - "error occurred!") + QString("User %1 couldn't be blocked, an " + "unknown error occurred!") .arg(this->userName_)); reenableBlockCheckbox(); }); - } - break; + }; + getHelix()->getFollowedChannel( + getApp()->getAccounts()->twitch.getCurrent()->getUserId(), + this->userId_, this, + [this, blockThisUser, + reenableBlockCheckbox](const auto &followStatus) { + if (!followStatus) + { + blockThisUser(); + return; + } + bool wasPinned = this->ensurePinned(); + auto btn = QMessageBox::warning( + this, u"Blocking " % this->userName_, + u"You're following %1 since %2.\n"_s + "Blocking %1 will unfollow them!\n\n" + "Are you sure you want to unfollow and block %1?" + .arg(this->userName_, + followStatus->followedAt.toString()), + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Cancel); + if (wasPinned) + { + this->togglePinned(); + } + if (btn != QMessageBox::Yes) + { + reenableBlockCheckbox(); + return; + } + blockThisUser(); + }, + [this, blockThisUser, + reenableBlockCheckbox](const auto &error) { + bool wasPinned = this->ensurePinned(); + auto btn = QMessageBox::warning( + this, u"Blocking " % this->userName_, + u"Failed to get following status for %1 (error: %2).\n"_s + "Blocking a followed channel will automatically " + "unfollow them.\n\n" + "Are you sure you want to block %1?".arg( + this->userName_, error), + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Cancel); + if (wasPinned) + { + this->togglePinned(); + } + if (btn == QMessageBox::Yes) + { + blockThisUser(); + } + else + { + reenableBlockCheckbox(); + } + }); + return; } + + qCWarning(chatterinoWidget) + << "Unexpected check-state when blocking" << this->userName_ + << QMetaEnum::fromType().valueToKey(newState); }); // ignore highlights From 0d0ffe435942b2615e15d85014ab3acb84bb5435 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 2 Oct 2024 11:43:44 +0200 Subject: [PATCH 2/4] chore: add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0755efc9d88..d871a3b679e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ - Minor: Removed experimental IRC support. (#5547) - Minor: Moderators can now see which mods start and cancel raids. (#5563) - Minor: The emote popup now reloads when Twitch emotes are reloaded. (#5580) +- Minor: When blocking a followed channel, Chatterino will now warn you about unfollowing that channel. (#5615) - Bugfix: Fixed tab move animation occasionally failing to start after closing a tab. (#5426) - Bugfix: If a network request errors with 200 OK, Qt's error code is now reported instead of the HTTP status. (#5378) - Bugfix: Fixed restricted users usernames not being clickable. (#5405) From 418e2b2073dc4b5b45abc78171d4a6addbcc220f Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 2 Oct 2024 11:59:49 +0200 Subject: [PATCH 3/4] fix: mock --- mocks/include/mocks/Helix.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mocks/include/mocks/Helix.hpp b/mocks/include/mocks/Helix.hpp index 5156de55750..fe309f0f8b8 100644 --- a/mocks/include/mocks/Helix.hpp +++ b/mocks/include/mocks/Helix.hpp @@ -422,7 +422,7 @@ class Helix : public IHelix // get followed channel MOCK_METHOD( void, getFollowedChannel, - (QString userID, QString broadcasterID, + (QString userID, QString broadcasterID, const QObject *caller, ResultCallback> successCallback, FailureCallback failureCallback), (override)); From 0f89162fe93486e12d8561eb567da9feda197f64 Mon Sep 17 00:00:00 2001 From: Nerixyz Date: Sun, 6 Oct 2024 12:58:46 +0200 Subject: [PATCH 4/4] refactor: always warn about blocking --- src/widgets/dialogs/UserInfoPopup.cpp | 103 ++++++++------------------ 1 file changed, 32 insertions(+), 71 deletions(-) diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index 2623e4da641..4a6e3364a55 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -651,79 +651,40 @@ void UserInfoPopup::installEvents() if (newState == Qt::Checked) { this->ui_.block->setEnabled(false); - auto blockThisUser = [this, reenableBlockCheckbox, - currentUser] { - getApp()->getAccounts()->twitch.getCurrent()->blockUser( - this->userId_, this, - [this, reenableBlockCheckbox, currentUser] { - this->channel_->addSystemMessage( - QString("You successfully blocked user %1") - .arg(this->userName_)); - reenableBlockCheckbox(); - }, - [this, reenableBlockCheckbox] { - this->channel_->addSystemMessage( - QString("User %1 couldn't be blocked, an " - "unknown error occurred!") - .arg(this->userName_)); - reenableBlockCheckbox(); - }); - }; - getHelix()->getFollowedChannel( - getApp()->getAccounts()->twitch.getCurrent()->getUserId(), + + bool wasPinned = this->ensurePinned(); + auto btn = QMessageBox::warning( + this, u"Blocking " % this->userName_, + u"Blocking %1 can cause unintended side-effects like unfollowing.\n\n"_s + "Are you sure you want to block %1?".arg(this->userName_), + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Cancel); + if (wasPinned) + { + this->togglePinned(); + } + if (btn != QMessageBox::Yes) + { + reenableBlockCheckbox(); + QSignalBlocker blocker(this->ui_.block); + this->ui_.block->setCheckState(Qt::Unchecked); + return; + } + + getApp()->getAccounts()->twitch.getCurrent()->blockUser( this->userId_, this, - [this, blockThisUser, - reenableBlockCheckbox](const auto &followStatus) { - if (!followStatus) - { - blockThisUser(); - return; - } - bool wasPinned = this->ensurePinned(); - auto btn = QMessageBox::warning( - this, u"Blocking " % this->userName_, - u"You're following %1 since %2.\n"_s - "Blocking %1 will unfollow them!\n\n" - "Are you sure you want to unfollow and block %1?" - .arg(this->userName_, - followStatus->followedAt.toString()), - QMessageBox::Yes | QMessageBox::Cancel, - QMessageBox::Cancel); - if (wasPinned) - { - this->togglePinned(); - } - if (btn != QMessageBox::Yes) - { - reenableBlockCheckbox(); - return; - } - blockThisUser(); + [this, reenableBlockCheckbox, currentUser] { + this->channel_->addSystemMessage( + QString("You successfully blocked user %1") + .arg(this->userName_)); + reenableBlockCheckbox(); }, - [this, blockThisUser, - reenableBlockCheckbox](const auto &error) { - bool wasPinned = this->ensurePinned(); - auto btn = QMessageBox::warning( - this, u"Blocking " % this->userName_, - u"Failed to get following status for %1 (error: %2).\n"_s - "Blocking a followed channel will automatically " - "unfollow them.\n\n" - "Are you sure you want to block %1?".arg( - this->userName_, error), - QMessageBox::Yes | QMessageBox::Cancel, - QMessageBox::Cancel); - if (wasPinned) - { - this->togglePinned(); - } - if (btn == QMessageBox::Yes) - { - blockThisUser(); - } - else - { - reenableBlockCheckbox(); - } + [this, reenableBlockCheckbox] { + this->channel_->addSystemMessage( + QString("User %1 couldn't be blocked, an " + "unknown error occurred!") + .arg(this->userName_)); + reenableBlockCheckbox(); }); return; }