Open usercard on mention click (#1674)
This commit is contained in:
@@ -148,9 +148,8 @@ void Channel::addOrReplaceTimeout(MessagePtr message)
|
||||
|
||||
int count = s->count + 1;
|
||||
|
||||
MessageBuilder replacement(systemMessage,
|
||||
message->searchText + QString(" (") +
|
||||
QString::number(count) + " times)");
|
||||
MessageBuilder replacement(timeoutMessage, message->searchText,
|
||||
count);
|
||||
|
||||
replacement->timeoutUser = message->timeoutUser;
|
||||
replacement->count = count;
|
||||
|
||||
@@ -63,6 +63,11 @@ void UsernameSet::insertPrefix(const QString &value)
|
||||
string = value;
|
||||
}
|
||||
|
||||
bool UsernameSet::contains(const QString &value) const
|
||||
{
|
||||
return this->items.count(value) == 1;
|
||||
}
|
||||
|
||||
//
|
||||
// Range
|
||||
//
|
||||
|
||||
@@ -76,6 +76,8 @@ public:
|
||||
std::pair<Iterator, bool> insert(const QString &value);
|
||||
std::pair<Iterator, bool> insert(QString &&value);
|
||||
|
||||
bool contains(const QString &value) const;
|
||||
|
||||
private:
|
||||
void insertPrefix(const QString &string);
|
||||
|
||||
|
||||
@@ -24,6 +24,11 @@ MessagePtr makeSystemMessage(const QString &text)
|
||||
return MessageBuilder(systemMessage, text).release();
|
||||
}
|
||||
|
||||
MessagePtr makeSystemMessage(const QString &text, const QTime &time)
|
||||
{
|
||||
return MessageBuilder(systemMessage, text, time).release();
|
||||
}
|
||||
|
||||
std::pair<MessagePtr, MessagePtr> makeAutomodMessage(
|
||||
const AutomodAction &action)
|
||||
{
|
||||
@@ -93,10 +98,11 @@ MessageBuilder::MessageBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
MessageBuilder::MessageBuilder(SystemMessageTag, const QString &text)
|
||||
MessageBuilder::MessageBuilder(SystemMessageTag, const QString &text,
|
||||
const QTime &time)
|
||||
: MessageBuilder()
|
||||
{
|
||||
this->emplace<TimestampElement>();
|
||||
this->emplace<TimestampElement>(time);
|
||||
|
||||
// check system message for links
|
||||
// (e.g. needed for sub ticket message in sub only mode)
|
||||
@@ -120,17 +126,40 @@ MessageBuilder::MessageBuilder(SystemMessageTag, const QString &text)
|
||||
this->message().searchText = text;
|
||||
}
|
||||
|
||||
MessageBuilder::MessageBuilder(TimeoutMessageTag,
|
||||
const QString &systemMessageText, int times)
|
||||
: MessageBuilder()
|
||||
{
|
||||
QString username = systemMessageText.split(" ").at(0);
|
||||
QString remainder = systemMessageText.mid(username.length() + 1);
|
||||
|
||||
QString text;
|
||||
|
||||
this->emplace<TimestampElement>();
|
||||
this->emplaceSystemTextAndUpdate(username, text)
|
||||
->setLink({Link::UserInfo, username});
|
||||
this->emplaceSystemTextAndUpdate(
|
||||
QString("%1 (%2 times)").arg(remainder.trimmed()).arg(times), text);
|
||||
|
||||
this->message().messageText = text;
|
||||
this->message().searchText = text;
|
||||
}
|
||||
|
||||
MessageBuilder::MessageBuilder(TimeoutMessageTag, const QString &username,
|
||||
const QString &durationInSeconds,
|
||||
const QString &reason, bool multipleTimes)
|
||||
: MessageBuilder()
|
||||
{
|
||||
QString fullText;
|
||||
QString text;
|
||||
|
||||
text.append(username);
|
||||
this->emplace<TimestampElement>();
|
||||
this->emplaceSystemTextAndUpdate(username, fullText)
|
||||
->setLink({Link::UserInfo, username});
|
||||
|
||||
if (!durationInSeconds.isEmpty())
|
||||
{
|
||||
text.append(" has been timed out");
|
||||
text.append("has been timed out");
|
||||
|
||||
// TODO: Implement who timed the user out
|
||||
|
||||
@@ -144,7 +173,7 @@ MessageBuilder::MessageBuilder(TimeoutMessageTag, const QString &username,
|
||||
}
|
||||
else
|
||||
{
|
||||
text.append(" has been permanently banned");
|
||||
text.append("has been permanently banned");
|
||||
}
|
||||
|
||||
if (reason.length() > 0)
|
||||
@@ -164,11 +193,10 @@ MessageBuilder::MessageBuilder(TimeoutMessageTag, const QString &username,
|
||||
this->message().flags.set(MessageFlag::Timeout);
|
||||
this->message().flags.set(MessageFlag::DoNotTriggerNotification);
|
||||
this->message().timeoutUser = username;
|
||||
this->emplace<TimestampElement>();
|
||||
this->emplace<TextElement>(text, MessageElementFlag::Text,
|
||||
MessageColor::System);
|
||||
this->message().messageText = text;
|
||||
this->message().searchText = text;
|
||||
|
||||
this->emplaceSystemTextAndUpdate(text, fullText);
|
||||
this->message().messageText = fullText;
|
||||
this->message().searchText = fullText;
|
||||
}
|
||||
|
||||
// XXX: This does not belong in the MessageBuilder, this should be part of the TwitchMessageBuilder
|
||||
@@ -187,77 +215,82 @@ MessageBuilder::MessageBuilder(const BanAction &action, uint32_t count)
|
||||
|
||||
if (action.target.id == current->getUserId())
|
||||
{
|
||||
text.append("You were ");
|
||||
this->emplaceSystemTextAndUpdate("You were", text);
|
||||
if (action.isBan())
|
||||
{
|
||||
text.append("banned");
|
||||
this->emplaceSystemTextAndUpdate("banned", text);
|
||||
}
|
||||
else
|
||||
{
|
||||
text.append(
|
||||
QString("timed out for %1").arg(formatTime(action.duration)));
|
||||
this->emplaceSystemTextAndUpdate(
|
||||
QString("timed out for %1").arg(formatTime(action.duration)),
|
||||
text);
|
||||
}
|
||||
|
||||
if (!action.source.name.isEmpty())
|
||||
{
|
||||
text.append(" by ");
|
||||
text.append(action.source.name);
|
||||
this->emplaceSystemTextAndUpdate("by", text);
|
||||
this->emplaceSystemTextAndUpdate(
|
||||
action.source.name + (action.reason.isEmpty() ? "." : ":"),
|
||||
text)
|
||||
->setLink({Link::UserInfo, action.source.name});
|
||||
}
|
||||
|
||||
if (action.reason.isEmpty())
|
||||
if (!action.reason.isEmpty())
|
||||
{
|
||||
text.append(".");
|
||||
}
|
||||
else
|
||||
{
|
||||
text.append(QString(": \"%1\".").arg(action.reason));
|
||||
this->emplaceSystemTextAndUpdate(
|
||||
QString("\"%1\".").arg(action.reason), text);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (action.isBan())
|
||||
{
|
||||
this->emplaceSystemTextAndUpdate(action.source.name, text)
|
||||
->setLink({Link::UserInfo, action.source.name});
|
||||
this->emplaceSystemTextAndUpdate("banned", text);
|
||||
if (action.reason.isEmpty())
|
||||
{
|
||||
text = QString("%1 banned %2.") //
|
||||
.arg(action.source.name)
|
||||
.arg(action.target.name);
|
||||
this->emplaceSystemTextAndUpdate(action.target.name, text)
|
||||
->setLink({Link::UserInfo, action.target.name});
|
||||
}
|
||||
else
|
||||
{
|
||||
text = QString("%1 banned %2: \"%3\".") //
|
||||
.arg(action.source.name)
|
||||
.arg(action.target.name)
|
||||
.arg(action.reason);
|
||||
this->emplaceSystemTextAndUpdate(action.target.name + ":", text)
|
||||
->setLink({Link::UserInfo, action.target.name});
|
||||
this->emplaceSystemTextAndUpdate(
|
||||
QString("\"%1\".").arg(action.reason), text);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this->emplaceSystemTextAndUpdate(action.source.name, text)
|
||||
->setLink({Link::UserInfo, action.source.name});
|
||||
this->emplaceSystemTextAndUpdate("timed out", text);
|
||||
this->emplaceSystemTextAndUpdate(action.target.name, text)
|
||||
->setLink({Link::UserInfo, action.target.name});
|
||||
if (action.reason.isEmpty())
|
||||
{
|
||||
text = QString("%1 timed out %2 for %3.") //
|
||||
.arg(action.source.name)
|
||||
.arg(action.target.name)
|
||||
.arg(formatTime(action.duration));
|
||||
this->emplaceSystemTextAndUpdate(
|
||||
QString("for %1.").arg(formatTime(action.duration)), text);
|
||||
}
|
||||
else
|
||||
{
|
||||
text = QString("%1 timed out %2 for %3: \"%4\".") //
|
||||
.arg(action.source.name)
|
||||
.arg(action.target.name)
|
||||
.arg(formatTime(action.duration))
|
||||
.arg(action.reason);
|
||||
this->emplaceSystemTextAndUpdate(
|
||||
QString("for %1: \"%2\".")
|
||||
.arg(formatTime(action.duration))
|
||||
.arg(action.reason),
|
||||
text);
|
||||
}
|
||||
|
||||
if (count > 1)
|
||||
{
|
||||
text.append(QString(" (%1 times)").arg(count));
|
||||
this->emplaceSystemTextAndUpdate(
|
||||
QString("(%1 times)").arg(count), text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->emplace<TextElement>(text, MessageElementFlag::Text,
|
||||
MessageColor::System);
|
||||
this->message().messageText = text;
|
||||
this->message().searchText = text;
|
||||
}
|
||||
@@ -271,14 +304,15 @@ MessageBuilder::MessageBuilder(const UnbanAction &action)
|
||||
|
||||
this->message().timeoutUser = action.target.name;
|
||||
|
||||
QString text =
|
||||
QString("%1 %2 %3.")
|
||||
.arg(action.source.name)
|
||||
.arg(QString(action.wasBan() ? "unbanned" : "untimedout"))
|
||||
.arg(action.target.name);
|
||||
QString text;
|
||||
|
||||
this->emplaceSystemTextAndUpdate(action.source.name, text)
|
||||
->setLink({Link::UserInfo, action.source.name});
|
||||
this->emplaceSystemTextAndUpdate(
|
||||
action.wasBan() ? "unbanned" : "untimedout", text);
|
||||
this->emplaceSystemTextAndUpdate(action.target.name, text)
|
||||
->setLink({Link::UserInfo, action.target.name});
|
||||
|
||||
this->emplace<TextElement>(text, MessageElementFlag::Text,
|
||||
MessageColor::System);
|
||||
this->message().messageText = text;
|
||||
this->message().searchText = text;
|
||||
}
|
||||
@@ -446,4 +480,12 @@ void MessageBuilder::addLink(const QString &origLink,
|
||||
});
|
||||
}
|
||||
|
||||
TextElement *MessageBuilder::emplaceSystemTextAndUpdate(const QString &text,
|
||||
QString &toUpdate)
|
||||
{
|
||||
toUpdate.append(text + " ");
|
||||
return this->emplace<TextElement>(text, MessageElementFlag::Text,
|
||||
MessageColor::System);
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
||||
@@ -22,6 +22,7 @@ const SystemMessageTag systemMessage{};
|
||||
const TimeoutMessageTag timeoutMessage{};
|
||||
|
||||
MessagePtr makeSystemMessage(const QString &text);
|
||||
MessagePtr makeSystemMessage(const QString &text, const QTime &time);
|
||||
std::pair<MessagePtr, MessagePtr> makeAutomodMessage(
|
||||
const AutomodAction &action);
|
||||
|
||||
@@ -37,7 +38,10 @@ class MessageBuilder
|
||||
{
|
||||
public:
|
||||
MessageBuilder();
|
||||
MessageBuilder(SystemMessageTag, const QString &text);
|
||||
MessageBuilder(SystemMessageTag, const QString &text,
|
||||
const QTime &time = QTime::currentTime());
|
||||
MessageBuilder(TimeoutMessageTag, const QString &systemMessageText,
|
||||
int times);
|
||||
MessageBuilder(TimeoutMessageTag, const QString &username,
|
||||
const QString &durationInSeconds, const QString &reason,
|
||||
bool multipleTimes);
|
||||
@@ -67,6 +71,12 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
// Helper method that emplaces some text stylized as system text
|
||||
// and then appends that text to the QString parameter "toUpdate".
|
||||
// Returns the TextElement that was emplaced.
|
||||
TextElement *emplaceSystemTextAndUpdate(const QString &text,
|
||||
QString &toUpdate);
|
||||
|
||||
std::shared_ptr<Message> message_;
|
||||
};
|
||||
|
||||
|
||||
@@ -634,7 +634,25 @@ std::vector<MessagePtr> IrcMessageHandler::parseNoticeMessage(
|
||||
{
|
||||
std::vector<MessagePtr> builtMessages;
|
||||
|
||||
builtMessages.emplace_back(makeSystemMessage(message->content()));
|
||||
if (message->tags().contains("historical"))
|
||||
{
|
||||
bool customReceived = false;
|
||||
qint64 ts = message->tags()
|
||||
.value("rm-received-ts")
|
||||
.toLongLong(&customReceived);
|
||||
if (!customReceived)
|
||||
{
|
||||
ts = message->tags().value("tmi-sent-ts").toLongLong();
|
||||
}
|
||||
|
||||
QDateTime dateTime = QDateTime::fromMSecsSinceEpoch(ts);
|
||||
builtMessages.emplace_back(
|
||||
makeSystemMessage(message->content(), dateTime.time()));
|
||||
}
|
||||
else
|
||||
{
|
||||
builtMessages.emplace_back(makeSystemMessage(message->content()));
|
||||
}
|
||||
|
||||
return builtMessages;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
|
||||
namespace {
|
||||
|
||||
// matches a mention with punctuation at the end, like "@username," or "@username!!!" where capture group would return "username"
|
||||
const QRegularExpression mentionRegex("^@(\\w+)[.,!?;]*?$");
|
||||
|
||||
const QSet<QString> zeroWidthEmotes{
|
||||
"SoSnowy", "IceCold", "SantaHat", "TopHat",
|
||||
"ReinDeer", "CandyCane", "cvMask", "cvHazmat",
|
||||
@@ -407,29 +410,50 @@ void TwitchMessageBuilder::addTextOrEmoji(const QString &string_)
|
||||
|
||||
// Actually just text
|
||||
auto linkString = this->matchLink(string);
|
||||
auto link = Link();
|
||||
auto textColor = this->action_ ? MessageColor(this->usernameColor_)
|
||||
: MessageColor(MessageColor::Text);
|
||||
|
||||
if (linkString.isEmpty())
|
||||
{
|
||||
if (string.startsWith('@'))
|
||||
{
|
||||
this->emplace<TextElement>(string, MessageElementFlag::BoldUsername,
|
||||
textColor, FontStyle::ChatMediumBold);
|
||||
this->emplace<TextElement>(
|
||||
string, MessageElementFlag::NonBoldUsername, textColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->emplace<TextElement>(string, MessageElementFlag::Text,
|
||||
textColor);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!linkString.isEmpty())
|
||||
{
|
||||
this->addLink(string, linkString);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.startsWith('@'))
|
||||
{
|
||||
auto match = mentionRegex.match(string);
|
||||
// Only treat as @mention if valid username
|
||||
if (match.hasMatch())
|
||||
{
|
||||
QString username = match.captured(1);
|
||||
this->emplace<TextElement>(string, MessageElementFlag::BoldUsername,
|
||||
textColor, FontStyle::ChatMediumBold)
|
||||
->setLink({Link::UserInfo, username});
|
||||
|
||||
this->emplace<TextElement>(
|
||||
string, MessageElementFlag::NonBoldUsername, textColor)
|
||||
->setLink({Link::UserInfo, username});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->twitchChannel != nullptr && getSettings()->findAllUsernames)
|
||||
{
|
||||
auto chatters = this->twitchChannel->accessChatters();
|
||||
if (chatters->contains(string))
|
||||
{
|
||||
this->emplace<TextElement>(string, MessageElementFlag::BoldUsername,
|
||||
textColor, FontStyle::ChatMediumBold)
|
||||
->setLink({Link::UserInfo, string});
|
||||
|
||||
this->emplace<TextElement>(
|
||||
string, MessageElementFlag::NonBoldUsername, textColor)
|
||||
->setLink({Link::UserInfo, string});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this->emplace<TextElement>(string, MessageElementFlag::Text, textColor);
|
||||
}
|
||||
|
||||
void TwitchMessageBuilder::parseMessageID()
|
||||
|
||||
@@ -94,6 +94,8 @@ public:
|
||||
BoolSetting enableSmoothScrollingNewMessages = {
|
||||
"/appearance/smoothScrollingNewMessages", false};
|
||||
BoolSetting boldUsernames = {"/appearance/messages/boldUsernames", false};
|
||||
BoolSetting findAllUsernames = {"/appearance/messages/findAllUsernames",
|
||||
false};
|
||||
// BoolSetting customizable splitheader
|
||||
BoolSetting headerViewerCount = {"/appearance/splitheader/showViewerCount",
|
||||
false};
|
||||
|
||||
@@ -511,6 +511,8 @@ void GeneralPage::initLayout(SettingsLayout &layout)
|
||||
layout.addCheckbox("Show parted users (< 1000 chatters)", s.showParts);
|
||||
layout.addCheckbox("Lowercase domains (anti-phishing)", s.lowercaseDomains);
|
||||
layout.addCheckbox("Bold @usernames", s.boldUsernames);
|
||||
layout.addCheckbox("Try to find usernames without @ prefix",
|
||||
s.findAllUsernames);
|
||||
layout.addDropdown<float>(
|
||||
"Username font weight", {"50", "Default", "75", "100"}, s.boldScale,
|
||||
[](auto val) {
|
||||
|
||||
Reference in New Issue
Block a user