Automatically load recent messages on reconnect (#3878)

* Add working reconnect recent messages

* Rename method to messagesUpdated

* Use audo declarations

* Add docs to new LimitedQueue methods

* Add more documentation, try atomic loading flag

* Update CHANGELOG.md

* Remove unused include

* Rename 'reconnected' signal to 'connected'

* Reserve before filtering on arbitrary update

* Extract recent messages fetching to own class

* Use std::atomic_flag instead of std::atomic_bool

* Add PostToThread include

* Add chatterino.recentmessages logging

* Remove unneeded parameters, lambda move capture

* Remove TwitchChannel::buildRecentMessages

* Add documentation, use more clear method name

* Reword changelog entry

I think it sounds better like this :)

* Rework how filling in missing messages is handled

This should hopefully prevent issues with filtered channels with old messages
that no longer exist in the underlying channel

* Check existing messages when looking for reply

* Clean up string distribution in file

* Try to improve documentation

* Use std::function for RecentMessagesApi

* Only trigger filledInMessages if we inserted

* Remove old unused lines

* Use make_shared<MessageLayout> instead of new MessageLayout

* Alphabetize QLogging categories

* Reorder CHANGELOG.md
This commit is contained in:
Daniel Sage
2022-08-06 12:18:34 -04:00
committed by GitHub
parent 2dd37ca210
commit 46f43f3ce8
15 changed files with 580 additions and 174 deletions
+75 -1
View File
@@ -220,7 +220,7 @@ void Channel::disableAllMessages()
}
}
void Channel::addMessagesAtStart(std::vector<MessagePtr> &_messages)
void Channel::addMessagesAtStart(const std::vector<MessagePtr> &_messages)
{
std::vector<MessagePtr> addedMessages =
this->messages_.pushFront(_messages);
@@ -231,6 +231,80 @@ void Channel::addMessagesAtStart(std::vector<MessagePtr> &_messages)
}
}
void Channel::fillInMissingMessages(const std::vector<MessagePtr> &messages)
{
auto snapshot = this->getMessageSnapshot();
std::unordered_set<QString> existingMessageIds;
existingMessageIds.reserve(snapshot.size());
// First, collect the ids of every message already present in the channel
for (auto &msg : snapshot)
{
if (msg->flags.has(MessageFlag::System) || msg->id.isEmpty())
{
continue;
}
existingMessageIds.insert(msg->id);
}
bool anyInserted = false;
// Keep track of the last message in the channel. We need this value
// to allow concurrent appends to the end of the channel while still
// being able to insert just-loaded historical messages at the end
// in the correct place.
auto lastMsg = snapshot[snapshot.size() - 1];
for (auto &msg : messages)
{
// check if message already exists
if (existingMessageIds.count(msg->id) != 0)
{
continue;
}
// If we get to this point, we know we'll be inserting a message
anyInserted = true;
bool insertedFlag = false;
for (auto &snapshotMsg : snapshot)
{
if (snapshotMsg->flags.has(MessageFlag::System))
{
continue;
}
if (msg->serverReceivedTime < snapshotMsg->serverReceivedTime)
{
// We found the first message that comes after the current message.
// Therefore, we can put the current message directly before. We
// assume that the messages we are filling in are in ascending
// order by serverReceivedTime.
this->messages_.insertBefore(snapshotMsg, msg);
insertedFlag = true;
break;
}
}
if (!insertedFlag)
{
// We never found a message already in the channel that came after
// the current message. Put it at the end and make sure to update
// which message is considered "the end".
this->messages_.insertAfter(lastMsg, msg);
lastMsg = msg;
}
}
if (anyInserted)
{
// We only invoke a signal once at the end of filling all messages to
// prevent doing any unnecessary repaints.
this->filledInMessages.invoke(messages);
}
}
void Channel::replaceMessage(MessagePtr message, MessagePtr replacement)
{
int index = this->messages_.replaceItem(message, replacement);
+10 -1
View File
@@ -57,8 +57,12 @@ public:
messageAppended;
pajlada::Signals::Signal<std::vector<MessagePtr> &> messagesAddedAtStart;
pajlada::Signals::Signal<size_t, MessagePtr &> messageReplaced;
/// Invoked when some number of messages were filled in using time received
pajlada::Signals::Signal<const std::vector<MessagePtr> &> filledInMessages;
pajlada::Signals::NoArgSignal destroyed;
pajlada::Signals::NoArgSignal displayNameChanged;
/// Invoked when AbstractIrcServer::onReadConnected occurs
pajlada::Signals::NoArgSignal connected;
Type getType() const;
const QString &getName() const;
@@ -75,12 +79,17 @@ public:
void addMessage(
MessagePtr message,
boost::optional<MessageFlags> overridingFlags = boost::none);
void addMessagesAtStart(std::vector<MessagePtr> &messages_);
void addMessagesAtStart(const std::vector<MessagePtr> &messages_);
/// Inserts the given messages in order by Message::serverReceivedTime.
void fillInMissingMessages(const std::vector<MessagePtr> &messages);
void addOrReplaceTimeout(MessagePtr message);
void disableAllMessages();
void replaceMessage(MessagePtr message, MessagePtr replacement);
void replaceMessage(size_t index, MessagePtr replacement);
void deleteMessage(QString messageID);
MessagePtr findMessage(QString messageID);
bool hasMessages() const;
+3 -1
View File
@@ -16,6 +16,7 @@ Q_LOGGING_CATEGORY(chatterinoEmoji, "chatterino.emoji", logThreshold);
Q_LOGGING_CATEGORY(chatterinoEnv, "chatterino.env", logThreshold);
Q_LOGGING_CATEGORY(chatterinoFfzemotes, "chatterino.ffzemotes", logThreshold);
Q_LOGGING_CATEGORY(chatterinoHelper, "chatterino.helper", logThreshold);
Q_LOGGING_CATEGORY(chatterinoHighlights, "chatterino.highlights", logThreshold);
Q_LOGGING_CATEGORY(chatterinoHotkeys, "chatterino.hotkeys", logThreshold);
Q_LOGGING_CATEGORY(chatterinoHTTP, "chatterino.http", logThreshold);
Q_LOGGING_CATEGORY(chatterinoImage, "chatterino.image", logThreshold);
@@ -30,6 +31,8 @@ Q_LOGGING_CATEGORY(chatterinoNotification, "chatterino.notification",
Q_LOGGING_CATEGORY(chatterinoNuulsuploader, "chatterino.nuulsuploader",
logThreshold);
Q_LOGGING_CATEGORY(chatterinoPubSub, "chatterino.pubsub", logThreshold);
Q_LOGGING_CATEGORY(chatterinoRecentMessages, "chatterino.recentmessages",
logThreshold);
Q_LOGGING_CATEGORY(chatterinoStreamlink, "chatterino.streamlink", logThreshold);
Q_LOGGING_CATEGORY(chatterinoStreamerMode, "chatterino.streamermode",
logThreshold);
@@ -40,4 +43,3 @@ Q_LOGGING_CATEGORY(chatterinoWebsocket, "chatterino.websocket", logThreshold);
Q_LOGGING_CATEGORY(chatterinoWidget, "chatterino.widget", logThreshold);
Q_LOGGING_CATEGORY(chatterinoWindowmanager, "chatterino.windowmanager",
logThreshold);
Q_LOGGING_CATEGORY(chatterinoHighlights, "chatterino.highlights", logThreshold);
+2 -1
View File
@@ -12,6 +12,7 @@ Q_DECLARE_LOGGING_CATEGORY(chatterinoEmoji);
Q_DECLARE_LOGGING_CATEGORY(chatterinoEnv);
Q_DECLARE_LOGGING_CATEGORY(chatterinoFfzemotes);
Q_DECLARE_LOGGING_CATEGORY(chatterinoHelper);
Q_DECLARE_LOGGING_CATEGORY(chatterinoHighlights);
Q_DECLARE_LOGGING_CATEGORY(chatterinoHotkeys);
Q_DECLARE_LOGGING_CATEGORY(chatterinoHTTP);
Q_DECLARE_LOGGING_CATEGORY(chatterinoImage);
@@ -23,6 +24,7 @@ Q_DECLARE_LOGGING_CATEGORY(chatterinoNativeMessage);
Q_DECLARE_LOGGING_CATEGORY(chatterinoNotification);
Q_DECLARE_LOGGING_CATEGORY(chatterinoNuulsuploader);
Q_DECLARE_LOGGING_CATEGORY(chatterinoPubSub);
Q_DECLARE_LOGGING_CATEGORY(chatterinoRecentMessages);
Q_DECLARE_LOGGING_CATEGORY(chatterinoStreamlink);
Q_DECLARE_LOGGING_CATEGORY(chatterinoStreamerMode);
Q_DECLARE_LOGGING_CATEGORY(chatterinoTokenizer);
@@ -31,4 +33,3 @@ Q_DECLARE_LOGGING_CATEGORY(chatterinoUpdate);
Q_DECLARE_LOGGING_CATEGORY(chatterinoWebsocket);
Q_DECLARE_LOGGING_CATEGORY(chatterinoWidget);
Q_DECLARE_LOGGING_CATEGORY(chatterinoWindowmanager);
Q_DECLARE_LOGGING_CATEGORY(chatterinoHighlights);