Added support for Twitch's Chat Replies (#3722)

Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
Daniel Sage
2022-07-31 06:45:25 -04:00
committed by GitHub
parent a280089693
commit 20c974fdab
53 changed files with 2022 additions and 310 deletions
+147 -47
View File
@@ -48,6 +48,66 @@ const QSet<QString> zeroWidthEmotes{
namespace chatterino {
namespace {
QString stylizeUsername(const QString &username, const Message &message)
{
auto app = getApp();
const QString &localizedName = message.localizedName;
bool hasLocalizedName = !localizedName.isEmpty();
// The full string that will be rendered in the chat widget
QString usernameText;
switch (getSettings()->usernameDisplayMode.getValue())
{
case UsernameDisplayMode::Username: {
usernameText = username;
}
break;
case UsernameDisplayMode::LocalizedName: {
if (hasLocalizedName)
{
usernameText = localizedName;
}
else
{
usernameText = username;
}
}
break;
default:
case UsernameDisplayMode::UsernameAndLocalizedName: {
if (hasLocalizedName)
{
usernameText = username + "(" + localizedName + ")";
}
else
{
usernameText = username;
}
}
break;
}
auto nicknames = getCSettings().nicknames.readOnly();
for (const auto &nickname : *nicknames)
{
if (nickname.match(usernameText))
{
break;
}
}
return usernameText;
}
} // namespace
TwitchMessageBuilder::TwitchMessageBuilder(
Channel *_channel, const Communi::IrcPrivateMessage *_ircMessage,
const MessageParseArgs &_args)
@@ -138,6 +198,70 @@ MessagePtr TwitchMessageBuilder::build()
this->message().flags.set(MessageFlag::FirstMessage);
}
// reply threads
if (this->thread_)
{
// set references
this->message().replyThread = this->thread_;
this->thread_->addToThread(this->weakOf());
// enable reply flag
this->message().flags.set(MessageFlag::ReplyMessage);
const auto &threadRoot = this->thread_->root();
QString usernameText =
stylizeUsername(threadRoot->loginName, *threadRoot.get());
this->emplace<ReplyCurveElement>();
// construct reply elements
this->emplace<TextElement>(
"Replying to", MessageElementFlag::RepliedMessage,
MessageColor::System, FontStyle::ChatMediumSmall)
->setLink({Link::ViewThread, this->thread_->rootId()});
this->emplace<TextElement>(
"@" + usernameText + ":", MessageElementFlag::RepliedMessage,
threadRoot->usernameColor, FontStyle::ChatMediumSmall)
->setLink({Link::UserInfo, threadRoot->displayName});
this->emplace<SingleLineTextElement>(
threadRoot->messageText, MessageElementFlag::RepliedMessage,
this->textColor_, FontStyle::ChatMediumSmall)
->setLink({Link::ViewThread, this->thread_->rootId()});
}
else if (this->tags.find("reply-parent-msg-id") != this->tags.end())
{
// Message is a reply but we couldn't find the original message.
// Render the message using the additional reply tags
auto replyDisplayName = this->tags.find("reply-parent-display-name");
auto replyBody = this->tags.find("reply-parent-msg-body");
if (replyDisplayName != this->tags.end() &&
replyBody != this->tags.end())
{
auto name = replyDisplayName->toString();
auto body = parseTagString(replyBody->toString());
this->emplace<ReplyCurveElement>();
this->emplace<TextElement>(
"Replying to", MessageElementFlag::RepliedMessage,
MessageColor::System, FontStyle::ChatMediumSmall);
this->emplace<TextElement>(
"@" + name + ":", MessageElementFlag::RepliedMessage,
this->textColor_, FontStyle::ChatMediumSmall)
->setLink({Link::UserInfo, name});
this->emplace<SingleLineTextElement>(
body, MessageElementFlag::RepliedMessage, this->textColor_,
FontStyle::ChatMediumSmall);
}
}
// timestamp
this->message().serverReceivedTime = calculateMessageTime(this->ircMessage);
this->emplace<TimestampElement>(this->message().serverReceivedTime.time());
@@ -217,6 +341,23 @@ MessagePtr TwitchMessageBuilder::build()
ColorProvider::instance().color(ColorType::Whisper);
}
if (this->thread_)
{
auto &img = getResources().buttons.replyThreadDark;
this->emplace<CircularImageElement>(Image::fromPixmap(img, 0.15), 2,
Qt::gray,
MessageElementFlag::ReplyButton)
->setLink({Link::ViewThread, this->thread_->rootId()});
}
else
{
auto &img = getResources().buttons.replyDark;
this->emplace<CircularImageElement>(Image::fromPixmap(img, 0.15), 2,
Qt::gray,
MessageElementFlag::ReplyButton)
->setLink({Link::ReplyToMessage, this->message().id});
}
return this->release();
}
@@ -559,53 +700,7 @@ void TwitchMessageBuilder::appendUsername()
}
}
bool hasLocalizedName = !localizedName.isEmpty();
// The full string that will be rendered in the chat widget
QString usernameText;
switch (getSettings()->usernameDisplayMode.getValue())
{
case UsernameDisplayMode::Username: {
usernameText = username;
}
break;
case UsernameDisplayMode::LocalizedName: {
if (hasLocalizedName)
{
usernameText = localizedName;
}
else
{
usernameText = username;
}
}
break;
default:
case UsernameDisplayMode::UsernameAndLocalizedName: {
if (hasLocalizedName)
{
usernameText = username + "(" + localizedName + ")";
}
else
{
usernameText = username;
}
}
break;
}
auto nicknames = getCSettings().nicknames.readOnly();
for (const auto &nickname : *nicknames)
{
if (nickname.match(usernameText))
{
break;
}
}
QString usernameText = stylizeUsername(username, this->message());
if (this->args.isSentWhisper)
{
@@ -1479,4 +1574,9 @@ void TwitchMessageBuilder::listOfUsersSystemMessage(QString prefix,
}
}
void TwitchMessageBuilder::setThread(std::shared_ptr<MessageThread> thread)
{
this->thread_ = std::move(thread);
}
} // namespace chatterino