Show visual feedback when BTTV and FFZ emotes are loaded (#1671)
Upon joining a channel or pressing F5, BTTV and FFZ emotes are (re)loaded. This change adds visual feedback of the network requests and their outcome, in the form of a system message in the associated channel's chat window. Non-error messages are suppressed when joining a channel (which automatically loads emotes).
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
#include "messages/Emote.hpp"
|
#include "messages/Emote.hpp"
|
||||||
#include "messages/Image.hpp"
|
#include "messages/Image.hpp"
|
||||||
#include "messages/ImageSet.hpp"
|
#include "messages/ImageSet.hpp"
|
||||||
|
#include "messages/MessageBuilder.hpp"
|
||||||
#include "providers/twitch/TwitchChannel.hpp"
|
#include "providers/twitch/TwitchChannel.hpp"
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
@@ -138,17 +139,51 @@ void BttvEmotes::loadEmotes()
|
|||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BttvEmotes::loadChannel(const QString &channelId,
|
void BttvEmotes::loadChannel(std::weak_ptr<Channel> channel,
|
||||||
std::function<void(EmoteMap &&)> callback)
|
const QString &channelId,
|
||||||
|
std::function<void(EmoteMap &&)> callback,
|
||||||
|
bool manualRefresh)
|
||||||
{
|
{
|
||||||
NetworkRequest(QString(bttvChannelEmoteApiUrl) + channelId)
|
NetworkRequest(QString(bttvChannelEmoteApiUrl) + channelId)
|
||||||
.timeout(3000)
|
.timeout(3000)
|
||||||
.onSuccess([callback = std::move(callback)](auto result) -> Outcome {
|
.onSuccess([callback = std::move(callback), channel,
|
||||||
|
manualRefresh](auto result) -> Outcome {
|
||||||
auto pair = parseChannelEmotes(result.parseJson());
|
auto pair = parseChannelEmotes(result.parseJson());
|
||||||
if (pair.first)
|
if (pair.first)
|
||||||
callback(std::move(pair.second));
|
callback(std::move(pair.second));
|
||||||
|
if (auto shared = channel.lock(); manualRefresh)
|
||||||
|
shared->addMessage(
|
||||||
|
makeSystemMessage("BetterTTV channel emotes reloaded."));
|
||||||
return pair.first;
|
return pair.first;
|
||||||
})
|
})
|
||||||
|
.onError([channelId, channel, manualRefresh](auto result) {
|
||||||
|
auto shared = channel.lock();
|
||||||
|
if (!shared)
|
||||||
|
return;
|
||||||
|
if (result.status() == 203)
|
||||||
|
{
|
||||||
|
// User does not have any BTTV emotes
|
||||||
|
if (manualRefresh)
|
||||||
|
shared->addMessage(makeSystemMessage(
|
||||||
|
"This channel has no BetterTTV channel emotes."));
|
||||||
|
}
|
||||||
|
else if (result.status() == NetworkResult::timedoutStatus)
|
||||||
|
{
|
||||||
|
// TODO: Auto retry in case of a timeout, with a delay
|
||||||
|
qDebug() << "Fetching BTTV emotes for channel" << channelId
|
||||||
|
<< "failed due to timeout";
|
||||||
|
shared->addMessage(makeSystemMessage(
|
||||||
|
"Failed to fetch BetterTTV channel emotes. (timed out)"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qDebug() << "Error fetching BTTV emotes for channel"
|
||||||
|
<< channelId << ", error" << result.status();
|
||||||
|
shared->addMessage(
|
||||||
|
makeSystemMessage("Failed to fetch BetterTTV channel "
|
||||||
|
"emotes. (unknown error)"));
|
||||||
|
}
|
||||||
|
})
|
||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "boost/optional.hpp"
|
#include "boost/optional.hpp"
|
||||||
#include "common/Aliases.hpp"
|
#include "common/Aliases.hpp"
|
||||||
#include "common/Atomic.hpp"
|
#include "common/Atomic.hpp"
|
||||||
|
#include "providers/twitch/TwitchChannel.hpp"
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
@@ -24,8 +25,10 @@ public:
|
|||||||
std::shared_ptr<const EmoteMap> emotes() const;
|
std::shared_ptr<const EmoteMap> emotes() const;
|
||||||
boost::optional<EmotePtr> emote(const EmoteName &name) const;
|
boost::optional<EmotePtr> emote(const EmoteName &name) const;
|
||||||
void loadEmotes();
|
void loadEmotes();
|
||||||
static void loadChannel(const QString &channelId,
|
static void loadChannel(std::weak_ptr<Channel> channel,
|
||||||
std::function<void(EmoteMap &&)> callback);
|
const QString &channelId,
|
||||||
|
std::function<void(EmoteMap &&)> callback,
|
||||||
|
bool manualRefresh);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Atomic<std::shared_ptr<const EmoteMap>> global_;
|
Atomic<std::shared_ptr<const EmoteMap>> global_;
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#include "common/Outcome.hpp"
|
#include "common/Outcome.hpp"
|
||||||
#include "messages/Emote.hpp"
|
#include "messages/Emote.hpp"
|
||||||
#include "messages/Image.hpp"
|
#include "messages/Image.hpp"
|
||||||
|
#include "messages/MessageBuilder.hpp"
|
||||||
|
#include "providers/twitch/TwitchChannel.hpp"
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace {
|
namespace {
|
||||||
@@ -182,8 +184,10 @@ void FfzEmotes::loadEmotes()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void FfzEmotes::loadChannel(
|
void FfzEmotes::loadChannel(
|
||||||
const QString &channelId, std::function<void(EmoteMap &&)> emoteCallback,
|
std::weak_ptr<Channel> channel, const QString &channelId,
|
||||||
std::function<void(boost::optional<EmotePtr>)> modBadgeCallback)
|
std::function<void(EmoteMap &&)> emoteCallback,
|
||||||
|
std::function<void(boost::optional<EmotePtr>)> modBadgeCallback,
|
||||||
|
bool manualRefresh)
|
||||||
{
|
{
|
||||||
qDebug() << "[FFZEmotes] Reload FFZ Channel Emotes for channel"
|
qDebug() << "[FFZEmotes] Reload FFZ Channel Emotes for channel"
|
||||||
<< channelId;
|
<< channelId;
|
||||||
@@ -192,32 +196,47 @@ void FfzEmotes::loadChannel(
|
|||||||
|
|
||||||
.timeout(20000)
|
.timeout(20000)
|
||||||
.onSuccess([emoteCallback = std::move(emoteCallback),
|
.onSuccess([emoteCallback = std::move(emoteCallback),
|
||||||
modBadgeCallback =
|
modBadgeCallback = std::move(modBadgeCallback), channel,
|
||||||
std::move(modBadgeCallback)](auto result) -> Outcome {
|
manualRefresh](auto result) -> Outcome {
|
||||||
auto json = result.parseJson();
|
auto json = result.parseJson();
|
||||||
auto emoteMap = parseChannelEmotes(json);
|
auto emoteMap = parseChannelEmotes(json);
|
||||||
auto modBadge = parseModBadge(json);
|
auto modBadge = parseModBadge(json);
|
||||||
|
|
||||||
emoteCallback(std::move(emoteMap));
|
emoteCallback(std::move(emoteMap));
|
||||||
modBadgeCallback(std::move(modBadge));
|
modBadgeCallback(std::move(modBadge));
|
||||||
|
if (auto shared = channel.lock(); manualRefresh)
|
||||||
|
shared->addMessage(
|
||||||
|
makeSystemMessage("FrankerFaceZ channel emotes reloaded."));
|
||||||
|
|
||||||
return Success;
|
return Success;
|
||||||
})
|
})
|
||||||
.onError([channelId](NetworkResult result) {
|
.onError([channelId, channel, manualRefresh](NetworkResult result) {
|
||||||
|
auto shared = channel.lock();
|
||||||
|
if (!shared)
|
||||||
|
return;
|
||||||
if (result.status() == 203)
|
if (result.status() == 203)
|
||||||
{
|
{
|
||||||
// User does not have any FFZ emotes
|
// User does not have any FFZ emotes
|
||||||
|
if (manualRefresh)
|
||||||
|
shared->addMessage(makeSystemMessage(
|
||||||
|
"This channel has no FrankerFaceZ channel emotes."));
|
||||||
}
|
}
|
||||||
else if (result.status() == NetworkResult::timedoutStatus)
|
else if (result.status() == NetworkResult::timedoutStatus)
|
||||||
{
|
{
|
||||||
// TODO: Auto retry in case of a timeout, with a delay
|
// TODO: Auto retry in case of a timeout, with a delay
|
||||||
qDebug() << "Fetching FFZ emotes for channel" << channelId
|
qDebug() << "Fetching FFZ emotes for channel" << channelId
|
||||||
<< "failed due to timeout";
|
<< "failed due to timeout";
|
||||||
|
shared->addMessage(
|
||||||
|
makeSystemMessage("Failed to fetch FrankerFaceZ channel "
|
||||||
|
"emotes. (timed out)"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
qDebug() << "Error fetching FFZ emotes for channel" << channelId
|
qDebug() << "Error fetching FFZ emotes for channel" << channelId
|
||||||
<< ", error" << result.status();
|
<< ", error" << result.status();
|
||||||
|
shared->addMessage(
|
||||||
|
makeSystemMessage("Failed to fetch FrankerFaceZ channel "
|
||||||
|
"emotes. (unknown error)"));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.execute();
|
.execute();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "boost/optional.hpp"
|
#include "boost/optional.hpp"
|
||||||
#include "common/Aliases.hpp"
|
#include "common/Aliases.hpp"
|
||||||
#include "common/Atomic.hpp"
|
#include "common/Atomic.hpp"
|
||||||
|
#include "providers/twitch/TwitchChannel.hpp"
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
@@ -23,9 +24,10 @@ public:
|
|||||||
boost::optional<EmotePtr> emote(const EmoteName &name) const;
|
boost::optional<EmotePtr> emote(const EmoteName &name) const;
|
||||||
void loadEmotes();
|
void loadEmotes();
|
||||||
static void loadChannel(
|
static void loadChannel(
|
||||||
const QString &channelId,
|
std::weak_ptr<Channel> channel, const QString &channelId,
|
||||||
std::function<void(EmoteMap &&)> emoteCallback,
|
std::function<void(EmoteMap &&)> emoteCallback,
|
||||||
std::function<void(boost::optional<EmotePtr>)> modBadgeCallback);
|
std::function<void(boost::optional<EmotePtr>)> modBadgeCallback,
|
||||||
|
bool manualRefresh);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Atomic<std::shared_ptr<const EmoteMap>> global_;
|
Atomic<std::shared_ptr<const EmoteMap>> global_;
|
||||||
|
|||||||
@@ -118,8 +118,8 @@ TwitchChannel::TwitchChannel(const QString &name,
|
|||||||
this->refreshLiveStatus();
|
this->refreshLiveStatus();
|
||||||
this->refreshBadges();
|
this->refreshBadges();
|
||||||
this->refreshCheerEmotes();
|
this->refreshCheerEmotes();
|
||||||
this->refreshFFZChannelEmotes();
|
this->refreshFFZChannelEmotes(false);
|
||||||
this->refreshBTTVChannelEmotes();
|
this->refreshBTTVChannelEmotes(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
// timers
|
// timers
|
||||||
@@ -155,20 +155,22 @@ bool TwitchChannel::canSendMessage() const
|
|||||||
return !this->isEmpty();
|
return !this->isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwitchChannel::refreshBTTVChannelEmotes()
|
void TwitchChannel::refreshBTTVChannelEmotes(bool manualRefresh)
|
||||||
{
|
{
|
||||||
BttvEmotes::loadChannel(
|
BttvEmotes::loadChannel(
|
||||||
this->roomId(), [this, weak = weakOf<Channel>(this)](auto &&emoteMap) {
|
weakOf<Channel>(this), this->roomId(),
|
||||||
|
[this, weak = weakOf<Channel>(this)](auto &&emoteMap) {
|
||||||
if (auto shared = weak.lock())
|
if (auto shared = weak.lock())
|
||||||
this->bttvEmotes_.set(
|
this->bttvEmotes_.set(
|
||||||
std::make_shared<EmoteMap>(std::move(emoteMap)));
|
std::make_shared<EmoteMap>(std::move(emoteMap)));
|
||||||
});
|
},
|
||||||
|
manualRefresh);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwitchChannel::refreshFFZChannelEmotes()
|
void TwitchChannel::refreshFFZChannelEmotes(bool manualRefresh)
|
||||||
{
|
{
|
||||||
FfzEmotes::loadChannel(
|
FfzEmotes::loadChannel(
|
||||||
this->roomId(),
|
weakOf<Channel>(this), this->roomId(),
|
||||||
[this, weak = weakOf<Channel>(this)](auto &&emoteMap) {
|
[this, weak = weakOf<Channel>(this)](auto &&emoteMap) {
|
||||||
if (auto shared = weak.lock())
|
if (auto shared = weak.lock())
|
||||||
this->ffzEmotes_.set(
|
this->ffzEmotes_.set(
|
||||||
@@ -179,7 +181,8 @@ void TwitchChannel::refreshFFZChannelEmotes()
|
|||||||
{
|
{
|
||||||
this->ffzCustomModBadge_.set(std::move(modBadge));
|
this->ffzCustomModBadge_.set(std::move(modBadge));
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
manualRefresh);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwitchChannel::sendMessage(const QString &message)
|
void TwitchChannel::sendMessage(const QString &message)
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ public:
|
|||||||
std::shared_ptr<const EmoteMap> bttvEmotes() const;
|
std::shared_ptr<const EmoteMap> bttvEmotes() const;
|
||||||
std::shared_ptr<const EmoteMap> ffzEmotes() const;
|
std::shared_ptr<const EmoteMap> ffzEmotes() const;
|
||||||
|
|
||||||
virtual void refreshBTTVChannelEmotes();
|
virtual void refreshBTTVChannelEmotes(bool manualRefresh);
|
||||||
virtual void refreshFFZChannelEmotes();
|
virtual void refreshFFZChannelEmotes(bool manualRefresh);
|
||||||
|
|
||||||
// Badges
|
// Badges
|
||||||
boost::optional<EmotePtr> ffzCustomModBadge() const;
|
boost::optional<EmotePtr> ffzCustomModBadge() const;
|
||||||
|
|||||||
@@ -725,8 +725,8 @@ void Split::reloadChannelAndSubscriberEmotes()
|
|||||||
|
|
||||||
if (auto twitchChannel = dynamic_cast<TwitchChannel *>(channel.get()))
|
if (auto twitchChannel = dynamic_cast<TwitchChannel *>(channel.get()))
|
||||||
{
|
{
|
||||||
twitchChannel->refreshBTTVChannelEmotes();
|
twitchChannel->refreshBTTVChannelEmotes(true);
|
||||||
twitchChannel->refreshFFZChannelEmotes();
|
twitchChannel->refreshFFZChannelEmotes(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -765,8 +765,8 @@ void SplitHeader::reloadChannelEmotes()
|
|||||||
|
|
||||||
if (auto twitchChannel = dynamic_cast<TwitchChannel *>(channel.get()))
|
if (auto twitchChannel = dynamic_cast<TwitchChannel *>(channel.get()))
|
||||||
{
|
{
|
||||||
twitchChannel->refreshFFZChannelEmotes();
|
twitchChannel->refreshFFZChannelEmotes(true);
|
||||||
twitchChannel->refreshBTTVChannelEmotes();
|
twitchChannel->refreshBTTVChannelEmotes(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user