From 1f0cbcf73b1068c49882a13739af0771e4b960c3 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Tue, 24 Oct 2023 21:39:00 -0700 Subject: [PATCH 01/17] Avoid thread root id when using reply command --- src/controllers/commands/CommandController.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 6f69cabf63e..dfbf07f4179 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -1563,20 +1563,16 @@ void CommandController::initialize(Settings &, Paths &paths) const auto &msg = *it; if (msg->loginName.compare(username, Qt::CaseInsensitive) == 0) { - std::shared_ptr thread; // found most recent message by user if (msg->replyThread == nullptr) { - thread = std::make_shared(msg); + // prepare thread if one does not exist + auto thread = std::make_shared(msg); twitchChannel->addReplyThread(thread); } - else - { - thread = msg->replyThread; - } QString reply = words.mid(2).join(" "); - twitchChannel->sendReply(reply, thread->rootId()); + twitchChannel->sendReply(reply, msg->id); return ""; } } From 0dc6b8402944561425cfafbca2d459519e05d356 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Sat, 28 Oct 2023 12:27:27 -0700 Subject: [PATCH 02/17] Show correct reply context --- src/messages/Message.hpp | 1 + src/providers/twitch/IrcMessageHandler.cpp | 118 +++++++++++++----- src/providers/twitch/TwitchMessageBuilder.cpp | 15 ++- src/providers/twitch/TwitchMessageBuilder.hpp | 2 + src/singletons/helper/LoggingChannel.cpp | 3 +- 5 files changed, 106 insertions(+), 33 deletions(-) diff --git a/src/messages/Message.hpp b/src/messages/Message.hpp index b248c93a116..51805c0e432 100644 --- a/src/messages/Message.hpp +++ b/src/messages/Message.hpp @@ -88,6 +88,7 @@ struct Message { // the reply thread will be cleaned up by the TwitchChannel. // The root of the thread does not have replyThread set. std::shared_ptr replyThread; + std::shared_ptr replyParent; uint32_t count = 1; std::vector> elements; diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index 38c15f289a3..5dc5d66e616 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -453,10 +453,11 @@ void IrcMessageHandler::populateReply( const std::vector &otherLoaded, TwitchMessageBuilder &builder) { const auto &tags = message->tags(); - if (const auto it = tags.find("reply-parent-msg-id"); it != tags.end()) + if (const auto it = tags.find("reply-thread-parent-msg-id"); it != tags.end()) { const QString replyID = it.value().toString(); auto threadIt = channel->threads_.find(replyID); + std::shared_ptr rootThread; if (threadIt != channel->threads_.end()) { auto owned = threadIt->second.lock(); @@ -466,43 +467,73 @@ void IrcMessageHandler::populateReply( updateReplyParticipatedStatus(tags, message->nick(), builder, owned, false); builder.setThread(owned); - return; + rootThread = owned; } } - MessagePtr foundMessage; - - // Thread does not yet exist, find root reply and create thread. - // Linear search is justified by the infrequent use of replies - for (auto &otherMsg : otherLoaded) + if (!rootThread) { - if (otherMsg->id == replyID) + MessagePtr foundMessage; + + // Thread does not yet exist, find root reply and create thread. + // Linear search is justified by the infrequent use of replies + for (auto &otherMsg : otherLoaded) { - // Found root reply message - foundMessage = otherMsg; - break; + if (otherMsg->id == replyID) + { + // Found root reply message + foundMessage = otherMsg; + break; + } } - } - if (!foundMessage) - { - // We didn't find the reply root message in the otherLoaded messages - // which are typically the already-parsed recent messages from the - // Recent Messages API. We could have a really old message that - // still exists being replied to, so check for that here. - foundMessage = channel->findMessage(replyID); + if (!foundMessage) + { + // We didn't find the reply root message in the otherLoaded messages + // which are typically the already-parsed recent messages from the + // Recent Messages API. We could have a really old message that + // still exists being replied to, so check for that here. + foundMessage = channel->findMessage(replyID); + } + + if (foundMessage) + { + std::shared_ptr newThread = + std::make_shared(foundMessage); + updateReplyParticipatedStatus(tags, message->nick(), builder, + newThread, true); + + builder.setThread(newThread); + rootThread = newThread; + // Store weak reference to thread in channel + channel->addReplyThread(newThread); + } } - if (foundMessage) + if (const auto parentIt = tags.find("reply-parent-msg-id"); parentIt != tags.end()) { - std::shared_ptr newThread = - std::make_shared(foundMessage); - updateReplyParticipatedStatus(tags, message->nick(), builder, - newThread, true); - - builder.setThread(newThread); - // Store weak reference to thread in channel - channel->addReplyThread(newThread); + const QString parentID = parentIt.value().toString(); + std::shared_ptr parent; + if (replyID == parentID) + { + if (rootThread) + { + parent = rootThread->root(); + } + } + else + { + auto parentThreadIt = channel->threads_.find(parentID); + if (parentThreadIt != channel->threads_.end() && !parentThreadIt->second.expired()) + { + parent = parentThreadIt->second.lock()->root(); + } + else + { + parent = channel->findMessage(parentID); + } + } + builder.setParent(parent); } } } @@ -570,10 +601,11 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message, 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-thread-parent-msg-id"); it != tags.end()) { const QString replyID = it.value().toString(); auto threadIt = channel->threads_.find(replyID); + std::shared_ptr rootThread; if (threadIt != channel->threads_.end() && !threadIt->second.expired()) { // Thread already exists (has a reply) @@ -581,6 +613,7 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message, updateReplyParticipatedStatus(tags, _message->nick(), builder, thread, false); builder.setThread(thread); + rootThread = thread; } else { @@ -594,10 +627,37 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message, newThread, true); builder.setThread(newThread); + rootThread = newThread; // Store weak reference to thread in channel channel->addReplyThread(newThread); } } + + if (const auto parentIt = tags.find("reply-parent-msg-id"); parentIt != tags.end()) + { + const QString parentID = parentIt.value().toString(); + std::shared_ptr parent; + if (replyID == parentID) + { + if (rootThread) + { + parent = rootThread->root(); + } + } + else + { + auto parentThreadIt = channel->threads_.find(parentID); + if (parentThreadIt != channel->threads_.end() && !parentThreadIt->second.expired()) + { + parent = parentThreadIt->second.lock()->root(); + } + else + { + parent = channel->findMessage(parentID); + } + } + builder.setParent(parent); + } } if (isSub || !builder.isIgnored()) diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index efb54584836..06d2484760b 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -623,15 +623,21 @@ void TwitchMessageBuilder::parseThread() { // set references this->message().replyThread = this->thread_; + this->message().replyParent = this->parent_; this->thread_->addToThread(this->weakOf()); // enable reply flag this->message().flags.set(MessageFlag::ReplyMessage); - const auto &threadRoot = this->thread_->root(); + std::shared_ptr threadRoot; + if (!this->parent_) { + threadRoot = this->thread_->root(); + } else { + threadRoot = this->parent_; + } QString usernameText = SharedMessageBuilder::stylizeUsername( - threadRoot->loginName, *threadRoot.get()); + threadRoot->loginName, *threadRoot); this->emplace(); @@ -1811,6 +1817,11 @@ void TwitchMessageBuilder::setThread(std::shared_ptr thread) this->thread_ = std::move(thread); } +void TwitchMessageBuilder::setParent(std::shared_ptr parent) +{ + this->parent_ = std::move(parent); +} + void TwitchMessageBuilder::setMessageOffset(int offset) { this->messageOffset_ = offset; diff --git a/src/providers/twitch/TwitchMessageBuilder.hpp b/src/providers/twitch/TwitchMessageBuilder.hpp index 8430bb6aba3..2092f7dcd65 100644 --- a/src/providers/twitch/TwitchMessageBuilder.hpp +++ b/src/providers/twitch/TwitchMessageBuilder.hpp @@ -58,6 +58,7 @@ class TwitchMessageBuilder : public SharedMessageBuilder MessagePtr build() override; void setThread(std::shared_ptr thread); + void setParent(std::shared_ptr parent); void setMessageOffset(int offset); static void appendChannelPointRewardMessage( @@ -131,6 +132,7 @@ class TwitchMessageBuilder : public SharedMessageBuilder bool bitsStacked = false; bool historicalMessage_ = false; std::shared_ptr thread_; + std::shared_ptr parent_; /** * Starting offset to be used on index-based operations on `originalMessage_`. diff --git a/src/singletons/helper/LoggingChannel.cpp b/src/singletons/helper/LoggingChannel.cpp index b47aec19d4e..084c8dd8adf 100644 --- a/src/singletons/helper/LoggingChannel.cpp +++ b/src/singletons/helper/LoggingChannel.cpp @@ -132,8 +132,7 @@ void LoggingChannel::addMessage(MessagePtr message) qsizetype colonIndex = messageText.indexOf(':'); if (colonIndex != -1) { - QString rootMessageChatter = - message->replyThread->root()->loginName; + QString rootMessageChatter = message->replyParent->loginName; messageText.insert(colonIndex + 1, " @" + rootMessageChatter); } } From 85de2f65dd603ad772b51f0b5257bb7be214b0da Mon Sep 17 00:00:00 2001 From: iProdigy Date: Sat, 28 Oct 2023 12:48:31 -0700 Subject: [PATCH 03/17] Reply to correct parent --- src/widgets/dialogs/ReplyThreadPopup.cpp | 2 +- src/widgets/helper/ChannelView.cpp | 20 ++++---------------- src/widgets/splits/Split.cpp | 2 +- src/widgets/splits/Split.hpp | 3 +-- src/widgets/splits/SplitInput.cpp | 12 ++++++------ src/widgets/splits/SplitInput.hpp | 6 +++--- 6 files changed, 16 insertions(+), 29 deletions(-) diff --git a/src/widgets/dialogs/ReplyThreadPopup.cpp b/src/widgets/dialogs/ReplyThreadPopup.cpp index 9b01f29fcdf..eeb0d82b4e4 100644 --- a/src/widgets/dialogs/ReplyThreadPopup.cpp +++ b/src/widgets/dialogs/ReplyThreadPopup.cpp @@ -189,7 +189,7 @@ ReplyThreadPopup::ReplyThreadPopup(bool closeAutomatically, QWidget *parent, void ReplyThreadPopup::setThread(std::shared_ptr thread) { this->thread_ = std::move(thread); - this->ui_.replyInput->setReply(this->thread_); + this->ui_.replyInput->setReply(this->thread_->root()); this->addMessagesFromThread(); this->updateInputUI(); diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index c80b1ae50a8..7081637d21f 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -2830,10 +2830,9 @@ void ChannelView::setInputReply(const MessagePtr &message) return; } - std::shared_ptr thread; - if (message->replyThread == nullptr) { + // Create thread if one does not exist auto getThread = [&](TwitchChannel *tc) { auto threadIt = tc->threads().find(message->id); if (threadIt != tc->threads().end() && !threadIt->second.expired()) @@ -2851,26 +2850,15 @@ void ChannelView::setInputReply(const MessagePtr &message) if (auto tc = dynamic_cast(this->underlyingChannel_.get())) { - thread = getThread(tc); + getThread(tc); } else if (auto tc = dynamic_cast(this->channel_.get())) { - thread = getThread(tc); - } - else - { - qCWarning(chatterinoCommon) << "Failed to create new reply thread"; - // Unable to create new reply thread. - // TODO(dnsge): Should probably notify user? - return; + getThread(tc); } } - else - { - thread = message->replyThread; - } - this->split_->setInputReply(thread); + this->split_->setInputReply(message); } void ChannelView::showReplyThreadPopup(const MessagePtr &message) diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index dd99bb906dd..5fa6d6a0dc5 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -1542,7 +1542,7 @@ void Split::drag() stopDraggingSplit(); } -void Split::setInputReply(const std::shared_ptr &reply) +void Split::setInputReply(const std::shared_ptr &reply) { this->input_->setReply(reply); } diff --git a/src/widgets/splits/Split.hpp b/src/widgets/splits/Split.hpp index 2275a07e86b..94fc4f7c09e 100644 --- a/src/widgets/splits/Split.hpp +++ b/src/widgets/splits/Split.hpp @@ -15,7 +15,6 @@ namespace chatterino { class ChannelView; -class MessageThread; class SplitHeader; class SplitInput; class SplitContainer; @@ -75,7 +74,7 @@ class Split : public BaseWidget void setContainer(SplitContainer *container); - void setInputReply(const std::shared_ptr &reply); + void setInputReply(const std::shared_ptr &reply); static pajlada::Signals::Signal modifierStatusChanged; diff --git a/src/widgets/splits/SplitInput.cpp b/src/widgets/splits/SplitInput.cpp index 5c92c99f926..add3b67fd5f 100644 --- a/src/widgets/splits/SplitInput.cpp +++ b/src/widgets/splits/SplitInput.cpp @@ -359,7 +359,7 @@ QString SplitInput::handleSendMessage(std::vector &arguments) if (this->enableInlineReplying_) { // Remove @username prefix that is inserted when doing inline replies - message.remove(0, this->replyThread_->root()->displayName.length() + + message.remove(0, this->replyThread_->displayName.length() + 1); // remove "@username" if (!message.isEmpty() && message.at(0) == ' ') @@ -373,7 +373,7 @@ QString SplitInput::handleSendMessage(std::vector &arguments) getApp()->commands->execCommand(message, c, false); // Reply within TwitchChannel - tc->sendReply(sendMessage, this->replyThread_->rootId()); + tc->sendReply(sendMessage, this->replyThread_->id); this->postMessageSend(message, arguments); return ""; @@ -992,7 +992,7 @@ void SplitInput::editTextChanged() // We need to verify that // 1. the @username prefix exists and // 2. if a character exists after the @username, it is a space - QString replyPrefix = "@" + this->replyThread_->root()->displayName; + QString replyPrefix = "@" + this->replyThread_->displayName; if (!text.startsWith(replyPrefix) || (text.length() > replyPrefix.length() && text.at(replyPrefix.length()) != ' ')) @@ -1065,7 +1065,7 @@ void SplitInput::giveFocus(Qt::FocusReason reason) this->ui_.textEdit->setFocus(reason); } -void SplitInput::setReply(std::shared_ptr reply, +void SplitInput::setReply(std::shared_ptr reply, bool showReplyingLabel) { this->replyThread_ = std::move(reply); @@ -1073,7 +1073,7 @@ void SplitInput::setReply(std::shared_ptr reply, if (this->enableInlineReplying_) { // Only enable reply label if inline replying - auto replyPrefix = "@" + this->replyThread_->root()->displayName; + auto replyPrefix = "@" + this->replyThread_->displayName; auto plainText = this->ui_.textEdit->toPlainText().trimmed(); if (!plainText.startsWith(replyPrefix)) { @@ -1086,7 +1086,7 @@ void SplitInput::setReply(std::shared_ptr reply, this->ui_.textEdit->resetCompletion(); } this->ui_.replyLabel->setText("Replying to @" + - this->replyThread_->root()->displayName); + this->replyThread_->displayName); } } diff --git a/src/widgets/splits/SplitInput.hpp b/src/widgets/splits/SplitInput.hpp index 5a15ebbdfcb..c6da57834b4 100644 --- a/src/widgets/splits/SplitInput.hpp +++ b/src/widgets/splits/SplitInput.hpp @@ -1,6 +1,7 @@ #pragma once #include "widgets/BaseWidget.hpp" +#include "messages/Message.hpp" #include #include @@ -19,7 +20,6 @@ class Split; class EmotePopup; class InputCompletionPopup; class EffectLabel; -class MessageThread; class ResizingTextEdit; class ChannelView; enum class CompletionKind; @@ -40,7 +40,7 @@ class SplitInput : public BaseWidget QString getInputText() const; void insertText(const QString &text); - void setReply(std::shared_ptr reply, + void setReply(std::shared_ptr reply, bool showInlineReplying = true); void setPlaceholderText(const QString &text); @@ -135,7 +135,7 @@ class SplitInput : public BaseWidget EffectLabel *cancelReplyButton; } ui_{}; - std::shared_ptr replyThread_ = nullptr; + std::shared_ptr replyThread_ = nullptr; bool enableInlineReplying_; pajlada::Signals::SignalHolder managedConnections_; From 598c861bd9ed83a11b542d1e4c573c27dcc763f5 Mon Sep 17 00:00:00 2001 From: iProdigy <8106344+iProdigy@users.noreply.github.com> Date: Sat, 28 Oct 2023 18:08:07 -0700 Subject: [PATCH 04/17] refactor: use threads getter Co-authored-by: nerix --- src/providers/twitch/IrcMessageHandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index 5dc5d66e616..fa33fe072f1 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -523,8 +523,8 @@ void IrcMessageHandler::populateReply( } else { - auto parentThreadIt = channel->threads_.find(parentID); - if (parentThreadIt != channel->threads_.end() && !parentThreadIt->second.expired()) + auto parentThreadIt = channel->threads().find(parentID); + if (parentThreadIt != channel->threads().end() && !parentThreadIt->second.expired()) { parent = parentThreadIt->second.lock()->root(); } From 629cb0519e2965e2382d100f233969dcae77d8cb Mon Sep 17 00:00:00 2001 From: iProdigy Date: Sat, 28 Oct 2023 18:31:17 -0700 Subject: [PATCH 05/17] chore: use getter in rest of IrcMessageHandler --- src/providers/twitch/IrcMessageHandler.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index fa33fe072f1..53aacb51156 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -456,9 +456,9 @@ void IrcMessageHandler::populateReply( if (const auto it = tags.find("reply-thread-parent-msg-id"); it != tags.end()) { const QString replyID = it.value().toString(); - auto threadIt = channel->threads_.find(replyID); + auto threadIt = channel->threads().find(replyID); std::shared_ptr rootThread; - if (threadIt != channel->threads_.end()) + if (threadIt != channel->threads().end()) { auto owned = threadIt->second.lock(); if (owned) @@ -604,9 +604,9 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message, if (const auto it = tags.find("reply-thread-parent-msg-id"); it != tags.end()) { const QString replyID = it.value().toString(); - auto threadIt = channel->threads_.find(replyID); + auto threadIt = channel->threads().find(replyID); std::shared_ptr rootThread; - if (threadIt != channel->threads_.end() && !threadIt->second.expired()) + if (threadIt != channel->threads().end() && !threadIt->second.expired()) { // Thread already exists (has a reply) auto thread = threadIt->second.lock(); @@ -646,8 +646,8 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message, } else { - auto parentThreadIt = channel->threads_.find(parentID); - if (parentThreadIt != channel->threads_.end() && !parentThreadIt->second.expired()) + auto parentThreadIt = channel->threads().find(parentID); + if (parentThreadIt != channel->threads().end() && !parentThreadIt->second.expired()) { parent = parentThreadIt->second.lock()->root(); } From c96dfacfd62cb939555128aab8e6fbf2a1f269cb Mon Sep 17 00:00:00 2001 From: iProdigy Date: Sat, 28 Oct 2023 18:54:42 -0700 Subject: [PATCH 06/17] refactor: use MessagePtr alias --- src/messages/Message.hpp | 6 +++--- src/providers/twitch/IrcMessageHandler.cpp | 4 ++-- src/providers/twitch/TwitchMessageBuilder.cpp | 4 ++-- src/providers/twitch/TwitchMessageBuilder.hpp | 4 ++-- src/widgets/splits/Split.cpp | 2 +- src/widgets/splits/Split.hpp | 2 +- src/widgets/splits/SplitInput.cpp | 2 +- src/widgets/splits/SplitInput.hpp | 4 ++-- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/messages/Message.hpp b/src/messages/Message.hpp index 51805c0e432..a503ea2757a 100644 --- a/src/messages/Message.hpp +++ b/src/messages/Message.hpp @@ -53,6 +53,8 @@ enum class MessageFlag : int64_t { }; using MessageFlags = FlagsEnum; +struct Message; +using MessagePtr = std::shared_ptr; struct Message { Message(); ~Message(); @@ -88,13 +90,11 @@ struct Message { // the reply thread will be cleaned up by the TwitchChannel. // The root of the thread does not have replyThread set. std::shared_ptr replyThread; - std::shared_ptr replyParent; + MessagePtr replyParent; uint32_t count = 1; std::vector> elements; ScrollbarHighlight getScrollBarHighlight() const; }; -using MessagePtr = std::shared_ptr; - } // namespace chatterino diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index 53aacb51156..85abaed3330 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -513,7 +513,7 @@ void IrcMessageHandler::populateReply( if (const auto parentIt = tags.find("reply-parent-msg-id"); parentIt != tags.end()) { const QString parentID = parentIt.value().toString(); - std::shared_ptr parent; + MessagePtr parent; if (replyID == parentID) { if (rootThread) @@ -636,7 +636,7 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message, if (const auto parentIt = tags.find("reply-parent-msg-id"); parentIt != tags.end()) { const QString parentID = parentIt.value().toString(); - std::shared_ptr parent; + MessagePtr parent; if (replyID == parentID) { if (rootThread) diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 06d2484760b..1abc18c8bc1 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -629,7 +629,7 @@ void TwitchMessageBuilder::parseThread() // enable reply flag this->message().flags.set(MessageFlag::ReplyMessage); - std::shared_ptr threadRoot; + MessagePtr threadRoot; if (!this->parent_) { threadRoot = this->thread_->root(); } else { @@ -1817,7 +1817,7 @@ void TwitchMessageBuilder::setThread(std::shared_ptr thread) this->thread_ = std::move(thread); } -void TwitchMessageBuilder::setParent(std::shared_ptr parent) +void TwitchMessageBuilder::setParent(MessagePtr parent) { this->parent_ = std::move(parent); } diff --git a/src/providers/twitch/TwitchMessageBuilder.hpp b/src/providers/twitch/TwitchMessageBuilder.hpp index 2092f7dcd65..cc1681acb1b 100644 --- a/src/providers/twitch/TwitchMessageBuilder.hpp +++ b/src/providers/twitch/TwitchMessageBuilder.hpp @@ -58,7 +58,7 @@ class TwitchMessageBuilder : public SharedMessageBuilder MessagePtr build() override; void setThread(std::shared_ptr thread); - void setParent(std::shared_ptr parent); + void setParent(MessagePtr parent); void setMessageOffset(int offset); static void appendChannelPointRewardMessage( @@ -132,7 +132,7 @@ class TwitchMessageBuilder : public SharedMessageBuilder bool bitsStacked = false; bool historicalMessage_ = false; std::shared_ptr thread_; - std::shared_ptr parent_; + MessagePtr parent_; /** * Starting offset to be used on index-based operations on `originalMessage_`. diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index 5fa6d6a0dc5..527d559c86e 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -1542,7 +1542,7 @@ void Split::drag() stopDraggingSplit(); } -void Split::setInputReply(const std::shared_ptr &reply) +void Split::setInputReply(const MessagePtr &reply) { this->input_->setReply(reply); } diff --git a/src/widgets/splits/Split.hpp b/src/widgets/splits/Split.hpp index 94fc4f7c09e..493cdf2f100 100644 --- a/src/widgets/splits/Split.hpp +++ b/src/widgets/splits/Split.hpp @@ -74,7 +74,7 @@ class Split : public BaseWidget void setContainer(SplitContainer *container); - void setInputReply(const std::shared_ptr &reply); + void setInputReply(const MessagePtr &reply); static pajlada::Signals::Signal modifierStatusChanged; diff --git a/src/widgets/splits/SplitInput.cpp b/src/widgets/splits/SplitInput.cpp index add3b67fd5f..d1b8dbb988f 100644 --- a/src/widgets/splits/SplitInput.cpp +++ b/src/widgets/splits/SplitInput.cpp @@ -1065,7 +1065,7 @@ void SplitInput::giveFocus(Qt::FocusReason reason) this->ui_.textEdit->setFocus(reason); } -void SplitInput::setReply(std::shared_ptr reply, +void SplitInput::setReply(MessagePtr reply, bool showReplyingLabel) { this->replyThread_ = std::move(reply); diff --git a/src/widgets/splits/SplitInput.hpp b/src/widgets/splits/SplitInput.hpp index c6da57834b4..3afa2b31279 100644 --- a/src/widgets/splits/SplitInput.hpp +++ b/src/widgets/splits/SplitInput.hpp @@ -40,7 +40,7 @@ class SplitInput : public BaseWidget QString getInputText() const; void insertText(const QString &text); - void setReply(std::shared_ptr reply, + void setReply(MessagePtr reply, bool showInlineReplying = true); void setPlaceholderText(const QString &text); @@ -135,7 +135,7 @@ class SplitInput : public BaseWidget EffectLabel *cancelReplyButton; } ui_{}; - std::shared_ptr replyThread_ = nullptr; + MessagePtr replyThread_ = nullptr; bool enableInlineReplying_; pajlada::Signals::SignalHolder managedConnections_; From 8dffbb66e3756cbb646c0e888dfb4acd40351475 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Sat, 28 Oct 2023 19:05:56 -0700 Subject: [PATCH 07/17] chore: reformat --- src/providers/twitch/IrcMessageHandler.cpp | 20 ++++++++++++------- src/providers/twitch/TwitchMessageBuilder.cpp | 7 +++++-- src/widgets/splits/SplitInput.cpp | 3 +-- src/widgets/splits/SplitInput.hpp | 5 ++--- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index 85abaed3330..35a60019970 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -453,7 +453,8 @@ void IrcMessageHandler::populateReply( const std::vector &otherLoaded, TwitchMessageBuilder &builder) { const auto &tags = message->tags(); - if (const auto it = tags.find("reply-thread-parent-msg-id"); it != tags.end()) + if (const auto it = tags.find("reply-thread-parent-msg-id"); + it != tags.end()) { const QString replyID = it.value().toString(); auto threadIt = channel->threads().find(replyID); @@ -499,7 +500,7 @@ void IrcMessageHandler::populateReply( if (foundMessage) { std::shared_ptr newThread = - std::make_shared(foundMessage); + std::make_shared(foundMessage); updateReplyParticipatedStatus(tags, message->nick(), builder, newThread, true); @@ -510,7 +511,8 @@ void IrcMessageHandler::populateReply( } } - if (const auto parentIt = tags.find("reply-parent-msg-id"); parentIt != tags.end()) + if (const auto parentIt = tags.find("reply-parent-msg-id"); + parentIt != tags.end()) { const QString parentID = parentIt.value().toString(); MessagePtr parent; @@ -524,7 +526,8 @@ void IrcMessageHandler::populateReply( else { auto parentThreadIt = channel->threads().find(parentID); - if (parentThreadIt != channel->threads().end() && !parentThreadIt->second.expired()) + if (parentThreadIt != channel->threads().end() && + !parentThreadIt->second.expired()) { parent = parentThreadIt->second.lock()->root(); } @@ -601,7 +604,8 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message, TwitchMessageBuilder builder(chan.get(), _message, args, content, isAction); builder.setMessageOffset(messageOffset); - if (const auto it = tags.find("reply-thread-parent-msg-id"); it != tags.end()) + if (const auto it = tags.find("reply-thread-parent-msg-id"); + it != tags.end()) { const QString replyID = it.value().toString(); auto threadIt = channel->threads().find(replyID); @@ -633,7 +637,8 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message, } } - if (const auto parentIt = tags.find("reply-parent-msg-id"); parentIt != tags.end()) + if (const auto parentIt = tags.find("reply-parent-msg-id"); + parentIt != tags.end()) { const QString parentID = parentIt.value().toString(); MessagePtr parent; @@ -647,7 +652,8 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message, else { auto parentThreadIt = channel->threads().find(parentID); - if (parentThreadIt != channel->threads().end() && !parentThreadIt->second.expired()) + if (parentThreadIt != channel->threads().end() && + !parentThreadIt->second.expired()) { parent = parentThreadIt->second.lock()->root(); } diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 1abc18c8bc1..9605f490915 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -630,9 +630,12 @@ void TwitchMessageBuilder::parseThread() this->message().flags.set(MessageFlag::ReplyMessage); MessagePtr threadRoot; - if (!this->parent_) { + if (!this->parent_) + { threadRoot = this->thread_->root(); - } else { + } + else + { threadRoot = this->parent_; } diff --git a/src/widgets/splits/SplitInput.cpp b/src/widgets/splits/SplitInput.cpp index d1b8dbb988f..b347ccdd71d 100644 --- a/src/widgets/splits/SplitInput.cpp +++ b/src/widgets/splits/SplitInput.cpp @@ -1065,8 +1065,7 @@ void SplitInput::giveFocus(Qt::FocusReason reason) this->ui_.textEdit->setFocus(reason); } -void SplitInput::setReply(MessagePtr reply, - bool showReplyingLabel) +void SplitInput::setReply(MessagePtr reply, bool showReplyingLabel) { this->replyThread_ = std::move(reply); diff --git a/src/widgets/splits/SplitInput.hpp b/src/widgets/splits/SplitInput.hpp index 3afa2b31279..56504437a27 100644 --- a/src/widgets/splits/SplitInput.hpp +++ b/src/widgets/splits/SplitInput.hpp @@ -1,7 +1,7 @@ #pragma once -#include "widgets/BaseWidget.hpp" #include "messages/Message.hpp" +#include "widgets/BaseWidget.hpp" #include #include @@ -40,8 +40,7 @@ class SplitInput : public BaseWidget QString getInputText() const; void insertText(const QString &text); - void setReply(MessagePtr reply, - bool showInlineReplying = true); + void setReply(MessagePtr reply, bool showInlineReplying = true); void setPlaceholderText(const QString &text); /** From c86a5e21c219e00ca933da480444b1f83a3ad1ea Mon Sep 17 00:00:00 2001 From: iProdigy <8106344+iProdigy@users.noreply.github.com> Date: Sat, 28 Oct 2023 19:11:53 -0700 Subject: [PATCH 08/17] chore: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62d908ee670..d7d2e9f0150 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Minor: The account switcher is now styled to match your theme. (#4817) - Minor: Add an invisible resize handle to the bottom of frameless user info popups and reply thread popups. (#4795) - Minor: The installer now checks for the VC Runtime version and shows more info when it's outdated. (#4847) +- Bugfix: Fixed thread popup window missing messages for nested threads. (#4923) - Bugfix: Fixed an issue where certain emojis did not send to Twitch chat correctly. (#4840) - Bugfix: Fixed capitalized channel names in log inclusion list not being logged. (#4848) - Bugfix: Trimmed custom streamlink paths on all platforms making sure you don't accidentally add spaces at the beginning or end of its path. (#4834) From b9cca1e7115c4cb9ab8a0acde10fcb33687ccf82 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Tue, 31 Oct 2023 16:03:04 -0700 Subject: [PATCH 09/17] fix: avoid illegal access when direct parent is not loaded --- src/singletons/helper/LoggingChannel.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/singletons/helper/LoggingChannel.cpp b/src/singletons/helper/LoggingChannel.cpp index 084c8dd8adf..8ab9000eb96 100644 --- a/src/singletons/helper/LoggingChannel.cpp +++ b/src/singletons/helper/LoggingChannel.cpp @@ -132,7 +132,14 @@ void LoggingChannel::addMessage(MessagePtr message) qsizetype colonIndex = messageText.indexOf(':'); if (colonIndex != -1) { - QString rootMessageChatter = message->replyParent->loginName; + QString rootMessageChatter; + if (message->replyParent) { + rootMessageChatter = message->replyParent->loginName; + } else { + // we actually want to use 'reply-parent-user-login' tag here, + // but it's not worth storing just for this edge case + rootMessageChatter = message->replyThread->root()->loginName; + } messageText.insert(colonIndex + 1, " @" + rootMessageChatter); } } From 2bf88b42ead9718d481cc176a2e4e48143a3b927 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Tue, 31 Oct 2023 16:25:01 -0700 Subject: [PATCH 10/17] feat: add menu option to reply to root thread message --- src/widgets/helper/ChannelView.cpp | 4 ++++ src/widgets/splits/SplitInput.cpp | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index 7081637d21f..6402fa33a7b 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -2258,6 +2258,10 @@ void ChannelView::addMessageContextMenuItems( if (messagePtr->replyThread != nullptr) { + menu.addAction("Reply to &original thread", [this, &messagePtr] { + this->setInputReply(messagePtr->replyThread->root()); + }); + menu.addAction("View &thread", [this, &messagePtr] { this->showReplyThreadPopup(messagePtr); }); diff --git a/src/widgets/splits/SplitInput.cpp b/src/widgets/splits/SplitInput.cpp index b347ccdd71d..071b0076e3f 100644 --- a/src/widgets/splits/SplitInput.cpp +++ b/src/widgets/splits/SplitInput.cpp @@ -1067,6 +1067,21 @@ void SplitInput::giveFocus(Qt::FocusReason reason) void SplitInput::setReply(MessagePtr reply, bool showReplyingLabel) { + auto oldParent = this->replyThread_; + if (this->enableInlineReplying_ && oldParent) + { + // Remove old reply prefix + auto replyPrefix = "@" + oldParent->displayName; + auto plainText = this->ui_.textEdit->toPlainText().trimmed(); + if (plainText.startsWith(replyPrefix)) + { + plainText.remove(0, replyPrefix.length()); + } + this->ui_.textEdit->setPlainText(plainText.trimmed()); + this->ui_.textEdit->moveCursor(QTextCursor::EndOfBlock); + this->ui_.textEdit->resetCompletion(); + } + this->replyThread_ = std::move(reply); if (this->enableInlineReplying_) From 5ae2f2f8c8c1f3bfac2ff1c8922ff08faef426cd Mon Sep 17 00:00:00 2001 From: iProdigy <8106344+iProdigy@users.noreply.github.com> Date: Tue, 31 Oct 2023 17:08:18 -0700 Subject: [PATCH 11/17] chore(LoggingChannel): reformat --- src/singletons/helper/LoggingChannel.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/singletons/helper/LoggingChannel.cpp b/src/singletons/helper/LoggingChannel.cpp index 8ab9000eb96..d73ec79e533 100644 --- a/src/singletons/helper/LoggingChannel.cpp +++ b/src/singletons/helper/LoggingChannel.cpp @@ -133,9 +133,12 @@ void LoggingChannel::addMessage(MessagePtr message) if (colonIndex != -1) { QString rootMessageChatter; - if (message->replyParent) { + if (message->replyParent) + { rootMessageChatter = message->replyParent->loginName; - } else { + } + else + { // we actually want to use 'reply-parent-user-login' tag here, // but it's not worth storing just for this edge case rootMessageChatter = message->replyThread->root()->loginName; From 839ff49a85fc516c9cd84f8658c393065c852096 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Tue, 31 Oct 2023 17:14:40 -0700 Subject: [PATCH 12/17] fix: use correct message reference --- src/providers/twitch/IrcMessageHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index f8480b903a4..e9c98df7d3b 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -1327,7 +1327,7 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *message, { // Thread already exists (has a reply) auto thread = threadIt->second.lock(); - updateReplyParticipatedStatus(tags, _message->nick(), builder, + updateReplyParticipatedStatus(tags, message->nick(), builder, thread, false); builder.setThread(thread); rootThread = thread; From 46715de938efc428119256184ab1be69bf0a88c9 Mon Sep 17 00:00:00 2001 From: iProdigy <8106344+iProdigy@users.noreply.github.com> Date: Tue, 31 Oct 2023 17:35:44 -0700 Subject: [PATCH 13/17] chore: update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c8f04a8a20..512db86a4f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ - Minor: The account switcher is now styled to match your theme. (#4817) - Minor: Add an invisible resize handle to the bottom of frameless user info popups and reply thread popups. (#4795) - Minor: The installer now checks for the VC Runtime version and shows more info when it's outdated. (#4847) -- Bugfix: Fixed thread popup window missing messages for nested threads. (#4923) +- Minor: Add menu actions to reply directly to a message or the original thread root. (#4923) - Bugfix: Fixed an issue where certain emojis did not send to Twitch chat correctly. (#4840) - Bugfix: Fixed capitalized channel names in log inclusion list not being logged. (#4848) - Bugfix: Trimmed custom streamlink paths on all platforms making sure you don't accidentally add spaces at the beginning or end of its path. (#4834) @@ -25,6 +25,7 @@ - Bugfix: Fixed the input completion popup from disappearing when clicking on it on Windows and macOS. (#4876) - Bugfix: Fixed double-click text selection moving its position with each new message. (#4898) - Bugfix: Fixed an issue where notifications on Windows would contain no or an old avatar. (#4899) +- Bugfix: Fixed thread popup window missing messages for nested threads. (#4923) - Dev: Change clang-format from v14 to v16. (#4929) - Dev: Fixed UTF16 encoding of `modes` file for the installer. (#4791) - Dev: Temporarily disable High DPI scaling on Qt6 builds on Windows. (#4767) From b63918df1dbbf2be01094626ddd07672e43f4e1a Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 5 Nov 2023 12:13:50 +0100 Subject: [PATCH 14/17] Directly call builder.setParent if message is a reply to the root thread --- src/providers/twitch/IrcMessageHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index e9c98df7d3b..51d81ed2773 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -310,7 +310,7 @@ void populateReply(TwitchChannel *channel, Communi::IrcMessage *message, { if (rootThread) { - parent = rootThread->root(); + builder.setParent(rootThread->root()); } } else From 827cc2f8180db48b0508aeddab2f398098718593 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 5 Nov 2023 12:14:14 +0100 Subject: [PATCH 15/17] Directly call builder.setParent when looking through channel threads, and don't rely on expired() --- src/providers/twitch/IrcMessageHandler.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index 51d81ed2773..963305a4b51 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -316,10 +316,13 @@ void populateReply(TwitchChannel *channel, Communi::IrcMessage *message, else { auto parentThreadIt = channel->threads().find(parentID); - if (parentThreadIt != channel->threads().end() && - !parentThreadIt->second.expired()) + if (parentThreadIt != channel->threads().end()) { - parent = parentThreadIt->second.lock()->root(); + auto thread = parentThreadIt->second.lock(); + if (thread) + { + builder.setParent(thread->root()); + } } else { From 38b74ced5ba4771fc9c327f26a6a9970a8f2c625 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 5 Nov 2023 12:14:35 +0100 Subject: [PATCH 16/17] Directly call builder.setParent if we have to iterate through messages. Also only call it if we actually found a message --- src/providers/twitch/IrcMessageHandler.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index 963305a4b51..2737ca5289f 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -305,7 +305,6 @@ void populateReply(TwitchChannel *channel, Communi::IrcMessage *message, parentIt != tags.end()) { const QString parentID = parentIt.value().toString(); - MessagePtr parent; if (replyID == parentID) { if (rootThread) @@ -326,10 +325,13 @@ void populateReply(TwitchChannel *channel, Communi::IrcMessage *message, } else { - parent = channel->findMessage(parentID); + auto parent = channel->findMessage(parentID); + if (parent) + { + builder.setParent(parent); + } } } - builder.setParent(parent); } } } From a35a43dfb54e95e3b26e5b9ecd104f475330bf52 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 5 Nov 2023 17:05:51 +0100 Subject: [PATCH 17/17] Apply same setParent changes to addMessage --- src/providers/twitch/IrcMessageHandler.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index 2737ca5289f..10df4435991 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -1359,28 +1359,33 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *message, parentIt != tags.end()) { const QString parentID = parentIt.value().toString(); - MessagePtr parent; if (replyID == parentID) { if (rootThread) { - parent = rootThread->root(); + builder.setParent(rootThread->root()); } } else { auto parentThreadIt = channel->threads().find(parentID); - if (parentThreadIt != channel->threads().end() && - !parentThreadIt->second.expired()) + if (parentThreadIt != channel->threads().end()) { - parent = parentThreadIt->second.lock()->root(); + auto thread = parentThreadIt->second.lock(); + if (thread) + { + builder.setParent(thread->root()); + } } else { - parent = channel->findMessage(parentID); + auto parent = channel->findMessage(parentID); + if (parent) + { + builder.setParent(parent); + } } } - builder.setParent(parent); } }