refactor: load Twitch emotes from Helix (#5239)
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
#include <QAbstractButton>
|
||||
#include <QHBoxLayout>
|
||||
#include <QRegularExpression>
|
||||
#include <QStringBuilder>
|
||||
#include <QTabWidget>
|
||||
|
||||
#include <utility>
|
||||
@@ -42,13 +43,14 @@ auto makeTitleMessage(const QString &title)
|
||||
return builder.release();
|
||||
}
|
||||
|
||||
auto makeEmoteMessage(const EmoteMap &map, const MessageElementFlag &emoteFlag)
|
||||
auto makeEmoteMessage(std::vector<EmotePtr> emotes,
|
||||
const MessageElementFlag &emoteFlag)
|
||||
{
|
||||
MessageBuilder builder;
|
||||
builder->flags.set(MessageFlag::Centered);
|
||||
builder->flags.set(MessageFlag::DisableCompactEmotes);
|
||||
|
||||
if (map.empty())
|
||||
if (emotes.empty())
|
||||
{
|
||||
builder.emplace<TextElement>("no emotes available",
|
||||
MessageElementFlag::Text,
|
||||
@@ -56,24 +58,41 @@ auto makeEmoteMessage(const EmoteMap &map, const MessageElementFlag &emoteFlag)
|
||||
return builder.release();
|
||||
}
|
||||
|
||||
std::vector<std::pair<EmoteName, EmotePtr>> vec(map.begin(), map.end());
|
||||
std::sort(vec.begin(), vec.end(),
|
||||
[](const std::pair<EmoteName, EmotePtr> &l,
|
||||
const std::pair<EmoteName, EmotePtr> &r) {
|
||||
return compareEmoteStrings(l.first.string, r.first.string);
|
||||
});
|
||||
for (const auto &emote : vec)
|
||||
std::sort(emotes.begin(), emotes.end(), [](const auto &l, const auto &r) {
|
||||
return compareEmoteStrings(l->name.string, r->name.string);
|
||||
});
|
||||
for (const auto &emote : emotes)
|
||||
{
|
||||
builder
|
||||
.emplace<EmoteElement>(
|
||||
emote.second,
|
||||
emote,
|
||||
MessageElementFlags{MessageElementFlag::AlwaysShow, emoteFlag})
|
||||
->setLink(Link(Link::InsertText, emote.first.string));
|
||||
->setLink(Link(Link::InsertText, emote->name.string));
|
||||
}
|
||||
|
||||
return builder.release();
|
||||
}
|
||||
|
||||
auto makeEmoteMessage(const EmoteMap &map, const MessageElementFlag &emoteFlag)
|
||||
{
|
||||
if (map.empty())
|
||||
{
|
||||
MessageBuilder builder;
|
||||
builder.emplace<TextElement>("no emotes available",
|
||||
MessageElementFlag::Text,
|
||||
MessageColor::System);
|
||||
return builder.release();
|
||||
}
|
||||
|
||||
std::vector<EmotePtr> vec;
|
||||
vec.reserve(map.size());
|
||||
for (const auto &[_name, ptr] : map)
|
||||
{
|
||||
vec.emplace_back(ptr);
|
||||
}
|
||||
return makeEmoteMessage(std::move(vec), emoteFlag);
|
||||
}
|
||||
|
||||
auto makeEmojiMessage(const std::vector<EmojiPtr> &emojiMap)
|
||||
{
|
||||
MessageBuilder builder;
|
||||
@@ -94,77 +113,46 @@ auto makeEmojiMessage(const std::vector<EmojiPtr> &emojiMap)
|
||||
return builder.release();
|
||||
}
|
||||
|
||||
void addTwitchEmoteSets(
|
||||
std::vector<std::shared_ptr<TwitchAccount::EmoteSet>> sets,
|
||||
Channel &globalChannel, Channel &subChannel, QString currentChannelName)
|
||||
void addEmotes(Channel &channel, auto emotes, const QString &title,
|
||||
const MessageElementFlag &emoteFlag)
|
||||
{
|
||||
QMap<QString, QPair<bool, std::vector<MessagePtr>>> mapOfSets;
|
||||
channel.addMessage(makeTitleMessage(title), MessageContext::Original);
|
||||
channel.addMessage(makeEmoteMessage(emotes, emoteFlag),
|
||||
MessageContext::Original);
|
||||
}
|
||||
|
||||
for (const auto &set : sets)
|
||||
void addTwitchEmoteSets(const std::shared_ptr<const EmoteMap> &local,
|
||||
const std::shared_ptr<const TwitchEmoteSetMap> &sets,
|
||||
Channel &globalChannel, Channel &subChannel,
|
||||
const QString ¤tChannelID,
|
||||
const QString &channelName)
|
||||
{
|
||||
if (!local->empty())
|
||||
{
|
||||
// Some emotes (e.g. follower ones) are only available in their origin channel
|
||||
if (set->local && currentChannelName != set->channelName)
|
||||
addEmotes(subChannel, *local, channelName % u" (follower)",
|
||||
MessageElementFlag::TwitchEmote);
|
||||
}
|
||||
|
||||
// Put current channel emotes at the top
|
||||
for (const auto &[_id, set] : *sets)
|
||||
{
|
||||
if (set.owner->id == currentChannelID)
|
||||
{
|
||||
addEmotes(subChannel, set.emotes, set.title(),
|
||||
MessageElementFlag::TwitchEmote);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &[id, set] : *sets)
|
||||
{
|
||||
if (set.owner->id == currentChannelID)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// TITLE
|
||||
auto channelName = set->channelName;
|
||||
auto text = set->text.isEmpty() ? "Twitch" : set->text;
|
||||
|
||||
// EMOTES
|
||||
MessageBuilder builder;
|
||||
builder->flags.set(MessageFlag::Centered);
|
||||
builder->flags.set(MessageFlag::DisableCompactEmotes);
|
||||
|
||||
// If value of map is empty, create init pair and add title.
|
||||
if (mapOfSets.find(channelName) == mapOfSets.end())
|
||||
{
|
||||
std::vector<MessagePtr> b;
|
||||
b.push_back(makeTitleMessage(text));
|
||||
mapOfSets[channelName] = qMakePair(set->key == "0", b);
|
||||
}
|
||||
|
||||
for (const auto &emote : set->emotes)
|
||||
{
|
||||
builder
|
||||
.emplace<EmoteElement>(
|
||||
getApp()->getEmotes()->getTwitchEmotes()->getOrCreateEmote(
|
||||
emote.id, emote.name),
|
||||
MessageElementFlags{MessageElementFlag::AlwaysShow,
|
||||
MessageElementFlag::TwitchEmote})
|
||||
->setLink(Link(Link::InsertText, emote.name.string));
|
||||
}
|
||||
|
||||
mapOfSets[channelName].second.push_back(builder.release());
|
||||
addEmotes(set.isSubLike ? subChannel : globalChannel, set.emotes,
|
||||
set.title(), MessageElementFlag::TwitchEmote);
|
||||
}
|
||||
|
||||
// Output to channel all created messages,
|
||||
// That contain title or emotes.
|
||||
// Put current channel emotes at the top
|
||||
auto currentChannelPair = mapOfSets[currentChannelName];
|
||||
for (const auto &message : currentChannelPair.second)
|
||||
{
|
||||
subChannel.addMessage(message, MessageContext::Original);
|
||||
}
|
||||
mapOfSets.remove(currentChannelName);
|
||||
|
||||
for (const auto &pair : mapOfSets)
|
||||
{
|
||||
auto &channel = pair.first ? globalChannel : subChannel;
|
||||
for (const auto &message : pair.second)
|
||||
{
|
||||
channel.addMessage(message, MessageContext::Original);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addEmotes(Channel &channel, const EmoteMap &map, const QString &title,
|
||||
const MessageElementFlag &emoteFlag)
|
||||
{
|
||||
channel.addMessage(makeTitleMessage(title), MessageContext::Original);
|
||||
channel.addMessage(makeEmoteMessage(map, emoteFlag),
|
||||
MessageContext::Original);
|
||||
}
|
||||
|
||||
void loadEmojis(ChannelView &view, const std::vector<EmojiPtr> &emojiMap)
|
||||
@@ -200,6 +188,22 @@ EmoteMap filterEmoteMap(const QString &text,
|
||||
return filteredMap;
|
||||
}
|
||||
|
||||
std::vector<EmotePtr> filterEmoteVec(const QString &text,
|
||||
const std::vector<EmotePtr> &emotes)
|
||||
{
|
||||
std::vector<EmotePtr> filtered;
|
||||
|
||||
for (const auto &emote : emotes)
|
||||
{
|
||||
if (emote->name.string.contains(text, Qt::CaseInsensitive))
|
||||
{
|
||||
filtered.emplace_back(emote);
|
||||
}
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace chatterino {
|
||||
@@ -404,8 +408,10 @@ void EmotePopup::loadChannel(ChannelPtr channel)
|
||||
|
||||
// twitch
|
||||
addTwitchEmoteSets(
|
||||
getApp()->getAccounts()->twitch.getCurrent()->accessEmotes()->emoteSets,
|
||||
*globalChannel, *subChannel, this->channel_->getName());
|
||||
twitchChannel_->localTwitchEmotes(),
|
||||
*getApp()->getAccounts()->twitch.getCurrent()->accessEmoteSets(),
|
||||
*globalChannel, *subChannel, twitchChannel_->roomId(),
|
||||
twitchChannel_->getName());
|
||||
|
||||
// global
|
||||
if (Settings::instance().enableBTTVGlobalEmotes)
|
||||
@@ -475,24 +481,22 @@ bool EmotePopup::eventFilter(QObject *object, QEvent *event)
|
||||
void EmotePopup::filterTwitchEmotes(std::shared_ptr<Channel> searchChannel,
|
||||
const QString &searchText)
|
||||
{
|
||||
auto twitchEmoteSets =
|
||||
getApp()->getAccounts()->twitch.getCurrent()->accessEmotes()->emoteSets;
|
||||
std::vector<std::shared_ptr<TwitchAccount::EmoteSet>> twitchGlobalEmotes{};
|
||||
|
||||
for (const auto &set : twitchEmoteSets)
|
||||
if (this->twitchChannel_)
|
||||
{
|
||||
auto setCopy = std::make_shared<TwitchAccount::EmoteSet>(*set);
|
||||
auto setIt =
|
||||
std::remove_if(setCopy->emotes.begin(), setCopy->emotes.end(),
|
||||
[searchText](auto &emote) {
|
||||
return !emote.name.string.contains(
|
||||
searchText, Qt::CaseInsensitive);
|
||||
});
|
||||
setCopy->emotes.resize(std::distance(setCopy->emotes.begin(), setIt));
|
||||
|
||||
if (!setCopy->emotes.empty())
|
||||
auto local = filterEmoteMap(searchText,
|
||||
this->twitchChannel_->localTwitchEmotes());
|
||||
if (!local.empty())
|
||||
{
|
||||
twitchGlobalEmotes.push_back(setCopy);
|
||||
addEmotes(*searchChannel, local,
|
||||
this->twitchChannel_->getName() % u" (local)",
|
||||
MessageElementFlag::TwitchEmote);
|
||||
}
|
||||
|
||||
for (const auto &[_id, set] :
|
||||
**getApp()->getAccounts()->twitch.getCurrent()->accessEmoteSets())
|
||||
{
|
||||
addEmotes(*searchChannel, filterEmoteVec(searchText, set.emotes),
|
||||
set.title(), MessageElementFlag::TwitchEmote);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -503,10 +507,6 @@ void EmotePopup::filterTwitchEmotes(std::shared_ptr<Channel> searchChannel,
|
||||
auto seventvGlobalEmotes = filterEmoteMap(
|
||||
searchText, getApp()->getSeventvEmotes()->globalEmotes());
|
||||
|
||||
// twitch
|
||||
addTwitchEmoteSets(twitchGlobalEmotes, *searchChannel, *searchChannel,
|
||||
this->channel_->getName());
|
||||
|
||||
// global
|
||||
if (!bttvGlobalEmotes.empty())
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user