diff --git a/Telegram/SourceFiles/data/data_saved_messages.cpp b/Telegram/SourceFiles/data/data_saved_messages.cpp index d7d171c676..95802c0b4a 100644 --- a/Telegram/SourceFiles/data/data_saved_messages.cpp +++ b/Telegram/SourceFiles/data/data_saved_messages.cpp @@ -239,6 +239,17 @@ void SavedMessages::requestSublist( } } +void SavedMessages::refreshPinned() { + if (parentChat()) { + return; + } + if (_pinnedRequestId) { + _refreshPinnedAfterRequest = true; + return; + } + loadPinned(); +} + rpl::producer<> SavedMessages::chatsListChanges() const { return _chatsListChanges.events(); } @@ -266,11 +277,15 @@ void SavedMessages::clearAllUnreadReactions() { } void SavedMessages::sendLoadMore() { - if (_loadMoreRequestId || _chatsList.loaded()) { + if (_loadMoreRequestId) { return; - } else if (!_pinnedLoaded) { + } + if (!_pinnedLoaded) { loadPinned(); } + if (_chatsList.loaded()) { + return; + } using Flag = MTPmessages_GetSavedDialogs::Flag; _loadMoreRequestId = _owner->session().api().request( MTPmessages_GetSavedDialogs( @@ -319,6 +334,10 @@ void SavedMessages::loadPinned() { _pinnedLoaded = true; applyReceivedSublists(result, true); _chatsListChanges.fire({}); + if (_refreshPinnedAfterRequest) { + _refreshPinnedAfterRequest = false; + loadPinned(); + } }).fail([=](const MTP::Error &error) { if (error.type() == u"SAVED_DIALOGS_UNSUPPORTED"_q) { markUnsupported(); @@ -326,6 +345,10 @@ void SavedMessages::loadPinned() { _pinnedLoaded = true; } _pinnedRequestId = 0; + if (_refreshPinnedAfterRequest) { + _refreshPinnedAfterRequest = false; + loadPinned(); + } }).send(); } @@ -348,20 +371,32 @@ SavedMessages::ApplyResult SavedMessages::applyReceivedSublists( } auto lastValid = false; auto result = ApplyResult(); + auto serverPinnedPeers = base::flat_set>(); const auto parentPeerId = _parentChat ? _parentChat->id : _owner->session().userPeerId(); for (const auto &dialog : *list) { dialog.match([&](const MTPDsavedDialog &data) { const auto peer = _owner->peer(peerFromMTP(data.vpeer())); + const auto entryPinned = pinned || data.is_pinned(); const auto topId = MsgId(data.vtop_message().v); - if (const auto item = _owner->message(parentPeerId, topId)) { + if (entryPinned) { + serverPinnedPeers.emplace(peer); + } + if (entryPinned) { + if (const auto loaded = sublistLoaded(peer)) { + _owner->setPinnedFromEntryList(loaded, true); + } + } + if (const auto item = _owner->message(parentPeerId, topId); + item + && item->isRegular() + && !item->isService()) { result.offset.peer = peer; result.offset.date = item->date(); result.offset.id = topId; lastValid = true; const auto entry = sublist(peer); - const auto entryPinned = pinned || data.is_pinned(); entry->applyMaybeLast(item); _owner->setPinnedFromEntryList(entry, entryPinned); } else { @@ -369,8 +404,14 @@ SavedMessages::ApplyResult SavedMessages::applyReceivedSublists( } }, [&](const MTPDmonoForumDialog &data) { const auto peer = _owner->peer(peerFromMTP(data.vpeer())); + if (pinned) { + serverPinnedPeers.emplace(peer); + } const auto topId = MsgId(data.vtop_message().v); - if (const auto item = _owner->message(parentPeerId, topId)) { + if (const auto item = _owner->message(parentPeerId, topId); + item + && item->isRegular() + && !item->isService()) { result.offset.peer = peer; result.offset.date = item->date(); result.offset.id = topId; @@ -382,6 +423,17 @@ SavedMessages::ApplyResult SavedMessages::applyReceivedSublists( }); } if (pinned) { + for (const auto &[peer, holder] : _sublists) { + const auto entry = holder.get(); + if (entry->isPinnedDialog(FilterId()) + && !serverPinnedPeers.contains(peer)) { + if (!entry->parentChat() && !entry->chatListMessage()) { + entry->setRestorePinnedWhenNonEmpty(true); + } + _owner->setChatPinned(entry, FilterId(), false); + } + entry->updateChatListExistence(); + } } else if (!lastValid) { LOG(("API Error: Unknown message in the end of a slice.")); result.allLoaded = true; diff --git a/Telegram/SourceFiles/data/data_saved_messages.h b/Telegram/SourceFiles/data/data_saved_messages.h index fe77fbb232..82b9aed5cb 100644 --- a/Telegram/SourceFiles/data/data_saved_messages.h +++ b/Telegram/SourceFiles/data/data_saved_messages.h @@ -52,6 +52,7 @@ public: [[nodiscard]] not_null sublist(not_null peer); [[nodiscard]] SavedSublist *sublistLoaded(not_null peer); void requestSublist(not_null peer, Fn done = nullptr); + void refreshPinned(); [[nodiscard]] rpl::producer<> chatsListChanges() const; [[nodiscard]] rpl::producer<> chatsListLoadedEvents() const; @@ -127,6 +128,7 @@ private: mtpRequestId _loadMoreRequestId = 0; mtpRequestId _pinnedRequestId = 0; + bool _refreshPinnedAfterRequest = false; SavedMessagesOffsets _offset; diff --git a/Telegram/SourceFiles/data/data_saved_sublist.cpp b/Telegram/SourceFiles/data/data_saved_sublist.cpp index 179569ce46..21209db5f1 100644 --- a/Telegram/SourceFiles/data/data_saved_sublist.cpp +++ b/Telegram/SourceFiles/data/data_saved_sublist.cpp @@ -97,6 +97,12 @@ bool SavedSublist::removeOne(not_null item) { const auto i = ranges::lower_bound(_list, id, std::greater<>()); changeUnreadCountByMessage(id, -1); if (i == end(_list) || *i != id) { + if (const auto known = _fullCount.current()) { + if (*known > 0) { + _fullCount = (*known - 1); + return true; + } + } return false; } _list.erase(i); @@ -188,6 +194,9 @@ rpl::producer<> SavedSublist::destroyed() const { } void SavedSublist::applyMaybeLast(not_null item) { + if (!item->isRegular() || item->isService()) { + return; + } if (!_lastServerMessage.value_or(nullptr) || (*_lastServerMessage)->id < item->id) { setLastServerMessage(item); @@ -198,11 +207,23 @@ void SavedSublist::applyMaybeLast(not_null item) { } void SavedSublist::applyItemAdded(not_null item) { + if (item->isService()) { + return; + } + const auto wasInChatList = shouldBeInChatList(); if (item->isRegular()) { setLastServerMessage(item); } else { setLastMessage(item); } + if (!_parent->parentChat() && !isPinnedDialog(FilterId())) { + if (_restorePinnedWhenNonEmpty) { + owner().setChatPinned(this, FilterId(), true); + _restorePinnedWhenNonEmpty = false; + } else if (!wasInChatList && shouldBeInChatList()) { + _parent->refreshPinned(); + } + } } void SavedSublist::applyItemRemoved(MsgId id) { @@ -220,16 +241,22 @@ void SavedSublist::applyItemRemoved(MsgId id) { if (chatListItem->id == id) { _chatListMessage = std::nullopt; crl::on_main(this, [=] { - // We didn't yet update _list here. - if (_chatListMessage.has_value()) { + const auto locallyKnownEmpty = _list.empty() + && (_fullCount.current() == 0); + if (_chatListMessage.value_or(nullptr)) { return; - } else if (_skippedAfter == 0) { + } else if ((_skippedAfter == 0) || locallyKnownEmpty) { if (!_list.empty()) { applyMaybeLast(owner().message( owningHistory()->peer, _list.front())); return; - } else if (_skippedBefore == 0) { + } else if ((_skippedBefore == 0) || locallyKnownEmpty) { + if (!_parent->parentChat() + && isPinnedDialog(FilterId())) { + _restorePinnedWhenNonEmpty = true; + owner().setChatPinned(this, FilterId(), false); + } setLastServerMessage(nullptr); updateChatListExistence(); return; @@ -251,6 +278,10 @@ void SavedSublist::requestChatListMessage() { } } +void SavedSublist::setRestorePinnedWhenNonEmpty(bool restore) { + _restorePinnedWhenNonEmpty = restore; +} + void SavedSublist::readTillEnd() { readTill(_lastKnownServerMessageId); } @@ -329,6 +360,7 @@ bool SavedSublist::applyUpdate(const MessageUpdate &update) { if (update.item->history() != owningHistory() || !update.item->isRegular() + || update.item->isService() || update.item->sublistPeerId() != sublistPeer()->id) { return false; } else if (update.flags & Flag::Destroyed) { @@ -405,7 +437,9 @@ bool SavedSublist::processMessagesIsEmpty( auto skipped = 0; for (const auto &message : list) { if (const auto item = owner().addNewMessage(message, localFlags, type)) { - if (item->sublistPeerId() == sublistPeer()->id) { + if (item->sublistPeerId() == sublistPeer()->id + && item->isRegular() + && !item->isService()) { if (toFront && item->id > _list.front()) { refreshed.push_back(item->id); } else if (_list.empty() || item->id < _list.back()) { @@ -871,14 +905,18 @@ int SavedSublist::fixedOnTopIndex() const { } bool SavedSublist::shouldBeInChatList() const { - if (const auto monoforum = _parent->parentChat()) { - if (monoforum == sublistPeer()) { - return false; - } + const auto monoforum = _parent->parentChat(); + if (monoforum && (monoforum == sublistPeer())) { + return false; + } + const auto last = lastMessage(); + const auto hasDisplayableLast = last && !last->isService(); + if (!monoforum) { + return hasDisplayableLast; } return isPinnedDialog(FilterId()) || !lastMessageKnown() - || (lastMessage() != nullptr); + || hasDisplayableLast; } HistoryItem *SavedSublist::lastMessage() const { @@ -1066,9 +1104,10 @@ void SavedSublist::setChatListMessage(HistoryItem *item) { } _chatListMessage = item; setChatListTimeId(item->date()); + updateChatListExistence(); } else if (!_chatListMessage || *_chatListMessage) { _chatListMessage = nullptr; - updateChatListEntry(); + updateChatListExistence(); } _parent->listMessageChanged(was, item); } @@ -1102,8 +1141,10 @@ Histories &SavedSublist::histories() { void SavedSublist::loadAround(MsgId id) { if (_loadingAround && *_loadingAround == id) { + _loadingAroundRetry = id; return; } + _loadingAroundRetry = std::nullopt; histories().cancelRequest(base::take(_beforeId)); histories().cancelRequest(base::take(_afterId)); @@ -1135,7 +1176,12 @@ void SavedSublist::loadAround(MsgId id) { _list.clear(); if (processMessagesIsEmpty(result)) { _fullCount = _skippedBefore = _skippedAfter = 0; - if (!_parent->parentChat() && !_chatListMessage) { + if (!_parent->parentChat() + && !_chatListMessage.value_or(nullptr)) { + if (isPinnedDialog(FilterId())) { + _restorePinnedWhenNonEmpty = true; + owner().setChatPinned(this, FilterId(), false); + } setLastServerMessage(nullptr); updateChatListExistence(); } @@ -1146,13 +1192,17 @@ void SavedSublist::loadAround(MsgId id) { } else if (_list.back() >= id) { _skippedBefore = 0; } - } else if (!_parent->parentChat() && !_chatListMessage) { + } else if (!_parent->parentChat() + && !_chatListMessage.value_or(nullptr)) { Assert(!_list.empty()); applyMaybeLast(owner().message( owningHistory()->peer, _list.front())); } checkReadTillEnd(); + if (const auto retry = base::take(_loadingAroundRetry)) { + loadAround(*retry); + } }).fail([=](const MTP::Error &error) { if (error.type() == u"SAVED_DIALOGS_UNSUPPORTED"_q) { _parent->markUnsupported(); @@ -1160,6 +1210,9 @@ void SavedSublist::loadAround(MsgId id) { _beforeId = 0; _loadingAround = std::nullopt; finish(); + if (const auto retry = base::take(_loadingAroundRetry)) { + loadAround(*retry); + } }).send(); }; _loadingAround = id; diff --git a/Telegram/SourceFiles/data/data_saved_sublist.h b/Telegram/SourceFiles/data/data_saved_sublist.h index 92098c16e4..38a06f83e3 100644 --- a/Telegram/SourceFiles/data/data_saved_sublist.h +++ b/Telegram/SourceFiles/data/data_saved_sublist.h @@ -72,6 +72,7 @@ public: not_null topItem); void readTillEnd(); void requestChatListMessage(); + void setRestorePinnedWhenNonEmpty(bool restore); TimeId adjustedChatListTimeId() const override; @@ -173,6 +174,7 @@ private: rpl::event_stream<> _listChanges; rpl::event_stream<> _instantChanges; std::optional _loadingAround; + std::optional _loadingAroundRetry; rpl::variable> _unreadCount; MsgId _inboxReadTillId = 0; MsgId _outboxReadTillId = 0; @@ -189,6 +191,8 @@ private: mtpRequestId _readRequestId = 0; MsgId _sentReadTill = 0; + bool _restorePinnedWhenNonEmpty = false; + mtpRequestId _reloadUnreadCountRequestId = 0; rpl::lifetime _lifetime;