From 8fd811517b371fb31f820c1c3aed79390f5665fd Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 22 Apr 2019 18:22:39 +0400 Subject: [PATCH] Count unread correctly in folders. --- Telegram/SourceFiles/apiwrap.cpp | 93 +++-- Telegram/SourceFiles/apiwrap.h | 18 +- .../boxes/peer_list_controllers.cpp | 6 +- .../boxes/peers/add_participants_box.cpp | 2 +- Telegram/SourceFiles/boxes/share_box.cpp | 2 +- Telegram/SourceFiles/data/data_folder.cpp | 379 +++++------------- Telegram/SourceFiles/data/data_folder.h | 68 +--- Telegram/SourceFiles/data/data_session.cpp | 264 +++++------- Telegram/SourceFiles/data/data_session.h | 52 +-- .../SourceFiles/dialogs/dialogs_entry.cpp | 24 +- Telegram/SourceFiles/dialogs/dialogs_entry.h | 26 +- .../dialogs/dialogs_inner_widget.cpp | 69 ++-- Telegram/SourceFiles/dialogs/dialogs_list.h | 2 +- .../SourceFiles/dialogs/dialogs_main_list.cpp | 130 ++++++ .../SourceFiles/dialogs/dialogs_main_list.h | 49 +++ .../dialogs/dialogs_pinned_list.cpp | 2 +- .../SourceFiles/dialogs/dialogs_pinned_list.h | 2 +- .../SourceFiles/dialogs/dialogs_widget.cpp | 9 +- Telegram/SourceFiles/history/history.cpp | 222 ++++------ Telegram/SourceFiles/history/history.h | 5 +- Telegram/SourceFiles/mainwidget.cpp | 30 +- .../SourceFiles/window/window_peer_menu.cpp | 15 +- Telegram/gyp/telegram_sources.txt | 2 + 23 files changed, 673 insertions(+), 798 deletions(-) create mode 100644 Telegram/SourceFiles/dialogs/dialogs_main_list.cpp create mode 100644 Telegram/SourceFiles/dialogs/dialogs_main_list.h diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 4887f0a8e6..2f251066d7 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -433,8 +433,8 @@ void ApiWrap::applyUpdates( App::main()->feedUpdates(updates, sentMessageRandomId); } -void ApiWrap::savePinnedOrder(FolderId folderId) { - const auto &order = _session->data().pinnedChatsOrder(folderId); +void ApiWrap::savePinnedOrder(Data::Folder *folder) { + const auto &order = _session->data().pinnedChatsOrder(folder); const auto input = [](const Dialogs::Key &key) { if (const auto history = key.history()) { return MTP_inputDialogPeer(history->peer->input); @@ -451,7 +451,7 @@ void ApiWrap::savePinnedOrder(FolderId folderId) { input); request(MTPmessages_ReorderPinnedDialogs( MTP_flags(MTPmessages_ReorderPinnedDialogs::Flag::f_force), - MTP_int(folderId), + MTP_int(folder ? folder->id() : 0), MTP_vector(peers) )).send(); } @@ -707,17 +707,17 @@ void ApiWrap::requestContacts() { }).send(); } -void ApiWrap::requestDialogs(FolderId folderId) { - if (folderId && !_foldersLoadState.contains(folderId)) { - _foldersLoadState.emplace(folderId, DialogsLoadState()); +void ApiWrap::requestDialogs(Data::Folder *folder) { + if (folder && !_foldersLoadState.contains(folder)) { + _foldersLoadState.emplace(folder, DialogsLoadState()); } - requestMoreDialogs(folderId); + requestMoreDialogs(folder); } -void ApiWrap::requestMoreDialogs(FolderId folderId) { - const auto state = dialogsLoadState(folderId); +void ApiWrap::requestMoreDialogs(Data::Folder *folder) { + const auto state = dialogsLoadState(folder); if (!state) { - if (!folderId) { + if (!folder) { _session->data().addAllSavedPeers(); } return; @@ -734,7 +734,7 @@ void ApiWrap::requestMoreDialogs(FolderId folderId) { const auto hash = 0; state->requestId = request(MTPmessages_GetDialogs( MTP_flags(flags), - MTP_int(folderId), + MTP_int(folder ? folder->id() : 0), MTP_int(state->offsetDate), MTP_int(state->offsetId), (state->offsetPeer @@ -743,31 +743,31 @@ void ApiWrap::requestMoreDialogs(FolderId folderId) { MTP_int(loadCount), MTP_int(hash) )).done([=](const MTPmessages_Dialogs &result) { - const auto state = dialogsLoadState(folderId); + const auto state = dialogsLoadState(folder); result.match([](const MTPDmessages_dialogsNotModified & data) { LOG(("API Error: not-modified received for requested dialogs.")); }, [&](const auto &data) { if constexpr (data.Is()) { if (state) { state->listReceived = true; - dialogsLoadFinish(folderId); // may kill 'state'. + dialogsLoadFinish(folder); // may kill 'state'. } } else { updateDialogsOffset( - folderId, + folder, data.vdialogs.v, data.vmessages.v); } _session->data().processUsers(data.vusers); _session->data().processChats(data.vchats); _session->data().applyDialogs( - folderId, + folder, data.vmessages.v, data.vdialogs.v); }); - if (!folderId) { - requestDialogs(folderId); + if (!folder) { + requestDialogs(folder); requestContacts(); if (!_dialogsLoadState || (!_dialogsLoadState->listReceived @@ -775,15 +775,15 @@ void ApiWrap::requestMoreDialogs(FolderId folderId) { refreshDialogsLoadBlocked(); } } - _session->data().chatsListChanged(folderId); + _session->data().chatsListChanged(folder); }).fail([=](const RPCError &error) { - dialogsLoadState(folderId)->requestId = 0; + dialogsLoadState(folder)->requestId = 0; }).send(); if (!state->pinnedReceived) { - requestPinnedDialogs(folderId); + requestPinnedDialogs(folder); } - if (!folderId) { + if (!folder) { refreshDialogsLoadBlocked(); } } @@ -801,7 +801,7 @@ void ApiWrap::refreshDialogsLoadBlocked() { } void ApiWrap::updateDialogsOffset( - FolderId folderId, + Data::Folder *folder, const QVector &dialogs, const QVector &messages) { auto lastDate = TimeId(0); @@ -834,7 +834,7 @@ void ApiWrap::updateDialogsOffset( break; } } - if (const auto state = dialogsLoadState(folderId)) { + if (const auto state = dialogsLoadState(folder)) { if (lastDate) { state->offsetDate = lastDate; state->offsetId = lastMsgId; @@ -842,31 +842,31 @@ void ApiWrap::updateDialogsOffset( state->requestId = 0; } else { state->listReceived = true; - dialogsLoadFinish(folderId); + dialogsLoadFinish(folder); } } } -auto ApiWrap::dialogsLoadState(FolderId folderId) -> DialogsLoadState* { - if (!folderId) { +auto ApiWrap::dialogsLoadState(Data::Folder *folder) -> DialogsLoadState* { + if (!folder) { return _dialogsLoadState.get(); } - const auto i = _foldersLoadState.find(folderId); + const auto i = _foldersLoadState.find(folder); return (i != end(_foldersLoadState)) ? &i->second : nullptr; } -void ApiWrap::dialogsLoadFinish(FolderId folderId) { +void ApiWrap::dialogsLoadFinish(Data::Folder *folder) { const auto notify = [&] { Core::App().postponeCall(crl::guard(_session, [=] { - _session->data().chatsListDone(folderId); + _session->data().chatsListDone(folder); })); }; - const auto state = dialogsLoadState(folderId); + const auto state = dialogsLoadState(folder); if (!state || !state->listReceived || !state->pinnedReceived) { return; } - if (folderId) { - _foldersLoadState.remove(folderId); + if (folder) { + _foldersLoadState.remove(folder); notify(); } else { _dialogsLoadState = nullptr; @@ -874,32 +874,32 @@ void ApiWrap::dialogsLoadFinish(FolderId folderId) { } } -void ApiWrap::requestPinnedDialogs(FolderId folderId) { - const auto state = dialogsLoadState(folderId); +void ApiWrap::requestPinnedDialogs(Data::Folder *folder) { + const auto state = dialogsLoadState(folder); if (!state || state->pinnedReceived || state->pinnedRequestId) { return; } const auto finalize = [=] { - if (const auto state = dialogsLoadState(folderId)) { + if (const auto state = dialogsLoadState(folder)) { state->pinnedRequestId = 0; state->pinnedReceived = true; - dialogsLoadFinish(folderId); + dialogsLoadFinish(folder); } }; state->pinnedRequestId = request(MTPmessages_GetPinnedDialogs( - MTP_int(folderId) + MTP_int(folder ? folder->id() : 0) )).done([=](const MTPmessages_PeerDialogs &result) { finalize(); result.match([&](const MTPDmessages_peerDialogs &data) { _session->data().processUsers(data.vusers); _session->data().processChats(data.vchats); - _session->data().clearPinnedChats(folderId); + _session->data().clearPinnedChats(folder); _session->data().applyDialogs( - folderId, + folder, data.vmessages.v, data.vdialogs.v); - _session->data().chatsListChanged(folderId); + _session->data().chatsListChanged(folder); }); }).fail([=](const RPCError &error) { finalize(); @@ -1258,10 +1258,15 @@ void ApiWrap::gotChatFull( channel->setInviteLink((f.vexported_invite.type() == mtpc_chatInviteExported) ? qs(f.vexported_invite.c_chatInviteExported().vlink) : QString()); if (const auto history = _session->data().historyLoaded(channel)) { history->clearUpTill(f.vavailable_min_id.v); - history->applyDialogFields( - f.vunread_count.v, - f.vread_inbox_max_id.v, - f.vread_outbox_max_id.v); + if (history->folderKnown()) { + history->applyDialogFields( + history->folder(), + f.vunread_count.v, + f.vread_inbox_max_id.v, + f.vread_outbox_max_id.v); + } else { + requestDialogEntry(history); + } } if (f.has_pinned_msg_id()) { channel->setPinnedMessageId(f.vpinned_msg_id.v); diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 6e03f71db6..24315594ac 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -64,7 +64,7 @@ public: void applyUpdates(const MTPUpdates &updates, uint64 sentMessageRandomId = 0); - void savePinnedOrder(FolderId folderId); + void savePinnedOrder(Data::Folder *folder); void toggleHistoryArchived( not_null history, bool archived, @@ -79,8 +79,8 @@ public: QString exportDirectMessageLink(not_null item); void requestContacts(); - void requestDialogs(FolderId folderId); - void requestPinnedDialogs(FolderId folderId); + void requestDialogs(Data::Folder *folder = nullptr); + void requestPinnedDialogs(Data::Folder *folder = nullptr); void requestMoreBlockedByDateDialogs(); rpl::producer dialogsLoadMayBlockByDate() const; rpl::producer dialogsLoadBlockedByDate() const; @@ -467,12 +467,12 @@ private: void setupSupportMode(); void refreshDialogsLoadBlocked(); void updateDialogsOffset( - FolderId folderId, + Data::Folder *folder, const QVector &dialogs, const QVector &messages); - void requestMoreDialogs(FolderId folderId); - DialogsLoadState *dialogsLoadState(FolderId folderId); - void dialogsLoadFinish(FolderId folderId); + void requestMoreDialogs(Data::Folder *folder); + DialogsLoadState *dialogsLoadState(Data::Folder *folder); + void dialogsLoadFinish(Data::Folder *folder); void checkQuitPreventFinished(); @@ -761,7 +761,9 @@ private: rpl::variable _dialogsLoadMayBlockByDate = false; rpl::variable _dialogsLoadBlockedByDate = false; - base::flat_map _foldersLoadState; + base::flat_map< + not_null, + DialogsLoadState> _foldersLoadState; rpl::event_stream _sendActions; diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index 3535f5e378..1d017d2fb9 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -19,7 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwidget.h" #include "lang/lang_keys.h" #include "history/history.h" -#include "dialogs/dialogs_indexed_list.h" +#include "dialogs/dialogs_main_list.h" #include "styles/style_boxes.h" #include "styles/style_profile.h" @@ -266,13 +266,13 @@ void ChatsListBoxController::rebuildRows() { ++added; } } - added += appendList(Auth().data().chatsList(Dialogs::Mode::All)); + added += appendList(Auth().data().chatsList()->indexed()); added += appendList(Auth().data().contactsNoChatsList()); if (!wasEmpty && added > 0) { // Place dialogs list before contactsNoDialogs list. delegate()->peerListPartitionRows([](const PeerListRow &a) { const auto history = static_cast(a).history(); - return history->inChatList(Dialogs::Mode::All); + return history->inChatList(); }); if (respectSavedMessagesChat()) { delegate()->peerListPartitionRows([](const PeerListRow &a) { diff --git a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp index 8dad27891a..e069043560 100644 --- a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp @@ -1080,7 +1080,7 @@ void AddSpecialBoxSearchController::addChatsContacts() { return result; }; const auto dialogsIndex = getSmallestIndex( - _peer->owner().chatsList(Dialogs::Mode::All)); + _peer->owner().chatsList()->indexed()); const auto contactsIndex = getSmallestIndex( _peer->owner().contactsNoChatsList()); diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp index 24e7143749..1257cdcfda 100644 --- a/Telegram/SourceFiles/boxes/share_box.cpp +++ b/Telegram/SourceFiles/boxes/share_box.cpp @@ -489,7 +489,7 @@ ShareBox::Inner::Inner( _rowHeight = st::shareRowHeight; setAttribute(Qt::WA_OpaquePaintEvent); - const auto dialogs = Auth().data().chatsList(Dialogs::Mode::All); + const auto dialogs = Auth().data().chatsList()->indexed(); const auto self = Auth().user(); if (_filterCallback(self)) { _chatsIndexed->addToEnd(self->owner().history(self)); diff --git a/Telegram/SourceFiles/data/data_folder.cpp b/Telegram/SourceFiles/data/data_folder.cpp index a1c546ef94..2fa038f8ac 100644 --- a/Telegram/SourceFiles/data/data_folder.cpp +++ b/Telegram/SourceFiles/data/data_folder.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item.h" #include "lang/lang_keys.h" #include "storage/storage_facade.h" +#include "core/application.h" //#include "storage/storage_feed_messages.h" // #feed #include "auth_session.h" #include "apiwrap.h" @@ -25,6 +26,16 @@ namespace { constexpr auto kLoadedChatsMinCount = 20; +rpl::producer PinnedDialogsInFolderMaxValue() { + return rpl::single( + rpl::empty_value() + ) | rpl::then( + Core::App().configUpdates() + ) | rpl::map([=] { + return Global::PinnedDialogsInFolderMax(); + }); +} + } // namespace // #feed @@ -40,9 +51,7 @@ constexpr auto kLoadedChatsMinCount = 20; Folder::Folder(not_null owner, FolderId id) : Entry(owner, this) , _id(id) -, _chatsList(Dialogs::SortMode::Date) -, _importantChatsList(Dialogs::SortMode::Date) -, _pinnedChatsList(Global::PinnedDialogsInFolderMax()) +, _chatsList(PinnedDialogsInFolderMaxValue()) , _name(lang(lng_archived_chats)) { indexNameParts(); } @@ -78,51 +87,19 @@ void Folder::indexNameParts() { } void Folder::registerOne(not_null history) { - //session().storage().invalidate( // #feed - // Storage::FeedMessagesInvalidate(_id)); - - if (unreadCountKnown()) { - if (history->unreadCountKnown()) { - // If history unreadCount is known that means that we've - // already had the channel information and if it was in the - // feed already (not yet known) it wouldn't get here. - // That means here we get if we add a new channel to feed. - if (const auto count = history->unreadCount()) { - unreadCountChanged(count, history->mute() ? count : 0); - } - } else { - session().api().requestDialogEntry(this); - } - } - if (_chatsList.size() == 1) { + if (_chatsList.indexed()->size() == 1) { updateChatListSortPosition(); } - owner().notifyFolderUpdated(this, FolderUpdateFlag::List); } void Folder::unregisterOne(not_null history) { - //session().storage().remove( // #feed - // Storage::FeedMessagesRemoveAll(_id, channel->bareId())); - - if (unreadCountKnown()) { - if (history->unreadCountKnown()) { - if (const auto delta = -history->unreadCount()) { - unreadCountChanged(delta, history->mute() ? delta : 0); - } - } else { - session().api().requestDialogEntry(this); - } - } if (_chatsList.empty()) { updateChatListExistence(); } - owner().notifyFolderUpdated(this, FolderUpdateFlag::List); } -not_null Folder::chatsList(Dialogs::Mode list) { - return (list == Dialogs::Mode::All) - ? &_chatsList - : &_importantChatsList; +not_null Folder::chatsList() { + return &_chatsList; } void Folder::loadUserpic() { @@ -163,71 +140,16 @@ void Folder::paintUserpic( } bool Folder::chatsListLoaded() const { - return _chatsListLoaded; + return _chatsList.loaded(); } void Folder::setChatsListLoaded(bool loaded) { - if (_chatsListLoaded != loaded) { - _chatsListLoaded = loaded; - owner().notifyFolderUpdated(this, FolderUpdateFlag::List); + if (_chatsList.loaded() == loaded) { + return; } + const auto notifier = unreadStateChangeNotifier(true); + _chatsList.setLoaded(loaded); } -// // #feed -//int32 Folder::chatsHash() const { -// const auto ordered = ranges::view::all( -// _histories -// ) | ranges::view::transform([](not_null history) { -// return history->peer->bareId(); -// }) | ranges::to_vector | ranges::action::sort; -// return Api::CountHash(ordered); -//} -// -//void Folder::setChats(std::vector> chats) { -// const auto remove = ranges::view::all( -// _histories -// ) | ranges::view::transform([](not_null history) { -// return history->peer; -// }) | ranges::view::filter([&](not_null peer) { -// return !base::contains(chats, peer); -// }) | ranges::to_vector; -// -// const auto add = ranges::view::all( -// chats -// ) | ranges::view::filter([&](not_null peer) { -// return ranges::find( -// _histories, -// peer, -// [](auto history) { return history->peer; } -// ) == end(_histories); -// }) | ranges::view::transform([](PeerData *peer) { -// return not_null(peer); -// }) | ranges::to_vector; -// -// changeChatsList(add, remove); -// -// setChatsLoaded(true); -//} -// -//void Folder::changeChatsList( -// const std::vector> &add, -// const std::vector> &remove) { -// _settingChats = true; -// const auto restore = gsl::finally([&] { _settingChats = false; }); -// -// for (const auto channel : remove) { -// channel->clearFeed(); -// } -// -// //// We assume the last message was correct before requesting the list. -// //// So we save it and don't allow channels from the list to change it. -// //// After that we restore it. -// const auto oldChatListMessage = base::take(_chatListMessage); -// for (const auto channel : add) { -// _chatListMessage = std::nullopt; -// channel->setFeed(this); -// } -// _chatListMessage = oldChatListMessage; -//} void Folder::requestChatListMessage() { if (!chatListMessageKnown()) { @@ -235,66 +157,14 @@ void Folder::requestChatListMessage() { } } -void Folder::setPinnedChatsLimit(int limit) { - _pinnedChatsList.setLimit(limit); -} - -void Folder::setChatPinned(const Dialogs::Key &key, bool pinned) { - _pinnedChatsList.setPinned(key, pinned); -} - -void Folder::addPinnedChat(const Dialogs::Key &key) { - _pinnedChatsList.addPinned(key); -} - -void Folder::applyPinnedChats(const QVector &list) { - _pinnedChatsList.applyList(&owner(), list); -} - -const std::vector &Folder::pinnedChatsOrder() const { - return _pinnedChatsList.order(); -} - -void Folder::clearPinnedChats() { - _pinnedChatsList.clear(); -} - -void Folder::reorderTwoPinnedChats( - const Dialogs::Key &key1, - const Dialogs::Key &key2) { - _pinnedChatsList.reorder(key1, key2); -} - TimeId Folder::adjustedChatListTimeId() const { - return _chatsList.empty() + const auto list = _chatsList.indexed(); + return list->empty() ? TimeId(0) - : (*_chatsList.begin())->entry()->adjustedChatListTimeId(); -} - -int Folder::unreadCount() const { - return _unreadCount.value_or(0); -} - -rpl::producer Folder::unreadCountValue() const { - return rpl::single( - unreadCount() - ) | rpl::then(_unreadCountChanges.events()); -} - -bool Folder::unreadCountKnown() const { - return !!_unreadCount; + : (*list->begin())->entry()->adjustedChatListTimeId(); } void Folder::applyDialog(const MTPDdialogFolder &data) { - //const auto addChannel = [&](ChannelId channelId) { // #feed - // if (const auto channel = owner().channelLoaded(channelId)) { - // channel->setFeed(this); - // } - //}; - //for (const auto &channelId : data.vfeed_other_channels.v) { - // addChannel(channelId.v); - //} - if (const auto peerId = peerFromMTP(data.vpeer)) { const auto history = owner().history(peerId); const auto fullId = FullMsgId( @@ -303,24 +173,36 @@ void Folder::applyDialog(const MTPDdialogFolder &data) { history->setFolder(this, App::histItemById(fullId)); } else { _chatsList.clear(); - _importantChatsList.clear(); updateChatListExistence(); } - setUnreadCounts( - data.vunread_unmuted_messages_count.v, - data.vunread_muted_messages_count.v); - //setUnreadMark(data.is_unread_mark()); - //setUnreadMentionsCount(data.vunread_mentions_count.v); - - //if (data.has_read_max_position()) { // #feed - // setUnreadPosition(FeedPositionFromMTP(data.vread_max_position)); - //} - - if (_chatsList.size() < kLoadedChatsMinCount) { - session().api().requestDialogs(_id); + updateCloudUnread(data); + if (_chatsList.indexed()->size() < kLoadedChatsMinCount) { + session().api().requestDialogs(this); } } +void Folder::updateCloudUnread(const MTPDdialogFolder &data) { + const auto notifier = unreadStateChangeNotifier(!_chatsList.loaded()); + + _cloudUnread.messagesCountMuted = data.vunread_muted_messages_count.v; + _cloudUnread.messagesCount = _cloudUnread.messagesCountMuted + + data.vunread_unmuted_messages_count.v; + _cloudUnread.chatsCountMuted = data.vunread_muted_peers_count.v; + _cloudUnread.chatsCount = _cloudUnread.chatsCountMuted + + data.vunread_unmuted_peers_count.v; +} + +Dialogs::UnreadState Folder::chatListUnreadState() const { + const auto state = _chatsList.loaded() + ? _chatsList.unreadState() + : _cloudUnread; + auto result = Dialogs::UnreadState(); + result.messagesCount = state.messagesCount; + result.messagesCountMuted = result.messagesCount.value_or(0); + result.chatsCount = result.chatsCountMuted = state.chatsCount; + return result; +} + void Folder::applyPinnedUpdate(const MTPDupdateDialogPinned &data) { const auto folderId = data.has_folder_id() ? data.vfolder_id.v : 0; if (folderId != 0) { @@ -329,129 +211,50 @@ void Folder::applyPinnedUpdate(const MTPDupdateDialogPinned &data) { owner().setChatPinned(this, data.is_pinned()); } -void Folder::changedInChatListHook(Dialogs::Mode list, bool added) { - if (list != Dialogs::Mode::All) { +void Folder::unreadStateChanged( + const Dialogs::UnreadState &wasState, + const Dialogs::UnreadState &nowState) { + const auto updateCloudUnread = _cloudUnread.messagesCount.has_value() + && wasState.messagesCount.has_value(); + const auto notify = _chatsList.loaded() || updateCloudUnread; + const auto notifier = unreadStateChangeNotifier(notify); + + _chatsList.unreadStateChanged(wasState, nowState); + if (!_cloudUnread.messagesCount.has_value() + || !wasState.messagesCount.has_value()) { return; } - if (const auto count = unreadCount()) { - const auto mutedCount = _unreadMutedCount; - const auto nonMutedCount = count - mutedCount; - const auto mutedDelta = added ? mutedCount : -mutedCount; - const auto nonMutedDelta = added ? nonMutedCount : -nonMutedCount; - owner().unreadIncrement(nonMutedDelta, false); - owner().unreadIncrement(mutedDelta, true); + Assert(nowState.messagesCount.has_value()); - const auto fullMuted = (nonMutedCount == 0); - const auto entriesWithUnreadDelta = added ? 1 : -1; - const auto mutedEntriesWithUnreadDelta = fullMuted - ? entriesWithUnreadDelta - : 0; - owner().unreadEntriesChanged( - entriesWithUnreadDelta, - mutedEntriesWithUnreadDelta); - } + *_cloudUnread.messagesCount += *nowState.messagesCount + - *wasState.messagesCount; + _cloudUnread.messagesCountMuted += nowState.messagesCountMuted + - wasState.messagesCountMuted; + _cloudUnread.chatsCount += nowState.chatsCount - wasState.chatsCount; + _cloudUnread.chatsCountMuted += nowState.chatsCountMuted + - wasState.chatsCountMuted; } -template -void Folder::updateUnreadCounts(PerformUpdate &&performUpdate) { - const auto wasUnreadCount = _unreadCount ? *_unreadCount : 0; - const auto wasUnreadMutedCount = _unreadMutedCount; - const auto wasFullMuted = (wasUnreadMutedCount > 0) - && (wasUnreadCount == wasUnreadMutedCount); +void Folder::unreadEntryChanged( + const Dialogs::UnreadState &state, + bool added) { + const auto updateCloudUnread = _cloudUnread.messagesCount.has_value() + && state.messagesCount.has_value(); + const auto notify = _chatsList.loaded() || updateCloudUnread; + const auto notifier = unreadStateChangeNotifier(notify); - performUpdate(); - Assert(_unreadCount.has_value()); - - _unreadCountChanges.fire(unreadCount()); - updateChatListEntry(); - - if (inChatList(Dialogs::Mode::All)) { - const auto nowUnreadCount = *_unreadCount; - const auto nowUnreadMutedCount = _unreadMutedCount; - const auto nowFullMuted = (nowUnreadMutedCount > 0) - && (nowUnreadCount == nowUnreadMutedCount); - - owner().unreadIncrement( - (nowUnreadCount - nowUnreadMutedCount) - - (wasUnreadCount - wasUnreadMutedCount), - false); - owner().unreadIncrement( - nowUnreadMutedCount - wasUnreadMutedCount, - true); - - const auto entriesDelta = (nowUnreadCount && !wasUnreadCount) - ? 1 - : (wasUnreadCount && !nowUnreadCount) - ? -1 - : 0; - const auto mutedEntriesDelta = (!wasFullMuted && nowFullMuted) - ? 1 - : (wasFullMuted && !nowFullMuted) - ? -1 - : 0; - owner().unreadEntriesChanged( - entriesDelta, - mutedEntriesDelta); - } -} - -void Folder::setUnreadCounts(int unreadNonMutedCount, int unreadMutedCount) { - if (unreadCountKnown() - && (*_unreadCount == unreadNonMutedCount + unreadMutedCount) - && (_unreadMutedCount == unreadMutedCount)) { + _chatsList.unreadEntryChanged(state, added); + if (!_cloudUnread.messagesCount.has_value() + || !state.messagesCount.has_value()) { return; } - updateUnreadCounts([&] { - _unreadCount = unreadNonMutedCount + unreadMutedCount; - _unreadMutedCount = unreadMutedCount; - }); + const auto delta = (added ? 1 : -1); + *_cloudUnread.messagesCount += delta * *state.messagesCount; + _cloudUnread.messagesCountMuted += delta * state.messagesCountMuted; + _cloudUnread.chatsCount += delta * state.chatsCount; + _cloudUnread.chatsCountMuted += delta * state.chatsCountMuted; } -// #feed -//void Folder::setUnreadPosition(const MessagePosition &position) { -// if (_unreadPosition.current() < position) { -// _unreadPosition = position; -// } -//} -void Folder::unreadCountChanged(int unreadCountDelta, int mutedCountDelta) { - if (!unreadCountKnown()) { - return; - } - updateUnreadCounts([&] { - accumulate_max(unreadCountDelta, -*_unreadCount); - *_unreadCount += unreadCountDelta; - - mutedCountDelta = snap( - mutedCountDelta, - -_unreadMutedCount, - *_unreadCount - _unreadMutedCount); - _unreadMutedCount += mutedCountDelta; - }); -} -// -//void Folder::setUnreadMark(bool unread) { -// if (_unreadMark != unread) { -// _unreadMark = unread; -// if (!_unreadCount || !*_unreadCount) { -// if (inChatList(Dialogs::Mode::All)) { -// const auto delta = _unreadMark ? 1 : -1; -// owner().unreadIncrement(delta, mute()); -// owner().unreadEntriesChanged( -// delta, -// mute() ? delta : 0); -// -// updateChatListEntry(); -// } -// } -// Notify::peerUpdatedDelayed( -// peer, -// Notify::PeerUpdate::Flag::UnreadViewChanged); -// } -//} -// -//bool Folder::unreadMark() const { -// return _unreadMark; -//} // #feed //MessagePosition Folder::unreadPosition() const { // return _unreadPosition.current(); @@ -462,7 +265,7 @@ void Folder::unreadCountChanged(int unreadCountDelta, int mutedCountDelta) { //} bool Folder::toImportant() const { - return !_importantChatsList.empty(); + return false; } int Folder::fixedOnTopIndex() const { @@ -474,7 +277,9 @@ bool Folder::shouldBeInChatList() const { } int Folder::chatListUnreadCount() const { - return unreadCount(); + return session().settings().countUnreadMessages() + ? chatListUnreadState().messagesCount.value_or(0) + : chatListUnreadState().chatsCount; } bool Folder::chatListUnreadMark() const { @@ -482,18 +287,20 @@ bool Folder::chatListUnreadMark() const { } bool Folder::chatListMutedBadge() const { - return _unreadCount ? (*_unreadCount <= _unreadMutedCount) : false; + return true; } HistoryItem *Folder::chatListMessage() const { - return _chatsList.empty() + const auto list = _chatsList.indexed(); + return list->empty() ? nullptr - : (*_chatsList.begin())->key().entry()->chatListMessage(); + : (*list->begin())->key().entry()->chatListMessage(); } bool Folder::chatListMessageKnown() const { - return _chatsList.empty() - || (*_chatsList.begin())->key().entry()->chatListMessageKnown(); + const auto list = _chatsList.indexed(); + return list->empty() + || (*list->begin())->key().entry()->chatListMessageKnown(); } const QString &Folder::chatListName() const { diff --git a/Telegram/SourceFiles/data/data_folder.h b/Telegram/SourceFiles/data/data_folder.h index a734006fa8..01f146d448 100644 --- a/Telegram/SourceFiles/data/data_folder.h +++ b/Telegram/SourceFiles/data/data_folder.h @@ -8,8 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "dialogs/dialogs_entry.h" -#include "dialogs/dialogs_indexed_list.h" -#include "dialogs/dialogs_pinned_list.h" +#include "dialogs/dialogs_main_list.h" #include "data/data_messages.h" class ChannelData; @@ -20,15 +19,6 @@ namespace Data { class Session; class Folder; -enum class FolderUpdateFlag { - List, -}; - -struct FolderUpdate { - not_null folder; - FolderUpdateFlag flag; -}; - //MessagePosition FeedPositionFromMTP(const MTPFeedPosition &position); // #feed class Folder final : public Dialogs::Entry { @@ -43,43 +33,21 @@ public: void registerOne(not_null history); void unregisterOne(not_null history); - not_null chatsList(Dialogs::Mode list); + not_null chatsList(); void applyDialog(const MTPDdialogFolder &data); void applyPinnedUpdate(const MTPDupdateDialogPinned &data); - void setUnreadCounts(int unreadNonMutedCount, int unreadMutedCount); - //void setUnreadPosition(const MessagePosition &position); // #feed - void unreadCountChanged( - int unreadCountDelta, - int mutedCountDelta); - rpl::producer unreadCountValue() const; //MessagePosition unreadPosition() const; // #feed //rpl::producer unreadPositionChanges() const; // #feed - //void setUnreadMark(bool unread); - //bool unreadMark() const; - //int unreadCountForBadge() const; // unreadCount || unreadMark ? 1 : 0. - - void setPinnedChatsLimit(int limit); - - // Places on the last place in the list otherwise. - // Does nothing if already pinned. - void addPinnedChat(const Dialogs::Key &key); - - // if (pinned) places on the first place in the list. - void setChatPinned(const Dialogs::Key &key, bool pinned); - - void applyPinnedChats(const QVector &list); - const std::vector &pinnedChatsOrder() const; - void clearPinnedChats(); - void reorderTwoPinnedChats( - const Dialogs::Key &key1, - const Dialogs::Key &key2); + void updateCloudUnread(const MTPDdialogFolder &data); + void unreadStateChanged( + const Dialogs::UnreadState &wasState, + const Dialogs::UnreadState &nowState); + void unreadEntryChanged(const Dialogs::UnreadState &state, bool added); TimeId adjustedChatListTimeId() const override; - int unreadCount() const; - bool unreadCountKnown() const; int fixedOnTopIndex() const override; bool toImportant() const override; @@ -87,13 +55,13 @@ public: int chatListUnreadCount() const override; bool chatListUnreadMark() const override; bool chatListMutedBadge() const override; + Dialogs::UnreadState chatListUnreadState() const override; HistoryItem *chatListMessage() const override; bool chatListMessageKnown() const override; void requestChatListMessage() override; const QString &chatListName() const override; const base::flat_set &chatListNameWords() const override; const base::flat_set &chatListFirstLetters() const override; - void changedInChatListHook(Dialogs::Mode list, bool added) override; void loadUserpic() override; void paintUserpic( @@ -103,34 +71,20 @@ public: int size) const override; bool chatsListLoaded() const; - void setChatsListLoaded(bool loaded); - //int32 chatsHash() const; - //void setChats(std::vector> chats); // #feed + void setChatsListLoaded(bool loaded = true); private: void indexNameParts(); - //void changeChatsList( - // const std::vector> &add, - // const std::vector> &remove); - - template - void updateUnreadCounts(PerformUpdate &&performUpdate); FolderId _id = 0; - Dialogs::IndexedList _chatsList; - Dialogs::IndexedList _importantChatsList; - Dialogs::PinnedList _pinnedChatsList; - bool _chatsListLoaded = false; + Dialogs::MainList _chatsList; QString _name; base::flat_set _nameWords; base::flat_set _nameFirstLetters; + Dialogs::UnreadState _cloudUnread; //rpl::variable _unreadPosition; - std::optional _unreadCount; - rpl::event_stream _unreadCountChanges; - int _unreadMutedCount = 0; - //bool _unreadMark = false; }; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 48d5ca19ea..b22367e587 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -140,6 +140,16 @@ MTPPhotoSize FindDocumentThumbnail(const MTPDdocument &data) { : MTPPhotoSize(MTP_photoSizeEmpty(MTP_string(""))); } +rpl::producer PinnedDialogsCountMaxValue() { + return rpl::single( + rpl::empty_value() + ) | rpl::then( + Core::App().configUpdates() + ) | rpl::map([=] { + return Global::PinnedDialogsCountMax(); + }); +} + } // namespace Session::Session(not_null session) @@ -150,11 +160,9 @@ Session::Session(not_null session) , _bigFileCache(Core::App().databases().get( Local::cacheBigFilePath(), Local::cacheBigFileSettings())) -, _chatsList(Dialogs::SortMode::Date) -, _importantChatsList(Dialogs::SortMode::Date) +, _chatsList(PinnedDialogsCountMaxValue()) , _contactsList(Dialogs::SortMode::Name) , _contactsNoChatsList(Dialogs::SortMode::Name) -, _pinnedChatsList(Global::PinnedDialogsCountMax()) , _selfDestructTimer([=] { checkSelfDestructItems(); }) , _sendActionsAnimation([=](crl::time now) { return sendActionsAnimationCallback(now); @@ -168,14 +176,6 @@ Session::Session(not_null session) setupChannelLeavingViewer(); setupPeerNameViewer(); setupUserIsContactViewer(); - - Core::App().configUpdates( - ) | rpl::start_with_next([=] { - _pinnedChatsList.setLimit(Global::PinnedDialogsCountMax()); - for (const auto &[folderId, folder] : _folders) { - folder->setPinnedChatsLimit(Global::PinnedDialogsInFolderMax()); - } - }, _lifetime); } void Session::clear() { @@ -786,7 +786,7 @@ bool Session::sendActionsAnimationCallback(crl::time now) { } bool Session::chatsListLoaded(Data::Folder *folder) { - return folder ? folder->chatsListLoaded() : _chatsListLoaded; + return chatsList(folder)->loaded(); } void Session::chatsListChanged(FolderId folderId) { @@ -797,12 +797,11 @@ void Session::chatsListChanged(Data::Folder *folder) { _chatsListChanged.fire_copy(folder); } -void Session::chatsListDone(FolderId folderId) { - const auto folder = folderId ? this->folder(folderId).get() : nullptr; +void Session::chatsListDone(Data::Folder *folder) { if (folder) { - folder->setChatsListLoaded(true); + folder->setChatsListLoaded(); } else { - _chatsListLoaded = true; + _chatsList.setLoaded(); } _chatsListLoadedEvents.fire_copy(folder); } @@ -964,13 +963,6 @@ void Session::setupPeerNameViewer() { ) | rpl::start_with_next([=](const Notify::PeerUpdate &update) { const auto peer = update.peer; const auto &oldLetters = update.oldNameFirstLetters; - _chatsList.peerNameChanged(Dialogs::Mode::All, peer, oldLetters); - if (Global::DialogsModeEnabled()) { - _importantChatsList.peerNameChanged( - Dialogs::Mode::Important, - peer, - oldLetters); - } _contactsNoChatsList.peerNameChanged(peer, oldLetters); _contactsList.peerNameChanged(peer, oldLetters); }, _lifetime); @@ -991,7 +983,7 @@ void Session::setupUserIsContactViewer() { if (user->contactStatus() == UserData::ContactStatus::Contact) { const auto history = user->owner().history(user->id); _contactsList.addByName(history); - if (!_chatsList.contains(history)) { + if (!_chatsList.indexed()->contains(history)) { _contactsNoChatsList.addByName(history); } } else if (const auto history = user->owner().historyLoaded(user)) { @@ -1288,16 +1280,6 @@ rpl::producer> Session::megagroupParticipantAdded( }); } -void Session::notifyFolderUpdated( - not_null folder, - FolderUpdateFlag update) { - _folderUpdates.fire({ folder, update }); -} - -rpl::producer Session::folderUpdated() const { - return _folderUpdates.events(); -} - void Session::notifyStickersUpdated() { _stickersUpdated.fire({}); } @@ -1355,33 +1337,24 @@ MessageIdsList Session::itemOrItsGroup(not_null item) const { void Session::setChatPinned(const Dialogs::Key &key, bool pinned) { Expects(key.entry()->folderKnown()); - if (const auto folder = key.entry()->folder()) { - folder->setChatPinned(key, pinned); - } else { - _pinnedChatsList.setPinned(key, pinned); - } + const auto list = chatsList(key.entry()->folder())->pinned(); + list->setPinned(key, pinned); } void Session::setPinnedFromDialog(const Dialogs::Key &key, bool pinned) { Expects(key.entry()->folderKnown()); - if (const auto folder = key.entry()->folder()) { - if (pinned) { - folder->addPinnedChat(key); - } else { - folder->setChatPinned(key, false); - } - } else if (pinned) { - _pinnedChatsList.addPinned(key); + const auto list = chatsList(key.entry()->folder())->pinned(); + if (pinned) { + list->addPinned(key); } else { - _pinnedChatsList.setPinned(key, false); + list->setPinned(key, false); } } void Session::applyPinnedChats( - FolderId folderId, + Data::Folder *folder, const QVector &list) { - const auto folder = folderId ? this->folder(folderId).get() : nullptr; for (const auto &peer : list) { peer.match([&](const MTPDdialogPeer &data) { const auto history = this->history(peerFromMTP(data.vpeer)); @@ -1396,33 +1369,31 @@ void Session::applyPinnedChats( } }); } - if (folder) { - folder->applyPinnedChats(list); - } else { - _pinnedChatsList.applyList(this, list); - } + chatsList(folder)->pinned()->applyList(this, list); } void Session::applyDialogs( - FolderId requestFolderId, + Data::Folder *requestFolder, const QVector &messages, const QVector &dialogs) { App::feedMsgs(messages, NewMessageLast); for (const auto &dialog : dialogs) { dialog.match([&](const auto &data) { - applyDialog(requestFolderId, data); + applyDialog(requestFolder, data); }); } } -void Session::applyDialog(FolderId requestFolderId, const MTPDdialog &data) { +void Session::applyDialog( + Data::Folder *requestFolder, + const MTPDdialog &data) { const auto peerId = peerFromMTP(data.vpeer); if (!peerId) { return; } const auto history = session().data().history(peerId); - history->applyDialog(requestFolderId, data); + history->applyDialog(requestFolder, data); setPinnedFromDialog(history, data.is_pinned()); if (!history->fixedOnTopIndex() && !history->isPinnedDialog()) { @@ -1443,10 +1414,10 @@ void Session::applyDialog(FolderId requestFolderId, const MTPDdialog &data) { } void Session::applyDialog( - FolderId requestFolderId, + Data::Folder *requestFolder, const MTPDdialogFolder &data) { - if (requestFolderId != 0) { - LOG(("API Error: requestFolderId != 0 for dialogFolder.")); + if (requestFolder) { + LOG(("API Error: requestFolder != nullptr for dialogFolder.")); } const auto folder = processFolder(data.vfolder); folder->applyDialog(data); @@ -1478,36 +1449,23 @@ void Session::addAllSavedPeers() { addSavedPeersAfter(QDateTime()); } -int Session::pinnedChatsCount(FolderId folderId) const { - return pinnedChatsOrder(folderId).size(); +int Session::pinnedChatsCount(Data::Folder *folder) const { + return pinnedChatsOrder(folder).size(); } -int Session::pinnedChatsLimit(FolderId folderId) const { - return folderId +int Session::pinnedChatsLimit(Data::Folder *folder) const { + return folder ? Global::PinnedDialogsInFolderMax() : Global::PinnedDialogsCountMax(); } const std::vector &Session::pinnedChatsOrder( - FolderId folderId) const { - if (folderId) { - if (const auto folder = folderLoaded(folderId)) { - return folder->pinnedChatsOrder(); - } - static const auto result = std::vector(); - return result; - } - return _pinnedChatsList.order(); + Data::Folder *folder) const { + return chatsList(folder)->pinned()->order(); } -void Session::clearPinnedChats(FolderId folderId) { - if (folderId) { - if (const auto folder = folderLoaded(folderId)) { - folder->clearPinnedChats(); - } - } else { - _pinnedChatsList.clear(); - } +void Session::clearPinnedChats(Data::Folder *folder) { + chatsList(folder)->pinned()->clear(); } void Session::reorderTwoPinnedChats( @@ -1516,12 +1474,7 @@ void Session::reorderTwoPinnedChats( Expects(key1.entry()->folderKnown() && key2.entry()->folderKnown()); Expects(key1.entry()->folder() == key2.entry()->folder()); - const auto folder = key1.entry()->folder(); - if (folder) { - folder->reorderTwoPinnedChats(key1, key2); - } else { - _pinnedChatsList.reorder(key1, key2); - } + chatsList(key1.entry()->folder())->pinned()->reorder(key1, key2); } NotifySettings &Session::defaultNotifySettings( @@ -1621,57 +1574,61 @@ void Session::updateSendActionAnimation( } int Session::unreadBadge() const { + const auto state = _chatsList.unreadState(); return computeUnreadBadge( - _unreadFull, - _unreadMuted, - _unreadEntriesFull, - _unreadEntriesMuted); + state.messagesCount.value_or(0), + state.messagesCountMuted, + state.chatsCount, + state.chatsCountMuted); } bool Session::unreadBadgeMuted() const { + const auto state = _chatsList.unreadState(); return computeUnreadBadgeMuted( - _unreadFull, - _unreadMuted, - _unreadEntriesFull, - _unreadEntriesMuted); + state.messagesCount.value_or(0), + state.messagesCountMuted, + state.chatsCount, + state.chatsCountMuted); } int Session::unreadBadgeIgnoreOne(History *history) const { - const auto removeCount = (history - && history->inChatList(Dialogs::Mode::All)) + const auto removeCount = (history && history->inChatList()) ? history->unreadCount() : 0; if (!removeCount) { return unreadBadge(); } - const auto removeMuted = history->mute(); + const auto state = _chatsList.unreadState(); + const auto removeMuted = history->mute() + || (history->folder() != nullptr); return computeUnreadBadge( - _unreadFull - removeCount, - _unreadMuted - (removeMuted ? removeCount : 0), - _unreadEntriesFull - 1, - _unreadEntriesMuted - (removeMuted ? 1 : 0)); + state.messagesCount.value_or(0) - removeCount, + state.messagesCountMuted - (removeMuted ? removeCount : 0), + state.chatsCount - 1, + state.chatsCountMuted - (removeMuted ? 1 : 0)); } bool Session::unreadBadgeMutedIgnoreOne(History *history) const { - const auto removeCount = (history - && history->inChatList(Dialogs::Mode::All)) + const auto removeCount = (history && history->inChatList()) ? history->unreadCount() : 0; if (!removeCount) { return unreadBadgeMuted(); } + const auto state = _chatsList.unreadState(); const auto removeMuted = history->mute(); return computeUnreadBadgeMuted( - _unreadFull - removeCount, - _unreadMuted - (removeMuted ? removeCount : 0), - _unreadEntriesFull - 1, - _unreadEntriesMuted - (removeMuted ? 1 : 0)); + state.messagesCount.value_or(0) - removeCount, + state.messagesCountMuted - (removeMuted ? removeCount : 0), + state.chatsCount - 1, + state.chatsCountMuted - (removeMuted ? 1 : 0)); } int Session::unreadOnlyMutedBadge() const { + const auto state = _chatsList.unreadState(); return _session->settings().countUnreadMessages() - ? _unreadMuted - : _unreadEntriesMuted; + ? state.messagesCountMuted + : state.chatsCountMuted; } int Session::computeUnreadBadge( @@ -1698,57 +1655,29 @@ bool Session::computeUnreadBadgeMuted( : (entriesMuted >= entriesFull); } -void Session::unreadIncrement(int count, bool muted) { - if (!count) { - return; - } - _unreadFull += count; - if (muted) { - _unreadMuted += count; - } - if (_session->settings().countUnreadMessages()) { - if (!muted || _session->settings().includeMutedCounter()) { - Notify::unreadCounterUpdated(); - } - } -} +void Session::unreadStateChanged( + const Dialogs::Key &key, + const Dialogs::UnreadState &wasState) { + Expects(key.entry()->folderKnown()); + Expects(key.entry()->inChatList()); -void Session::unreadMuteChanged(int count, bool muted) { - const auto wasAll = (_unreadMuted == _unreadFull); - if (muted) { - _unreadMuted += count; + const auto nowState = key.entry()->chatListUnreadState(); + if (const auto folder = key.entry()->folder()) { + folder->unreadStateChanged(wasState, nowState); } else { - _unreadMuted -= count; - } - if (_session->settings().countUnreadMessages()) { - const auto nowAll = (_unreadMuted == _unreadFull); - const auto changed = !_session->settings().includeMutedCounter() - || (wasAll != nowAll); - if (changed) { - Notify::unreadCounterUpdated(); - } + _chatsList.unreadStateChanged(wasState, nowState); } + Notify::unreadCounterUpdated(); } -void Session::unreadEntriesChanged( - int withUnreadDelta, - int mutedWithUnreadDelta) { - if (!withUnreadDelta && !mutedWithUnreadDelta) { - return; - } - const auto wasAll = (_unreadEntriesMuted == _unreadEntriesFull); - _unreadEntriesFull += withUnreadDelta; - _unreadEntriesMuted += mutedWithUnreadDelta; - if (!_session->settings().countUnreadMessages()) { - const auto nowAll = (_unreadEntriesMuted == _unreadEntriesFull); - const auto withMuted = _session->settings().includeMutedCounter(); - const auto withMutedChanged = withMuted - && (withUnreadDelta != 0 || wasAll != nowAll); - const auto withoutMutedChanged = !withMuted - && (withUnreadDelta != mutedWithUnreadDelta); - if (withMutedChanged || withoutMutedChanged) { - Notify::unreadCounterUpdated(); - } +void Session::unreadEntryChanged(const Dialogs::Key &key, bool added) { + Expects(key.entry()->folderKnown()); + + const auto state = key.entry()->chatListUnreadState(); + if (const auto folder = key.entry()->folder()) { + folder->unreadEntryChanged(state, added); + } else { + _chatsList.unreadEntryChanged(state, added); } } @@ -2986,10 +2915,13 @@ not_null Session::processFolder(const MTPDfolder &data) { // return _defaultFeedId.value(); //} -not_null Session::chatsList(Dialogs::Mode list) { - return (list == Dialogs::Mode::All) - ? &_chatsList - : &_importantChatsList; +not_null Session::chatsList(Data::Folder *folder) { + return folder ? folder->chatsList().get() : &_chatsList; +} + +not_null Session::chatsList( + Data::Folder *folder) const { + return folder ? folder->chatsList() : &_chatsList; } not_null Session::contactsList() { @@ -3006,7 +2938,7 @@ auto Session::refreshChatListEntry(Dialogs::Key key) const auto entry = key.entry(); auto result = RefreshChatListEntryResult(); - result.changed = !entry->inChatList(Mode::All); + result.changed = !entry->inChatList(); if (result.changed) { const auto mainRow = entry->addToChatList(Mode::All); _contactsNoChatsList.del(key, mainRow); @@ -3241,7 +3173,7 @@ void Session::serviceNotification( MTPstring())); } const auto history = this->history(PeerData::kServiceNotificationsId); - if (!history->lastMessageKnown()) { + if (!history->folderKnown()) { _session->api().requestDialogEntry(history, [=] { insertCheckedServiceNotification(message, media, date); }); @@ -3263,10 +3195,6 @@ void Session::insertCheckedServiceNotification( const MTPMessageMedia &media, TimeId date) { const auto history = this->history(PeerData::kServiceNotificationsId); - if (!history->isReadyFor(ShowAtUnreadMsgId)) { - history->setUnreadCount(0); - history->getReadyFor(ShowAtTheEndMsgId); - } const auto flags = MTPDmessage::Flag::f_entities | MTPDmessage::Flag::f_from_id | MTPDmessage_ClientFlag::f_clientside_unread diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 1593abc568..2839009209 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -11,7 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/stickers.h" #include "dialogs/dialogs_key.h" #include "dialogs/dialogs_indexed_list.h" -#include "dialogs/dialogs_pinned_list.h" +#include "dialogs/dialogs_main_list.h" #include "data/data_groups.h" #include "data/data_notify_settings.h" #include "history/history_location_manager.h" @@ -51,8 +51,6 @@ struct SavedCredentials; namespace Data { class Folder; -enum class FolderUpdateFlag; -struct FolderUpdate; class WallPaper; @@ -148,7 +146,7 @@ public: } void chatsListChanged(FolderId folderId); void chatsListChanged(Data::Folder *folder); - void chatsListDone(FolderId folderId); + void chatsListDone(Data::Folder *folder); struct ItemVisibilityQuery { not_null item; @@ -209,9 +207,6 @@ public: [[nodiscard]] rpl::producer> megagroupParticipantAdded( not_null channel) const; - void notifyFolderUpdated(not_null folder, FolderUpdateFlag update); - [[nodiscard]] rpl::producer folderUpdated() const; - void notifyStickersUpdated(); [[nodiscard]] rpl::producer<> stickersUpdated() const; void notifySavedGifsUpdated(); @@ -299,20 +294,20 @@ public: void applyUpdate(const MTPDupdateChatDefaultBannedRights &update); void applyDialogs( - FolderId requestFolderId, + Data::Folder *requestFolder, const QVector &messages, const QVector &dialogs); void addSavedPeersAfter(const QDateTime &date); void addAllSavedPeers(); - int pinnedChatsCount(FolderId folderId) const; - int pinnedChatsLimit(FolderId folderId) const; + int pinnedChatsCount(Data::Folder *folder) const; + int pinnedChatsLimit(Data::Folder *folder) const; const std::vector &pinnedChatsOrder( - FolderId folderId) const; + Data::Folder *folder) const; void setChatPinned(const Dialogs::Key &key, bool pinned); - void clearPinnedChats(FolderId folderId); + void clearPinnedChats(Data::Folder *folder); void applyPinnedChats( - FolderId folderId, + Data::Folder *folder, const QVector &list); void reorderTwoPinnedChats( const Dialogs::Key &key1, @@ -346,11 +341,10 @@ public: bool unreadBadgeMutedIgnoreOne(History *history) const; int unreadOnlyMutedBadge() const; - void unreadIncrement(int count, bool muted); - void unreadMuteChanged(int count, bool muted); - void unreadEntriesChanged( - int withUnreadDelta, - int mutedWithUnreadDelta); + void unreadStateChanged( + const Dialogs::Key &key, + const Dialogs::UnreadState &wasState); + void unreadEntryChanged(const Dialogs::Key &key, bool added); void selfDestructIn(not_null item, crl::time delay); @@ -525,7 +519,9 @@ public: //FeedId defaultFeedId() const; //rpl::producer defaultFeedIdValue() const; - not_null chatsList(Dialogs::Mode list); + not_null chatsList(Data::Folder *folder = nullptr); + not_null chatsList( + Data::Folder *folder = nullptr) const; not_null contactsList(); not_null contactsNoChatsList(); @@ -601,6 +597,7 @@ private: void setupUserIsContactViewer(); void checkSelfDestructItems(); + int computeUnreadBadge( int full, int muted, @@ -612,8 +609,10 @@ private: int entriesFull, int entriesMuted) const; - void applyDialog(FolderId requestFolderId, const MTPDdialog &data); - void applyDialog(FolderId requestFolderId, const MTPDdialogFolder &data); + void applyDialog(Data::Folder *requestFolder, const MTPDdialog &data); + void applyDialog( + Data::Folder *requestFolder, + const MTPDdialogFolder &data); void photoApplyFields( not_null photo, @@ -734,7 +733,6 @@ private: QPointer _exportSuggestion; rpl::variable _contactsLoaded = false; - bool _chatsListLoaded = false; rpl::event_stream _chatsListLoadedEvents; rpl::event_stream _chatsListChanged; base::Observable _queryItemVisibility; @@ -756,7 +754,6 @@ private: rpl::event_stream> _historyChanged; rpl::event_stream _megagroupParticipantRemoved; rpl::event_stream _megagroupParticipantAdded; - rpl::event_stream _folderUpdates; rpl::event_stream _dialogsRowReplacements; rpl::event_stream<> _stickersUpdated; @@ -773,16 +770,9 @@ private: Stickers::Order _archivedStickerSetsOrder; Stickers::SavedGifs _savedGifs; - int _unreadFull = 0; - int _unreadMuted = 0; - int _unreadEntriesFull = 0; - int _unreadEntriesMuted = 0; - - Dialogs::IndexedList _chatsList; - Dialogs::IndexedList _importantChatsList; + Dialogs::MainList _chatsList; Dialogs::IndexedList _contactsList; Dialogs::IndexedList _contactsNoChatsList; - Dialogs::PinnedList _pinnedChatsList; base::Timer _selfDestructTimer; std::vector _selfDestructItems; diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp index 1dad18c818..1e0b4aeee4 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp @@ -77,7 +77,7 @@ void Entry::cacheProxyPromoted(bool promoted) { } bool Entry::needUpdateInChatList() const { - return inChatList(Dialogs::Mode::All) || shouldBeInChatList(); + return inChatList() || shouldBeInChatList(); } void Entry::updateChatListSortPosition() { @@ -102,6 +102,10 @@ void Entry::updateChatListExistence() { setChatListExistence(shouldBeInChatList()); } +void Entry::notifyUnreadStateChange(const UnreadState &wasState) { + owner().unreadStateChanged(_key, wasState); +} + void Entry::setChatListExistence(bool exists) { if (const auto main = App::main()) { if (exists && _sortKeyInChatList) { @@ -117,9 +121,6 @@ TimeId Entry::adjustedChatListTimeId() const { return chatListTimeId(); } -void Entry::changedInChatListHook(Dialogs::Mode list, bool added) { -} - void Entry::changedChatListPinHook() { } @@ -160,7 +161,9 @@ int Entry::posInChatList(Dialogs::Mode list) const { not_null Entry::addToChatList(Mode list) { if (!inChatList(list)) { chatListLinks(list) = myChatsList(list)->addToEnd(_key); - changedInChatListHook(list, true); + if (list == Mode::All) { + owner().unreadEntryChanged(_key, true); + } } return mainChatListLink(list); } @@ -169,7 +172,9 @@ void Entry::removeFromChatList(Dialogs::Mode list) { if (inChatList(list)) { myChatsList(list)->del(_key); chatListLinks(list).clear(); - changedInChatListHook(list, false); + if (list == Mode::All) { + owner().unreadEntryChanged(_key, false); + } } } @@ -194,7 +199,7 @@ void Entry::addChatListEntryByLetter( void Entry::updateChatListEntry() const { if (const auto main = App::main()) { - if (inChatList(Mode::All)) { + if (inChatList()) { main->repaintDialogRow( Mode::All, mainChatListLink(Mode::All)); @@ -212,10 +217,7 @@ void Entry::updateChatListEntry() const { } not_null Entry::myChatsList(Mode list) const { - if (const auto current = folder()) { - return current->chatsList(list); - } - return owner().chatsList(list); + return owner().chatsList(folder())->indexed(list); } } // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.h b/Telegram/SourceFiles/dialogs/dialogs_entry.h index bd40f42780..6f11954d7c 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.h +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.h @@ -40,6 +40,15 @@ struct PositionChange { int to = -1; }; +struct UnreadState { + std::optional messagesCount; + int messagesCountMuted = 0; + int chatsCount = 0; + int chatsCountMuted = 0; + bool mark = false; + bool markMuted = false; +}; + class Entry { public: Entry(not_null owner, const Key &key); @@ -51,7 +60,7 @@ public: AuthSession &session() const; PositionChange adjustByPosInChatList(Mode list); - bool inChatList(Mode list) const { + bool inChatList(Mode list = Mode::All) const { return !chatListLinks(list).empty(); } int posInChatList(Mode list) const; @@ -89,6 +98,7 @@ public: virtual int chatListUnreadCount() const = 0; virtual bool chatListUnreadMark() const = 0; virtual bool chatListMutedBadge() const = 0; + virtual UnreadState chatListUnreadState() const = 0; virtual HistoryItem *chatListMessage() const = 0; virtual bool chatListMessageKnown() const = 0; virtual void requestChatListMessage() = 0; @@ -125,10 +135,22 @@ public: mutable const HistoryItem *textCachedFor = nullptr; // cache mutable Text lastItemTextCache; +protected: + auto unreadStateChangeNotifier(bool required) { + const auto notify = required && inChatList(); + const auto wasState = notify ? chatListUnreadState() : UnreadState(); + return gsl::finally([=] { + if (notify) { + notifyUnreadStateChange(wasState); + } + }); + } + private: - virtual void changedInChatListHook(Mode list, bool added); virtual void changedChatListPinHook(); + void notifyUnreadStateChange(const UnreadState &wasState); + void setChatListExistence(bool exists); RowsByLetter &chatListLinks(Mode list); const RowsByLetter &chatListLinks(Mode list) const; diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 3a951a0b98..d8dd8c2739 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -96,6 +96,14 @@ DialogsInner::DialogsInner(QWidget *parent, not_null contro subscribe(session().downloaderTaskFinished(), [=] { update(); }); + subscribe(session().notifications().settingsChanged(), [=]( + Window::Notifications::ChangeType change) { + if (change == Window::Notifications::ChangeType::CountMessages) { + // Folder rows change their unread badge with this setting. + update(); + } + }); + session().data().contactsLoaded().changes( ) | rpl::start_with_next([=] { refresh(); @@ -143,8 +151,9 @@ DialogsInner::DialogsInner(QWidget *parent, not_null contro ) | rpl::filter([=](Data::Folder *folder) { return (folder == _openedFolder); }) | rpl::start_with_next([=] { + const auto mode = Global::DialogsMode(); if (_openedFolder - && _openedFolder->chatsList(Global::DialogsMode())->empty()) { + && _openedFolder->chatsList()->indexed(mode)->empty()) { _openedFolder->updateChatListSortPosition(); cancelFolder(); } else { @@ -188,10 +197,6 @@ DialogsInner::DialogsInner(QWidget *parent, not_null contro } } })); - session().data().folderUpdated( - ) | rpl::start_with_next([=](const Data::FolderUpdate &update) { - updateDialogRow({ update.folder, FullMsgId() }); - }, lifetime()); _controller->activeChatEntryValue( ) | rpl::combine_previous( @@ -218,8 +223,7 @@ void DialogsInner::handleChatMigration(not_null chat) { if (const auto from = chat->owner().historyLoaded(chat)) { if (const auto to = chat->owner().historyLoaded(channel)) { - if (to->inChatList(Dialogs::Mode::All) - && from->inChatList(Dialogs::Mode::All)) { + if (to->inChatList() && from->inChatList()) { removeDialog(from); } } @@ -227,8 +231,11 @@ void DialogsInner::handleChatMigration(not_null chat) { } int DialogsInner::dialogsOffset() const { - return (_openedFolder ? openedFolderSkip() : 0) - + (_importantSwitch ? st::dialogsImportantBarHeight : 0); + return _openedFolder + ? openedFolderSkip() + : _importantSwitch + ? st::dialogsImportantBarHeight + : 0; } int DialogsInner::proxyPromotedCount() const { @@ -298,6 +305,9 @@ bool DialogsInner::changeOpenedFolder(Data::Folder *folder) { Ui::DialogTextOptions()); } refresh(); + if (_loadMoreCallback) { + _loadMoreCallback(); + } return true; } @@ -320,7 +330,7 @@ void DialogsInner::paintEvent(QPaintEvent *e) { } if (_state == State::Default) { auto rows = shownDialogs(); - if (_importantSwitch) { + if (!_openedFolder && _importantSwitch) { auto selected = isPressed() ? _importantSwitchPressed : _importantSwitchSelected; Dialogs::Layout::paintImportantSwitch(p, Global::DialogsMode(), fullWidth, selected); dialogsClip.translate(0, -st::dialogsImportantBarHeight); @@ -807,12 +817,21 @@ void DialogsInner::selectByMouse(QPoint globalPosition) { _mouseSelection = true; _lastMousePosition = globalPosition; - int w = width(), mouseY = local.y(); + const auto w = width(); + const auto mouseY = local.y(); clearIrrelevantState(); if (_state == State::Default) { - auto importantSwitchSelected = (_importantSwitch && mouseY >= 0 && mouseY < dialogsOffset()); - mouseY -= dialogsOffset(); - auto selected = importantSwitchSelected ? nullptr : (mouseY >= 0 ? shownDialogs()->rowAtY(mouseY, st::dialogsRowHeight) : nullptr); + const auto switchTop = _openedFolder ? openedFolderSkip() : 0; + const auto switchBottom = dialogsOffset(); + const auto importantSwitchSelected = _importantSwitch + && !_openedFolder + && (mouseY >= switchTop) + && (mouseY < switchBottom); + const auto selected = importantSwitchSelected + ? nullptr + : (mouseY >= switchBottom) + ? shownDialogs()->rowAtY(mouseY - switchBottom, st::dialogsRowHeight) + : nullptr; if (_selected != selected || _importantSwitchSelected != importantSwitchSelected) { updateSelectedRow(); _selected = selected; @@ -945,8 +964,7 @@ void DialogsInner::checkReorderPinnedStart(QPoint localPosition) { if (updateReorderIndexGetCount() < 2) { _dragging = nullptr; } else { - const auto folderId = _openedFolder ? _openedFolder->id() : 0; - const auto &order = session().data().pinnedChatsOrder(folderId); + const auto &order = session().data().pinnedChatsOrder(_openedFolder); _pinnedOnDragStart = base::flat_set{ order.begin(), order.end() @@ -989,8 +1007,7 @@ int DialogsInner::countPinnedIndex(Dialogs::Row *ofRow) { } void DialogsInner::savePinnedOrder() { - const auto folderId = _openedFolder ? _openedFolder->id() : 0; - const auto &newOrder = session().data().pinnedChatsOrder(folderId); + const auto &newOrder = session().data().pinnedChatsOrder(_openedFolder); if (newOrder.size() != _pinnedOnDragStart.size()) { return; // Something has changed in the set of pinned chats. } @@ -999,7 +1016,7 @@ void DialogsInner::savePinnedOrder() { return; // Something has changed in the set of pinned chats. } } - session().api().savePinnedOrder(folderId); + session().api().savePinnedOrder(_openedFolder); } void DialogsInner::finishReorderPinned() { @@ -1513,7 +1530,8 @@ void DialogsInner::updateSelectedRow(Dialogs::Key key) { } else if (_selected) { update(0, dialogsOffset() + _selected->pos() * st::dialogsRowHeight, width(), st::dialogsRowHeight); } else if (_importantSwitchSelected) { - update(0, 0, width(), st::dialogsImportantBarHeight); + const auto switchTop = _openedFolder ? openedFolderSkip() : 0; + update(0, switchTop, width(), st::dialogsImportantBarHeight); } } else if (_state == State::Filtered) { if (key) { @@ -1536,9 +1554,8 @@ void DialogsInner::updateSelectedRow(Dialogs::Key key) { } not_null DialogsInner::shownDialogs() const { - return _openedFolder - ? _openedFolder->chatsList(Global::DialogsMode()) - : session().data().chatsList(Global::DialogsMode()); + const auto mode = Global::DialogsMode(); + return session().data().chatsList(_openedFolder)->indexed(mode); } void DialogsInner::leaveEventHook(QEvent *e) { @@ -1676,7 +1693,7 @@ void DialogsInner::applyFilterUpdate(QString newFilter, bool force) { _filterResultsGlobal.clear(); if (!_searchInChat && !words.isEmpty()) { const Dialogs::List *toFilter = nullptr; - if (const auto list = session().data().chatsList(Dialogs::Mode::All); !list->empty()) { + if (const auto list = session().data().chatsList()->indexed(); !list->empty()) { for (fi = fb; fi != fe; ++fi) { const auto found = list->filtered(fi->at(0)); if (!found || found->empty()) { @@ -2001,7 +2018,7 @@ void DialogsInner::peerSearchReceived( for (const auto &mtpPeer : result) { if (const auto peer = session().data().peerLoaded(peerFromMTP(mtpPeer))) { if (const auto history = peer->owner().historyLoaded(peer)) { - if (history->inChatList(Dialogs::Mode::All)) { + if (history->inChatList()) { continue; // skip existing chats } } @@ -2017,7 +2034,7 @@ void DialogsInner::peerSearchReceived( } void DialogsInner::notify_historyMuteUpdated(History *history) { - if (!_importantSwitch || !history->inChatList(Dialogs::Mode::All)) { + if (!_importantSwitch || !history->inChatList()) { return; } refreshDialog(history); diff --git a/Telegram/SourceFiles/dialogs/dialogs_list.h b/Telegram/SourceFiles/dialogs/dialogs_list.h index b383c39ffd..34fb53d147 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_list.h +++ b/Telegram/SourceFiles/dialogs/dialogs_list.h @@ -14,7 +14,7 @@ namespace Dialogs { enum class SortMode; -class List { +class List final { public: List(SortMode sortMode); List(const List &other) = delete; diff --git a/Telegram/SourceFiles/dialogs/dialogs_main_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_main_list.cpp new file mode 100644 index 0000000000..347cfbd2ea --- /dev/null +++ b/Telegram/SourceFiles/dialogs/dialogs_main_list.cpp @@ -0,0 +1,130 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "dialogs/dialogs_main_list.h" + +#include "observer_peer.h" +#include "history/history.h" + +namespace Dialogs { +namespace { + +UnreadState ApplyMarkToCounters(const UnreadState &state) { + auto result = UnreadState(); + const auto count = state.messagesCount.value_or(0); + result.messagesCount = (count > 0) + ? count + : state.mark + ? 1 + : 0; + result.messagesCountMuted = (state.messagesCountMuted > 0) + ? state.messagesCountMuted + : state.markMuted + ? 1 + : 0; + result.chatsCount = (state.chatsCount > 0) + ? state.chatsCount + : state.mark + ? 1 + : 0; + result.chatsCountMuted = (state.chatsCountMuted > 0) + ? state.chatsCountMuted + : state.markMuted + ? 1 + : 0; + return result; +} + +} // namespace + +MainList::MainList(rpl::producer pinnedLimit) +: _all(SortMode::Date) +, _important(SortMode::Date) +, _pinned(1) { + _unreadState.messagesCount = 0; + + std::move( + pinnedLimit + ) | rpl::start_with_next([=](int limit) { + _pinned.setLimit(limit); + }, _lifetime); + + Notify::PeerUpdateViewer( + Notify::PeerUpdate::Flag::NameChanged + ) | rpl::start_with_next([=](const Notify::PeerUpdate &update) { + const auto peer = update.peer; + const auto &oldLetters = update.oldNameFirstLetters; + _all.peerNameChanged(Mode::All, peer, oldLetters); + _important.peerNameChanged(Mode::Important, peer, oldLetters); + }, _lifetime); +} + +bool MainList::empty() const { + return _all.empty(); +} + +bool MainList::loaded() const { + return _loaded; +} + +void MainList::setLoaded(bool loaded) { + _loaded = loaded; +} + +void MainList::clear() { + _all.clear(); + _important.clear(); + _unreadState = UnreadState(); +} + +void MainList::unreadStateChanged( + const UnreadState &wasState, + const UnreadState &nowState) { + const auto wasWithMark = ApplyMarkToCounters(wasState); + const auto nowWithMark = ApplyMarkToCounters(nowState); + *_unreadState.messagesCount += *nowWithMark.messagesCount + - *wasWithMark.messagesCount; + _unreadState.messagesCountMuted += nowWithMark.messagesCountMuted + - wasWithMark.messagesCountMuted; + _unreadState.chatsCount += nowWithMark.chatsCount + - wasWithMark.chatsCount; + _unreadState.chatsCountMuted += nowWithMark.chatsCountMuted + - wasWithMark.chatsCountMuted; +} + +void MainList::unreadEntryChanged( + const Dialogs::UnreadState &state, + bool added) { + const auto withMark = ApplyMarkToCounters(state); + const auto delta = (added ? 1 : -1); + *_unreadState.messagesCount += delta * *withMark.messagesCount; + _unreadState.messagesCountMuted += delta * withMark.messagesCountMuted; + _unreadState.chatsCount += delta * withMark.chatsCount; + _unreadState.chatsCountMuted += delta * withMark.chatsCountMuted; +} + +UnreadState MainList::unreadState() const { + return _unreadState; +} + +not_null MainList::indexed(Mode list) { + return (list == Mode::All) ? &_all : &_important; +} + +not_null MainList::indexed(Mode list) const { + return (list == Mode::All) ? &_all : &_important; +} + +not_null MainList::pinned() { + return &_pinned; +} + +not_null MainList::pinned() const { + return &_pinned; +} + +} // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/dialogs_main_list.h b/Telegram/SourceFiles/dialogs/dialogs_main_list.h new file mode 100644 index 0000000000..ce58311054 --- /dev/null +++ b/Telegram/SourceFiles/dialogs/dialogs_main_list.h @@ -0,0 +1,49 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "dialogs/dialogs_indexed_list.h" +#include "dialogs/dialogs_pinned_list.h" + +namespace Dialogs { + +class MainList final { +public: + explicit MainList(rpl::producer pinnedLimit); + + bool empty() const; + bool loaded() const; + void setLoaded(bool loaded = true); + void clear(); + + void unreadStateChanged( + const UnreadState &wasState, + const UnreadState &nowState); + void unreadEntryChanged( + const Dialogs::UnreadState &state, + bool added); + UnreadState unreadState() const; + + not_null indexed(Mode list = Mode::All); + not_null indexed(Mode list = Mode::All) const; + not_null pinned(); + not_null pinned() const; + +private: + IndexedList _all; + IndexedList _important; + PinnedList _pinned; + UnreadState _unreadState; + + bool _loaded = false; + + rpl::lifetime _lifetime; + +}; + +} // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/dialogs_pinned_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_pinned_list.cpp index fecabf373f..d98ebf8615 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_pinned_list.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_pinned_list.cpp @@ -41,7 +41,7 @@ int PinnedList::addPinnedGetPosition(const Key &key) { applyLimit(_limit - 1); const auto position = int(_data.size()); _data.push_back(key); - key.entry()->cachePinnedIndex(position); + key.entry()->cachePinnedIndex(position + 1); return position; } diff --git a/Telegram/SourceFiles/dialogs/dialogs_pinned_list.h b/Telegram/SourceFiles/dialogs/dialogs_pinned_list.h index bda94285e1..c7910fc2dc 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_pinned_list.h +++ b/Telegram/SourceFiles/dialogs/dialogs_pinned_list.h @@ -15,7 +15,7 @@ namespace Dialogs { class Key; -class PinnedList { +class PinnedList final { public: explicit PinnedList(int limit); diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index bd9a5dcab5..286dfd9e20 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -248,9 +248,8 @@ DialogsWidget::DialogsWidget(QWidget *parent, not_null cont onSearchMore(); } else { const auto folder = _inner->shownFolder(); - const auto folderId = folder ? folder->id() : FolderId(0); - if (!folderId || !folder->chatsListLoaded()) { - session().api().requestDialogs(folderId); + if (!folder || !folder->chatsListLoaded()) { + session().api().requestDialogs(folder); } } }); @@ -378,13 +377,13 @@ void DialogsWidget::activate() { } void DialogsWidget::refreshDialog(Dialogs::Key key) { - const auto creating = !key.entry()->inChatList(Dialogs::Mode::All); + const auto creating = !key.entry()->inChatList(); _inner->refreshDialog(key); const auto history = key.history(); if (creating && history && history->peer->migrateFrom()) { if (const auto migrated = history->owner().historyLoaded( history->peer->migrateFrom())) { - if (migrated->inChatList(Dialogs::Mode::All)) { + if (migrated->inChatList()) { _inner->removeDialog(migrated); } } diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 48cb5373c2..aa56cc0279 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -1610,98 +1610,69 @@ int History::unreadCountForBadge() const { } bool History::unreadCountKnown() const { - return !!_unreadCount; + return _unreadCount.has_value(); } void History::setUnreadCount(int newUnreadCount) { - if (!_unreadCount || *_unreadCount != newUnreadCount) { - const auto wasUnread = _unreadMark || unreadCount(); - const auto unreadCountDelta = _unreadCount | [&](int count) { - return newUnreadCount - count; - }; - if (newUnreadCount == 1) { - if (loadedAtBottom()) { - _firstUnreadView = !isEmpty() - ? blocks.back()->messages.back().get() - : nullptr; - } - if (const auto last = msgIdForRead()) { - setInboxReadTill(last - 1); - } - } else if (!newUnreadCount) { - _firstUnreadView = nullptr; - if (const auto last = msgIdForRead()) { - setInboxReadTill(last); - } - } else { - if (!_firstUnreadView && !_unreadBarView && loadedAtBottom()) { - calculateFirstUnreadMessage(); - } - } - const auto unreadMarkDelta = [&] { - if (_unreadMark) { - const auto was = _unreadCount && (*_unreadCount > 0); - const auto now = (newUnreadCount > 0); - if (was != now) { - return was ? 1 : -1; - } - } - return 0; - }(); - _unreadCount = newUnreadCount; + Expects(folderKnown()); - if (_unreadBarView) { - const auto count = chatListUnreadCount(); - if (count > 0) { - _unreadBarView->setUnreadBarCount(count); - } else { - _unreadBarView->setUnreadBarFreezed(); - } - } - - if (inChatList(Dialogs::Mode::All)) { - const auto delta = unreadMarkDelta + (unreadCountDelta - ? *unreadCountDelta - : newUnreadCount); - owner().unreadIncrement(delta, mute()); - - const auto nowUnread = (*_unreadCount > 0) || _unreadMark; - const auto entriesDelta = (wasUnread && !nowUnread) - ? -1 - : (nowUnread && !wasUnread) - ? 1 - : 0; - owner().unreadEntriesChanged( - entriesDelta, - mute() ? entriesDelta : 0); - } - Notify::peerUpdatedDelayed( - peer, - Notify::PeerUpdate::Flag::UnreadViewChanged); + if (_unreadCount == newUnreadCount) { + return; } + const auto notifier = unreadStateChangeNotifier(true); + + _unreadCount = newUnreadCount; + + if (newUnreadCount == 1) { + if (loadedAtBottom()) { + _firstUnreadView = !isEmpty() + ? blocks.back()->messages.back().get() + : nullptr; + } + if (const auto last = msgIdForRead()) { + setInboxReadTill(last - 1); + } + } else if (!newUnreadCount) { + _firstUnreadView = nullptr; + if (const auto last = msgIdForRead()) { + setInboxReadTill(last); + } + } else { + if (!_firstUnreadView && !_unreadBarView && loadedAtBottom()) { + calculateFirstUnreadMessage(); + } + } + if (_unreadBarView) { + const auto count = chatListUnreadCount(); + if (count > 0) { + _unreadBarView->setUnreadBarCount(count); + } else { + _unreadBarView->setUnreadBarFreezed(); + } + } + Notify::peerUpdatedDelayed( + peer, + Notify::PeerUpdate::Flag::UnreadViewChanged); } void History::setUnreadMark(bool unread) { if (clearUnreadOnClientSide()) { unread = false; } - if (_unreadMark != unread) { - _unreadMark = unread; - if (!_unreadCount || !*_unreadCount) { - if (inChatList(Dialogs::Mode::All)) { - const auto delta = _unreadMark ? 1 : -1; - owner().unreadIncrement(delta, mute()); - owner().unreadEntriesChanged( - delta, - mute() ? delta : 0); - - updateChatListEntry(); - } - } - Notify::peerUpdatedDelayed( - peer, - Notify::PeerUpdate::Flag::UnreadViewChanged); + if (_unreadMark == unread) { + return; } + const auto noUnreadMessages = !unreadCount(); + const auto notifier = unreadStateChangeNotifier(noUnreadMessages); + + _unreadMark = unread; + + if (inChatList() && noUnreadMessages) { + updateChatListEntry(); + } + Notify::peerUpdatedDelayed( + peer, + Notify::PeerUpdate::Flag::UnreadViewChanged); } bool History::unreadMark() const { @@ -1728,38 +1699,15 @@ bool History::changeMute(bool newMute) { if (_mute == newMute) { return false; } + const auto notify = (unreadCountForBadge() > 0); + const auto notifier = unreadStateChangeNotifier(notify); + _mute = newMute; - //const auto feed = peer->isChannel() // #feed - // ? peer->asChannel()->feed() - // : nullptr; - //if (feed) { - // if (_unreadCount) { - // if (*_unreadCount) { - // const auto unreadCountDelta = 0; - // const auto mutedCountDelta = _mute ? *_unreadCount : -*_unreadCount; - // feed->unreadCountChanged(unreadCountDelta, mutedCountDelta); - // } - // } else { - // session().api().requestDialogEntry(this); - // session().api().requestDialogEntry(feed); - // } - //} - if (inChatList(Dialogs::Mode::All)) { - if (const auto count = unreadCountForBadge()) { - owner().unreadMuteChanged(count, _mute); - - const auto entriesWithUnreadDelta = 0; - const auto mutedEntriesWithUnreadDelta = _mute ? 1 : -1; - owner().unreadEntriesChanged( - entriesWithUnreadDelta, - mutedEntriesWithUnreadDelta); - - Notify::unreadCounterUpdated(); - } + if (inChatList()) { Notify::historyMuteUpdated(this); + updateChatListEntry(); } - updateChatListEntry(); Notify::peerUpdatedDelayed( peer, Notify::PeerUpdate::Flag::NotificationsEnabled); @@ -1839,9 +1787,9 @@ void History::setFolderPointer(Data::Folder *folder) { owner().setChatPinned(this, false); } const auto wasKnown = folderKnown(); - const auto wasInAll = inChatList(Mode::All); - const auto wasInImportant = wasInAll && inChatList(Mode::Important); - if (wasInAll) { + const auto wasInList = inChatList(); + const auto wasInImportant = wasInList && inChatList(Mode::Important); + if (wasInList) { removeFromChatList(Mode::All); if (wasInImportant) { removeFromChatList(Mode::Important); @@ -1852,7 +1800,7 @@ void History::setFolderPointer(Data::Folder *folder) { if (was) { was->unregisterOne(this); } - if (wasInAll) { + if (wasInList) { addToChatList(Mode::All); if (wasInImportant) { addToChatList(Mode::Important); @@ -2068,6 +2016,18 @@ bool History::chatListMutedBadge() const { return mute(); } +Dialogs::UnreadState History::chatListUnreadState() const { + auto result = Dialogs::UnreadState(); + const auto count = _unreadCount.value_or(0); + result.messagesCount = _unreadCount; + result.messagesCountMuted = (_unreadCount && mute()) ? count : 0; + result.chatsCount = count ? 1 : 0; + result.chatsCountMuted = (count && mute()) ? 1 : 0; + result.mark = _unreadMark; + result.markMuted = mute() ? _unreadMark : false; + return result; +} + HistoryItem *History::chatListMessage() const { return _chatListMessage.value_or(nullptr); } @@ -2482,8 +2442,16 @@ bool History::isServerSideUnread(not_null item) const { : (!_inboxReadBefore || (item->id >= *_inboxReadBefore)); } -void History::applyDialog(FolderId requestFolderId, const MTPDdialog &data) { +void History::applyDialog( + Data::Folder *requestFolder, + const MTPDdialog &data) { + const auto folder = data.has_folder_id() + ? (data.vfolder_id.v + ? owner().folder(data.vfolder_id.v).get() + : nullptr) + : requestFolder; applyDialogFields( + folder, data.vunread_count.v, data.vread_inbox_max_id.v, data.vread_outbox_max_id.v); @@ -2512,15 +2480,6 @@ void History::applyDialog(FolderId requestFolderId, const MTPDdialog &data) { if (data.has_draft() && data.vdraft.type() == mtpc_draftMessage) { Data::applyPeerCloudDraft(peer->id, data.vdraft.c_draftMessage()); } - - const auto folderId = data.has_folder_id() - ? data.vfolder_id.v - : requestFolderId; - if (folderId) { - setFolder(owner().folder(folderId)); - } else { - clearFolder(); - } session().api().dialogEntryApplied(this); } @@ -2593,9 +2552,15 @@ bool History::skipUnreadUpdate() const { } void History::applyDialogFields( + Data::Folder *folder, int unreadCount, MsgId maxInboxRead, MsgId maxOutboxRead) { + if (folder) { + setFolder(folder); + } else { + clearFolder(); + } if (!skipUnreadUpdate()) { setUnreadCount(unreadCount); setInboxReadTill(maxInboxRead); @@ -3089,19 +3054,6 @@ void History::applyGroupAdminChanges( } } -void History::changedInChatListHook(Dialogs::Mode list, bool added) { - if (list == Dialogs::Mode::All) { - if (const auto delta = unreadCountForBadge() * (added ? 1 : -1)) { - owner().unreadIncrement(delta, mute()); - - const auto entriesDelta = added ? 1 : -1; - owner().unreadEntriesChanged( - entriesDelta, - mute() ? entriesDelta : 0); - } - } -} - void History::changedChatListPinHook() { Notify::peerUpdatedDelayed( peer, diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 480e71664e..5697d3da20 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -191,9 +191,10 @@ public: bool lastMessageKnown() const; void unknownMessageDeleted(MsgId messageId); void applyDialogTopMessage(MsgId topMessageId); - void applyDialog(FolderId requestFolderId, const MTPDdialog &data); + void applyDialog(Data::Folder *requestFolder, const MTPDdialog &data); void applyPinnedUpdate(const MTPDupdateDialogPinned &data); void applyDialogFields( + Data::Folder *folder, int unreadCount, MsgId maxInboxRead, MsgId maxOutboxRead); @@ -299,6 +300,7 @@ public: int chatListUnreadCount() const override; bool chatListUnreadMark() const override; bool chatListMutedBadge() const override; + Dialogs::UnreadState chatListUnreadState() const override; HistoryItem *chatListMessage() const override; bool chatListMessageKnown() const override; void requestChatListMessage() override; @@ -417,7 +419,6 @@ private: void removeNotification(not_null item); TimeId adjustedChatListTimeId() const override; - void changedInChatListHook(Dialogs::Mode list, bool added) override; void changedChatListPinHook() override; void setInboxReadTill(MsgId upTo); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 53ad5ca1e1..b5ec3c2e4a 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -2872,16 +2872,16 @@ void MainWidget::gotChannelDifference( history->setNotLoadedAtBottom(); session().api().requestChannelRangeDifference(history); } - App::feedMsgs(data.vmessages, NewMessageLast); data.vdialog.match([&](const MTPDdialog &data) { if (data.has_pts()) { channel->ptsInit(data.vpts.v); } - if (history) { - history->applyDialog(data); - } }, [&](const MTPDdialogFolder &) { }); + session().data().applyDialogs( + nullptr, + data.vmessages.v, + QVector(1, data.vdialog)); if (_history->peer() == channel) { _history->updateHistoryDownVisibility(); _history->preloadHistoryIfNeeded(); @@ -4270,6 +4270,11 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { case mtpc_updatePinnedDialogs: { const auto &d = update.c_updatePinnedDialogs(); const auto folderId = d.has_folder_id() ? d.vfolder_id.v : 0; + const auto loaded = !folderId + || (session().data().folderLoaded(folderId) != nullptr); + const auto folder = folderId + ? session().data().folder(folderId).get() + : nullptr; const auto done = [&] { if (!d.has_order()) { return false; @@ -4280,6 +4285,11 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { return !session().data().historyLoaded( peerFromMTP(data.vpeer)); }, [&](const MTPDdialogPeerFolder &data) { + if (folderId) { + LOG(("API Error: " + "updatePinnedDialogs has nested folders.")); + return true; + } return !session().data().folderLoaded(data.vfolder_id.v); }); }; @@ -4288,17 +4298,23 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { if (!allLoaded) { return false; } - session().data().applyPinnedChats(folderId, order); + session().data().applyPinnedChats(folder, order); return true; }(); if (!done) { - session().api().requestPinnedDialogs(folderId); + session().api().requestPinnedDialogs(folder); + } + if (!loaded) { + session().api().requestDialogEntry(folder); } } break; case mtpc_updateDialogPinned: { const auto &d = update.c_updateDialogPinned(); const auto folderId = d.has_folder_id() ? d.vfolder_id.v : 0; + const auto folder = folderId + ? session().data().folder(folderId).get() + : nullptr; const auto done = d.vpeer.match([&](const MTPDdialogPeer &data) { const auto id = peerFromMTP(data.vpeer); if (const auto history = session().data().historyLoaded(id)) { @@ -4329,7 +4345,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { return false; }); if (!done) { - session().api().requestPinnedDialogs(folderId); + session().api().requestPinnedDialogs(folder); } } break; diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 89085a940b..0f07e72e51 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -94,13 +94,13 @@ private: }; -History *FindWastedPin(FolderId folderId) { - const auto &order = Auth().data().pinnedChatsOrder(folderId); +History *FindWastedPin(Data::Folder *folder) { + const auto &order = Auth().data().pinnedChatsOrder(folder); for (const auto &pinned : order) { if (const auto history = pinned.history()) { if (history->peer->isChat() && history->peer->asChat()->isDeactivated() - && !history->inChatList(Dialogs::Mode::All)) { + && !history->inChatList()) { return history; } } @@ -116,17 +116,16 @@ bool PinnedLimitReached(Dialogs::Key key) { Expects(key.entry()->folderKnown()); const auto folder = key.entry()->folder(); - const auto folderId = folder ? folder->id() : 0; - const auto pinnedCount = Auth().data().pinnedChatsCount(folderId); - const auto pinnedMax = Auth().data().pinnedChatsLimit(folderId); + const auto pinnedCount = Auth().data().pinnedChatsCount(folder); + const auto pinnedMax = Auth().data().pinnedChatsLimit(folder); if (pinnedCount < pinnedMax) { return false; } // Some old chat, that was converted, maybe is still pinned. - if (const auto wasted = FindWastedPin(folderId)) { + if (const auto wasted = FindWastedPin(folder)) { Auth().data().setChatPinned(wasted, false); Auth().data().setChatPinned(key, true); - Auth().api().savePinnedOrder(folderId); + Auth().api().savePinnedOrder(folder); } else { auto errorText = lng_error_pinned_max( lt_count, diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index daeea1868f..ed2e3bc14a 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -228,6 +228,8 @@ <(src_loc)/dialogs/dialogs_layout.h <(src_loc)/dialogs/dialogs_list.cpp <(src_loc)/dialogs/dialogs_list.h +<(src_loc)/dialogs/dialogs_main_list.cpp +<(src_loc)/dialogs/dialogs_main_list.h <(src_loc)/dialogs/dialogs_pinned_list.cpp <(src_loc)/dialogs/dialogs_pinned_list.h <(src_loc)/dialogs/dialogs_row.cpp