Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecated /(un)follow commands and respective usercard action #3078

Merged
merged 10 commits into from
Aug 4, 2021
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unversioned

- Major: Newly uploaded Twitch emotes are once again present in emote picker and can be autocompleted with Tab as well. (#2992)
- Major: Deprecated `/(un)follow` commands and (un)following in the usercards as Twitch has removed this feature for 3rd party applications. (#3076, #3078)
- Minor: Added autocompletion in /whispers for Twitch emotes, Global Bttv/Ffz emotes and emojis. (#2999, #3033)
- Minor: Received Twitch messages now use the exact same timestamp (obtained from Twitch's server) for every Chatterino user instead of assuming message timestamp on client's side. (#3021)
- Minor: Received IRC messages use `time` message tag for timestamp if it's available. (#3021)
Expand Down
100 changes: 16 additions & 84 deletions src/controllers/commands/CommandController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,22 @@ void CommandController::initialize(Settings &, Paths &paths)
return "";
});

this->registerCommand("/follow", [](const auto &words, auto channel) {
channel->addMessage(makeSystemMessage(
"Twitch has removed the ability to follow users through "
"third-party applications. For more information, see "
"https://github.com/Chatterino/chatterino2/issues/3076"));
return "";
});

this->registerCommand("/unfollow", [](const auto &words, auto channel) {
channel->addMessage(makeSystemMessage(
"Twitch has removed the ability to unfollow users through "
"third-party applications. For more information, see "
"https://github.com/Chatterino/chatterino2/issues/3076"));
return "";
});

/// Supported commands

this->registerCommand(
Expand Down Expand Up @@ -407,90 +423,6 @@ void CommandController::initialize(Settings &, Paths &paths)

this->registerCommand("/unblock", unblockLambda);

this->registerCommand("/follow", [](const auto &words, auto channel) {
if (words.size() < 2)
{
channel->addMessage(makeSystemMessage("Usage: /follow [user]"));
return "";
}

auto currentUser = getApp()->accounts->twitch.getCurrent();

if (currentUser->isAnon())
{
channel->addMessage(
makeSystemMessage("You must be logged in to follow someone!"));
return "";
}

auto target = words.at(1);

getHelix()->getUserByName(
target,
[currentUser, channel, target](const auto &targetUser) {
getHelix()->followUser(
currentUser->getUserId(), targetUser.id,
[channel, target]() {
channel->addMessage(makeSystemMessage(
"You successfully followed " + target));
},
[channel, target]() {
channel->addMessage(makeSystemMessage(
QString("User %1 could not be followed, an unknown "
"error occurred!")
.arg(target)));
});
},
[channel, target] {
channel->addMessage(
makeSystemMessage(QString("User %1 could not be followed, "
"no user with that name found!")
.arg(target)));
});

return "";
});

this->registerCommand("/unfollow", [](const auto &words, auto channel) {
if (words.size() < 2)
{
channel->addMessage(makeSystemMessage("Usage: /unfollow [user]"));
return "";
}

auto currentUser = getApp()->accounts->twitch.getCurrent();

if (currentUser->isAnon())
{
channel->addMessage(makeSystemMessage(
"You must be logged in to unfollow someone!"));
return "";
}

auto target = words.at(1);

getHelix()->getUserByName(
target,
[currentUser, channel, target](const auto &targetUser) {
getHelix()->unfollowUser(
currentUser->getUserId(), targetUser.id,
[channel, target]() {
channel->addMessage(makeSystemMessage(
"You successfully unfollowed " + target));
},
[channel, target]() {
channel->addMessage(makeSystemMessage(
"An error occurred while unfollowing " + target));
});
},
[channel, target] {
channel->addMessage(makeSystemMessage(
QString("User %1 could not be followed!").arg(target)));
});

return "";
});

this->registerCommand("/user", [](const auto &words, auto channel) {
if (words.size() < 2)
{
Expand Down
17 changes: 0 additions & 17 deletions src/providers/twitch/TwitchAccount.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,23 +186,6 @@ void TwitchAccount::unblockUser(QString userId, std::function<void()> onSuccess,
std::move(onFailure));
}

void TwitchAccount::checkFollow(const QString targetUserID,
std::function<void(FollowResult)> onFinished)
{
const auto onResponse = [onFinished](bool following, const auto &record) {
if (!following)
{
onFinished(FollowResult_NotFollowing);
return;
}

onFinished(FollowResult_Following);
};

getHelix()->getUserFollow(this->getUserId(), targetUserID, onResponse,
[] {});
}

SharedAccessGuard<const std::set<TwitchUser>> TwitchAccount::accessBlocks()
const
{
Expand Down
3 changes: 0 additions & 3 deletions src/providers/twitch/TwitchAccount.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,6 @@ class TwitchAccount : public Account
void unblockUser(QString userId, std::function<void()> onSuccess,
std::function<void()> onFailure);

void checkFollow(const QString targetUserID,
std::function<void(FollowResult)> onFinished);

SharedAccessGuard<const std::set<QString>> accessBlockedUserIds() const;
SharedAccessGuard<const std::set<TwitchUser>> accessBlocks() const;

Expand Down
63 changes: 0 additions & 63 deletions src/providers/twitch/api/Helix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,25 +142,6 @@ void Helix::getUserFollowers(
std::move(failureCallback));
}

void Helix::getUserFollow(
QString userId, QString targetId,
ResultCallback<bool, HelixUsersFollowsRecord> successCallback,
HelixFailureCallback failureCallback)
{
this->fetchUsersFollows(
std::move(userId), std::move(targetId),
[successCallback](const auto &response) {
if (response.data.empty())
{
successCallback(false, HelixUsersFollowsRecord());
return;
}

successCallback(true, response.data[0]);
},
std::move(failureCallback));
}

void Helix::fetchStreams(
QStringList userIds, QStringList userLogins,
ResultCallback<std::vector<HelixStream>> successCallback,
Expand Down Expand Up @@ -354,50 +335,6 @@ void Helix::getGameById(QString gameId,
failureCallback);
}

void Helix::followUser(QString userId, QString targetId,
std::function<void()> successCallback,
HelixFailureCallback failureCallback)
{
QUrlQuery urlQuery;

urlQuery.addQueryItem("from_id", userId);
urlQuery.addQueryItem("to_id", targetId);

this->makeRequest("users/follows", urlQuery)
.type(NetworkRequestType::Post)
.onSuccess([successCallback](auto /*result*/) -> Outcome {
successCallback();
return Success;
})
.onError([failureCallback](auto /*result*/) {
// TODO: make better xd
failureCallback();
})
.execute();
}

void Helix::unfollowUser(QString userId, QString targetId,
std::function<void()> successCallback,
HelixFailureCallback failureCallback)
{
QUrlQuery urlQuery;

urlQuery.addQueryItem("from_id", userId);
urlQuery.addQueryItem("to_id", targetId);

this->makeRequest("users/follows", urlQuery)
.type(NetworkRequestType::Delete)
.onSuccess([successCallback](auto /*result*/) -> Outcome {
successCallback();
return Success;
})
.onError([failureCallback](auto /*result*/) {
// TODO: make better xd
failureCallback();
})
.execute();
}

void Helix::createClip(QString channelId,
ResultCallback<HelixClip> successCallback,
std::function<void(HelixClipError)> failureCallback,
Expand Down
15 changes: 0 additions & 15 deletions src/providers/twitch/api/Helix.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,11 +341,6 @@ class Helix final : boost::noncopyable
ResultCallback<HelixUsersFollowsResponse> successCallback,
HelixFailureCallback failureCallback);

void getUserFollow(
QString userId, QString targetId,
ResultCallback<bool, HelixUsersFollowsRecord> successCallback,
HelixFailureCallback failureCallback);

// https://dev.twitch.tv/docs/api/reference#get-streams
void fetchStreams(QStringList userIds, QStringList userLogins,
ResultCallback<std::vector<HelixStream>> successCallback,
Expand All @@ -372,16 +367,6 @@ class Helix final : boost::noncopyable
void getGameById(QString gameId, ResultCallback<HelixGame> successCallback,
HelixFailureCallback failureCallback);

// https://dev.twitch.tv/docs/api/reference#create-user-follows
void followUser(QString userId, QString targetId,
std::function<void()> successCallback,
HelixFailureCallback failureCallback);

// https://dev.twitch.tv/docs/api/reference#delete-user-follows
void unfollowUser(QString userId, QString targetlId,
std::function<void()> successCallback,
HelixFailureCallback failureCallback);

// https://dev.twitch.tv/docs/api/reference#create-clip
void createClip(QString channelId,
ResultCallback<HelixClip> successCallback,
Expand Down
20 changes: 0 additions & 20 deletions src/providers/twitch/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,26 +47,6 @@ URL: https://dev.twitch.tv/docs/api/reference#get-streams
- `TwitchChannel` to get live status, game, title, and viewer count of a channel
- `NotificationController` to provide notifications for channels you might not have open in Chatterino, but are still interested in getting notifications for

### Follow User

URL: https://dev.twitch.tv/docs/api/reference#create-user-follows
Requires `user:edit:follows` scope

- We implement this in `providers/twitch/api/Helix.cpp followUser`
Used in:
- `widgets/dialogs/UserInfoPopup.cpp` to follow a user by ticking follow checkbox in usercard
- `controllers/commands/CommandController.cpp` in /follow command

### Unfollow User

URL: https://dev.twitch.tv/docs/api/reference#delete-user-follows
Requires `user:edit:follows` scope

- We implement this in `providers/twitch/api/Helix.cpp unfollowUser`
Used in:
- `widgets/dialogs/UserInfoPopup.cpp` to unfollow a user by unticking follow checkbox in usercard
- `controllers/commands/CommandController.cpp` in /unfollow command

### Create Clip

URL: https://dev.twitch.tv/docs/api/reference#create-clip
Expand Down
67 changes: 0 additions & 67 deletions src/widgets/dialogs/UserInfoPopup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,6 @@ UserInfoPopup::UserInfoPopup(bool closeAutomatically, QWidget *parent)
{
user->addStretch(1);

user.emplace<QCheckBox>("Follow").assign(&this->ui_.follow);
user.emplace<QCheckBox>("Block").assign(&this->ui_.block);
user.emplace<QCheckBox>("Ignore highlights")
.assign(&this->ui_.ignoreHighlights);
Expand Down Expand Up @@ -403,56 +402,6 @@ void UserInfoPopup::scaleChangedEvent(float /*scale*/)

void UserInfoPopup::installEvents()
{
std::weak_ptr<bool> hack = this->hack_;

// follow
QObject::connect(
this->ui_.follow, &QCheckBox::stateChanged,
[this](int newState) mutable {
auto currentUser = getApp()->accounts->twitch.getCurrent();

const auto reenableFollowCheckbox = [this] {
this->ui_.follow->setEnabled(true);
};

if (!this->ui_.follow->isEnabled())
{
// We received a state update while the checkbox was disabled
// This can only happen from the "check current follow state" call
// The state has been updated to properly reflect the users current follow state
reenableFollowCheckbox();
return;
}

switch (newState)
{
case Qt::CheckState::Unchecked: {
this->ui_.follow->setEnabled(false);
getHelix()->unfollowUser(currentUser->getUserId(),
this->userId_,
reenableFollowCheckbox, [] {
//
});
}
break;

case Qt::CheckState::PartiallyChecked: {
// We deliberately ignore this state
}
break;

case Qt::CheckState::Checked: {
this->ui_.follow->setEnabled(false);
getHelix()->followUser(currentUser->getUserId(),
this->userId_,
reenableFollowCheckbox, [] {
//
});
}
break;
}
});

std::shared_ptr<bool> ignoreNext = std::make_shared<bool>(false);

// block
Expand Down Expand Up @@ -616,8 +565,6 @@ void UserInfoPopup::updateLatestMessages()

void UserInfoPopup::updateUserData()
{
this->ui_.follow->setEnabled(false);

std::weak_ptr<bool> hack = this->hack_;
auto currentUser = getApp()->accounts->twitch.getCurrent();

Expand Down Expand Up @@ -683,19 +630,6 @@ void UserInfoPopup::updateUserData()
// on failure
});

// get follow state
currentUser->checkFollow(user.id, [this, hack](auto result) {
if (!hack.lock())
{
return;
}
if (result != FollowResult_Failed)
{
this->ui_.follow->setChecked(result == FollowResult_Following);
this->ui_.follow->setEnabled(true);
}
});

// get ignore state
bool isIgnoring = false;

Expand Down Expand Up @@ -772,7 +706,6 @@ void UserInfoPopup::updateUserData()
getHelix()->getUserByName(this->userName_, onUserFetched,
onUserFetchFailed);

this->ui_.follow->setEnabled(false);
this->ui_.block->setEnabled(false);
this->ui_.ignoreHighlights->setEnabled(false);
}
Expand Down
1 change: 0 additions & 1 deletion src/widgets/dialogs/UserInfoPopup.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ class UserInfoPopup final : public BaseWindow
Label *followageLabel = nullptr;
Label *subageLabel = nullptr;

QCheckBox *follow = nullptr;
QCheckBox *block = nullptr;
QCheckBox *ignoreHighlights = nullptr;

Expand Down