feat: Live Emote Updates for 7TV (#4090)
Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
@@ -46,4 +46,23 @@ EmotePtr cachedOrMakeEmotePtr(
|
||||
}
|
||||
}
|
||||
|
||||
EmoteMap::const_iterator EmoteMap::findEmote(const QString &emoteNameHint,
|
||||
const QString &emoteID) const
|
||||
{
|
||||
auto it = this->end();
|
||||
if (!emoteNameHint.isEmpty())
|
||||
{
|
||||
it = this->find(EmoteName{emoteNameHint});
|
||||
}
|
||||
|
||||
if (it == this->end() || it->second->id.string != emoteID)
|
||||
{
|
||||
it = std::find_if(this->begin(), this->end(),
|
||||
[emoteID](const auto entry) {
|
||||
return entry.second->id.string == emoteID;
|
||||
});
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/Atomic.hpp"
|
||||
#include "messages/Image.hpp"
|
||||
#include "messages/ImageSet.hpp"
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
@@ -15,6 +17,13 @@ struct Emote {
|
||||
Tooltip tooltip;
|
||||
Url homePage;
|
||||
bool zeroWidth;
|
||||
EmoteId id;
|
||||
EmoteAuthor author;
|
||||
/**
|
||||
* If this emote is aliased, this contains
|
||||
* the original (base) name of the emote.
|
||||
*/
|
||||
boost::optional<EmoteName> baseName;
|
||||
|
||||
// FOURTF: no solution yet, to be refactored later
|
||||
const QString &getCopyString() const
|
||||
@@ -30,6 +39,20 @@ using EmotePtr = std::shared_ptr<const Emote>;
|
||||
|
||||
class EmoteMap : public std::unordered_map<EmoteName, EmotePtr>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Finds an emote by it's id with a hint to it's name.
|
||||
*
|
||||
* 1. Searches by name for the emote, checking if the ids match (fast-path).
|
||||
* 2. Searches through the map for an emote with the `emoteID` (slow-path).
|
||||
*
|
||||
* @param emoteNameHint A hint to the name of the searched emote,
|
||||
* may be empty.
|
||||
* @param emoteID The emote id to search for.
|
||||
* @return An iterator to the found emote (possibly this->end()).
|
||||
*/
|
||||
EmoteMap::const_iterator findEmote(const QString &emoteNameHint,
|
||||
const QString &emoteID) const;
|
||||
};
|
||||
using EmoteIdMap = std::unordered_map<EmoteId, EmotePtr>;
|
||||
using WeakEmoteMap = std::unordered_map<EmoteName, std::weak_ptr<const Emote>>;
|
||||
|
||||
@@ -45,6 +45,9 @@ enum class MessageFlag : int64_t {
|
||||
ElevatedMessage = (1LL << 25),
|
||||
ParticipatedThread = (1LL << 26),
|
||||
CheerMessage = (1LL << 27),
|
||||
LiveUpdatesAdd = (1LL << 28),
|
||||
LiveUpdatesRemove = (1LL << 29),
|
||||
LiveUpdatesUpdate = (1LL << 30),
|
||||
};
|
||||
using MessageFlags = FlagsEnum<MessageFlag>;
|
||||
|
||||
|
||||
@@ -23,6 +23,45 @@ QRegularExpression IRC_COLOR_PARSE_REGEX(
|
||||
"(\u0003(\\d{1,2})?(,(\\d{1,2}))?|\u000f)",
|
||||
QRegularExpression::UseUnicodePropertiesOption);
|
||||
|
||||
QString formatUpdatedEmoteList(const QString &platform,
|
||||
const std::vector<QString> &emoteNames,
|
||||
bool isAdd, bool isFirstWord)
|
||||
{
|
||||
QString text = "";
|
||||
if (isAdd)
|
||||
{
|
||||
text += isFirstWord ? "Added" : "added";
|
||||
}
|
||||
else
|
||||
{
|
||||
text += isFirstWord ? "Removed" : "removed";
|
||||
}
|
||||
|
||||
if (emoteNames.size() == 1)
|
||||
{
|
||||
text += QString(" %1 emote ").arg(platform);
|
||||
}
|
||||
else
|
||||
{
|
||||
text += QString(" %1 %2 emotes ").arg(emoteNames.size()).arg(platform);
|
||||
}
|
||||
|
||||
auto i = 0;
|
||||
for (const auto &emoteName : emoteNames)
|
||||
{
|
||||
i++;
|
||||
if (i > 1)
|
||||
{
|
||||
text += i == emoteNames.size() ? " and " : ", ";
|
||||
}
|
||||
text += emoteName;
|
||||
}
|
||||
|
||||
text += ".";
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace chatterino {
|
||||
@@ -473,6 +512,133 @@ MessageBuilder::MessageBuilder(const AutomodUserAction &action)
|
||||
MessageColor::System);
|
||||
}
|
||||
|
||||
MessageBuilder::MessageBuilder(LiveUpdatesAddEmoteMessageTag /*unused*/,
|
||||
const QString &platform, const QString &actor,
|
||||
const std::vector<QString> &emoteNames)
|
||||
: MessageBuilder()
|
||||
{
|
||||
auto text =
|
||||
formatUpdatedEmoteList(platform, emoteNames, true, actor.isEmpty());
|
||||
|
||||
this->emplace<TimestampElement>();
|
||||
if (!actor.isEmpty())
|
||||
{
|
||||
this->emplace<TextElement>(actor, MessageElementFlag::Username,
|
||||
MessageColor::System)
|
||||
->setLink({Link::UserInfo, actor});
|
||||
}
|
||||
this->emplace<TextElement>(text, MessageElementFlag::Text,
|
||||
MessageColor::System);
|
||||
|
||||
QString finalText;
|
||||
if (actor.isEmpty())
|
||||
{
|
||||
finalText = text;
|
||||
}
|
||||
else
|
||||
{
|
||||
finalText = QString("%1 %2").arg(actor, text);
|
||||
}
|
||||
|
||||
this->message().loginName = actor;
|
||||
this->message().messageText = finalText;
|
||||
this->message().searchText = finalText;
|
||||
|
||||
this->message().flags.set(MessageFlag::System);
|
||||
this->message().flags.set(MessageFlag::LiveUpdatesAdd);
|
||||
this->message().flags.set(MessageFlag::DoNotTriggerNotification);
|
||||
}
|
||||
|
||||
MessageBuilder::MessageBuilder(LiveUpdatesRemoveEmoteMessageTag /*unused*/,
|
||||
const QString &platform, const QString &actor,
|
||||
const std::vector<QString> &emoteNames)
|
||||
: MessageBuilder()
|
||||
{
|
||||
auto text =
|
||||
formatUpdatedEmoteList(platform, emoteNames, false, actor.isEmpty());
|
||||
|
||||
this->emplace<TimestampElement>();
|
||||
if (!actor.isEmpty())
|
||||
{
|
||||
this->emplace<TextElement>(actor, MessageElementFlag::Username,
|
||||
MessageColor::System)
|
||||
->setLink({Link::UserInfo, actor});
|
||||
}
|
||||
this->emplace<TextElement>(text, MessageElementFlag::Text,
|
||||
MessageColor::System);
|
||||
|
||||
QString finalText;
|
||||
if (actor.isEmpty())
|
||||
{
|
||||
finalText = text;
|
||||
}
|
||||
else
|
||||
{
|
||||
finalText = QString("%1 %2").arg(actor, text);
|
||||
}
|
||||
|
||||
this->message().loginName = actor;
|
||||
this->message().messageText = finalText;
|
||||
this->message().searchText = finalText;
|
||||
|
||||
this->message().flags.set(MessageFlag::System);
|
||||
this->message().flags.set(MessageFlag::LiveUpdatesRemove);
|
||||
this->message().flags.set(MessageFlag::DoNotTriggerNotification);
|
||||
}
|
||||
|
||||
MessageBuilder::MessageBuilder(LiveUpdatesUpdateEmoteMessageTag /*unused*/,
|
||||
const QString &platform, const QString &actor,
|
||||
const QString &emoteName,
|
||||
const QString &oldEmoteName)
|
||||
: MessageBuilder()
|
||||
{
|
||||
auto text = QString("renamed %1 emote %2 to %3.")
|
||||
.arg(platform, oldEmoteName, emoteName);
|
||||
|
||||
this->emplace<TimestampElement>();
|
||||
this->emplace<TextElement>(actor, MessageElementFlag::Username,
|
||||
MessageColor::System)
|
||||
->setLink({Link::UserInfo, actor});
|
||||
this->emplace<TextElement>(text, MessageElementFlag::Text,
|
||||
MessageColor::System);
|
||||
|
||||
auto finalText = QString("%1 %2").arg(actor, text);
|
||||
|
||||
this->message().loginName = actor;
|
||||
this->message().messageText = finalText;
|
||||
this->message().searchText = finalText;
|
||||
|
||||
this->message().flags.set(MessageFlag::System);
|
||||
this->message().flags.set(MessageFlag::LiveUpdatesUpdate);
|
||||
this->message().flags.set(MessageFlag::DoNotTriggerNotification);
|
||||
}
|
||||
|
||||
MessageBuilder::MessageBuilder(LiveUpdatesUpdateEmoteSetMessageTag /*unused*/,
|
||||
const QString &platform, const QString &actor,
|
||||
const QString &emoteSetName)
|
||||
: MessageBuilder()
|
||||
{
|
||||
auto text = QString("switched the active %1 Emote Set to \"%2\".")
|
||||
.arg(platform, emoteSetName);
|
||||
|
||||
this->emplace<TimestampElement>();
|
||||
this->emplace<TextElement>(actor, MessageElementFlag::Username,
|
||||
MessageColor::System)
|
||||
->setLink({Link::UserInfo, actor});
|
||||
this->emplace<TextElement>(text, MessageElementFlag::Text,
|
||||
MessageColor::System);
|
||||
|
||||
auto finalText = QString("%1 %2").arg(actor, text);
|
||||
|
||||
this->message().loginName = actor;
|
||||
this->message().messageText = finalText;
|
||||
this->message().searchText = finalText;
|
||||
|
||||
this->message().flags.set(MessageFlag::System);
|
||||
this->message().flags.set(MessageFlag::LiveUpdatesUpdate);
|
||||
this->message().flags.set(MessageFlag::DoNotTriggerNotification);
|
||||
}
|
||||
|
||||
Message *MessageBuilder::operator->()
|
||||
{
|
||||
return this->message_.get();
|
||||
|
||||
@@ -19,8 +19,20 @@ struct SystemMessageTag {
|
||||
};
|
||||
struct TimeoutMessageTag {
|
||||
};
|
||||
struct LiveUpdatesUpdateEmoteMessageTag {
|
||||
};
|
||||
struct LiveUpdatesRemoveEmoteMessageTag {
|
||||
};
|
||||
struct LiveUpdatesAddEmoteMessageTag {
|
||||
};
|
||||
struct LiveUpdatesUpdateEmoteSetMessageTag {
|
||||
};
|
||||
const SystemMessageTag systemMessage{};
|
||||
const TimeoutMessageTag timeoutMessage{};
|
||||
const LiveUpdatesUpdateEmoteMessageTag liveUpdatesUpdateEmoteMessage{};
|
||||
const LiveUpdatesRemoveEmoteMessageTag liveUpdatesRemoveEmoteMessage{};
|
||||
const LiveUpdatesAddEmoteMessageTag liveUpdatesAddEmoteMessage{};
|
||||
const LiveUpdatesUpdateEmoteSetMessageTag liveUpdatesUpdateEmoteSetMessage{};
|
||||
|
||||
MessagePtr makeSystemMessage(const QString &text);
|
||||
MessagePtr makeSystemMessage(const QString &text, const QTime &time);
|
||||
@@ -53,6 +65,19 @@ public:
|
||||
MessageBuilder(const BanAction &action, uint32_t count = 1);
|
||||
MessageBuilder(const UnbanAction &action);
|
||||
MessageBuilder(const AutomodUserAction &action);
|
||||
|
||||
MessageBuilder(LiveUpdatesAddEmoteMessageTag, const QString &platform,
|
||||
const QString &actor,
|
||||
const std::vector<QString> &emoteNames);
|
||||
MessageBuilder(LiveUpdatesRemoveEmoteMessageTag, const QString &platform,
|
||||
const QString &actor,
|
||||
const std::vector<QString> &emoteNames);
|
||||
MessageBuilder(LiveUpdatesUpdateEmoteMessageTag, const QString &platform,
|
||||
const QString &actor, const QString &emoteName,
|
||||
const QString &oldEmoteName);
|
||||
MessageBuilder(LiveUpdatesUpdateEmoteSetMessageTag, const QString &platform,
|
||||
const QString &actor, const QString &emoteSetName);
|
||||
|
||||
virtual ~MessageBuilder() = default;
|
||||
|
||||
Message *operator->();
|
||||
|
||||
Reference in New Issue
Block a user