From 5b7f7ed70e8e357657bf354d23bb8c4c3ec75ba1 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 19 Feb 2020 19:35:26 +0400 Subject: [PATCH] Fix reading of currently opened chat. --- Telegram/SourceFiles/apiwrap.cpp | 5 +- Telegram/SourceFiles/data/data_histories.cpp | 78 ++++++++++-- Telegram/SourceFiles/data/data_histories.h | 8 +- Telegram/SourceFiles/history/history.cpp | 117 +++++------------- Telegram/SourceFiles/history/history.h | 6 +- .../history/history_inner_widget.cpp | 5 +- .../SourceFiles/history/history_widget.cpp | 2 +- .../SourceFiles/window/window_peer_menu.cpp | 3 +- 8 files changed, 115 insertions(+), 109 deletions(-) diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 9e6d5d085a..d2aa285480 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_chat.h" #include "data/data_user.h" #include "data/data_cloud_themes.h" +#include "data/data_histories.h" #include "dialogs/dialogs_key.h" #include "core/core_cloud_password.h" #include "core/application.h" @@ -4432,7 +4433,7 @@ void ApiWrap::userPhotosDone( //} void ApiWrap::sendAction(const SendAction &action) { - action.history->readInbox(); + session().data().histories().readInbox(action.history); action.history->getReadyFor(ShowAtTheEndMsgId); _sendActions.fire_copy(action); } @@ -4459,7 +4460,7 @@ void ApiWrap::forwardMessages( const auto history = action.history; const auto peer = history->peer; - history->readInbox(); + session().data().histories().readInbox(history); const auto channelPost = peer->isChannel() && !peer->isMegagroup(); const auto silentPost = action.options.silent diff --git a/Telegram/SourceFiles/data/data_histories.cpp b/Telegram/SourceFiles/data/data_histories.cpp index 27aef7d020..5e06e4367b 100644 --- a/Telegram/SourceFiles/data/data_histories.cpp +++ b/Telegram/SourceFiles/data/data_histories.cpp @@ -63,10 +63,32 @@ void Histories::clearAll() { _map.clear(); } -void Histories::readInboxTill( - not_null history, - not_null item) { +void Histories::readInbox(not_null history) { + if (history->lastServerMessageKnown()) { + const auto last = history->lastServerMessage(); + readInboxTill(history, last ? last->id : 0); + return; + } else if (history->loadedAtBottom()) { + if (const auto lastId = history->maxMsgId()) { + readInboxTill(history, lastId); + return; + } else if (history->loadedAtTop()) { + readInboxTill(history, 0); + return; + } + } + session().api().requestDialogEntry(history, [=] { + Expects(history->lastServerMessageKnown()); + + const auto last = history->lastServerMessage(); + readInboxTill(history, last ? last->id : 0); + }); +} + +void Histories::readInboxTill(not_null item) { + const auto history = item->history(); if (!IsServerMsgId(item->id)) { + readClientSideMessage(item); auto view = item->mainView(); if (!view) { return; @@ -102,15 +124,23 @@ void Histories::readInboxTill( } void Histories::readInboxTill(not_null history, MsgId tillId) { - if (!history->readInboxTillNeedsRequest(tillId)) { - return; - } - const auto maybeState = lookup(history); - if (maybeState && maybeState->readTill >= tillId) { + readInboxTill(history, tillId, false); +} +void Histories::readInboxTill( + not_null history, + MsgId tillId, + bool force) { + if (!history->readInboxTillNeedsRequest(tillId) && !force) { return; + } else if (!force) { + const auto maybeState = lookup(history); + if (maybeState && maybeState->readTill >= tillId) { + return; + } } const auto stillUnread = history->countStillUnreadLocal(tillId); - if (stillUnread + if (!force + && stillUnread && history->unreadCountKnown() && *stillUnread == history->unreadCount()) { history->setInboxReadTill(tillId); @@ -119,10 +149,12 @@ void Histories::readInboxTill(not_null history, MsgId tillId) { auto &state = _states[history]; const auto wasReadTill = state.readTill; state.readTill = tillId; - if (!stillUnread) { + if (force || !stillUnread || !*stillUnread) { state.readWhen = 0; sendReadRequests(); - return; + if (!stillUnread) { + return; + } } else if (!wasReadTill) { state.readWhen = crl::now() + kReadRequestTimeout; if (!_readRequestsTimer.isActive()) { @@ -134,6 +166,25 @@ void Histories::readInboxTill(not_null history, MsgId tillId) { history->updateChatListEntry(); } +void Histories::readInboxOnNewMessage(not_null item) { + if (!IsServerMsgId(item->id)) { + readClientSideMessage(item); + } else { + readInboxTill(item->history(), item->id, true); + } +} + +void Histories::readClientSideMessage(not_null item) { + if (item->out() || !item->unread()) { + return; + } + const auto history = item->history(); + item->markClientSideAsRead(); + if (const auto unread = history->unreadCount()) { + history->setUnreadCount(unread - 1); + } +} + void Histories::sendPendingReadInbox(not_null history) { if (const auto state = lookup(history)) { if (state->readTill && state->readWhen) { @@ -255,10 +306,11 @@ void Histories::checkPostponed(not_null history, int requestId) { const auto action = chooseAction(*state, entry.second.type, true); if (action == Action::Send) { const auto id = entry.first; + const auto postponed = std::move(entry.second); state->postponed.remove(id); state->sent.emplace(id, SentRequest{ - entry.second.generator([=] { checkPostponed(history, id); }), - entry.second.type + postponed.generator([=] { checkPostponed(history, id); }), + postponed.type }); if (base::take(state->thenRequestEntry)) { session().api().requestDialogEntry(history); diff --git a/Telegram/SourceFiles/data/data_histories.h b/Telegram/SourceFiles/data/data_histories.h index ebf6f86488..42a9641ce3 100644 --- a/Telegram/SourceFiles/data/data_histories.h +++ b/Telegram/SourceFiles/data/data_histories.h @@ -33,10 +33,11 @@ public: void unloadAll(); void clearAll(); - void readInboxTill( - not_null history, - not_null item); + void readInbox(not_null history); + void readInboxTill(not_null item); void readInboxTill(not_null history, MsgId tillId); + void readInboxOnNewMessage(not_null item); + void readClientSideMessage(not_null item); void sendPendingReadInbox(not_null history); private: @@ -69,6 +70,7 @@ private: bool thenRequestEntry = false; }; + void readInboxTill(not_null history, MsgId tillId, bool force); void sendReadRequests(); void sendReadRequest(not_null history, State &state); [[nodiscard]] State *lookup(not_null history); diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 15834f0d4d..66e46c0e78 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -136,6 +136,9 @@ void History::itemRemoved(not_null item) { _joinedMessage = nullptr; } item->removeMainView(); + if (_lastServerMessage == item) { + _lastServerMessage = std::nullopt; + } if (lastMessage() == item) { _lastMessage = std::nullopt; if (loadedAtBottom()) { @@ -1546,27 +1549,6 @@ void History::addToSharedMedia( } } -std::optional History::countUnread(MsgId upTo) const { - if (!folderKnown() || !loadedAtBottom()) { - return std::nullopt; - } - auto result = 0; - for (auto i = blocks.cend(), e = blocks.cbegin(); i != e;) { - --i; - const auto &messages = (*i)->messages; - for (auto j = messages.cend(), en = messages.cbegin(); j != en;) { - --j; - const auto item = (*j)->data(); - if (item->id > 0 && item->id <= upTo) { - return result; - } else if (!item->out() && item->unread()) { - ++result; - } - } - } - return std::nullopt; -} - void History::calculateFirstUnreadMessage() { if (_firstUnreadView || !_inboxReadBefore) { return; @@ -1603,64 +1585,12 @@ bool History::readInboxTillNeedsRequest(MsgId tillId) { } void History::readClientSideMessages() { - auto unread = unreadCount(); + auto &histories = owner().histories(); for (const auto item : _localMessages) { - if (!item->out() && item->unread()) { - item->markClientSideAsRead(); - if (unread > 0) { - setUnreadCount(--unread); - } - } + histories.readClientSideMessage(item); } } -void History::readInbox() { - if (_lastMessage) { - if (!*_lastMessage) { - owner().histories().readInboxTill(this, 0); - return; - } else if (IsServerMsgId((*_lastMessage)->id)) { - readInboxTill(*_lastMessage); - return; - } - } - if (loadedAtBottom()) { - const auto last = [&]() -> HistoryItem* { - for (const auto &block : ranges::view::reverse(blocks)) { - const auto &messages = block->messages; - for (const auto &item : ranges::view::reverse(messages)) { - if (IsServerMsgId(item->data()->id)) { - return item->data(); - } - } - } - return nullptr; - }(); - if (last) { - readInboxTill(last); - return; - } else if (loadedAtTop()) { - owner().histories().readInboxTill(this, 0); - return; - } - } - session().api().requestDialogEntry(this, [=] { - Expects(_lastMessage.has_value()); - - if (!*_lastMessage) { - owner().histories().readInboxTill(this, 0); - } else if (IsServerMsgId((*_lastMessage)->id)) { - readInboxTill(*_lastMessage); - } else { - Unexpected("Local _lastMessage after requestDialogEntry."); - } - }); -} - -void History::readInboxTill(not_null item) { - owner().histories().readInboxTill(this, item); -} - bool History::unreadCountRefreshNeeded(MsgId readTillId) const { return !unreadCountKnown() || ((readTillId + 1) > _inboxReadBefore.value_or(0)); @@ -1691,17 +1621,22 @@ std::optional History::countStillUnreadLocal(MsgId readTillId) const { } } } - if (!loadedAtBottom() || minMsgId() > readTillId) { + const auto minimalServerId = minMsgId(); + if (!loadedAtBottom() + || (!loadedAtTop() && !minimalServerId) + || minimalServerId > readTillId) { return std::nullopt; } auto result = 0; - for (const auto &block : blocks) { - for (const auto &message : block->messages) { + for (const auto &block : ranges::view::reverse(blocks)) { + for (const auto &message : ranges::view::reverse(block->messages)) { const auto item = message->data(); - if (!item->out() - && IsServerMsgId(item->id) - && item->id > readTillId) { - ++result; + if (IsServerMsgId(item->id)) { + if (item->id <= readTillId) { + return result; + } else if (!item->out()) { + ++result; + } } } } @@ -1734,7 +1669,7 @@ void History::inboxRead(MsgId upTo, std::optional stillUnread) { } if (stillUnread.has_value() && folderKnown()) { setUnreadCount(*stillUnread); - } else if (const auto still = countUnread(upTo)) { + } else if (const auto still = countStillUnreadLocal(upTo)) { setUnreadCount(*still); } else { session().api().requestDialogEntry(this); @@ -2370,6 +2305,7 @@ void History::clearSharedMedia() { } void History::setLastServerMessage(HistoryItem *item) { + _lastServerMessage = item; if (_lastMessage && *_lastMessage && !IsServerMsgId((*_lastMessage)->id) @@ -2384,6 +2320,9 @@ void History::setLastMessage(HistoryItem *item) { return; } _lastMessage = item; + if (!item || IsServerMsgId(item->id)) { + _lastServerMessage = item; + } if (peer->migrateTo()) { // We don't want to request last message for all deactivated chats. // This is a heavy request for them, because we need to get last @@ -2583,6 +2522,14 @@ bool History::lastMessageKnown() const { return _lastMessage.has_value(); } +HistoryItem *History::lastServerMessage() const { + return _lastServerMessage.value_or(nullptr); +} + +bool History::lastServerMessageKnown() const { + return _lastServerMessage.has_value(); +} + void History::updateChatListExistence() { Entry::updateChatListExistence(); //if (const auto channel = peer->asChannel()) { // #feed @@ -2691,7 +2638,9 @@ void History::applyDialog( } void History::dialogEntryApplied() { - if (!lastMessageKnown()) { + if (!lastServerMessageKnown()) { + setLastServerMessage(nullptr); + } else if (!lastMessageKnown()) { setLastMessage(nullptr); } if (peer->migrateTo()) { diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 9385c6da7e..f54cfc1f84 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -158,8 +158,6 @@ public: void unregisterLocalMessage(not_null item); [[nodiscard]] HistoryItem *latestSendingMessage() const; - void readInbox(); - void readInboxTill(not_null item); [[nodiscard]] bool readInboxTillNeedsRequest(MsgId tillId); void applyInboxReadUpdate( FolderId folderId, @@ -203,7 +201,9 @@ public: void getReadyFor(MsgId msgId); [[nodiscard]] HistoryItem *lastMessage() const; + [[nodiscard]] HistoryItem *lastServerMessage() const; [[nodiscard]] bool lastMessageKnown() const; + [[nodiscard]] bool lastServerMessageKnown() const; void unknownMessageDeleted(MsgId messageId); void applyDialogTopMessage(MsgId topMessageId); void applyDialog(Data::Folder *requestFolder, const MTPDdialog &data); @@ -478,7 +478,6 @@ private: HistoryItem *lastAvailableMessage() const; void getNextFirstUnreadMessage(); bool nonEmptyCountMoreThan(int count) const; - std::optional countUnread(MsgId upTo) const; // Creates if necessary a new block for adding item. // Depending on isBuildingFrontBlock() gets front or back block. @@ -511,6 +510,7 @@ private: std::optional _unreadMentionsCount; base::flat_set _unreadMentions; std::optional _lastMessage; + std::optional _lastServerMessage; base::flat_set> _localMessages; // This almost always is equal to _lastMessage. The only difference is diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 85e7c97165..815bb31755 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -54,6 +54,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_photo.h" #include "data/data_user.h" #include "data/data_file_origin.h" +#include "data/data_histories.h" #include "facades.h" #include "app.h" @@ -704,7 +705,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { p.restore(); if (readTill) { - _history->readInboxTill(readTill); + session().data().histories().readInboxTill(readTill); } } @@ -2059,7 +2060,7 @@ void HistoryInner::checkHistoryActivation() { } } } - _history->readInboxTill(view->data()); + session().data().histories().readInboxTill(view->data()); } void HistoryInner::recountHistoryGeometry() { diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index d34e0cc137..8eeccd3ea6 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -2259,7 +2259,7 @@ void HistoryWidget::unreadMessageAdded(not_null item) { if (item->isUnreadMention() && !item->isUnreadMedia()) { session().api().markMediaRead(item); } - _history->readInboxTill(item); + session().data().histories().readInboxOnNewMessage(item); // Also clear possible scheduled messages notifications. session().notifications().clearFromHistory(_history); diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 47881396a8..64220b5a2c 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -44,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_drafts.h" #include "data/data_user.h" #include "data/data_scheduled_messages.h" +#include "data/data_histories.h" #include "dialogs/dialogs_key.h" #include "boxes/peers/edit_peer_info_box.h" #include "facades.h" @@ -303,7 +304,7 @@ void Filler::addToggleUnreadMark() { const auto markAsRead = isUnread(peer); const auto handle = [&](not_null history) { if (markAsRead) { - history->readInbox(); + peer->session().data().histories().readInbox(history); } else { peer->session().api().changeDialogUnreadMark( history,