Fixed stale pinned Saved Messages sublists on clear that subsection.

This commit is contained in:
23rd
2026-03-13 14:46:17 +03:00
parent dc2d6e9d12
commit 73cbef5b82
4 changed files with 129 additions and 18 deletions
@@ -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<not_null<PeerData*>>();
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;
@@ -52,6 +52,7 @@ public:
[[nodiscard]] not_null<SavedSublist*> sublist(not_null<PeerData*> peer);
[[nodiscard]] SavedSublist *sublistLoaded(not_null<PeerData*> peer);
void requestSublist(not_null<PeerData*> peer, Fn<void()> 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;
@@ -97,6 +97,12 @@ bool SavedSublist::removeOne(not_null<HistoryItem*> 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<HistoryItem*> 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<HistoryItem*> item) {
}
void SavedSublist::applyItemAdded(not_null<HistoryItem*> 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;
@@ -72,6 +72,7 @@ public:
not_null<HistoryItem*> 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<MsgId> _loadingAround;
std::optional<MsgId> _loadingAroundRetry;
rpl::variable<std::optional<int>> _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;