diff --git a/chatterino.pro b/chatterino.pro index 314090ddf..d24cde4ca 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -188,7 +188,6 @@ HEADERS += \ src/const.hpp \ src/widgets/tooltipwidget.hpp \ src/precompiled_headers.hpp \ - src/messages/wordflags.hpp \ src/singletons/thememanager.hpp \ src/twitch/twitchaccountmanager.hpp \ src/singletons/helper/completionmodel.hpp \ diff --git a/src/channel.cpp b/src/channel.cpp index ee1ae9cfd..d1e94fe5b 100644 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -56,15 +56,24 @@ void Channel::addMessage(std::shared_ptr message) this->messageAppended(message); } -void Channel::addMessagesAtStart(std::vector &messages) +void Channel::addMessagesAtStart(std::vector &_messages) { - std::vector addedMessages = this->messages.pushFront(messages); + std::vector addedMessages = this->messages.pushFront(_messages); if (addedMessages.size() != 0) { this->messagesAddedAtStart(addedMessages); } } +void Channel::replaceMessage(messages::SharedMessage message, messages::SharedMessage replacement) +{ + int index = this->messages.replaceItem(message, replacement); + + if (index >= 0) { + this->messageReplaced((size_t)index, replacement); + } +} + void Channel::addRecentChatter(const std::shared_ptr &message) { assert(!message->loginName.isEmpty()); diff --git a/src/channel.hpp b/src/channel.hpp index 0e3af4bd9..920987c08 100644 --- a/src/channel.hpp +++ b/src/channel.hpp @@ -27,12 +27,14 @@ public: boost::signals2::signal messageRemovedFromStart; boost::signals2::signal messageAppended; boost::signals2::signal &)> messagesAddedAtStart; + boost::signals2::signal messageReplaced; virtual bool isEmpty() const; messages::LimitedQueueSnapshot getMessageSnapshot(); void addMessage(messages::SharedMessage message); void addMessagesAtStart(std::vector &messages); + void replaceMessage(messages::SharedMessage message, messages::SharedMessage replacement); void addRecentChatter(const std::shared_ptr &message); struct NameOptions { diff --git a/src/messages/limitedqueue.hpp b/src/messages/limitedqueue.hpp index 8d19f7166..44e5b8c6c 100644 --- a/src/messages/limitedqueue.hpp +++ b/src/messages/limitedqueue.hpp @@ -132,6 +132,40 @@ public: return acceptedItems; } + // replaces a single item, return true if successful + int replaceItem(const T &item, const T &replacement) + { + std::lock_guard lock(this->mutex); + + int x = 0; + + for (size_t i = 0; i < this->chunks->size(); i++) { + Chunk &chunk = this->chunks->at(i); + + size_t start = i == 0 ? this->firstChunkOffset : 0; + size_t end = i == chunk->size() - 1 ? this->lastChunkEnd : chunk->size(); + + for (size_t j = start; j < end; j++) { + if (chunk->at(j) == item) { + Chunk newChunk = std::make_shared>(); + newChunk->resize(chunk->size()); + + for (size_t k = 0; k < chunk->size(); k++) { + newChunk->at(k) = chunk->at(k); + } + + newChunk->at(j) = replacement; + this->chunks->at(i) = newChunk; + + return x; + } + x++; + } + } + + return -1; + } + // void insertAfter(const std::vector &items, const T &index) messages::LimitedQueueSnapshot getSnapshot() diff --git a/src/messages/message.cpp b/src/messages/message.cpp index 281499843..26b01750e 100644 --- a/src/messages/message.cpp +++ b/src/messages/message.cpp @@ -55,6 +55,26 @@ std::vector &Message::getWords() return this->words; } +Message::MessageFlags Message::getFlags() const +{ + return this->flags; +} + +void Message::setFlags(MessageFlags _flags) +{ + this->flags = flags; +} + +void Message::addFlags(MessageFlags _flags) +{ + this->flags = (MessageFlags)((MessageFlagsType)this->flags | (MessageFlagsType)_flags); +} + +void Message::removeFlags(MessageFlags _flags) +{ + this->flags = (MessageFlags)((MessageFlagsType)this->flags & ~((MessageFlagsType)_flags)); +} + bool Message::isDisabled() const { return this->disabled; @@ -147,12 +167,13 @@ Message *Message::createSystemMessage(const QString &text) MessageColor(MessageColor::Type::System), singletons::FontManager::Medium, word, QString())); } + message->addFlags(Message::System); return message; } Message *Message::createTimeoutMessage(const QString &username, const QString &durationInSeconds, - const QString &reason) + const QString &reason, bool multipleTimes) { QString text; @@ -181,7 +202,14 @@ Message *Message::createTimeoutMessage(const QString &username, const QString &d } text.append("."); - return Message::createSystemMessage(text); + if (multipleTimes) { + text.append(" (multiple times)"); + } + + Message *message = Message::createSystemMessage(text); + message->addFlags(Message::Timeout); + message->timeoutUser = username; + return message; } } // namespace messages diff --git a/src/messages/message.hpp b/src/messages/message.hpp index c6f405228..ead25b538 100644 --- a/src/messages/message.hpp +++ b/src/messages/message.hpp @@ -14,10 +14,17 @@ namespace messages { class Message; typedef std::shared_ptr SharedMessage; +typedef uint32_t MessageFlagsType; class Message { public: + enum MessageFlags : uint32_t { + None = 0, + System = (1 << 1), + Timeout = (1 << 2), + }; + bool containsHighlightedPhrase() const; void setHighlight(bool value); const QString &getTimeoutUser() const; @@ -25,6 +32,10 @@ public: const QString &getContent() const; const std::chrono::time_point &getParseTime() const; std::vector &getWords(); + MessageFlags getFlags() const; + void setFlags(MessageFlags flags); + void addFlags(MessageFlags flags); + void removeFlags(MessageFlags flags); bool isDisabled() const; void setDisabled(bool value); const QString &getId() const; @@ -45,7 +56,7 @@ public: static Message *createSystemMessage(const QString &text); static Message *createTimeoutMessage(const QString &username, const QString &durationInSeconds, - const QString &reason); + const QString &reason, bool multipleTimes); private: static LazyLoadedImage *badgeStaff; @@ -58,6 +69,8 @@ private: static QRegularExpression *cheerRegex; + MessageFlags flags = MessageFlags::None; + // what is highlightTab? bool highlightTab = false; diff --git a/src/messages/wordflags.hpp b/src/messages/wordflags.hpp deleted file mode 100644 index 50e96676b..000000000 --- a/src/messages/wordflags.hpp +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/src/singletons/helper/ircmessagehandler.cpp b/src/singletons/helper/ircmessagehandler.cpp index 69a6d98c9..60b7ebdda 100644 --- a/src/singletons/helper/ircmessagehandler.cpp +++ b/src/singletons/helper/ircmessagehandler.cpp @@ -71,8 +71,7 @@ void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message) // check if the chat has been cleared by a moderator if (message->parameters().length() == 1) { - std::shared_ptr msg( - Message::createSystemMessage("Chat has been cleared by a moderator.")); + SharedMessage msg(Message::createSystemMessage("Chat has been cleared by a moderator.")); c->addMessage(msg); @@ -95,20 +94,35 @@ void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message) } // add the notice that the user has been timed out - SharedMessage msg(Message::createTimeoutMessage(username, durationInSeconds, reason)); + LimitedQueueSnapshot snapshot = c->getMessageSnapshot(); + bool addMessage = true; - c->addMessage(msg); + for (int i = std::max(0, (int)snapshot.getLength() - 20); i < snapshot.getLength(); i++) { + if (snapshot[i]->getFlags() & Message::Timeout && snapshot[i]->timeoutUser == username) { + SharedMessage replacement( + Message::createTimeoutMessage(username, durationInSeconds, reason, true)); + c->replaceMessage(snapshot[i], replacement); + addMessage = false; + break; + } + } + + if (addMessage) { + SharedMessage msg( + Message::createTimeoutMessage(username, durationInSeconds, reason, false)); + c->addMessage(msg); + } // disable the messages from the user - LimitedQueueSnapshot snapshot = c->getMessageSnapshot(); for (int i = 0; i < snapshot.getLength(); i++) { - if (snapshot[i]->getTimeoutUser() == username) { + if (!(snapshot[i]->getFlags() & Message::Timeout) && + snapshot[i]->getTimeoutUser() == username) { snapshot[i]->setDisabled(true); } } // refresh all - WindowManager::getInstance().layoutVisibleChatWidgets(c.get()); + WindowManager::getInstance().repaintVisibleChatWidgets(c.get()); } void IrcMessageHandler::handleUserStateMessage(Communi::IrcMessage *message) diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index 16351f3bf..11a33484f 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -104,6 +104,8 @@ ChannelView::~ChannelView() this->messageRemovedConnection.disconnect(); this->repaintGifsConnection.disconnect(); this->layoutConnection.disconnect(); + this->messageAddedAtStartConnection.disconnect(); + this->messageReplacedConnection.disconnect(); } void ChannelView::queueUpdate() @@ -429,6 +431,15 @@ void ChannelView::setChannel(std::shared_ptr newChannel) this->layoutMessages(); }); + // on message replaced + this->messageReplacedConnection = + newChannel->messageReplaced.connect([this](size_t index, SharedMessage replacement) { + SharedMessageRef newItem(new MessageRef(replacement)); + + this->messages.replaceItem(this->messages.getSnapshot()[index], newItem); + this->layoutMessages(); + }); + auto snapshot = newChannel->getMessageSnapshot(); for (size_t i = 0; i < snapshot.getLength(); i++) { diff --git a/src/widgets/helper/channelview.hpp b/src/widgets/helper/channelview.hpp index 37a7b2319..3ea1f0eb3 100644 --- a/src/widgets/helper/channelview.hpp +++ b/src/widgets/helper/channelview.hpp @@ -117,6 +117,7 @@ private: boost::signals2::connection messageAppendedConnection; boost::signals2::connection messageAddedAtStartConnection; boost::signals2::connection messageRemovedConnection; + boost::signals2::connection messageReplacedConnection; boost::signals2::connection repaintGifsConnection; boost::signals2::connection layoutConnection;