refactor: Remove Leading Mention in Replies and Highlight Participated Threads (#4047)
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Unversioned
|
## Unversioned
|
||||||
|
|
||||||
- Major: Added support for Twitch's Chat Replies. [Wiki Page](https://wiki.chatterino.com/Features/#message-replies) (#3722, #3989, #4041)
|
- Major: Added support for Twitch's Chat Replies. [Wiki Page](https://wiki.chatterino.com/Features/#message-replies) (#3722, #3989, #4041, #4047)
|
||||||
- Major: Added multi-channel searching to search dialog via keyboard shortcut. (Ctrl+Shift+F by default) (#3694, #3875)
|
- Major: Added multi-channel searching to search dialog via keyboard shortcut. (Ctrl+Shift+F by default) (#3694, #3875)
|
||||||
- Minor: Added highlights for `Elevated Messages`. (#4016)
|
- Minor: Added highlights for `Elevated Messages`. (#4016)
|
||||||
- Minor: Removed total views from the usercard, as Twitch no longer updates the number. (#3792)
|
- Minor: Removed total views from the usercard, as Twitch no longer updates the number. (#3792)
|
||||||
|
|||||||
@@ -11,10 +11,12 @@ auto highlightPhraseCheck(const HighlightPhrase &highlight) -> HighlightCheck
|
|||||||
return HighlightCheck{
|
return HighlightCheck{
|
||||||
[highlight](const auto &args, const auto &badges,
|
[highlight](const auto &args, const auto &badges,
|
||||||
const auto &senderName, const auto &originalMessage,
|
const auto &senderName, const auto &originalMessage,
|
||||||
|
const auto &flags,
|
||||||
const auto self) -> boost::optional<HighlightResult> {
|
const auto self) -> boost::optional<HighlightResult> {
|
||||||
(void)args; // unused
|
(void)args; // unused
|
||||||
(void)badges; // unused
|
(void)badges; // unused
|
||||||
(void)senderName; // unused
|
(void)senderName; // unused
|
||||||
|
(void)flags; // unused
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
@@ -60,11 +62,12 @@ void rebuildSubscriptionHighlights(Settings &settings,
|
|||||||
|
|
||||||
checks.emplace_back(HighlightCheck{
|
checks.emplace_back(HighlightCheck{
|
||||||
[=](const auto &args, const auto &badges, const auto &senderName,
|
[=](const auto &args, const auto &badges, const auto &senderName,
|
||||||
const auto &originalMessage,
|
const auto &originalMessage, const auto &flags,
|
||||||
const auto self) -> boost::optional<HighlightResult> {
|
const auto self) -> boost::optional<HighlightResult> {
|
||||||
(void)badges; // unused
|
(void)badges; // unused
|
||||||
(void)senderName; // unused
|
(void)senderName; // unused
|
||||||
(void)originalMessage; // unused
|
(void)originalMessage; // unused
|
||||||
|
(void)flags; // unused
|
||||||
(void)self; // unused
|
(void)self; // unused
|
||||||
|
|
||||||
if (!args.isSubscriptionMessage)
|
if (!args.isSubscriptionMessage)
|
||||||
@@ -105,11 +108,12 @@ void rebuildWhisperHighlights(Settings &settings,
|
|||||||
|
|
||||||
checks.emplace_back(HighlightCheck{
|
checks.emplace_back(HighlightCheck{
|
||||||
[=](const auto &args, const auto &badges, const auto &senderName,
|
[=](const auto &args, const auto &badges, const auto &senderName,
|
||||||
const auto &originalMessage,
|
const auto &originalMessage, const auto &flags,
|
||||||
const auto self) -> boost::optional<HighlightResult> {
|
const auto self) -> boost::optional<HighlightResult> {
|
||||||
(void)badges; // unused
|
(void)badges; // unused
|
||||||
(void)senderName; // unused
|
(void)senderName; // unused
|
||||||
(void)originalMessage; // unused
|
(void)originalMessage; // unused
|
||||||
|
(void)flags; // unused
|
||||||
(void)self; // unused
|
(void)self; // unused
|
||||||
|
|
||||||
if (!args.isReceivedWhisper)
|
if (!args.isReceivedWhisper)
|
||||||
@@ -128,6 +132,44 @@ void rebuildWhisperHighlights(Settings &settings,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rebuildReplyThreadHighlight(Settings &settings,
|
||||||
|
std::vector<HighlightCheck> &checks)
|
||||||
|
{
|
||||||
|
if (settings.enableThreadHighlight)
|
||||||
|
{
|
||||||
|
auto highlightSound = settings.enableThreadHighlightSound.getValue();
|
||||||
|
auto highlightAlert = settings.enableThreadHighlightTaskbar.getValue();
|
||||||
|
auto highlightSoundUrlValue =
|
||||||
|
settings.threadHighlightSoundUrl.getValue();
|
||||||
|
boost::optional<QUrl> highlightSoundUrl;
|
||||||
|
if (!highlightSoundUrlValue.isEmpty())
|
||||||
|
{
|
||||||
|
highlightSoundUrl = highlightSoundUrlValue;
|
||||||
|
}
|
||||||
|
auto highlightInMentions =
|
||||||
|
settings.showThreadHighlightInMentions.getValue();
|
||||||
|
checks.emplace_back(HighlightCheck{
|
||||||
|
[=](const auto & /*args*/, const auto & /*badges*/,
|
||||||
|
const auto & /*senderName*/, const auto & /*originalMessage*/,
|
||||||
|
const auto &flags,
|
||||||
|
const auto self) -> boost::optional<HighlightResult> {
|
||||||
|
if (flags.has(MessageFlag::ParticipatedThread) && !self)
|
||||||
|
{
|
||||||
|
return HighlightResult{
|
||||||
|
highlightAlert,
|
||||||
|
highlightSound,
|
||||||
|
highlightSoundUrl,
|
||||||
|
ColorProvider::instance().color(
|
||||||
|
ColorType::ThreadMessageHighlight),
|
||||||
|
highlightInMentions,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return boost::none;
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void rebuildMessageHighlights(Settings &settings,
|
void rebuildMessageHighlights(Settings &settings,
|
||||||
std::vector<HighlightCheck> &checks)
|
std::vector<HighlightCheck> &checks)
|
||||||
{
|
{
|
||||||
@@ -163,10 +205,12 @@ void rebuildUserHighlights(Settings &settings,
|
|||||||
checks.emplace_back(HighlightCheck{
|
checks.emplace_back(HighlightCheck{
|
||||||
[highlight](const auto &args, const auto &badges,
|
[highlight](const auto &args, const auto &badges,
|
||||||
const auto &senderName, const auto &originalMessage,
|
const auto &senderName, const auto &originalMessage,
|
||||||
|
const auto &flags,
|
||||||
const auto self) -> boost::optional<HighlightResult> {
|
const auto self) -> boost::optional<HighlightResult> {
|
||||||
(void)args; // unused
|
(void)args; // unused
|
||||||
(void)badges; // unused
|
(void)badges; // unused
|
||||||
(void)originalMessage; // unused
|
(void)originalMessage; // unused
|
||||||
|
(void)flags; // unused
|
||||||
(void)self; // unused
|
(void)self; // unused
|
||||||
|
|
||||||
if (!highlight.isMatch(senderName))
|
if (!highlight.isMatch(senderName))
|
||||||
@@ -201,10 +245,12 @@ void rebuildBadgeHighlights(Settings &settings,
|
|||||||
checks.emplace_back(HighlightCheck{
|
checks.emplace_back(HighlightCheck{
|
||||||
[highlight](const auto &args, const auto &badges,
|
[highlight](const auto &args, const auto &badges,
|
||||||
const auto &senderName, const auto &originalMessage,
|
const auto &senderName, const auto &originalMessage,
|
||||||
|
const auto &flags,
|
||||||
const auto self) -> boost::optional<HighlightResult> {
|
const auto self) -> boost::optional<HighlightResult> {
|
||||||
(void)args; // unused
|
(void)args; // unused
|
||||||
(void)senderName; // unused
|
(void)senderName; // unused
|
||||||
(void)originalMessage; // unused
|
(void)originalMessage; // unused
|
||||||
|
(void)flags; // unused
|
||||||
(void)self; // unused
|
(void)self; // unused
|
||||||
|
|
||||||
for (const Badge &badge : badges)
|
for (const Badge &badge : badges)
|
||||||
@@ -247,6 +293,11 @@ void HighlightController::initialize(Settings &settings, Paths & /*paths*/)
|
|||||||
this->rebuildListener_.addSetting(settings.enableSubHighlight);
|
this->rebuildListener_.addSetting(settings.enableSubHighlight);
|
||||||
this->rebuildListener_.addSetting(settings.enableSubHighlightSound);
|
this->rebuildListener_.addSetting(settings.enableSubHighlightSound);
|
||||||
this->rebuildListener_.addSetting(settings.enableSubHighlightTaskbar);
|
this->rebuildListener_.addSetting(settings.enableSubHighlightTaskbar);
|
||||||
|
this->rebuildListener_.addSetting(settings.enableThreadHighlight);
|
||||||
|
this->rebuildListener_.addSetting(settings.enableThreadHighlightSound);
|
||||||
|
this->rebuildListener_.addSetting(settings.enableThreadHighlightTaskbar);
|
||||||
|
this->rebuildListener_.addSetting(settings.threadHighlightSoundUrl);
|
||||||
|
this->rebuildListener_.addSetting(settings.showThreadHighlightInMentions);
|
||||||
|
|
||||||
this->rebuildListener_.setCB([this, &settings] {
|
this->rebuildListener_.setCB([this, &settings] {
|
||||||
qCDebug(chatterinoHighlights)
|
qCDebug(chatterinoHighlights)
|
||||||
@@ -294,7 +345,7 @@ void HighlightController::rebuildChecks(Settings &settings)
|
|||||||
checks->clear();
|
checks->clear();
|
||||||
|
|
||||||
// CURRENT ORDER:
|
// CURRENT ORDER:
|
||||||
// Subscription -> Whisper -> User -> Message -> Badge
|
// Subscription -> Whisper -> User -> Message -> Reply Threads -> Badge
|
||||||
|
|
||||||
rebuildSubscriptionHighlights(settings, *checks);
|
rebuildSubscriptionHighlights(settings, *checks);
|
||||||
|
|
||||||
@@ -304,12 +355,15 @@ void HighlightController::rebuildChecks(Settings &settings)
|
|||||||
|
|
||||||
rebuildMessageHighlights(settings, *checks);
|
rebuildMessageHighlights(settings, *checks);
|
||||||
|
|
||||||
|
rebuildReplyThreadHighlight(settings, *checks);
|
||||||
|
|
||||||
rebuildBadgeHighlights(settings, *checks);
|
rebuildBadgeHighlights(settings, *checks);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<bool, HighlightResult> HighlightController::check(
|
std::pair<bool, HighlightResult> HighlightController::check(
|
||||||
const MessageParseArgs &args, const std::vector<Badge> &badges,
|
const MessageParseArgs &args, const std::vector<Badge> &badges,
|
||||||
const QString &senderName, const QString &originalMessage) const
|
const QString &senderName, const QString &originalMessage,
|
||||||
|
const MessageFlags &messageFlags) const
|
||||||
{
|
{
|
||||||
bool highlighted = false;
|
bool highlighted = false;
|
||||||
auto result = HighlightResult::emptyResult();
|
auto result = HighlightResult::emptyResult();
|
||||||
@@ -322,8 +376,8 @@ std::pair<bool, HighlightResult> HighlightController::check(
|
|||||||
|
|
||||||
for (const auto &check : *checks)
|
for (const auto &check : *checks)
|
||||||
{
|
{
|
||||||
if (auto checkResult =
|
if (auto checkResult = check.cb(args, badges, senderName,
|
||||||
check.cb(args, badges, senderName, originalMessage, self);
|
originalMessage, messageFlags, self);
|
||||||
checkResult)
|
checkResult)
|
||||||
{
|
{
|
||||||
highlighted = true;
|
highlighted = true;
|
||||||
|
|||||||
@@ -142,7 +142,8 @@ struct HighlightResult {
|
|||||||
struct HighlightCheck {
|
struct HighlightCheck {
|
||||||
using Checker = std::function<boost::optional<HighlightResult>(
|
using Checker = std::function<boost::optional<HighlightResult>(
|
||||||
const MessageParseArgs &args, const std::vector<Badge> &badges,
|
const MessageParseArgs &args, const std::vector<Badge> &badges,
|
||||||
const QString &senderName, const QString &originalMessage, bool self)>;
|
const QString &senderName, const QString &originalMessage,
|
||||||
|
const MessageFlags &messageFlags, bool self)>;
|
||||||
Checker cb;
|
Checker cb;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -156,7 +157,8 @@ public:
|
|||||||
**/
|
**/
|
||||||
[[nodiscard]] std::pair<bool, HighlightResult> check(
|
[[nodiscard]] std::pair<bool, HighlightResult> check(
|
||||||
const MessageParseArgs &args, const std::vector<Badge> &badges,
|
const MessageParseArgs &args, const std::vector<Badge> &badges,
|
||||||
const QString &senderName, const QString &originalMessage) const;
|
const QString &senderName, const QString &originalMessage,
|
||||||
|
const MessageFlags &messageFlags) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -213,6 +213,36 @@ void HighlightModel::afterInit()
|
|||||||
|
|
||||||
this->insertCustomRow(elevatedMessageRow,
|
this->insertCustomRow(elevatedMessageRow,
|
||||||
HighlightRowIndexes::ElevatedMessageRow);
|
HighlightRowIndexes::ElevatedMessageRow);
|
||||||
|
|
||||||
|
// Highlight settings for reply threads
|
||||||
|
std::vector<QStandardItem *> threadMessageRow = this->createRow();
|
||||||
|
setBoolItem(threadMessageRow[Column::Pattern],
|
||||||
|
getSettings()->enableThreadHighlight.getValue(), true, false);
|
||||||
|
threadMessageRow[Column::Pattern]->setData("Participated Reply Threads",
|
||||||
|
Qt::DisplayRole);
|
||||||
|
setBoolItem(threadMessageRow[Column::ShowInMentions],
|
||||||
|
getSettings()->showThreadHighlightInMentions.getValue(), true,
|
||||||
|
false);
|
||||||
|
setBoolItem(threadMessageRow[Column::FlashTaskbar],
|
||||||
|
getSettings()->enableThreadHighlightTaskbar.getValue(), true,
|
||||||
|
false);
|
||||||
|
setBoolItem(threadMessageRow[Column::PlaySound],
|
||||||
|
getSettings()->enableThreadHighlightSound.getValue(), true,
|
||||||
|
false);
|
||||||
|
threadMessageRow[Column::UseRegex]->setFlags({});
|
||||||
|
threadMessageRow[Column::CaseSensitive]->setFlags({});
|
||||||
|
|
||||||
|
QUrl threadMessageSound =
|
||||||
|
QUrl(getSettings()->threadHighlightSoundUrl.getValue());
|
||||||
|
setFilePathItem(threadMessageRow[Column::SoundPath], threadMessageSound,
|
||||||
|
false);
|
||||||
|
|
||||||
|
auto threadMessageColor =
|
||||||
|
ColorProvider::instance().color(ColorType::ThreadMessageHighlight);
|
||||||
|
setColorItem(threadMessageRow[Column::Color], *threadMessageColor, false);
|
||||||
|
|
||||||
|
this->insertCustomRow(threadMessageRow,
|
||||||
|
HighlightRowIndexes::ThreadMessageRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HighlightModel::customRowSetData(const std::vector<QStandardItem *> &row,
|
void HighlightModel::customRowSetData(const std::vector<QStandardItem *> &row,
|
||||||
@@ -252,6 +282,11 @@ void HighlightModel::customRowSetData(const std::vector<QStandardItem *> &row,
|
|||||||
getSettings()->enableElevatedMessageHighlight.setValue(
|
getSettings()->enableElevatedMessageHighlight.setValue(
|
||||||
value.toBool());
|
value.toBool());
|
||||||
}
|
}
|
||||||
|
else if (rowIndex == HighlightRowIndexes::ThreadMessageRow)
|
||||||
|
{
|
||||||
|
getSettings()->enableThreadHighlight.setValue(
|
||||||
|
value.toBool());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -263,6 +298,11 @@ void HighlightModel::customRowSetData(const std::vector<QStandardItem *> &row,
|
|||||||
getSettings()->showSelfHighlightInMentions.setValue(
|
getSettings()->showSelfHighlightInMentions.setValue(
|
||||||
value.toBool());
|
value.toBool());
|
||||||
}
|
}
|
||||||
|
else if (rowIndex == HighlightRowIndexes::ThreadMessageRow)
|
||||||
|
{
|
||||||
|
getSettings()->showThreadHighlightInMentions.setValue(
|
||||||
|
value.toBool());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -300,6 +340,11 @@ void HighlightModel::customRowSetData(const std::vector<QStandardItem *> &row,
|
|||||||
// ->enableElevatedMessageHighlightTaskbar.setvalue(
|
// ->enableElevatedMessageHighlightTaskbar.setvalue(
|
||||||
// value.toBool());
|
// value.toBool());
|
||||||
}
|
}
|
||||||
|
else if (rowIndex == HighlightRowIndexes::ThreadMessageRow)
|
||||||
|
{
|
||||||
|
getSettings()->enableThreadHighlightTaskbar.setValue(
|
||||||
|
value.toBool());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -336,6 +381,11 @@ void HighlightModel::customRowSetData(const std::vector<QStandardItem *> &row,
|
|||||||
// getSettings()->enableElevatedMessageHighlightSound.setValue(
|
// getSettings()->enableElevatedMessageHighlightSound.setValue(
|
||||||
// value.toBool());
|
// value.toBool());
|
||||||
}
|
}
|
||||||
|
else if (rowIndex == HighlightRowIndexes::ThreadMessageRow)
|
||||||
|
{
|
||||||
|
getSettings()->enableThreadHighlightSound.setValue(
|
||||||
|
value.toBool());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -381,6 +431,11 @@ void HighlightModel::customRowSetData(const std::vector<QStandardItem *> &row,
|
|||||||
getSettings()->elevatedMessageHighlightSoundUrl.setValue(
|
getSettings()->elevatedMessageHighlightSoundUrl.setValue(
|
||||||
value.toString());
|
value.toString());
|
||||||
}
|
}
|
||||||
|
else if (rowIndex == HighlightRowIndexes::ThreadMessageRow)
|
||||||
|
{
|
||||||
|
getSettings()->threadHighlightSoundUrl.setValue(
|
||||||
|
value.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -424,6 +479,13 @@ void HighlightModel::customRowSetData(const std::vector<QStandardItem *> &row,
|
|||||||
.updateColor(ColorType::ElevatedMessageHighlight,
|
.updateColor(ColorType::ElevatedMessageHighlight,
|
||||||
QColor(colorName));
|
QColor(colorName));
|
||||||
}
|
}
|
||||||
|
else if (rowIndex == HighlightRowIndexes::ThreadMessageRow)
|
||||||
|
{
|
||||||
|
getSettings()->threadHighlightColor.setValue(colorName);
|
||||||
|
const_cast<ColorProvider &>(ColorProvider::instance())
|
||||||
|
.updateColor(ColorType::ThreadMessageHighlight,
|
||||||
|
QColor(colorName));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public:
|
|||||||
RedeemedRow = 3,
|
RedeemedRow = 3,
|
||||||
FirstMessageRow = 4,
|
FirstMessageRow = 4,
|
||||||
ElevatedMessageRow = 5,
|
ElevatedMessageRow = 5,
|
||||||
|
ThreadMessageRow = 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ QColor HighlightPhrase::FALLBACK_FIRST_MESSAGE_HIGHLIGHT_COLOR =
|
|||||||
QColor(72, 127, 63, 60);
|
QColor(72, 127, 63, 60);
|
||||||
QColor HighlightPhrase::FALLBACK_ELEVATED_MESSAGE_HIGHLIGHT_COLOR =
|
QColor HighlightPhrase::FALLBACK_ELEVATED_MESSAGE_HIGHLIGHT_COLOR =
|
||||||
QColor(255, 174, 66, 60);
|
QColor(255, 174, 66, 60);
|
||||||
|
QColor HighlightPhrase::FALLBACK_THREAD_HIGHLIGHT_COLOR =
|
||||||
|
QColor(143, 48, 24, 60);
|
||||||
QColor HighlightPhrase::FALLBACK_SUB_COLOR = QColor(196, 102, 255, 100);
|
QColor HighlightPhrase::FALLBACK_SUB_COLOR = QColor(196, 102, 255, 100);
|
||||||
|
|
||||||
bool HighlightPhrase::operator==(const HighlightPhrase &other) const
|
bool HighlightPhrase::operator==(const HighlightPhrase &other) const
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ public:
|
|||||||
static QColor FALLBACK_SUB_COLOR;
|
static QColor FALLBACK_SUB_COLOR;
|
||||||
static QColor FALLBACK_FIRST_MESSAGE_HIGHLIGHT_COLOR;
|
static QColor FALLBACK_FIRST_MESSAGE_HIGHLIGHT_COLOR;
|
||||||
static QColor FALLBACK_ELEVATED_MESSAGE_HIGHLIGHT_COLOR;
|
static QColor FALLBACK_ELEVATED_MESSAGE_HIGHLIGHT_COLOR;
|
||||||
|
static QColor FALLBACK_THREAD_HIGHLIGHT_COLOR;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString pattern_;
|
QString pattern_;
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ enum class MessageFlag : int64_t {
|
|||||||
FirstMessage = (1LL << 23),
|
FirstMessage = (1LL << 23),
|
||||||
ReplyMessage = (1LL << 24),
|
ReplyMessage = (1LL << 24),
|
||||||
ElevatedMessage = (1LL << 25),
|
ElevatedMessage = (1LL << 25),
|
||||||
|
ParticipatedThread = (1LL << 26),
|
||||||
};
|
};
|
||||||
using MessageFlags = FlagsEnum<MessageFlag>;
|
using MessageFlags = FlagsEnum<MessageFlag>;
|
||||||
|
|
||||||
|
|||||||
@@ -58,4 +58,14 @@ size_t MessageThread::liveCount(
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MessageThread::participated() const
|
||||||
|
{
|
||||||
|
return this->participated_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageThread::markParticipated()
|
||||||
|
{
|
||||||
|
this->participated_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ public:
|
|||||||
/// Returns the number of live reply references
|
/// Returns the number of live reply references
|
||||||
size_t liveCount(const std::shared_ptr<const Message> &exclude) const;
|
size_t liveCount(const std::shared_ptr<const Message> &exclude) const;
|
||||||
|
|
||||||
|
bool participated() const;
|
||||||
|
|
||||||
|
void markParticipated();
|
||||||
|
|
||||||
const QString &rootId() const
|
const QString &rootId() const
|
||||||
{
|
{
|
||||||
return rootMessageId_;
|
return rootMessageId_;
|
||||||
@@ -42,6 +46,7 @@ private:
|
|||||||
const QString rootMessageId_;
|
const QString rootMessageId_;
|
||||||
const std::shared_ptr<const Message> rootMessage_;
|
const std::shared_ptr<const Message> rootMessage_;
|
||||||
std::vector<std::weak_ptr<const Message>> replies_;
|
std::vector<std::weak_ptr<const Message>> replies_;
|
||||||
|
bool participated_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|||||||
@@ -149,7 +149,8 @@ void SharedMessageBuilder::parseHighlights()
|
|||||||
|
|
||||||
auto badges = SharedMessageBuilder::parseBadgeTag(this->tags);
|
auto badges = SharedMessageBuilder::parseBadgeTag(this->tags);
|
||||||
auto [highlighted, highlightResult] = getIApp()->getHighlights()->check(
|
auto [highlighted, highlightResult] = getIApp()->getHighlights()->check(
|
||||||
this->args, badges, this->ircMessage->nick(), this->originalMessage_);
|
this->args, badges, this->ircMessage->nick(), this->originalMessage_,
|
||||||
|
this->message().flags);
|
||||||
|
|
||||||
if (!highlighted)
|
if (!highlighted)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -147,6 +147,20 @@ void ColorProvider::initTypeColorMap()
|
|||||||
std::make_shared<QColor>(
|
std::make_shared<QColor>(
|
||||||
HighlightPhrase::FALLBACK_ELEVATED_MESSAGE_HIGHLIGHT_COLOR)});
|
HighlightPhrase::FALLBACK_ELEVATED_MESSAGE_HIGHLIGHT_COLOR)});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customColor = getSettings()->threadHighlightColor;
|
||||||
|
if (QColor(customColor).isValid())
|
||||||
|
{
|
||||||
|
this->typeColorMap_.insert({ColorType::ThreadMessageHighlight,
|
||||||
|
std::make_shared<QColor>(customColor)});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->typeColorMap_.insert(
|
||||||
|
{ColorType::ThreadMessageHighlight,
|
||||||
|
std::make_shared<QColor>(
|
||||||
|
HighlightPhrase::FALLBACK_THREAD_HIGHLIGHT_COLOR)});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ColorProvider::initDefaultColors()
|
void ColorProvider::initDefaultColors()
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ enum class ColorType {
|
|||||||
RedeemedHighlight,
|
RedeemedHighlight,
|
||||||
FirstMessageHighlight,
|
FirstMessageHighlight,
|
||||||
ElevatedMessageHighlight,
|
ElevatedMessageHighlight,
|
||||||
|
ThreadMessageHighlight,
|
||||||
};
|
};
|
||||||
|
|
||||||
class ColorProvider
|
class ColorProvider
|
||||||
|
|||||||
@@ -67,6 +67,65 @@ MessagePtr generateBannedMessage(bool confirmedBan)
|
|||||||
return builder.release();
|
return builder.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int stripLeadingReplyMention(const QVariantMap &tags, QString &content)
|
||||||
|
{
|
||||||
|
if (!getSettings()->stripReplyMention)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto it = tags.find("reply-parent-display-name");
|
||||||
|
it != tags.end())
|
||||||
|
{
|
||||||
|
auto displayName = it.value().toString();
|
||||||
|
if (content.startsWith('@') &&
|
||||||
|
content.at(1 + displayName.length()) == ' ' &&
|
||||||
|
content.indexOf(displayName, 1) == 1)
|
||||||
|
{
|
||||||
|
int messageOffset = 1 + displayName.length() + 1;
|
||||||
|
content.remove(0, messageOffset);
|
||||||
|
return messageOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateReplyParticipatedStatus(const QVariantMap &tags,
|
||||||
|
const QString &senderLogin,
|
||||||
|
TwitchMessageBuilder &builder,
|
||||||
|
std::shared_ptr<MessageThread> &thread,
|
||||||
|
bool isNew)
|
||||||
|
{
|
||||||
|
const auto ¤tLogin =
|
||||||
|
getApp()->accounts->twitch.getCurrent()->getUserName();
|
||||||
|
if (thread->participated())
|
||||||
|
{
|
||||||
|
builder.message().flags.set(MessageFlag::ParticipatedThread);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNew)
|
||||||
|
{
|
||||||
|
if (const auto it = tags.find("reply-parent-user-login");
|
||||||
|
it != tags.end())
|
||||||
|
{
|
||||||
|
auto name = it.value().toString();
|
||||||
|
if (name == currentLogin)
|
||||||
|
{
|
||||||
|
thread->markParticipated();
|
||||||
|
builder.message().flags.set(MessageFlag::ParticipatedThread);
|
||||||
|
return; // already marked as participated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (senderLogin == currentLogin)
|
||||||
|
{
|
||||||
|
thread->markParticipated();
|
||||||
|
// don't set the highlight here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
@@ -259,9 +318,12 @@ std::vector<MessagePtr> IrcMessageHandler::parseMessageWithReply(
|
|||||||
return this->parsePrivMessage(channel, privMsg);
|
return this->parsePrivMessage(channel, privMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString content = privMsg->content();
|
||||||
|
int messageOffset = stripLeadingReplyMention(privMsg->tags(), content);
|
||||||
MessageParseArgs args;
|
MessageParseArgs args;
|
||||||
TwitchMessageBuilder builder(channel, message, args, privMsg->content(),
|
TwitchMessageBuilder builder(channel, message, args, content,
|
||||||
privMsg->isAction());
|
privMsg->isAction());
|
||||||
|
builder.setMessageOffset(messageOffset);
|
||||||
|
|
||||||
this->populateReply(tc, message, otherLoaded, builder);
|
this->populateReply(tc, message, otherLoaded, builder);
|
||||||
|
|
||||||
@@ -295,10 +357,12 @@ void IrcMessageHandler::populateReply(
|
|||||||
auto threadIt = channel->threads_.find(replyID);
|
auto threadIt = channel->threads_.find(replyID);
|
||||||
if (threadIt != channel->threads_.end())
|
if (threadIt != channel->threads_.end())
|
||||||
{
|
{
|
||||||
const auto owned = threadIt->second.lock();
|
auto owned = threadIt->second.lock();
|
||||||
if (owned)
|
if (owned)
|
||||||
{
|
{
|
||||||
// Thread already exists (has a reply)
|
// Thread already exists (has a reply)
|
||||||
|
updateReplyParticipatedStatus(tags, message->nick(), builder,
|
||||||
|
owned, false);
|
||||||
builder.setThread(owned);
|
builder.setThread(owned);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -331,6 +395,8 @@ void IrcMessageHandler::populateReply(
|
|||||||
{
|
{
|
||||||
std::shared_ptr<MessageThread> newThread =
|
std::shared_ptr<MessageThread> newThread =
|
||||||
std::make_shared<MessageThread>(foundMessage);
|
std::make_shared<MessageThread>(foundMessage);
|
||||||
|
updateReplyParticipatedStatus(tags, message->nick(), builder,
|
||||||
|
newThread, true);
|
||||||
|
|
||||||
builder.setThread(newThread);
|
builder.setThread(newThread);
|
||||||
// Store weak reference to thread in channel
|
// Store weak reference to thread in channel
|
||||||
@@ -341,7 +407,7 @@ void IrcMessageHandler::populateReply(
|
|||||||
|
|
||||||
void IrcMessageHandler::addMessage(Communi::IrcMessage *_message,
|
void IrcMessageHandler::addMessage(Communi::IrcMessage *_message,
|
||||||
const QString &target,
|
const QString &target,
|
||||||
const QString &content,
|
const QString &content_,
|
||||||
TwitchIrcServer &server, bool isSub,
|
TwitchIrcServer &server, bool isSub,
|
||||||
bool isAction)
|
bool isAction)
|
||||||
{
|
{
|
||||||
@@ -384,7 +450,7 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message,
|
|||||||
[=, &server](ChannelPointReward reward) {
|
[=, &server](ChannelPointReward reward) {
|
||||||
if (reward.id == rewardId)
|
if (reward.id == rewardId)
|
||||||
{
|
{
|
||||||
this->addMessage(clone, target, content, server, isSub,
|
this->addMessage(clone, target, content_, server, isSub,
|
||||||
isAction);
|
isAction);
|
||||||
clone->deleteLater();
|
clone->deleteLater();
|
||||||
return true;
|
return true;
|
||||||
@@ -396,7 +462,11 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message,
|
|||||||
args.channelPointRewardId = rewardId;
|
args.channelPointRewardId = rewardId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString content = content_;
|
||||||
|
int messageOffset = stripLeadingReplyMention(tags, content);
|
||||||
|
|
||||||
TwitchMessageBuilder builder(chan.get(), _message, args, content, isAction);
|
TwitchMessageBuilder builder(chan.get(), _message, args, content, isAction);
|
||||||
|
builder.setMessageOffset(messageOffset);
|
||||||
|
|
||||||
if (const auto it = tags.find("reply-parent-msg-id"); it != tags.end())
|
if (const auto it = tags.find("reply-parent-msg-id"); it != tags.end())
|
||||||
{
|
{
|
||||||
@@ -405,7 +475,10 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message,
|
|||||||
if (threadIt != channel->threads_.end() && !threadIt->second.expired())
|
if (threadIt != channel->threads_.end() && !threadIt->second.expired())
|
||||||
{
|
{
|
||||||
// Thread already exists (has a reply)
|
// Thread already exists (has a reply)
|
||||||
builder.setThread(threadIt->second.lock());
|
auto thread = threadIt->second.lock();
|
||||||
|
updateReplyParticipatedStatus(tags, _message->nick(), builder,
|
||||||
|
thread, false);
|
||||||
|
builder.setThread(thread);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -414,7 +487,9 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message,
|
|||||||
if (root)
|
if (root)
|
||||||
{
|
{
|
||||||
// Found root reply message
|
// Found root reply message
|
||||||
const auto newThread = std::make_shared<MessageThread>(root);
|
auto newThread = std::make_shared<MessageThread>(root);
|
||||||
|
updateReplyParticipatedStatus(tags, _message->nick(), builder,
|
||||||
|
newThread, true);
|
||||||
|
|
||||||
builder.setThread(newThread);
|
builder.setThread(newThread);
|
||||||
// Store weak reference to thread in channel
|
// Store weak reference to thread in channel
|
||||||
|
|||||||
@@ -999,8 +999,10 @@ void TwitchMessageBuilder::appendTwitchEmote(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto start = correctPositions[coords.at(0).toUInt()];
|
auto start =
|
||||||
auto end = correctPositions[coords.at(1).toUInt()];
|
correctPositions[coords.at(0).toUInt() - this->messageOffset_];
|
||||||
|
auto end =
|
||||||
|
correctPositions[coords.at(1).toUInt() - this->messageOffset_];
|
||||||
|
|
||||||
if (start >= end || start < 0 || end > this->originalMessage_.length())
|
if (start >= end || start < 0 || end > this->originalMessage_.length())
|
||||||
{
|
{
|
||||||
@@ -1589,4 +1591,9 @@ void TwitchMessageBuilder::setThread(std::shared_ptr<MessageThread> thread)
|
|||||||
this->thread_ = std::move(thread);
|
this->thread_ = std::move(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TwitchMessageBuilder::setMessageOffset(int offset)
|
||||||
|
{
|
||||||
|
this->messageOffset_ = offset;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ public:
|
|||||||
MessagePtr build() override;
|
MessagePtr build() override;
|
||||||
|
|
||||||
void setThread(std::shared_ptr<MessageThread> thread);
|
void setThread(std::shared_ptr<MessageThread> thread);
|
||||||
|
void setMessageOffset(int offset);
|
||||||
|
|
||||||
static void appendChannelPointRewardMessage(
|
static void appendChannelPointRewardMessage(
|
||||||
const ChannelPointReward &reward, MessageBuilder *builder, bool isMod,
|
const ChannelPointReward &reward, MessageBuilder *builder, bool isMod,
|
||||||
@@ -110,6 +111,18 @@ private:
|
|||||||
bool historicalMessage_ = false;
|
bool historicalMessage_ = false;
|
||||||
std::shared_ptr<MessageThread> thread_;
|
std::shared_ptr<MessageThread> thread_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starting offset to be used on index-based operations on `originalMessage_`.
|
||||||
|
*
|
||||||
|
* For example:
|
||||||
|
* originalMessage_ = "there"
|
||||||
|
* messageOffset_ = 4
|
||||||
|
* (the irc message is "hey there")
|
||||||
|
*
|
||||||
|
* then the index 6 would resolve to 6 - 4 = 2 => 'e'
|
||||||
|
*/
|
||||||
|
int messageOffset_ = 0;
|
||||||
|
|
||||||
QString userId_;
|
QString userId_;
|
||||||
bool senderIsBroadcaster{};
|
bool senderIsBroadcaster{};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ public:
|
|||||||
// BoolSetting collapseLongMessages =
|
// BoolSetting collapseLongMessages =
|
||||||
// {"/appearance/messages/collapseLongMessages", false};
|
// {"/appearance/messages/collapseLongMessages", false};
|
||||||
BoolSetting showReplyButton = {"/appearance/showReplyButton", false};
|
BoolSetting showReplyButton = {"/appearance/showReplyButton", false};
|
||||||
|
BoolSetting stripReplyMention = {"/appearance/stripReplyMention", true};
|
||||||
IntSetting collpseMessagesMinLines = {
|
IntSetting collpseMessagesMinLines = {
|
||||||
"/appearance/messages/collapseMessagesMinLines", 0};
|
"/appearance/messages/collapseMessagesMinLines", 0};
|
||||||
BoolSetting alternateMessages = {
|
BoolSetting alternateMessages = {
|
||||||
@@ -327,6 +328,19 @@ public:
|
|||||||
""};
|
""};
|
||||||
QStringSetting subHighlightColor = {"/highlighting/subHighlightColor", ""};
|
QStringSetting subHighlightColor = {"/highlighting/subHighlightColor", ""};
|
||||||
|
|
||||||
|
BoolSetting enableThreadHighlight = {
|
||||||
|
"/highlighting/thread/nameIsHighlightKeyword", true};
|
||||||
|
BoolSetting showThreadHighlightInMentions = {
|
||||||
|
"/highlighting/thread/showSelfHighlightInMentions", true};
|
||||||
|
BoolSetting enableThreadHighlightSound = {
|
||||||
|
"/highlighting/thread/enableSound", true};
|
||||||
|
BoolSetting enableThreadHighlightTaskbar = {
|
||||||
|
"/highlighting/thread/enableTaskbarFlashing", true};
|
||||||
|
QStringSetting threadHighlightSoundUrl = {
|
||||||
|
"/highlighting/threadHighlightSoundUrl", ""};
|
||||||
|
QStringSetting threadHighlightColor = {"/highlighting/threadHighlightColor",
|
||||||
|
""};
|
||||||
|
|
||||||
QStringSetting highlightColor = {"/highlighting/color", ""};
|
QStringSetting highlightColor = {"/highlighting/color", ""};
|
||||||
|
|
||||||
BoolSetting longAlerts = {"/highlighting/alerts", false};
|
BoolSetting longAlerts = {"/highlighting/alerts", false};
|
||||||
|
|||||||
@@ -719,6 +719,7 @@ void GeneralPage::initLayout(GeneralPageView &layout)
|
|||||||
layout.addCheckbox("Combine multiple bit tips into one", s.stackBits);
|
layout.addCheckbox("Combine multiple bit tips into one", s.stackBits);
|
||||||
layout.addCheckbox("Messages in /mentions highlights tab",
|
layout.addCheckbox("Messages in /mentions highlights tab",
|
||||||
s.highlightMentions);
|
s.highlightMentions);
|
||||||
|
layout.addCheckbox("Strip leading mention in replies", s.stripReplyMention);
|
||||||
|
|
||||||
// Helix timegate settings
|
// Helix timegate settings
|
||||||
auto helixTimegateGetValue = [](auto val) {
|
auto helixTimegateGetValue = [](auto val) {
|
||||||
|
|||||||
@@ -485,6 +485,7 @@ struct TestCase {
|
|||||||
std::vector<Badge> badges;
|
std::vector<Badge> badges;
|
||||||
QString senderName;
|
QString senderName;
|
||||||
QString originalMessage;
|
QString originalMessage;
|
||||||
|
MessageFlags flags;
|
||||||
} input;
|
} input;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@@ -727,8 +728,9 @@ TEST_F(HighlightControllerTest, A)
|
|||||||
|
|
||||||
for (const auto &[input, expected] : tests)
|
for (const auto &[input, expected] : tests)
|
||||||
{
|
{
|
||||||
auto [isMatch, matchResult] = this->controller->check(
|
auto [isMatch, matchResult] =
|
||||||
input.args, input.badges, input.senderName, input.originalMessage);
|
this->controller->check(input.args, input.badges, input.senderName,
|
||||||
|
input.originalMessage, input.flags);
|
||||||
|
|
||||||
EXPECT_EQ(isMatch, expected.state)
|
EXPECT_EQ(isMatch, expected.state)
|
||||||
<< qUtf8Printable(input.senderName) << ": "
|
<< qUtf8Printable(input.senderName) << ": "
|
||||||
|
|||||||
Reference in New Issue
Block a user