From 7bc63ba38f6321b5dba90c85be047fd478d39734 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 13 May 2018 17:53:24 +0200 Subject: [PATCH] Added result code to ignore/unignore calls Add ignore/unignore calls that take the user ID to avoid double api calls Fully implement the account popup ignore/unignore feature Fix #247 --- .../commands/commandcontroller.cpp | 4 +- src/providers/twitch/twitchaccount.cpp | 198 +++++++++++------- src/providers/twitch/twitchaccount.hpp | 31 ++- src/util/networkrequest.hpp | 2 +- src/widgets/accountpopup.cpp | 98 +++++---- src/widgets/accountpopup.hpp | 49 ++++- 6 files changed, 260 insertions(+), 122 deletions(-) diff --git a/src/controllers/commands/commandcontroller.cpp b/src/controllers/commands/commandcontroller.cpp index ef83da322..9b897f902 100644 --- a/src/controllers/commands/commandcontroller.cpp +++ b/src/controllers/commands/commandcontroller.cpp @@ -130,7 +130,7 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, return ""; } - user->ignore(target, [channel](const QString &message) { + user->ignore(target, [channel](auto resultCode, const QString &message) { channel->addMessage(messages::Message::createSystemMessage(message)); }); @@ -147,7 +147,7 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, return ""; } - user->unignore(target, [channel](const QString &message) { + user->unignore(target, [channel](auto resultCode, const QString &message) { channel->addMessage(messages::Message::createSystemMessage(message)); }); diff --git a/src/providers/twitch/twitchaccount.cpp b/src/providers/twitch/twitchaccount.cpp index 9a0788496..c089a7c22 100644 --- a/src/providers/twitch/twitchaccount.cpp +++ b/src/providers/twitch/twitchaccount.cpp @@ -3,6 +3,7 @@ #include "const.hpp" #include "debug/log.hpp" #include "util/networkrequest.hpp" +#include "util/rapidjson-helpers.hpp" #include "util/urlfetch.hpp" namespace chatterino { @@ -118,93 +119,138 @@ void TwitchAccount::loadIgnores() } void TwitchAccount::ignore(const QString &targetName, - std::function onFinished) + std::function onFinished) { util::twitch::getUserID(targetName, QThread::currentThread(), [=](QString targetUserID) { - QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + "/blocks/" + - targetUserID); - - util::NetworkRequest req(url); - req.setRequestType(util::NetworkRequest::PutRequest); - req.setCaller(QThread::currentThread()); - req.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken()); - - req.onError([=](int errorCode) { - onFinished("An unknown error occured while trying to ignore user " + targetName + " (" + - QString::number(errorCode) + ")"); - - return true; - }); - - req.onSuccess([=](const rapidjson::Document &document) { - if (!document.IsObject()) { - onFinished("Bad JSON data while ignoring user " + targetName); - return false; - } - - auto userIt = document.FindMember("user"); - if (userIt == document.MemberEnd()) { - onFinished("Bad JSON data while ignoring user (missing user) " + targetName); - return false; - } - - auto ignoredUser = TwitchUser::fromJSON(userIt->value); - { - std::lock_guard lock(this->ignoresMutex); - - auto res = this->ignores.insert(ignoredUser); - if (!res.second) { - const TwitchUser &existingUser = *(res.first); - existingUser.update(ignoredUser); - onFinished("User " + targetName + " is already ignored"); - return false; - } - } - onFinished("Successfully ignored user " + targetName); - - return true; - }); - - req.execute(); + this->ignoreByID(targetUserID, targetName, onFinished); // }); } +void TwitchAccount::ignoreByID(const QString &targetUserID, const QString &targetName, + std::function onFinished) +{ + QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + "/blocks/" + + targetUserID); + + util::NetworkRequest req(url); + req.setRequestType(util::NetworkRequest::PutRequest); + req.setCaller(QThread::currentThread()); + req.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken()); + + req.onError([=](int errorCode) { + onFinished(IgnoreResult_Failed, "An unknown error occured while trying to ignore user " + + targetName + " (" + QString::number(errorCode) + ")"); + + return true; + }); + + req.onSuccess([=](const rapidjson::Document &document) { + if (!document.IsObject()) { + onFinished(IgnoreResult_Failed, "Bad JSON data while ignoring user " + targetName); + return false; + } + + auto userIt = document.FindMember("user"); + if (userIt == document.MemberEnd()) { + onFinished(IgnoreResult_Failed, + "Bad JSON data while ignoring user (missing user) " + targetName); + return false; + } + + auto ignoredUser = TwitchUser::fromJSON(userIt->value); + { + std::lock_guard lock(this->ignoresMutex); + + auto res = this->ignores.insert(ignoredUser); + if (!res.second) { + const TwitchUser &existingUser = *(res.first); + existingUser.update(ignoredUser); + onFinished(IgnoreResult_AlreadyIgnored, + "User " + targetName + " is already ignored"); + return false; + } + } + onFinished(IgnoreResult_Success, "Successfully ignored user " + targetName); + + return true; + }); + + req.execute(); +} + void TwitchAccount::unignore(const QString &targetName, - std::function onFinished) + std::function onFinished) { util::twitch::getUserID(targetName, QThread::currentThread(), [=](QString targetUserID) { - QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + "/blocks/" + - targetUserID); - - util::NetworkRequest req(url); - req.setRequestType(util::NetworkRequest::DeleteRequest); - req.setCaller(QThread::currentThread()); - req.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken()); - - req.onError([=](int errorCode) { - onFinished("An unknown error occured while trying to unignore user " + targetName + - " (" + QString::number(errorCode) + ")"); - - return true; - }); - - req.onSuccess([=](const rapidjson::Document &document) { - TwitchUser ignoredUser; - ignoredUser.id = targetUserID; - { - std::lock_guard lock(this->ignoresMutex); - - this->ignores.erase(ignoredUser); - } - onFinished("Successfully unignored user " + targetName); - - return true; - }); - - req.execute(); + this->unignoreByID(targetUserID, targetName, onFinished); // }); } +void TwitchAccount::unignoreByID( + const QString &targetUserID, const QString &targetName, + std::function onFinished) +{ + QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + "/blocks/" + + targetUserID); + + util::NetworkRequest req(url); + req.setRequestType(util::NetworkRequest::DeleteRequest); + req.setCaller(QThread::currentThread()); + req.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken()); + + req.onError([=](int errorCode) { + onFinished(UnignoreResult_Failed, + "An unknown error occured while trying to unignore user " + targetName + " (" + + QString::number(errorCode) + ")"); + + return true; + }); + + req.onSuccess([=](const rapidjson::Document &document) { + TwitchUser ignoredUser; + ignoredUser.id = targetUserID; + { + std::lock_guard lock(this->ignoresMutex); + + this->ignores.erase(ignoredUser); + } + onFinished(UnignoreResult_Success, "Successfully unignored user " + targetName); + + return true; + }); + + req.execute(); +} + +void TwitchAccount::checkFollow(const QString targetUserID, + std::function onFinished) +{ + QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + "/follows/channels/" + + targetUserID); + + util::NetworkRequest req(url); + req.setRequestType(util::NetworkRequest::GetRequest); + req.setCaller(QThread::currentThread()); + req.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken()); + + req.onError([=](int errorCode) { + if (errorCode == 203) { + onFinished(FollowResult_NotFollowing); + } else { + onFinished(FollowResult_Failed); + } + + return true; + }); + + req.onSuccess([=](const rapidjson::Document &document) { + onFinished(FollowResult_Following); + return true; + }); + + req.execute(); +} + std::set TwitchAccount::getIgnores() const { std::lock_guard lock(this->ignoresMutex); diff --git a/src/providers/twitch/twitchaccount.hpp b/src/providers/twitch/twitchaccount.hpp index 2f510c519..649fd1f81 100644 --- a/src/providers/twitch/twitchaccount.hpp +++ b/src/providers/twitch/twitchaccount.hpp @@ -9,6 +9,24 @@ #include namespace chatterino { + +enum IgnoreResult { + IgnoreResult_Success, + IgnoreResult_AlreadyIgnored, + IgnoreResult_Failed, +}; + +enum UnignoreResult { + UnignoreResult_Success, + UnignoreResult_Failed, +}; + +enum FollowResult { + FollowResult_Following, + FollowResult_NotFollowing, + FollowResult_Failed, +}; + namespace providers { namespace twitch { @@ -37,8 +55,17 @@ public: bool isAnon() const; void loadIgnores(); - void ignore(const QString &targetName, std::function onFinished); - void unignore(const QString &targetName, std::function onFinished); + + void ignore(const QString &targetName, + std::function onFinished); + void ignoreByID(const QString &targetUserID, const QString &targetName, + std::function onFinished); + void unignore(const QString &targetName, + std::function onFinished); + void unignoreByID(const QString &targetUserID, const QString &targetName, + std::function onFinished); + + void checkFollow(const QString targetUserID, std::function onFinished); std::set getIgnores() const; diff --git a/src/util/networkrequest.hpp b/src/util/networkrequest.hpp index 2c9b158e5..f1de1066d 100644 --- a/src/util/networkrequest.hpp +++ b/src/util/networkrequest.hpp @@ -372,7 +372,7 @@ private: QObject::connect(worker, &NetworkWorker::doneUrl, this->data.caller, [data = this->data](auto reply) mutable { if (reply->error() != QNetworkReply::NetworkError::NoError) { - // TODO: We might want to call an onError callback here + data.onError(reply->error()); return; } diff --git a/src/widgets/accountpopup.cpp b/src/widgets/accountpopup.cpp index 5ba54a5bd..560aa9d34 100644 --- a/src/widgets/accountpopup.cpp +++ b/src/widgets/accountpopup.cpp @@ -98,32 +98,51 @@ AccountPopupWidget::AccountPopupWidget(ChannelPtr _channel) "/follows/channels/" + this->popupWidgetUser.userID); this->ui->follow->setEnabled(false); - if (!this->relationship.following) { + if (!this->relationship.isFollowing()) { util::twitch::put(requestUrl, [this](QJsonObject obj) { qDebug() << "follows channel: " << obj; - this->relationship.following = true; + this->relationship.setFollowing(true); emit refreshButtons(); }); } else { util::twitch::sendDelete(requestUrl, [this] { - this->relationship.following = false; + this->relationship.setFollowing(false); emit refreshButtons(); }); } }); QObject::connect(this->ui->ignore, &QPushButton::clicked, this, [=]() { - QUrl requestUrl("https://api.twitch.tv/kraken/users/" + this->loggedInUser.userID + - "/blocks/" + this->popupWidgetUser.userID); + auto currentUser = getApp()->accounts->Twitch.getCurrent(); - if (!this->relationship.ignoring) { - util::twitch::put(requestUrl, [this](auto) { - this->relationship.ignoring = true; // - }); + if (!this->relationship.isIgnoring()) { + currentUser->ignoreByID(this->popupWidgetUser.userID, this->popupWidgetUser.username, + [=](auto result, const auto &message) { + switch (result) { + case IgnoreResult_Success: { + this->relationship.setIgnoring(true); + emit refreshButtons(); + } break; + case IgnoreResult_AlreadyIgnored: { + this->relationship.setIgnoring(true); + emit refreshButtons(); + } break; + case IgnoreResult_Failed: { + } break; + } + }); } else { - util::twitch::sendDelete(requestUrl, [this] { - this->relationship.ignoring = false; // - }); + currentUser->unignoreByID(this->popupWidgetUser.userID, this->popupWidgetUser.username, + [=](auto result, const auto &message) { + switch (result) { + case UnignoreResult_Success: { + this->relationship.setIgnoring(false); + emit refreshButtons(); + } break; + case UnignoreResult_Failed: { + } break; + } + }); } }); @@ -158,8 +177,7 @@ AccountPopupWidget::AccountPopupWidget(ChannelPtr _channel) void AccountPopupWidget::setName(const QString &name) { - this->relationship.following = false; - this->relationship.ignoring = false; + this->relationship.reset(); this->popupWidgetUser.username = name; this->ui->lblUsername->setText(name); @@ -206,17 +224,24 @@ void AccountPopupWidget::getUserData() this->loadAvatar(QUrl(obj.value("logo").toString())); }); - util::twitch::get("https://api.twitch.tv/kraken/users/" + this->loggedInUser.userID + - "/follows/channels/" + this->popupWidgetUser.userID, - this, [=](const QJsonObject &obj) { - this->ui->follow->setEnabled(true); - this->relationship.following = obj.contains("channel"); + auto app = getApp(); + auto currentUser = app->accounts->Twitch.getCurrent(); - emit refreshButtons(); - }); + currentUser->checkFollow(this->popupWidgetUser.userID, [=](auto result) { + this->relationship.setFollowing(result == FollowResult_Following); - // TODO: Get ignore relationship between logged in user and popup widget user and update - // relationship.ignoring + emit refreshButtons(); + }); + + bool isIgnoring = false; + for (const auto &ignoredUser : currentUser->getIgnores()) { + if (this->popupWidgetUser.userID == ignoredUser.id) { + isIgnoring = true; + break; + } + } + this->relationship.setIgnoring(isIgnoring); + emit refreshButtons(); } void AccountPopupWidget::loadAvatar(const QUrl &avatarUrl) @@ -327,28 +352,24 @@ void AccountPopupWidget::refreshLayouts() void AccountPopupWidget::actuallyRefreshButtons() { - if (this->relationship.following) { - if (this->ui->follow->text() != "Unfollow") { + if (this->relationship.isFollowingSet()) { + if (this->relationship.isFollowing()) { this->ui->follow->setText("Unfollow"); - this->ui->follow->setEnabled(true); - } - } else { - if (this->ui->follow->text() != "Follow") { + } else { this->ui->follow->setText("Follow"); - this->ui->follow->setEnabled(true); } + + this->ui->follow->setEnabled(true); } - if (this->relationship.ignoring) { - if (this->ui->ignore->text() != "Unignore") { + if (this->relationship.isIgnoringSet()) { + if (this->relationship.isIgnoring()) { this->ui->ignore->setText("Unignore"); - this->ui->ignore->setEnabled(true); - } - } else { - if (this->ui->ignore->text() != "Ignore") { + } else { this->ui->ignore->setText("Ignore"); - this->ui->ignore->setEnabled(true); } + + this->ui->ignore->setEnabled(true); } } @@ -368,8 +389,7 @@ void AccountPopupWidget::showEvent(QShowEvent *) this->popupWidgetUser.refreshUserType(this->channel, false); this->ui->follow->setEnabled(false); - // XXX: Uncomment when ignore/unignore is fully implemented - // this->ui->ignore->setEnabled(false); + this->ui->ignore->setEnabled(false); this->refreshButtons(); diff --git a/src/widgets/accountpopup.hpp b/src/widgets/accountpopup.hpp index f8fed98b2..05d6ef0e6 100644 --- a/src/widgets/accountpopup.hpp +++ b/src/widgets/accountpopup.hpp @@ -71,8 +71,53 @@ private: User popupWidgetUser; struct { - bool following = false; - bool ignoring = false; + void reset() + { + this->following = -1; + this->ignoring = -1; + } + + bool isFollowing() const + { + return this->following == 1; + } + + bool isFollowingSet() const + { + return this->following != -1; + } + + void setFollowing(bool newVal) + { + if (newVal) { + this->following = 1; + } else { + this->following = 0; + } + } + + bool isIgnoring() const + { + return this->ignoring == 1; + } + + bool isIgnoringSet() const + { + return this->ignoring != -1; + } + + void setIgnoring(bool newVal) + { + if (newVal) { + this->ignoring = 1; + } else { + this->ignoring = 0; + } + } + + private: + int following = -1; + int ignoring = -1; } relationship; protected: