From e27a8fe05889bf18a43e84afb4f8a988395dea7c Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 17 Mar 2020 17:04:30 +0400 Subject: [PATCH] Support pinned locally in filters. --- Telegram/SourceFiles/apiwrap.cpp | 8 +- .../boxes/filters/edit_filter_box.cpp | 13 ++- .../boxes/filters/edit_filter_chats_list.cpp | 8 +- Telegram/SourceFiles/boxes/peer_list_box.cpp | 5 +- Telegram/SourceFiles/boxes/peer_list_box.h | 11 ++- .../SourceFiles/data/data_chat_filters.cpp | 59 ++++++++++--- Telegram/SourceFiles/data/data_chat_filters.h | 5 ++ Telegram/SourceFiles/data/data_folder.cpp | 2 +- Telegram/SourceFiles/data/data_session.cpp | 53 +++++++++--- Telegram/SourceFiles/data/data_session.h | 15 ++-- .../SourceFiles/dialogs/dialogs_entry.cpp | 63 ++++++++++---- Telegram/SourceFiles/dialogs/dialogs_entry.h | 28 +++--- .../dialogs/dialogs_indexed_list.cpp | 26 +++--- .../dialogs/dialogs_indexed_list.h | 3 +- .../dialogs/dialogs_inner_widget.cpp | 32 ++++--- .../SourceFiles/dialogs/dialogs_layout.cpp | 16 ++-- Telegram/SourceFiles/dialogs/dialogs_layout.h | 2 +- Telegram/SourceFiles/dialogs/dialogs_list.cpp | 14 +-- Telegram/SourceFiles/dialogs/dialogs_list.h | 3 +- .../SourceFiles/dialogs/dialogs_main_list.cpp | 4 +- .../dialogs/dialogs_pinned_list.cpp | 54 +++++++++--- .../SourceFiles/dialogs/dialogs_pinned_list.h | 11 ++- Telegram/SourceFiles/dialogs/dialogs_row.cpp | 6 +- Telegram/SourceFiles/dialogs/dialogs_row.h | 2 +- Telegram/SourceFiles/history/history.cpp | 28 ++++-- Telegram/SourceFiles/history/history.h | 5 ++ .../view/history_view_top_bar_widget.cpp | 1 + .../SourceFiles/info/info_wrap_widget.cpp | 1 + .../SourceFiles/window/window_peer_menu.cpp | 85 ++++++++++++++----- .../SourceFiles/window/window_peer_menu.h | 1 + 30 files changed, 409 insertions(+), 155 deletions(-) diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index ac5ba4ede..2b91cbe5b 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -484,7 +484,9 @@ void ApiWrap::applyUpdates( } void ApiWrap::savePinnedOrder(Data::Folder *folder) { - const auto &order = _session->data().pinnedChatsOrder(folder); + const auto &order = _session->data().pinnedChatsOrder( + folder, + FilterId()); const auto input = [](const Dialogs::Key &key) { if (const auto history = key.history()) { return MTP_inputDialogPeer(history->peer->input); @@ -513,7 +515,7 @@ void ApiWrap::toggleHistoryArchived( if (const auto already = _historyArchivedRequests.take(history)) { request(already->first).cancel(); } - const auto isPinned = history->isPinnedDialog(); + const auto isPinned = history->isPinnedDialog(0); const auto archiveId = Data::Folder::kId; const auto requestId = request(MTPfolders_EditPeerFolders( MTP_vector<MTPInputFolderPeer>( @@ -980,7 +982,7 @@ void ApiWrap::requestPinnedDialogs(Data::Folder *folder) { result.match([&](const MTPDmessages_peerDialogs &data) { _session->data().processUsers(data.vusers()); _session->data().processChats(data.vchats()); - _session->data().clearPinnedChats(folder); + _session->data().clearPinnedChats(folder, FilterId()); _session->data().applyDialogs( folder, data.vmessages().v, diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp index 9360b4732..558c96b82 100644 --- a/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp @@ -106,20 +106,24 @@ not_null<FilterChatsPreview*> SetupChatsPreview( data->title(), (data->flags() & ~flag), data->always(), + data->pinned(), data->never()); }, preview->lifetime()); preview->peerRemoved( ) | rpl::start_with_next([=](not_null<History*> history) { auto always = data->always(); + auto pinned = data->pinned(); auto never = data->never(); always.remove(history); + pinned.erase(ranges::remove(pinned, history), end(pinned)); never.remove(history); *data = Data::ChatFilter( data->id(), data->title(), data->flags(), std::move(always), + std::move(pinned), std::move(never)); }, preview->lifetime()); @@ -272,7 +276,7 @@ void EditExceptions( const auto peers = box->peerListCollectSelectedRows(); auto &&histories = ranges::view::all( peers - ) | ranges::view::transform([=](not_null<PeerData*> peer) { + ) | ranges::view::transform([=](not_null<PeerData*> peer) { return window->session().data().history(peer); }); auto changed = base::flat_set<not_null<History*>>{ @@ -283,11 +287,17 @@ void EditExceptions( for (const auto &history : changed) { removeFrom.remove(history); } + auto pinned = data->pinned(); + pinned.erase(ranges::remove_if(pinned, [&](not_null<History*> history) { + const auto contains = changed.contains(history); + return include ? !contains : contains; + }), end(pinned)); *data = Data::ChatFilter( data->id(), data->title(), (data->flags() & ~options) | rawController->chosenOptions(), include ? std::move(changed) : std::move(removeFrom), + std::move(pinned), include ? std::move(removeFrom) : std::move(changed)); refresh(); box->closeBox(); @@ -407,6 +417,7 @@ void EditFilterBox( title, data->flags(), data->always(), + data->pinned(), data->never()); box->closeBox(); diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp index 5740dc545..7650c00bf 100644 --- a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp @@ -380,7 +380,10 @@ object_ptr<Ui::RpWidget> EditFilterChatsListController::prepareTypesList() { if (_selected & flag) { if (const auto row = delegate->peerListFindRow(TypeId(flag))) { content->changeCheckState(row, true, anim::type::instant); - this->delegate()->peerListSetForeignRowChecked(row, true); + this->delegate()->peerListSetForeignRowChecked( + row, + true, + anim::type::instant); } } } @@ -397,7 +400,8 @@ object_ptr<Ui::RpWidget> EditFilterChatsListController::prepareTypesList() { ) | rpl::start_with_next([=](RowSelectionChange update) { this->delegate()->peerListSetForeignRowChecked( update.row, - update.checked); + update.checked, + anim::type::normal); }, _lifetime); _deselectOption = [=](PeerListRowId itemId) { diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index 3e1042993..2f86bf5e1 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -203,9 +203,10 @@ void PeerListBox::peerListSetRowChecked( void PeerListBox::peerListSetForeignRowChecked( not_null<PeerListRow*> row, - bool checked) { + bool checked, + anim::type animated) { if (checked) { - addSelectItem(row, anim::type::normal); + addSelectItem(row, animated); // This call deletes row from _searchRows. _select->entity()->clearQuery(); diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h index f3ab5df0e..005fa563a 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.h +++ b/Telegram/SourceFiles/boxes/peer_list_box.h @@ -271,7 +271,10 @@ public: virtual void peerListConvertRowToSearchResult(not_null<PeerListRow*> row) = 0; virtual bool peerListIsRowChecked(not_null<PeerListRow*> row) = 0; virtual void peerListSetRowChecked(not_null<PeerListRow*> row, bool checked) = 0; - virtual void peerListSetForeignRowChecked(not_null<PeerListRow*> row, bool checked) = 0; + virtual void peerListSetForeignRowChecked( + not_null<PeerListRow*> row, + bool checked, + anim::type animated) = 0; virtual not_null<PeerListRow*> peerListRowAt(int index) = 0; virtual void peerListRefreshRows() = 0; virtual void peerListScrollToTop() = 0; @@ -724,7 +727,8 @@ public: } void peerListSetForeignRowChecked( not_null<PeerListRow*> row, - bool checked) override { + bool checked, + anim::type animated) override { } int peerListFullRowsCount() override { return _content->fullRowsCount(); @@ -816,7 +820,8 @@ public: bool checked) override; void peerListSetForeignRowChecked( not_null<PeerListRow*> row, - bool checked) override; + bool checked, + anim::type animated) override; bool peerListIsRowChecked(not_null<PeerListRow*> row) override; int peerListSelectedRowsCount() override; std::vector<not_null<PeerData*>> peerListCollectSelectedRows() override; diff --git a/Telegram/SourceFiles/data/data_chat_filters.cpp b/Telegram/SourceFiles/data/data_chat_filters.cpp index 97beb7e81..d5e1c6aad 100644 --- a/Telegram/SourceFiles/data/data_chat_filters.cpp +++ b/Telegram/SourceFiles/data/data_chat_filters.cpp @@ -25,10 +25,12 @@ ChatFilter::ChatFilter( const QString &title, Flags flags, base::flat_set<not_null<History*>> always, + std::vector<not_null<History*>> pinned, base::flat_set<not_null<History*>> never) : _id(id) , _title(title) , _always(std::move(always)) +, _pinned(std::move(pinned)) , _never(std::move(never)) , _flags(flags) { } @@ -66,17 +68,26 @@ ChatFilter ChatFilter::FromTL( }) | ranges::view::transform([](History *history) { return not_null<History*>(history); }); - auto &&always = ranges::view::all( + auto &&always = ranges::view::concat( data.vinclude_peers().v ) | to_histories; + auto pinned = ranges::view::all( + data.vpinned_peers().v + ) | to_histories | ranges::to_vector; auto &&never = ranges::view::all( data.vexclude_peers().v ) | to_histories; + auto &&all = ranges::view::concat(always, pinned); + auto list = base::flat_set<not_null<History*>>{ + all.begin(), + all.end() + }; return ChatFilter( data.vid().v, qs(data.vtitle()), flags, - { always.begin(), always.end() }, + std::move(list), + std::move(pinned), { never.begin(), never.end() }); }); } @@ -94,10 +105,17 @@ MTPDialogFilter ChatFilter::tl() const { | ((_flags & Flag::NoArchived) ? TLFlag::f_exclude_archived : TLFlag(0)); - auto always = QVector<MTPInputPeer>(); - always.reserve(_always.size()); - for (const auto history : _always) { - always.push_back(history->peer->input); + auto always = _always; + auto pinned = QVector<MTPInputPeer>(); + pinned.reserve(_pinned.size()); + for (const auto history : _pinned) { + pinned.push_back(history->peer->input); + always.remove(history); + } + auto include = QVector<MTPInputPeer>(); + include.reserve(always.size()); + for (const auto history : always) { + include.push_back(history->peer->input); } auto never = QVector<MTPInputPeer>(); never.reserve(_never.size()); @@ -109,8 +127,8 @@ MTPDialogFilter ChatFilter::tl() const { MTP_int(_id), MTP_string(_title), MTPstring(), // emoticon - MTP_vector<MTPInputPeer>(), - MTP_vector<MTPInputPeer>(always), + MTP_vector<MTPInputPeer>(pinned), + MTP_vector<MTPInputPeer>(include), MTP_vector<MTPInputPeer>(never)); } @@ -130,6 +148,10 @@ const base::flat_set<not_null<History*>> &ChatFilter::always() const { return _always; } +const std::vector<not_null<History*>> &ChatFilter::pinned() const { + return _pinned; +} + const base::flat_set<not_null<History*>> &ChatFilter::never() const { return _never; } @@ -196,7 +218,7 @@ not_null<Dialogs::MainList*> ChatFilters::chatsList(FilterId filterId) { if (!pointer) { pointer = std::make_unique<Dialogs::MainList>( filterId, - rpl::single(1)); + rpl::single(ChatFilter::kPinnedLimit)); } return pointer.get(); } @@ -284,7 +306,7 @@ void ChatFilters::applyInsert(ChatFilter filter, int position) { _list.insert( begin(_list) + position, - ChatFilter(filter.id(), {}, {}, {}, {})); + ChatFilter(filter.id(), {}, {}, {}, {}, {})); applyChange(*(begin(_list) + position), std::move(filter)); } @@ -301,7 +323,7 @@ void ChatFilters::applyRemove(int position) { Expects(position >= 0 && position < _list.size()); const auto i = begin(_list) + position; - applyChange(*i, ChatFilter(i->id(), {}, {}, {}, {})); + applyChange(*i, ChatFilter(i->id(), {}, {}, {}, {}, {})); _list.erase(i); } @@ -309,12 +331,21 @@ bool ChatFilters::applyChange(ChatFilter &filter, ChatFilter &&updated) { const auto rulesChanged = (filter.flags() != updated.flags()) || (filter.always() != updated.always()) || (filter.never() != updated.never()); + const auto pinnedChanged = (filter.pinned() != updated.pinned()); + if (!rulesChanged + && !pinnedChanged + && filter.title() == updated.title()) { + return false; + } if (rulesChanged) { const auto id = filter.id(); const auto filterList = _owner->chatsFilters().chatsList(id); const auto feedHistory = [&](not_null<History*> history) { const auto now = updated.contains(history); const auto was = filter.contains(history); + if (now) { + history->applyFilterPinnedIndex(id, updated); + } if (now != was) { if (now) { history->addToChatList(id, filterList); @@ -334,8 +365,10 @@ bool ChatFilters::applyChange(ChatFilter &filter, ChatFilter &&updated) { if (const auto folder = _owner->folderLoaded(Data::Folder::kId)) { feedList(folder->chatsList()); } - } else if (filter.title() == updated.title()) { - return false; + } else if (pinnedChanged) { + const auto id = filter.id(); + const auto filterList = _owner->chatsFilters().chatsList(id); + filterList->pinned()->applyList(updated.pinned()); } filter = std::move(updated); return true; diff --git a/Telegram/SourceFiles/data/data_chat_filters.h b/Telegram/SourceFiles/data/data_chat_filters.h index 7e6b2775d..b5a6efbc2 100644 --- a/Telegram/SourceFiles/data/data_chat_filters.h +++ b/Telegram/SourceFiles/data/data_chat_filters.h @@ -34,12 +34,15 @@ public: friend constexpr inline bool is_flag_type(Flag) { return true; }; using Flags = base::flags<Flag>; + static constexpr int kPinnedLimit = 100; + ChatFilter() = default; ChatFilter( FilterId id, const QString &title, Flags flags, base::flat_set<not_null<History*>> always, + std::vector<not_null<History*>> pinned, base::flat_set<not_null<History*>> never); [[nodiscard]] static ChatFilter FromTL( @@ -51,6 +54,7 @@ public: [[nodiscard]] QString title() const; [[nodiscard]] Flags flags() const; [[nodiscard]] const base::flat_set<not_null<History*>> &always() const; + [[nodiscard]] const std::vector<not_null<History*>> &pinned() const; [[nodiscard]] const base::flat_set<not_null<History*>> &never() const; [[nodiscard]] bool contains(not_null<History*> history) const; @@ -59,6 +63,7 @@ private: FilterId _id = 0; QString _title; base::flat_set<not_null<History*>> _always; + std::vector<not_null<History*>> _pinned; base::flat_set<not_null<History*>> _never; Flags _flags; diff --git a/Telegram/SourceFiles/data/data_folder.cpp b/Telegram/SourceFiles/data/data_folder.cpp index 4cbcbb214..0d07bd833 100644 --- a/Telegram/SourceFiles/data/data_folder.cpp +++ b/Telegram/SourceFiles/data/data_folder.cpp @@ -347,7 +347,7 @@ void Folder::applyPinnedUpdate(const MTPDupdateDialogPinned &data) { if (folderId != 0) { LOG(("API Error: Nested folders detected.")); } - owner().setChatPinned(this, data.is_pinned()); + owner().setChatPinned(this, FilterId(), data.is_pinned()); } // #feed diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index b36cb50cc..20e9df135 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -780,7 +780,7 @@ void Session::deleteConversationLocally(not_null<PeerData*> peer) { const auto history = historyLoaded(peer); if (history) { if (history->folderKnown()) { - setChatPinned(history, false); + setChatPinned(history, FilterId(), false); } App::main()->removeDialog(history); history->clear(peer->isChannel() @@ -1459,11 +1459,16 @@ MessageIdsList Session::itemOrItsGroup(not_null<HistoryItem*> item) const { return { 1, item->fullId() }; } -void Session::setChatPinned(const Dialogs::Key &key, bool pinned) { +void Session::setChatPinned( + const Dialogs::Key &key, + FilterId filterId, + bool pinned) { Expects(key.entry()->folderKnown()); - const auto list = chatsList(key.entry()->folder())->pinned(); - list->setPinned(key, pinned); + const auto list = filterId + ? chatsFilters().chatsList(filterId) + : chatsList(key.entry()->folder()); + list->pinned()->setPinned(key, pinned); notifyPinnedDialogsOrderUpdated(); } @@ -1549,32 +1554,53 @@ void Session::applyDialog( setPinnedFromDialog(folder, data.is_pinned()); } -int Session::pinnedChatsCount(Data::Folder *folder) const { - return pinnedChatsOrder(folder).size(); +int Session::pinnedChatsCount( + Data::Folder *folder, + FilterId filterId) const { + Expects(!folder || !filterId); + + if (!filterId) { + return pinnedChatsOrder(folder, filterId).size(); + } + const auto &list = chatsFilters().list(); + const auto i = ranges::find(list, filterId, &Data::ChatFilter::id); + return (i != end(list)) ? i->pinned().size() : 0; } -int Session::pinnedChatsLimit(Data::Folder *folder) const { - return folder +int Session::pinnedChatsLimit( + Data::Folder *folder, + FilterId filterId) const { + return filterId + ? Data::ChatFilter::kPinnedLimit + : folder ? Global::PinnedDialogsInFolderMax() : Global::PinnedDialogsCountMax(); } const std::vector<Dialogs::Key> &Session::pinnedChatsOrder( - Data::Folder *folder) const { - return chatsList(folder)->pinned()->order(); + Data::Folder *folder, + FilterId filterId) const { + const auto list = filterId + ? chatsFilters().chatsList(filterId) + : chatsList(folder); + return list->pinned()->order(); } -void Session::clearPinnedChats(Data::Folder *folder) { +void Session::clearPinnedChats(Data::Folder *folder, FilterId filterId) { chatsList(folder)->pinned()->clear(); } void Session::reorderTwoPinnedChats( + FilterId filterId, const Dialogs::Key &key1, const Dialogs::Key &key2) { Expects(key1.entry()->folderKnown() && key2.entry()->folderKnown()); - Expects(key1.entry()->folder() == key2.entry()->folder()); + Expects(filterId || (key1.entry()->folder() == key2.entry()->folder())); - chatsList(key1.entry()->folder())->pinned()->reorder(key1, key2); + const auto list = filterId + ? chatsFilters().chatsList(filterId) + : chatsList(key1.entry()->folder()); + list->pinned()->reorder(key1, key2); notifyPinnedDialogsOrderUpdated(); } @@ -3349,6 +3375,7 @@ auto Session::refreshChatListEntry( const auto filterList = chatsFilters().chatsList(id); auto filterResult = RefreshChatListEntryResult(); if (filter.contains(history)) { + history->applyFilterPinnedIndex(id, filter); filterResult.changed = !entry->inChatList(id); if (filterResult.changed) { entry->addToChatList(id, filterList); diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 8ff99c45e..fecc1fc87 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -362,16 +362,21 @@ public: const QVector<MTPDialog> &dialogs, std::optional<int> count = std::nullopt); - int pinnedChatsCount(Data::Folder *folder) const; - int pinnedChatsLimit(Data::Folder *folder) const; + int pinnedChatsCount(Data::Folder *folder, FilterId filterId) const; + int pinnedChatsLimit(Data::Folder *folder, FilterId filterId) const; const std::vector<Dialogs::Key> &pinnedChatsOrder( - Data::Folder *folder) const; - void setChatPinned(const Dialogs::Key &key, bool pinned); - void clearPinnedChats(Data::Folder *folder); + Data::Folder *folder, + FilterId filterId) const; + void setChatPinned( + const Dialogs::Key &key, + FilterId filterId, + bool pinned); + void clearPinnedChats(Data::Folder *folder, FilterId filterId); void applyPinnedChats( Data::Folder *folder, const QVector<MTPDialogPeer> &list); void reorderTwoPinnedChats( + FilterId filterId, const Dialogs::Key &key1, const Dialogs::Key &key2); diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp index c94c07814..ecf99794b 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp @@ -55,19 +55,34 @@ Main::Session &Entry::session() const { return _owner->session(); } -void Entry::cachePinnedIndex(int index) { - if (_pinnedIndex != index) { - const auto wasPinned = isPinnedDialog(); - _pinnedIndex = index; - if (session().supportMode()) { - // Force reorder in support mode. - _sortKeyInChatList = 0; - } - updateChatListSortPosition(); - updateChatListEntry(); - if (wasPinned != isPinnedDialog()) { - changedChatListPinHook(); +void Entry::pinnedIndexChanged(int was, int now) { + if (session().supportMode()) { + // Force reorder in support mode. + _sortKeyInChatList = 0; + } + updateChatListSortPosition(); + updateChatListEntry(); + if ((was != 0) != (now != 0)) { + changedChatListPinHook(); + } +} + +void Entry::cachePinnedIndex(FilterId filterId, int index) { + const auto i = _pinnedIndex.find(filterId); + const auto was = (i != end(_pinnedIndex)) ? i->second : 0; + if (index == was) { + return; + } + if (!index) { + _pinnedIndex.erase(i); + pinnedIndexChanged(was, index); + } else { + if (!was) { + _pinnedIndex.emplace(filterId, index); + } else { + i->second = index; } + pinnedIndexChanged(was, index); } } @@ -97,9 +112,7 @@ void Entry::updateChatListSortPosition() { const auto fixedIndex = fixedOnTopIndex(); _sortKeyInChatList = fixedIndex ? FixedOnTopDialogPos(fixedIndex) - : isPinnedDialog() - ? PinnedDialogPos(_pinnedIndex) - : _sortKeyByDate; + : computeSortPosition(0); if (needUpdateInChatList()) { setChatListExistence(true); } else { @@ -107,6 +120,23 @@ void Entry::updateChatListSortPosition() { } } +int Entry::lookupPinnedIndex(FilterId filterId) const { + if (filterId) { + const auto i = _pinnedIndex.find(filterId); + return (i != end(_pinnedIndex)) ? i->second : 0; + } else if (!_pinnedIndex.empty()) { + return _pinnedIndex.front().first + ? 0 + : _pinnedIndex.front().second; + } + return 0; +} + +uint64 Entry::computeSortPosition(FilterId filterId) const { + const auto index = lookupPinnedIndex(filterId); + return index ? PinnedDialogPos(index) : _sortKeyByDate; +} + void Entry::updateChatListExistence() { setChatListExistence(shouldBeInChatList()); } @@ -205,6 +235,9 @@ void Entry::removeFromChatList( return; } _chatListLinks.erase(i); + if (isPinnedDialog(filterId)) { + owner().setChatPinned(_key, filterId, false); + } list->removeEntry(_key); } diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.h b/Telegram/SourceFiles/dialogs/dialogs_entry.h index eb0bfe194..b9e21a369 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.h +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.h @@ -32,10 +32,9 @@ struct RowsByLetter { }; enum class SortMode { - Complex = 0x00, - Date = 0x01, - Name = 0x02, - Add = 0x04, + Date = 0x00, + Name = 0x01, + Add = 0x02, }; struct PositionChange { @@ -117,19 +116,18 @@ public: QChar letter, not_null<Row*> row); void updateChatListEntry() const; - bool isPinnedDialog() const { - return _pinnedIndex > 0; + [[nodiscard]] bool isPinnedDialog(FilterId filterId) const { + return lookupPinnedIndex(filterId) != 0; } - void cachePinnedIndex(int index); + void cachePinnedIndex(FilterId filterId, int index); bool isProxyPromoted() const { return _isProxyPromoted; } void cacheProxyPromoted(bool promoted); - uint64 sortKeyInChatList() const { - return _sortKeyInChatList; - } - uint64 sortKeyByDate() const { - return _sortKeyByDate; + [[nodiscard]] uint64 sortKeyInChatList(FilterId filterId) const { + return filterId + ? computeSortPosition(filterId) + : _sortKeyInChatList; } void updateChatListSortPosition(); void setChatListTimeId(TimeId date); @@ -194,8 +192,12 @@ protected: }); } + [[nodiscard]] int lookupPinnedIndex(FilterId filterId) const; + private: virtual void changedChatListPinHook(); + void pinnedIndexChanged(int was, int now); + [[nodiscard]] uint64 computeSortPosition(FilterId filterId) const; void setChatListExistence(bool exists); RowsByLetter *chatListLinks(FilterId filterId); @@ -208,7 +210,7 @@ private: base::flat_map<FilterId, RowsByLetter> _chatListLinks; uint64 _sortKeyInChatList = 0; uint64 _sortKeyByDate = 0; - int _pinnedIndex = 0; + base::flat_map<FilterId, int> _pinnedIndex; bool _isProxyPromoted = false; TimeId _timeId = 0; diff --git a/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp index 5af4ca39c..19a4957d1 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp @@ -13,10 +13,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Dialogs { -IndexedList::IndexedList(SortMode sortMode) +IndexedList::IndexedList(SortMode sortMode, FilterId filterId) : _sortMode(sortMode) -, _list(sortMode) -, _empty(sortMode) { +, _filterId(filterId) +, _list(sortMode, filterId) +, _empty(sortMode, filterId) { } RowsByLetter IndexedList::addToEnd(Key key) { @@ -28,7 +29,7 @@ RowsByLetter IndexedList::addToEnd(Key key) { for (const auto ch : key.entry()->chatListFirstLetters()) { auto j = _index.find(ch); if (j == _index.cend()) { - j = _index.emplace(ch, _sortMode).first; + j = _index.emplace(ch, _sortMode, _filterId).first; } result.letters.emplace(ch, j->second.addToEnd(key)); } @@ -44,7 +45,7 @@ Row *IndexedList::addByName(Key key) { for (const auto ch : key.entry()->chatListFirstLetters()) { auto j = _index.find(ch); if (j == _index.cend()) { - j = _index.emplace(ch, _sortMode).first; + j = _index.emplace(ch, _sortMode, _filterId).first; } j->second.addByName(key); } @@ -79,7 +80,8 @@ void IndexedList::movePinned(Row *row, int deltaSign) { Assert(swapPinnedIndexWith != cbegin()); --swapPinnedIndexWith; } - Auth().data().reorderTwoPinnedChats( + row->key().entry()->owner().reorderTwoPinnedChats( + _filterId, row->key(), (*swapPinnedIndexWith)->key()); } @@ -87,7 +89,7 @@ void IndexedList::movePinned(Row *row, int deltaSign) { void IndexedList::peerNameChanged( not_null<PeerData*> peer, const base::flat_set<QChar> &oldLetters) { - Expects(_sortMode != SortMode::Date && _sortMode != SortMode::Complex); + Expects(_sortMode != SortMode::Date); if (const auto history = peer->owner().historyLoaded(peer)) { if (_sortMode == SortMode::Name) { @@ -102,7 +104,7 @@ void IndexedList::peerNameChanged( FilterId filterId, not_null<PeerData*> peer, const base::flat_set<QChar> &oldLetters) { - Expects(_sortMode == SortMode::Date || _sortMode == SortMode::Complex); + Expects(_sortMode == SortMode::Date); if (const auto history = peer->owner().historyLoaded(peer)) { adjustNames(filterId, history, oldLetters); @@ -139,7 +141,7 @@ void IndexedList::adjustByName( for (auto ch : toAdd) { auto j = _index.find(ch); if (j == _index.cend()) { - j = _index.emplace(ch, _sortMode).first; + j = _index.emplace(ch, _sortMode, _filterId).first; } j->second.addByName(key); } @@ -165,7 +167,7 @@ void IndexedList::adjustNames( } } for (auto ch : toRemove) { - if (_sortMode == SortMode::Date || _sortMode == SortMode::Complex) { + if (_sortMode == SortMode::Date) { history->removeChatListEntryByLetter(filterId, ch); } if (auto it = _index.find(ch); it != _index.cend()) { @@ -175,10 +177,10 @@ void IndexedList::adjustNames( for (auto ch : toAdd) { auto j = _index.find(ch); if (j == _index.cend()) { - j = _index.emplace(ch, _sortMode).first; + j = _index.emplace(ch, _sortMode, _filterId).first; } auto row = j->second.addToEnd(key); - if (_sortMode == SortMode::Date || _sortMode == SortMode::Complex) { + if (_sortMode == SortMode::Date) { history->addChatListEntryByLetter(filterId, ch, row); } } diff --git a/Telegram/SourceFiles/dialogs/dialogs_indexed_list.h b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.h index e2ae1346f..99c362571 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_indexed_list.h +++ b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.h @@ -16,7 +16,7 @@ namespace Dialogs { class IndexedList { public: - IndexedList(SortMode sortMode); + IndexedList(SortMode sortMode, FilterId filterId = 0); RowsByLetter addToEnd(Key key); Row *addByName(Key key); @@ -81,6 +81,7 @@ private: const base::flat_set<QChar> &oldChars); SortMode _sortMode = SortMode(); + FilterId _filterId = 0; List _list, _empty; base::flat_map<QChar, List> _index; diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 5fe6aa593..f3bea9083 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -66,12 +66,14 @@ int FixedOnTopDialogsCount(not_null<Dialogs::IndexedList*> list) { return result; } -int PinnedDialogsCount(not_null<Dialogs::IndexedList*> list) { +int PinnedDialogsCount( + FilterId filterId, + not_null<Dialogs::IndexedList*> list) { auto result = 0; for (const auto row : *list) { if (row->entry()->fixedOnTopIndex()) { continue; - } else if (!row->entry()->isPinnedDialog()) { + } else if (!row->entry()->isPinnedDialog(filterId)) { break; } ++result; @@ -1075,7 +1077,7 @@ void InnerWidget::mousePressEvent(QMouseEvent *e) { }); } if (anim::Disabled() - && (!_pressed || !_pressed->entry()->isPinnedDialog())) { + && (!_pressed || !_pressed->entry()->isPinnedDialog(_filterId))) { mousePressReleased(e->globalPos(), e->button()); } } @@ -1091,7 +1093,9 @@ void InnerWidget::checkReorderPinnedStart(QPoint localPosition) { if (updateReorderIndexGetCount() < 2) { _dragging = nullptr; } else { - const auto &order = session().data().pinnedChatsOrder(_openedFolder); + const auto &order = session().data().pinnedChatsOrder( + _openedFolder, + _filterId); _pinnedOnDragStart = base::flat_set<Key>{ order.begin(), order.end() @@ -1103,14 +1107,14 @@ void InnerWidget::checkReorderPinnedStart(QPoint localPosition) { } int InnerWidget::countPinnedIndex(Row *ofRow) { - if (!ofRow || !ofRow->entry()->isPinnedDialog()) { + if (!ofRow || !ofRow->entry()->isPinnedDialog(_filterId)) { return -1; } auto result = 0; for (const auto row : *shownDialogs()) { if (row->entry()->fixedOnTopIndex()) { continue; - } else if (!row->entry()->isPinnedDialog()) { + } else if (!row->entry()->isPinnedDialog(_filterId)) { break; } else if (row == ofRow) { return result; @@ -1121,7 +1125,9 @@ int InnerWidget::countPinnedIndex(Row *ofRow) { } void InnerWidget::savePinnedOrder() { - const auto &newOrder = session().data().pinnedChatsOrder(_openedFolder); + const auto &newOrder = session().data().pinnedChatsOrder( + _openedFolder, + _filterId); if (newOrder.size() != _pinnedOnDragStart.size()) { return; // Something has changed in the set of pinned chats. } @@ -1130,7 +1136,11 @@ void InnerWidget::savePinnedOrder() { return; // Something has changed in the set of pinned chats. } } - session().api().savePinnedOrder(_openedFolder); + if (_filterId) { + // #TODO pinned reorder data and to server + } else { + session().api().savePinnedOrder(_openedFolder); + } } void InnerWidget::finishReorderPinned() { @@ -1162,7 +1172,7 @@ int InnerWidget::updateReorderIndexGetCount() { return 0; } - const auto count = Dialogs::PinnedDialogsCount(shownDialogs()); + const auto count = Dialogs::PinnedDialogsCount(_filterId, shownDialogs()); Assert(index < count); if (count < 2) { stopReorderPinned(); @@ -1789,6 +1799,7 @@ void InnerWidget::contextMenuEvent(QContextMenuEvent *e) { Window::FillPeerMenu( _controller, history->peer, + _filterId, [&](const QString &text, Fn<void()> callback) { return _menu->addAction(text, std::move(callback)); }, @@ -2541,6 +2552,7 @@ void InnerWidget::switchToFilter(FilterId filterId) { &Data::ChatFilter::id)) { filterId = 0; } + stopReorderPinned(); _filterId = filterId; refreshWithCollapsedRows(true); _collapsedSelected = 0; @@ -2983,7 +2995,7 @@ void InnerWidget::setupShortcuts() { for (const auto [command, index] : pinned) { request->check(command) && request->handle([=, index = index] { const auto list = session().data().chatsList()->indexed(); - const auto count = Dialogs::PinnedDialogsCount(list); + const auto count = Dialogs::PinnedDialogsCount(_filterId, list); if (index >= count) { return false; } diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index 879cf8e3c..f5e681e25 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -218,6 +218,7 @@ void paintRow( not_null<const BasicRow*> row, not_null<Entry*> entry, Dialogs::Key chat, + FilterId filterId, PeerData *from, const HiddenSenderInfo *hiddenSenderInfo, HistoryItem *item, @@ -322,7 +323,7 @@ void paintRow( } auto availableWidth = namewidth; - if (entry->isPinnedDialog() && !entry->fixedOnTopIndex()) { + if (entry->isPinnedDialog(filterId) && (filterId || !entry->fixedOnTopIndex())) { auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon)); icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth); availableWidth -= icon.width() + st::dialogsUnreadPadding; @@ -349,7 +350,7 @@ void paintRow( } } else if (!item) { auto availableWidth = namewidth; - if (entry->isPinnedDialog() && !entry->fixedOnTopIndex()) { + if (entry->isPinnedDialog(filterId) && (filterId || !entry->fixedOnTopIndex())) { auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon)); icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth); availableWidth -= icon.width() + st::dialogsUnreadPadding; @@ -366,7 +367,7 @@ void paintRow( } paintItemCallback(nameleft, namewidth); - } else if (entry->isPinnedDialog() && !entry->fixedOnTopIndex()) { + } else if (entry->isPinnedDialog(filterId) && (filterId || !entry->fixedOnTopIndex())) { auto availableWidth = namewidth; auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon)); icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth); @@ -613,7 +614,7 @@ void paintUnreadCount( void RowPainter::paint( Painter &p, not_null<const Row*> row, - int filterId, + FilterId filterId, int fullWidth, bool active, bool selected, @@ -669,9 +670,8 @@ void RowPainter::paint( const auto displayPinnedIcon = !displayUnreadCounter && !displayMentionBadge && !displayUnreadMark - && !filterId - && entry->isPinnedDialog() - && !entry->fixedOnTopIndex(); + && entry->isPinnedDialog(filterId) + && (filterId || !entry->fixedOnTopIndex()); const auto from = history ? (history->peer->migrateTo() @@ -749,6 +749,7 @@ void RowPainter::paint( row, entry, row->key(), + filterId, from, nullptr, item, @@ -872,6 +873,7 @@ void RowPainter::paint( row, history, history, + FilterId(), from, hiddenSenderInfo, item, diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.h b/Telegram/SourceFiles/dialogs/dialogs_layout.h index 6745569de..2ef8f29e9 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.h +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.h @@ -29,7 +29,7 @@ public: static void paint( Painter &p, not_null<const Row*> row, - int filterId, + FilterId filterId, int fullWidth, bool active, bool selected, diff --git a/Telegram/SourceFiles/dialogs/dialogs_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_list.cpp index 7d3c196b4..51d8f6734 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_list.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_list.cpp @@ -14,7 +14,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Dialogs { -List::List(SortMode sortMode) : _sortMode(sortMode) { +List::List(SortMode sortMode, FilterId filterId) +: _sortMode(sortMode) +, _filterId(filterId) { } List::const_iterator List::cfind(Row *value) const { @@ -32,7 +34,7 @@ not_null<Row*> List::addToEnd(Key key) { std::make_unique<Row>(key, _rows.size()) ).first->second.get(); _rows.emplace_back(result); - if (_sortMode == SortMode::Date || _sortMode == SortMode::Complex) { + if (_sortMode == SortMode::Date) { adjustByDate(result); } return result; @@ -82,20 +84,20 @@ void List::adjustByName(not_null<Row*> row) { } void List::adjustByDate(not_null<Row*> row) { - Expects(_sortMode == SortMode::Date || _sortMode == SortMode::Complex); + Expects(_sortMode == SortMode::Date); - const auto key = row->sortKey(_sortMode); + const auto key = row->sortKey(_filterId); const auto index = row->pos(); const auto i = _rows.begin() + index; const auto before = std::find_if(i + 1, _rows.end(), [&](Row *row) { - return (row->sortKey(_sortMode) <= key); + return (row->sortKey(_filterId) <= key); }); if (before != i + 1) { rotate(i, i + 1, before); } else { const auto from = std::make_reverse_iterator(i); const auto after = std::find_if(from, _rows.rend(), [&](Row *row) { - return (row->sortKey(_sortMode) >= key); + return (row->sortKey(_filterId) >= key); }).base(); if (after != i) { rotate(after, i, i + 1); diff --git a/Telegram/SourceFiles/dialogs/dialogs_list.h b/Telegram/SourceFiles/dialogs/dialogs_list.h index 34fb53d14..00e4985e6 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_list.h +++ b/Telegram/SourceFiles/dialogs/dialogs_list.h @@ -16,7 +16,7 @@ enum class SortMode; class List final { public: - List(SortMode sortMode); + List(SortMode sortMode, FilterId filterId = 0); List(const List &other) = delete; List &operator=(const List &other) = delete; List(List &&other) = default; @@ -78,6 +78,7 @@ private: std::vector<not_null<Row*>>::iterator last); SortMode _sortMode = SortMode(); + FilterId _filterId = 0; std::vector<not_null<Row*>> _rows; std::map<Key, std::unique_ptr<Row>> _rowByKey; diff --git a/Telegram/SourceFiles/dialogs/dialogs_main_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_main_list.cpp index d24dcf722..ee552a265 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_main_list.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_main_list.cpp @@ -14,8 +14,8 @@ namespace Dialogs { MainList::MainList(FilterId filterId, rpl::producer<int> pinnedLimit) : _filterId(filterId) -, _all(filterId ? SortMode::Date : SortMode::Complex) -, _pinned(1) { +, _all(SortMode::Date, filterId) +, _pinned(filterId, 1) { _unreadState.known = true; std::move( diff --git a/Telegram/SourceFiles/dialogs/dialogs_pinned_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_pinned_list.cpp index 6b6e1bdd7..5b79fa9b8 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_pinned_list.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_pinned_list.cpp @@ -9,11 +9,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "dialogs/dialogs_key.h" #include "dialogs/dialogs_entry.h" +#include "history/history.h" #include "data/data_session.h" namespace Dialogs { -PinnedList::PinnedList(int limit) : _limit(limit) { +PinnedList::PinnedList(FilterId filterId, int limit) +: _filterId(filterId) +, _limit(limit) { Expects(limit > 0); } @@ -41,7 +44,7 @@ int PinnedList::addPinnedGetPosition(const Key &key) { applyLimit(_limit - 1); const auto position = int(_data.size()); _data.push_back(key); - key.entry()->cachePinnedIndex(position + 1); + key.entry()->cachePinnedIndex(_filterId, position + 1); return position; } @@ -54,19 +57,34 @@ void PinnedList::setPinned(const Key &key, bool pinned) { const auto begin = _data.begin(); std::rotate(begin, begin + position, begin + position + 1); for (auto i = 0; i != position + 1; ++i) { - _data[i].entry()->cachePinnedIndex(i + 1); + _data[i].entry()->cachePinnedIndex(_filterId, i + 1); } } } else if (const auto it = ranges::find(_data, key); it != end(_data)) { const auto index = int(it - begin(_data)); _data.erase(it); - key.entry()->cachePinnedIndex(0); + key.entry()->cachePinnedIndex(_filterId, 0); for (auto i = index, count = int(size(_data)); i != count; ++i) { - _data[i].entry()->cachePinnedIndex(i + 1); + _data[i].entry()->cachePinnedIndex(_filterId, i + 1); } } } +void PinnedList::applyFilterPinned( + FilterId filterId, + not_null<History*> history, + int index) { + Expects(index > 0); + + history->cachePinnedIndex(filterId, index); + + const auto key = Key{ history }; + if (ranges::find(_data, key) == end(_data)) { + _data.push_back(key); + // #TODO pinned + } +} + void PinnedList::applyLimit(int limit) { Expects(limit >= 0); @@ -83,18 +101,32 @@ void PinnedList::applyList( not_null<Data::Session*> owner, const QVector<MTPDialogPeer> &list) { clear(); - for (const auto &peer : ranges::view::reverse(list)) { + for (const auto &peer : list) { peer.match([&](const MTPDdialogPeer &data) { if (const auto peerId = peerFromMTP(data.vpeer())) { - setPinned(owner->history(peerId), true); + addPinned(owner->history(peerId)); } }, [&](const MTPDdialogPeerFolder &data) { - const auto folderId = data.vfolder_id().v; - setPinned(owner->folder(folderId), true); + addPinned(owner->folder(data.vfolder_id().v)); }); } } +void PinnedList::applyList(const std::vector<not_null<History*>> &list) { + Expects(_filterId != 0); + + clear(); + const auto count = int(list.size()); + _data.reserve(count); + for (auto i = 0; i != count; ++i) { + const auto history = list[i]; + if (history->inChatList()) { + _data.emplace_back(history); + history->cachePinnedIndex(_filterId, i + 1); + } + } +} + void PinnedList::reorder(const Key &key1, const Key &key2) { const auto index1 = ranges::find(_data, key1) - begin(_data); const auto index2 = ranges::find(_data, key2) - begin(_data); @@ -102,8 +134,8 @@ void PinnedList::reorder(const Key &key1, const Key &key2) { Assert(index2 >= 0 && index2 < _data.size()); Assert(index1 != index2); std::swap(_data[index1], _data[index2]); - key1.entry()->cachePinnedIndex(index2 + 1); - key2.entry()->cachePinnedIndex(index1 + 1); + key1.entry()->cachePinnedIndex(_filterId, index2 + 1); + key2.entry()->cachePinnedIndex(_filterId, index1 + 1); } } // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/dialogs_pinned_list.h b/Telegram/SourceFiles/dialogs/dialogs_pinned_list.h index c7910fc2d..9e064da1a 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_pinned_list.h +++ b/Telegram/SourceFiles/dialogs/dialogs_pinned_list.h @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +class History; + namespace Data { class Session; } // namespace Data @@ -17,7 +19,7 @@ class Key; class PinnedList final { public: - explicit PinnedList(int limit); + PinnedList(FilterId filterId, int limit); void setLimit(int limit); @@ -28,11 +30,17 @@ public: // if (pinned) places on the first place in the list. void setPinned(const Key &key, bool pinned); + void applyFilterPinned( + FilterId filterId, + not_null<History*> history, + int index); + void clear(); void applyList( not_null<Data::Session*> owner, const QVector<MTPDialogPeer> &list); + void applyList(const std::vector<not_null<History*>> &list); void reorder(const Key &key1, const Key &key2); const std::vector<Key> &order() const { @@ -43,6 +51,7 @@ private: int addPinnedGetPosition(const Key &key); void applyLimit(int limit); + FilterId _filterId = 0; int _limit = 0; std::vector<Key> _data; diff --git a/Telegram/SourceFiles/dialogs/dialogs_row.cpp b/Telegram/SourceFiles/dialogs/dialogs_row.cpp index 00b31be44..f08568d9c 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_row.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_row.cpp @@ -220,10 +220,8 @@ Row::Row(Key key, int pos) : _id(key), _pos(pos) { } } -uint64 Row::sortKey(SortMode mode) const { - return (mode == SortMode::Complex) - ? _id.entry()->sortKeyInChatList() - : _id.entry()->sortKeyByDate(); +uint64 Row::sortKey(FilterId filterId) const { + return _id.entry()->sortKeyInChatList(filterId); } void Row::validateListEntryCache() const { diff --git a/Telegram/SourceFiles/dialogs/dialogs_row.h b/Telegram/SourceFiles/dialogs/dialogs_row.h index 8fc58f74a..b43b07af2 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_row.h +++ b/Telegram/SourceFiles/dialogs/dialogs_row.h @@ -90,7 +90,7 @@ public: int pos() const { return _pos; } - uint64 sortKey(SortMode mode) const; + uint64 sortKey(FilterId filterId) const; void validateListEntryCache() const; const Ui::Text::String &listEntryCache() const { diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 0d9680c30..b6cf3ed74 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -1921,9 +1921,6 @@ void History::setFolderPointer(Data::Folder *folder) { if (_folder == folder) { return; } - if (isPinnedDialog()) { - owner().setChatPinned(this, false); - } auto &filters = owner().chatsFilters(); const auto wasKnown = folderKnown(); const auto wasInList = inChatList(); @@ -1946,6 +1943,7 @@ void History::setFolderPointer(Data::Folder *folder) { for (const auto &filter : filters.list()) { if (filter.contains(this)) { const auto id = filter.id(); + applyFilterPinnedIndex(id, filter); addToChatList(id, filters.chatsList(id)); } } @@ -1959,6 +1957,24 @@ void History::setFolderPointer(Data::Folder *folder) { } } +void History::applyFilterPinnedIndex( + FilterId filterId, + const Data::ChatFilter &filter) { + const auto &pinned = filter.pinned(); + const auto i = ranges::find(pinned, this); + if (i == end(pinned)) { + return; + } + const auto index = (i - begin(pinned)) + 1; + if (index == lookupPinnedIndex(filterId)) { + return; + } + owner().chatsFilters().chatsList(filterId)->pinned()->applyFilterPinned( + filterId, + this, + index); +} + void History::applyPinnedUpdate(const MTPDupdateDialogPinned &data) { const auto folderId = data.vfolder_id().value_or_empty(); if (!folderKnown()) { @@ -1968,7 +1984,7 @@ void History::applyPinnedUpdate(const MTPDupdateDialogPinned &data) { clearFolder(); } } - owner().setChatPinned(this, data.is_pinned()); + owner().setChatPinned(this, FilterId(), data.is_pinned()); } TimeId History::adjustedChatListTimeId() const { @@ -2576,7 +2592,7 @@ bool History::useProxyPromotion() const { if (!isProxyPromoted()) { return false; } else if (const auto channel = peer->asChannel()) { - return !isPinnedDialog() && !channel->amIn(); + return !isPinnedDialog(FilterId()) && !channel->amIn(); } return false; } @@ -2595,7 +2611,7 @@ bool History::trackUnreadMessages() const { bool History::shouldBeInChatList() const { if (peer->migrateTo() || !folderKnown()) { return false; - } else if (isPinnedDialog()) { + } else if (isPinnedDialog(FilterId())) { return true; } else if (const auto channel = peer->asChannel()) { if (!channel->amIn()) { diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 9c321efaf..2e6a06e40 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -31,6 +31,7 @@ namespace Data { struct Draft; class Session; class Folder; +class ChatFilter; } // namespace Data namespace Dialogs { @@ -368,6 +369,10 @@ public: HistoryItem *folderDialogItem = nullptr); void clearFolder(); + void applyFilterPinnedIndex( + FilterId filterId, + const Data::ChatFilter &filter); + // Interface for Data::Histories. void setInboxReadTill(MsgId upTo); std::optional<int> countStillUnreadLocal(MsgId readTillId) const; diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp index 412f90afd..c184251d6 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -226,6 +226,7 @@ void TopBarWidget::showMenu() { Window::FillPeerMenu( _controller, peer, + FilterId(), addAction, Window::PeerMenuSource::History); } else if (const auto folder = _activeChat.folder()) { diff --git a/Telegram/SourceFiles/info/info_wrap_widget.cpp b/Telegram/SourceFiles/info/info_wrap_widget.cpp index 0e50252db..188ae0261 100644 --- a/Telegram/SourceFiles/info/info_wrap_widget.cpp +++ b/Telegram/SourceFiles/info/info_wrap_widget.cpp @@ -574,6 +574,7 @@ void WrapWidget::showTopBarMenu() { Window::FillPeerMenu( _controller->parentController(), peer, + FilterId(), addAction, Window::PeerMenuSource::Profile); //} else if (const auto feed = key().feed()) { // #feed diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index fa85d2984..c268daa6d 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -64,6 +64,7 @@ public: Filler( not_null<SessionController*> controller, not_null<PeerData*> peer, + FilterId filterId, const PeerMenuCallback &addAction, PeerMenuSource source); void fill(); @@ -84,6 +85,7 @@ private: not_null<SessionController*> _controller; not_null<PeerData*> _peer; + FilterId _filterId = 0; const PeerMenuCallback &_addAction; PeerMenuSource _source; @@ -115,7 +117,7 @@ private: }; History *FindWastedPin(not_null<Data::Session*> data, Data::Folder *folder) { - const auto &order = data->pinnedChatsOrder(folder); + const auto &order = data->pinnedChatsOrder(folder, FilterId()); for (const auto &pinned : order) { if (const auto history = pinned.history()) { if (history->peer->isChat() @@ -134,24 +136,25 @@ void AddChatMembers( AddParticipantsBoxController::Start(navigation, chat); } -bool PinnedLimitReached(Dialogs::Key key) { +bool PinnedLimitReached(Dialogs::Key key, FilterId filterId) { Expects(key.entry()->folderKnown()); const auto entry = key.entry(); const auto owner = &entry->owner(); const auto folder = entry->folder(); - const auto pinnedCount = owner->pinnedChatsCount(folder); - const auto pinnedMax = owner->pinnedChatsLimit(folder); + const auto pinnedCount = owner->pinnedChatsCount(folder, filterId); + const auto pinnedMax = owner->pinnedChatsLimit(folder, filterId); if (pinnedCount < pinnedMax) { return false; } // Some old chat, that was converted, maybe is still pinned. - if (const auto wasted = FindWastedPin(owner, folder)) { - owner->setChatPinned(wasted, false); - owner->setChatPinned(key, true); + const auto wasted = filterId ? nullptr : FindWastedPin(owner, folder); + if (wasted) { + owner->setChatPinned(wasted, FilterId(), false); + owner->setChatPinned(key, FilterId(), true); entry->session().api().savePinnedOrder(folder); } else { - auto errorText = tr::lng_error_pinned_max( + const auto errorText = tr::lng_error_pinned_max( tr::now, lt_count, pinnedMax); @@ -165,12 +168,12 @@ void TogglePinnedDialog(Dialogs::Key key) { return; } const auto owner = &key.entry()->owner(); - const auto isPinned = !key.entry()->isPinnedDialog(); - if (isPinned && PinnedLimitReached(key)) { + const auto isPinned = !key.entry()->isPinnedDialog(0); + if (isPinned && PinnedLimitReached(key, 0)) { return; } - owner->setChatPinned(key, isPinned); + owner->setChatPinned(key, FilterId(), isPinned); const auto flags = isPinned ? MTPmessages_ToggleDialogPin::Flag::f_pinned : MTPmessages_ToggleDialogPin::Flag(0); @@ -194,13 +197,50 @@ void TogglePinnedDialog(Dialogs::Key key) { } } +void TogglePinnedDialog(Dialogs::Key key, FilterId filterId) { + if (!filterId) { + return TogglePinnedDialog(key); + } + const auto owner = &key.entry()->owner(); + const auto isPinned = !key.entry()->isPinnedDialog(filterId); + if (isPinned && PinnedLimitReached(key, filterId)) { + return; + } + + owner->setChatPinned(key, filterId, isPinned); + // #TODO pinned save data and to server + //const auto flags = isPinned + // ? MTPmessages_ToggleDialogPin::Flag::f_pinned + // : MTPmessages_ToggleDialogPin::Flag(0); + //if (const auto history = key.history()) { + // history->session().api().request(MTPmessages_ToggleDialogPin( + // MTP_flags(flags), + // MTP_inputDialogPeer(key.history()->peer->input) + // )).done([=](const MTPBool &result) { + // owner->notifyPinnedDialogsOrderUpdated(); + // }).send(); + //} else if (const auto folder = key.folder()) { + // folder->session().api().request(MTPmessages_ToggleDialogPin( + // MTP_flags(flags), + // MTP_inputDialogPeerFolder(MTP_int(folder->id())) + // )).send(); + //} + if (isPinned) { + if (const auto main = App::main()) { + main->dialogsToUp(); + } + } +} + Filler::Filler( not_null<SessionController*> controller, not_null<PeerData*> peer, + FilterId filterId, const PeerMenuCallback &addAction, PeerMenuSource source) : _controller(controller) , _peer(peer) +, _filterId(filterId) , _addAction(addAction) , _source(source) { } @@ -242,27 +282,29 @@ bool Filler::showTogglePin() { } void Filler::addTogglePin() { - auto peer = _peer; + const auto filterId = _filterId; + const auto peer = _peer; auto isPinned = false; - if (auto history = peer->owner().historyLoaded(peer)) { - isPinned = history->isPinnedDialog(); + if (const auto history = peer->owner().historyLoaded(peer)) { + isPinned = history->isPinnedDialog(filterId); } - auto pinText = [](bool isPinned) { + const auto pinText = [](bool isPinned) { return isPinned ? tr::lng_context_unpin_from_top(tr::now) : tr::lng_context_pin_to_top(tr::now); }; - auto pinToggle = [=] { - TogglePinnedDialog(peer->owner().history(peer)); + const auto pinToggle = [=] { + TogglePinnedDialog(peer->owner().history(peer), filterId); }; - auto pinAction = _addAction(pinText(isPinned), pinToggle); + const auto pinAction = _addAction(pinText(isPinned), pinToggle); const auto lifetime = Ui::CreateChild<rpl::lifetime>(pinAction); Notify::PeerUpdateViewer( peer, Notify::PeerUpdate::Flag::ChatPinnedChanged - ) | rpl::start_with_next([peer, pinAction, pinText] { - auto isPinned = peer->owner().history(peer)->isPinnedDialog(); + ) | rpl::start_with_next([=] { + const auto history = peer->owner().history(peer); + const auto isPinned = history->isPinnedDialog(filterId); pinAction->setText(pinText(isPinned)); }, *lifetime); } @@ -1041,9 +1083,10 @@ Fn<void()> DeleteAndLeaveHandler(not_null<PeerData*> peer) { void FillPeerMenu( not_null<SessionController*> controller, not_null<PeerData*> peer, + FilterId filterId, const PeerMenuCallback &callback, PeerMenuSource source) { - Filler filler(controller, peer, callback, source); + Filler filler(controller, peer, filterId, callback, source); filler.fill(); } diff --git a/Telegram/SourceFiles/window/window_peer_menu.h b/Telegram/SourceFiles/window/window_peer_menu.h index 2b2a96dd0..cfdeecd8c 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.h +++ b/Telegram/SourceFiles/window/window_peer_menu.h @@ -39,6 +39,7 @@ using PeerMenuCallback = Fn<QAction*( void FillPeerMenu( not_null<SessionController*> controller, not_null<PeerData*> peer, + FilterId filterId, const PeerMenuCallback &addAction, PeerMenuSource source); void FillFolderMenu(