From 58519300ea5736a64f847f2b7bcb6507499e4a0e Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 19 Apr 2019 12:47:49 +0400 Subject: [PATCH] Support pinned chats in folders. --- Telegram/SourceFiles/apiwrap.cpp | 172 +++++++----------- Telegram/SourceFiles/apiwrap.h | 18 +- Telegram/SourceFiles/core/application.cpp | 12 +- Telegram/SourceFiles/core/application.h | 3 + Telegram/SourceFiles/data/data_folder.cpp | 41 ++++- Telegram/SourceFiles/data/data_folder.h | 20 ++ Telegram/SourceFiles/data/data_session.cpp | 171 +++++++++-------- Telegram/SourceFiles/data/data_session.h | 23 ++- .../SourceFiles/dialogs/dialogs_entry.cpp | 2 +- .../dialogs/dialogs_indexed_list.cpp | 2 +- .../dialogs/dialogs_inner_widget.cpp | 74 +++++--- .../dialogs/dialogs_inner_widget.h | 5 +- .../dialogs/dialogs_pinned_list.cpp | 109 +++++++++++ .../SourceFiles/dialogs/dialogs_pinned_list.h | 51 ++++++ .../SourceFiles/dialogs/dialogs_widget.cpp | 14 +- Telegram/SourceFiles/facades.cpp | 2 + Telegram/SourceFiles/facades.h | 1 + Telegram/SourceFiles/history/history.cpp | 15 ++ Telegram/SourceFiles/history/history.h | 1 + Telegram/SourceFiles/mainwidget.cpp | 61 ++++--- Telegram/SourceFiles/mtproto/mtp_instance.cpp | 7 +- .../SourceFiles/window/window_peer_menu.cpp | 62 ++++--- Telegram/gyp/telegram_sources.txt | 2 + 23 files changed, 564 insertions(+), 304 deletions(-) create mode 100644 Telegram/SourceFiles/dialogs/dialogs_pinned_list.cpp create mode 100644 Telegram/SourceFiles/dialogs/dialogs_pinned_list.h diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 9c1aa61f75..4887f0a8e6 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -433,21 +433,24 @@ void ApiWrap::applyUpdates( App::main()->feedUpdates(updates, sentMessageRandomId); } -void ApiWrap::savePinnedOrder() { - const auto &order = _session->data().pinnedDialogsOrder(); +void ApiWrap::savePinnedOrder(FolderId folderId) { + const auto &order = _session->data().pinnedChatsOrder(folderId); + const auto input = [](const Dialogs::Key &key) { + if (const auto history = key.history()) { + return MTP_inputDialogPeer(history->peer->input); + } else if (const auto folder = key.folder()) { + return MTP_inputDialogPeerFolder(MTP_int(folder->id())); + } + Unexpected("Key type in pinnedDialogsOrder()."); + }; auto peers = QVector(); peers.reserve(order.size()); - for (const auto &pinned : ranges::view::reverse(order)) { - if (const auto history = pinned.history()) { - peers.push_back(MTP_inputDialogPeer(history->peer->input)); -// } else if (const auto feed = pinned.feed()) { // #feed -// peers.push_back(MTP_inputDialogPeerFeed(MTP_int(feed->id()))); - } - } - const auto folderId = 0; - const auto flags = MTPmessages_ReorderPinnedDialogs::Flag::f_force; + ranges::transform( + order, + ranges::back_inserter(peers), + input); request(MTPmessages_ReorderPinnedDialogs( - MTP_flags(flags), + MTP_flags(MTPmessages_ReorderPinnedDialogs::Flag::f_force), MTP_int(folderId), MTP_vector(peers) )).send(); @@ -704,12 +707,8 @@ void ApiWrap::requestContacts() { }).send(); } -void ApiWrap::requestDialogs() { - requestMoreDialogs(FolderId(0)); -} - -void ApiWrap::requestFolderDialogs(FolderId folderId) { - if (!_foldersLoadState.contains(folderId)) { +void ApiWrap::requestDialogs(FolderId folderId) { + if (folderId && !_foldersLoadState.contains(folderId)) { _foldersLoadState.emplace(folderId, DialogsLoadState()); } requestMoreDialogs(folderId); @@ -743,12 +742,16 @@ void ApiWrap::requestMoreDialogs(FolderId folderId) { : MTP_inputPeerEmpty()), MTP_int(loadCount), MTP_int(hash) - )).done([=](const MTPmessages_Dialogs & result) { + )).done([=](const MTPmessages_Dialogs &result) { + const auto state = dialogsLoadState(folderId); result.match([](const MTPDmessages_dialogsNotModified & data) { LOG(("API Error: not-modified received for requested dialogs.")); - }, [&](const auto & data) { + }, [&](const auto &data) { if constexpr (data.Is()) { - dialogsLoadFinish(folderId); + if (state) { + state->listReceived = true; + dialogsLoadFinish(folderId); // may kill 'state'. + } } else { updateDialogsOffset( folderId, @@ -764,9 +767,11 @@ void ApiWrap::requestMoreDialogs(FolderId folderId) { }); if (!folderId) { - requestDialogs(); + requestDialogs(folderId); requestContacts(); - if (!_dialogsLoadState || !_dialogsLoadState->requestId) { + if (!_dialogsLoadState + || (!_dialogsLoadState->listReceived + && !_dialogsLoadState->requestId)) { refreshDialogsLoadBlocked(); } } @@ -774,16 +779,21 @@ void ApiWrap::requestMoreDialogs(FolderId folderId) { }).fail([=](const RPCError &error) { dialogsLoadState(folderId)->requestId = 0; }).send(); - if (!_pinnedDialogsReceived) { - requestPinnedDialogs(); + + if (!state->pinnedReceived) { + requestPinnedDialogs(folderId); + } + if (!folderId) { + refreshDialogsLoadBlocked(); } - refreshDialogsLoadBlocked(); } void ApiWrap::refreshDialogsLoadBlocked() { _dialogsLoadMayBlockByDate = _dialogsLoadState + && !_dialogsLoadState->listReceived && (_dialogsLoadTill > 0); _dialogsLoadBlockedByDate = _dialogsLoadState + && !_dialogsLoadState->listReceived && !_dialogsLoadState->requestId && (_dialogsLoadTill > 0) && (_dialogsLoadState->offsetDate > 0) @@ -824,15 +834,16 @@ void ApiWrap::updateDialogsOffset( break; } } - if (lastDate) { - if (const auto state = dialogsLoadState(folderId)) { + if (const auto state = dialogsLoadState(folderId)) { + if (lastDate) { state->offsetDate = lastDate; state->offsetId = lastMsgId; state->offsetPeer = _session->data().peer(lastPeer); state->requestId = 0; + } else { + state->listReceived = true; + dialogsLoadFinish(folderId); } - } else { - dialogsLoadFinish(folderId); } } @@ -850,47 +861,48 @@ void ApiWrap::dialogsLoadFinish(FolderId folderId) { _session->data().chatsListDone(folderId); })); }; + const auto state = dialogsLoadState(folderId); + if (!state || !state->listReceived || !state->pinnedReceived) { + return; + } if (folderId) { _foldersLoadState.remove(folderId); notify(); } else { _dialogsLoadState = nullptr; - if (_pinnedDialogsReceived) { - notify(); - } + notify(); } } -void ApiWrap::requestPinnedDialogs() { - if (_pinnedDialogsRequestId) { +void ApiWrap::requestPinnedDialogs(FolderId folderId) { + const auto state = dialogsLoadState(folderId); + if (!state || state->pinnedReceived || state->pinnedRequestId) { return; } - const auto folderId = FolderId(0); - _pinnedDialogsRequestId = request(MTPmessages_GetPinnedDialogs( + const auto finalize = [=] { + if (const auto state = dialogsLoadState(folderId)) { + state->pinnedRequestId = 0; + state->pinnedReceived = true; + dialogsLoadFinish(folderId); + } + }; + state->pinnedRequestId = request(MTPmessages_GetPinnedDialogs( MTP_int(folderId) )).done([=](const MTPmessages_PeerDialogs &result) { + finalize(); result.match([&](const MTPDmessages_peerDialogs &data) { - const auto folderId = FolderId(0); - _session->data().processUsers(data.vusers); _session->data().processChats(data.vchats); - _session->data().clearPinnedDialogs(); + _session->data().clearPinnedChats(folderId); _session->data().applyDialogs( folderId, data.vmessages.v, data.vdialogs.v); - - _pinnedDialogsRequestId = 0; - _pinnedDialogsReceived = true; - _session->data().chatsListChanged(folderId); - if (!_dialogsLoadState) { - _session->data().chatsListDone(folderId); - } }); }).fail([=](const RPCError &error) { - _pinnedDialogsRequestId = 0; + finalize(); }).send(); } @@ -902,7 +914,7 @@ void ApiWrap::requestMoreBlockedByDateDialogs() { _dialogsLoadTill = _dialogsLoadState->offsetDate ? (_dialogsLoadState->offsetDate - max) : (unixtime() - max); - requestDialogs(); + requestDialogs(FolderId(0)); } rpl::producer ApiWrap::dialogsLoadMayBlockByDate() const { @@ -932,29 +944,6 @@ void ApiWrap::requestDialogEntry(not_null folder) { }).send(); } -//void ApiWrap::requestFeedDialogsEntries(not_null feed) { -// if (_dialogFeedRequests.contains(feed)) { -// return; -// } -// _dialogFeedRequests.emplace(feed); -// -// const auto hash = 0; -// request(MTPmessages_GetDialogs( -// MTP_flags(MTPmessages_GetDialogs::Flag::f_feed_id), -// MTP_int(feed->id()), -// MTP_int(0), // offset_date -// MTP_int(0), // offset_id -// MTP_inputPeerEmpty(), // offset_peer -// MTP_int(Data::Feed::kChannelsLimit), -// MTP_int(hash) -// )).done([=](const MTPmessages_Dialogs &result) { -// applyFeedDialogs(feed, result); -// _dialogFeedRequests.remove(feed); -// }).fail([=](const RPCError &error) { -// _dialogFeedRequests.remove(feed); -// }).send(); -//} - void ApiWrap::requestDialogEntry( not_null history, Fn callback) { @@ -1054,45 +1043,6 @@ void ApiWrap::applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs) { } _session->data().sendHistoryChangeNotifications(); } -// // #feed -//void ApiWrap::applyFeedDialogs( -// not_null feed, -// const MTPmessages_Dialogs &dialogs) { -// if (dialogs.type() == mtpc_messages_dialogsNotModified) { -// LOG(("API Error: " -// "messages.dialogsNotModified in ApiWrap::applyFeedDialogs.")); -// return; -// } -// auto channels = std::vector>(); -// dialogs.match([&](const MTPDmessages_dialogsNotModified &) { -// Unexpected("Type in ApiWrap::applyFeedDialogs."); -// }, [&](const auto &data) { -// _session->data().processUsers(data.vusers); -// _session->data().processChats(data.vchats); -// App::feedMsgs(data.vmessages.v, NewMessageLast); -// channels.reserve(data.vdialogs.v.size()); -// for (const auto &dialog : data.vdialogs.v) { -// dialog.match([&](const MTPDdialog &data) { -// if (const auto peerId = peerFromMTP(data.vpeer)) { -// if (peerIsChannel(peerId)) { // #TODO archive -// const auto history = _session->data().history(peerId); -// history->applyDialog(data); -// channels.emplace_back(history->peer->asChannel()); -// } else { -// LOG(("API Error: " -// "Unexpected peer in folder dialogs list.")); -// } -// } -// }, [&](const MTPDdialogFolder &data) { -// LOG(("API Error: " -// "Unexpected dialogFolder in folder dialogs list.")); -// }); -// } -// }); -// -// feed->setChannels(channels); -// _session->data().sendHistoryChangeNotifications(); -//} void ApiWrap::changeDialogUnreadMark( not_null history, diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index e2a74cfb0c..6e03f71db6 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(); + void savePinnedOrder(FolderId folderId); void toggleHistoryArchived( not_null history, bool archived, @@ -79,15 +79,13 @@ public: QString exportDirectMessageLink(not_null item); void requestContacts(); - void requestDialogs(); - void requestFolderDialogs(FolderId folderId); - void requestPinnedDialogs(); + void requestDialogs(FolderId folderId); + void requestPinnedDialogs(FolderId folderId); void requestMoreBlockedByDateDialogs(); rpl::producer dialogsLoadMayBlockByDate() const; rpl::producer dialogsLoadBlockedByDate() const; void requestDialogEntry(not_null folder); - //void requestFeedDialogsEntries(not_null feed); // #feed void requestDialogEntry( not_null history, Fn callback = nullptr); @@ -460,6 +458,10 @@ private: MsgId offsetId = 0; PeerData *offsetPeer = nullptr; mtpRequestId requestId = 0; + bool listReceived = false; + + mtpRequestId pinnedRequestId = 0; + bool pinnedReceived = false; }; void setupSupportMode(); @@ -485,9 +487,6 @@ private: QVector collectMessageIds(const MessageDataRequests &requests); MessageDataRequests *messageDataRequests(ChannelData *channel, bool onlyExisting = false); void applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs); - //void applyFeedDialogs( // #feed - // not_null feed, - // const MTPmessages_Dialogs &dialogs); void gotChatFull( not_null peer, @@ -762,9 +761,6 @@ private: rpl::variable _dialogsLoadMayBlockByDate = false; rpl::variable _dialogsLoadBlockedByDate = false; - bool _pinnedDialogsReceived = false; - mtpRequestId _pinnedDialogsRequestId = 0; - base::flat_map _foldersLoadState; rpl::event_stream _sendActions; diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index 015e61d757..ef74de534a 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -593,7 +593,7 @@ void Application::allKeysDestroyed() { } void Application::suggestMainDcId(MTP::DcId mainDcId) { - Assert(_mtproto != nullptr); + Expects(_mtproto != nullptr); _mtproto->suggestMainDcId(mainDcId); if (_private->mtpConfig.mainDcId != MTP::Instance::Config::kNotSetMainDc) { @@ -602,7 +602,7 @@ void Application::suggestMainDcId(MTP::DcId mainDcId) { } void Application::destroyStaleAuthorizationKeys() { - Assert(_mtproto != nullptr); + Expects(_mtproto != nullptr); for (const auto &key : _mtproto->getKeysForWrite()) { // Disable this for now. @@ -616,6 +616,14 @@ void Application::destroyStaleAuthorizationKeys() { } } +void Application::configUpdated() { + _configUpdates.fire({}); +} + +rpl::producer<> Application::configUpdates() const { + return _configUpdates.events(); +} + void Application::resetAuthorizationKeys() { _mtproto = nullptr; startMtp(); diff --git a/Telegram/SourceFiles/core/application.h b/Telegram/SourceFiles/core/application.h index 3cd9edc330..59090f3368 100644 --- a/Telegram/SourceFiles/core/application.h +++ b/Telegram/SourceFiles/core/application.h @@ -140,6 +140,8 @@ public: } void suggestMainDcId(MTP::DcId mainDcId); void destroyStaleAuthorizationKeys(); + void configUpdated(); + [[nodiscard]] rpl::producer<> configUpdates() const; // Databases Storage::Databases &databases() { @@ -276,6 +278,7 @@ private: std::unique_ptr _dcOptions; std::unique_ptr _mtproto; std::unique_ptr _mtprotoForKeysDestroy; + rpl::event_stream<> _configUpdates; std::unique_ptr _authSession; base::Observable _authSessionChanged; base::Observable _passcodedChanged; diff --git a/Telegram/SourceFiles/data/data_folder.cpp b/Telegram/SourceFiles/data/data_folder.cpp index 67b7b18c77..a1c546ef94 100644 --- a/Telegram/SourceFiles/data/data_folder.cpp +++ b/Telegram/SourceFiles/data/data_folder.cpp @@ -42,6 +42,7 @@ Folder::Folder(not_null owner, FolderId id) , _id(id) , _chatsList(Dialogs::SortMode::Date) , _importantChatsList(Dialogs::SortMode::Date) +, _pinnedChatsList(Global::PinnedDialogsInFolderMax()) , _name(lang(lng_archived_chats)) { indexNameParts(); } @@ -234,6 +235,36 @@ 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() ? TimeId(0) @@ -286,10 +317,18 @@ void Folder::applyDialog(const MTPDdialogFolder &data) { //} if (_chatsList.size() < kLoadedChatsMinCount) { - session().api().requestFolderDialogs(_id); + session().api().requestDialogs(_id); } } +void Folder::applyPinnedUpdate(const MTPDupdateDialogPinned &data) { + const auto folderId = data.has_folder_id() ? data.vfolder_id.v : 0; + if (folderId != 0) { + LOG(("API Error: Nested folders detected.")); + } + owner().setChatPinned(this, data.is_pinned()); +} + void Folder::changedInChatListHook(Dialogs::Mode list, bool added) { if (list != Dialogs::Mode::All) { return; diff --git a/Telegram/SourceFiles/data/data_folder.h b/Telegram/SourceFiles/data/data_folder.h index 135b4c39b4..a734006fa8 100644 --- a/Telegram/SourceFiles/data/data_folder.h +++ b/Telegram/SourceFiles/data/data_folder.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "dialogs/dialogs_entry.h" #include "dialogs/dialogs_indexed_list.h" +#include "dialogs/dialogs_pinned_list.h" #include "data/data_messages.h" class ChannelData; @@ -45,6 +46,8 @@ public: not_null chatsList(Dialogs::Mode list); void applyDialog(const MTPDdialogFolder &data); + void applyPinnedUpdate(const MTPDupdateDialogPinned &data); + void setUnreadCounts(int unreadNonMutedCount, int unreadMutedCount); //void setUnreadPosition(const MessagePosition &position); // #feed void unreadCountChanged( @@ -58,6 +61,22 @@ public: //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); + TimeId adjustedChatListTimeId() const override; int unreadCount() const; bool unreadCountKnown() const; @@ -100,6 +119,7 @@ private: FolderId _id = 0; Dialogs::IndexedList _chatsList; Dialogs::IndexedList _importantChatsList; + Dialogs::PinnedList _pinnedChatsList; bool _chatsListLoaded = false; QString _name; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 01befbde72..48d5ca19ea 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -154,6 +154,7 @@ Session::Session(not_null session) , _importantChatsList(Dialogs::SortMode::Date) , _contactsList(Dialogs::SortMode::Name) , _contactsNoChatsList(Dialogs::SortMode::Name) +, _pinnedChatsList(Global::PinnedDialogsCountMax()) , _selfDestructTimer([=] { checkSelfDestructItems(); }) , _sendActionsAnimation([=](crl::time now) { return sendActionsAnimationCallback(now); @@ -167,6 +168,14 @@ 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() { @@ -731,7 +740,9 @@ History *Session::historyLoaded(const PeerData *peer) { void Session::deleteConversationLocally(not_null peer) { const auto history = historyLoaded(peer); if (history) { - setPinnedDialog(history, false); + if (history->folderKnown()) { + setChatPinned(history, false); + } App::main()->removeDialog(history); history->clear(peer->isChannel() ? History::ClearType::Unload @@ -1341,37 +1352,61 @@ MessageIdsList Session::itemOrItsGroup(not_null item) const { return { 1, item->fullId() }; } -void Session::setPinnedDialog(const Dialogs::Key &key, bool pinned) { - setIsPinned(key, pinned); +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); + } } -void Session::applyPinnedDialogs(const QVector &list) { - clearPinnedDialogs(); - for (auto i = list.size(); i != 0;) { - list[--i].match([&](const MTPDdialogPeer &data) { - if (const auto peerId = peerFromMTP(data.vpeer)) { - setPinnedDialog(history(peerId), true); +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); + } else { + _pinnedChatsList.setPinned(key, false); + } +} + +void Session::applyPinnedChats( + FolderId folderId, + 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)); + if (folder) { + history->setFolder(folder); + } else { + history->clearFolder(); } }, [&](const MTPDdialogPeerFolder &data) { - const auto folderId = data.vfolder_id.v; - setPinnedDialog(folder(folderId), true); + if (folder) { + LOG(("API Error: Nested folders detected.")); + } }); } + if (folder) { + folder->applyPinnedChats(list); + } else { + _pinnedChatsList.applyList(this, list); + } } void Session::applyDialogs( FolderId requestFolderId, const QVector &messages, const QVector &dialogs) { - for (const auto &dialog : dialogs | ranges::view::reverse) { - dialog.match([&](const MTPDdialog &data) { - if (const auto peer = peerFromMTP(data.vpeer)) { - setPinnedDialog(history(peer), data.is_pinned()); - } - }, [&](const MTPDdialogFolder &data) { - setPinnedDialog(processFolder(data.vfolder), data.is_pinned()); - }); - } App::feedMsgs(messages, NewMessageLast); for (const auto &dialog : dialogs) { dialog.match([&](const auto &data) { @@ -1388,6 +1423,7 @@ void Session::applyDialog(FolderId requestFolderId, const MTPDdialog &data) { const auto history = session().data().history(peerId); history->applyDialog(requestFolderId, data); + setPinnedFromDialog(history, data.is_pinned()); if (!history->fixedOnTopIndex() && !history->isPinnedDialog()) { const auto date = history->chatListTimeId(); @@ -1408,12 +1444,13 @@ void Session::applyDialog(FolderId requestFolderId, const MTPDdialog &data) { void Session::applyDialog( FolderId requestFolderId, - const MTPDdialogFolder &dialog) { + const MTPDdialogFolder &data) { if (requestFolderId != 0) { LOG(("API Error: requestFolderId != 0 for dialogFolder.")); } - const auto folder = processFolder(dialog.vfolder); - folder->applyDialog(dialog); + const auto folder = processFolder(data.vfolder); + folder->applyDialog(data); + setPinnedFromDialog(folder, data.is_pinned()); if (!folder->fixedOnTopIndex() && !folder->isPinnedDialog()) { const auto date = folder->chatListTimeId(); @@ -1441,69 +1478,49 @@ void Session::addAllSavedPeers() { addSavedPeersAfter(QDateTime()); } -int Session::pinnedDialogsCount() const { - return _pinnedDialogs.size(); +int Session::pinnedChatsCount(FolderId folderId) const { + return pinnedChatsOrder(folderId).size(); } -const std::deque &Session::pinnedDialogsOrder() const { - return _pinnedDialogs; +int Session::pinnedChatsLimit(FolderId folderId) const { + return folderId + ? Global::PinnedDialogsInFolderMax() + : Global::PinnedDialogsCountMax(); } -void Session::clearPinnedDialogs() { - while (!_pinnedDialogs.empty()) { - setPinnedDialog(_pinnedDialogs.back(), false); +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(); +} + +void Session::clearPinnedChats(FolderId folderId) { + if (folderId) { + if (const auto folder = folderLoaded(folderId)) { + folder->clearPinnedChats(); + } + } else { + _pinnedChatsList.clear(); } } -void Session::reorderTwoPinnedDialogs( +void Session::reorderTwoPinnedChats( const Dialogs::Key &key1, const Dialogs::Key &key2) { - const auto &order = pinnedDialogsOrder(); - const auto index1 = ranges::find(order, key1) - begin(order); - const auto index2 = ranges::find(order, key2) - begin(order); - Assert(index1 >= 0 && index1 < order.size()); - Assert(index2 >= 0 && index2 < order.size()); - Assert(index1 != index2); - std::swap(_pinnedDialogs[index1], _pinnedDialogs[index2]); - key1.entry()->cachePinnedIndex(index2 + 1); - key2.entry()->cachePinnedIndex(index1 + 1); -} + Expects(key1.entry()->folderKnown() && key2.entry()->folderKnown()); + Expects(key1.entry()->folder() == key2.entry()->folder()); -void Session::setIsPinned(const Dialogs::Key &key, bool pinned) { - const auto already = ranges::find(_pinnedDialogs, key); - if (pinned) { - if (already != end(_pinnedDialogs)) { - auto saved = std::move(*already); - const auto alreadyIndex = already - end(_pinnedDialogs); - const auto count = int(size(_pinnedDialogs)); - Assert(alreadyIndex < count); - for (auto index = alreadyIndex + 1; index != count; ++index) { - _pinnedDialogs[index - 1] = std::move(_pinnedDialogs[index]); - _pinnedDialogs[index - 1].entry()->cachePinnedIndex(index); - } - _pinnedDialogs.back() = std::move(saved); - _pinnedDialogs.back().entry()->cachePinnedIndex(count); - } else { - _pinnedDialogs.push_back(key); - if (_pinnedDialogs.size() > Global::PinnedDialogsCountMax()) { - _pinnedDialogs.front().entry()->cachePinnedIndex(0); - _pinnedDialogs.pop_front(); - - auto index = 0; - for (const auto &pinned : _pinnedDialogs) { - pinned.entry()->cachePinnedIndex(++index); - } - } else { - key.entry()->cachePinnedIndex(_pinnedDialogs.size()); - } - } - } else if (!pinned && already != end(_pinnedDialogs)) { - key.entry()->cachePinnedIndex(0); - _pinnedDialogs.erase(already); - auto index = 0; - for (const auto &pinned : _pinnedDialogs) { - pinned.entry()->cachePinnedIndex(++index); - } + const auto folder = key1.entry()->folder(); + if (folder) { + folder->reorderTwoPinnedChats(key1, key2); + } else { + _pinnedChatsList.reorder(key1, key2); } } @@ -2937,7 +2954,7 @@ not_null Session::folder(FolderId id) { return it->second.get(); } -Folder *Session::folderLoaded(FolderId id) { +Folder *Session::folderLoaded(FolderId id) const { const auto it = _folders.find(id); return (it == end(_folders)) ? nullptr : it->second.get(); } diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 96aba09357..1593abc568 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -11,6 +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 "data/data_groups.h" #include "data/data_notify_settings.h" #include "history/history_location_manager.h" @@ -304,12 +305,16 @@ public: void addSavedPeersAfter(const QDateTime &date); void addAllSavedPeers(); - int pinnedDialogsCount() const; - const std::deque &pinnedDialogsOrder() const; - void setPinnedDialog(const Dialogs::Key &key, bool pinned); - void clearPinnedDialogs(); - void applyPinnedDialogs(const QVector &list); - void reorderTwoPinnedDialogs( + int pinnedChatsCount(FolderId folderId) const; + int pinnedChatsLimit(FolderId folderId) const; + const std::vector &pinnedChatsOrder( + FolderId folderId) const; + void setChatPinned(const Dialogs::Key &key, bool pinned); + void clearPinnedChats(FolderId folderId); + void applyPinnedChats( + FolderId folderId, + const QVector &list); + void reorderTwoPinnedChats( const Dialogs::Key &key1, const Dialogs::Key &key2); @@ -513,7 +518,7 @@ public: void unregisterItemView(not_null view); [[nodiscard]] not_null folder(FolderId id); - [[nodiscard]] Folder *folderLoaded(FolderId id); + [[nodiscard]] Folder *folderLoaded(FolderId id) const; not_null processFolder(const MTPFolder &data); not_null processFolder(const MTPDfolder &data); //void setDefaultFeedId(FeedId id); // #feed @@ -694,7 +699,7 @@ private: } void userIsContactUpdated(not_null user); - void setIsPinned(const Dialogs::Key &key, bool pinned); + void setPinnedFromDialog(const Dialogs::Key &key, bool pinned); NotifySettings &defaultNotifySettings(not_null peer); const NotifySettings &defaultNotifySettings( @@ -777,6 +782,7 @@ private: Dialogs::IndexedList _importantChatsList; Dialogs::IndexedList _contactsList; Dialogs::IndexedList _contactsNoChatsList; + Dialogs::PinnedList _pinnedChatsList; base::Timer _selfDestructTimer; std::vector _selfDestructItems; @@ -835,7 +841,6 @@ private: base::flat_set> _gamesUpdated; base::flat_set> _pollsUpdated; - std::deque _pinnedDialogs; base::flat_map> _folders; //rpl::variable _defaultFeedId = FeedId(); // #feed Groups _groups; diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp index 0f605ef732..1dad18c818 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp @@ -34,7 +34,7 @@ uint64 FixedOnTopDialogPos(int index) { } uint64 PinnedDialogPos(int pinnedIndex) { - return 0xFFFFFFFF00000000ULL + pinnedIndex; + return 0xFFFFFFFF000000FFULL - pinnedIndex; } } // namespace diff --git a/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp index 2d284f7abe..623df9af94 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp @@ -81,7 +81,7 @@ void IndexedList::movePinned(Row *row, int deltaSign) { Assert(swapPinnedIndexWith != cbegin()); --swapPinnedIndexWith; } - Auth().data().reorderTwoPinnedDialogs( + Auth().data().reorderTwoPinnedChats( row->key(), (*swapPinnedIndexWith)->key()); } diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 49364276f1..3a951a0b98 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -275,13 +275,28 @@ int DialogsInner::openedFolderSkip() const { return st::dialogsSearchInHeight; } +bool DialogsInner::openFolder(not_null folder) { + return changeOpenedFolder(folder); +} + bool DialogsInner::cancelFolder() { - if (!_openedFolder) { + return changeOpenedFolder(nullptr); +} + +bool DialogsInner::changeOpenedFolder(Data::Folder *folder) { + if (_openedFolder == folder) { return false; } + stopReorderPinned(); clearSelection(); - _openedFolder = nullptr; - _closeOpenedFolder->hide(); + _openedFolder = folder; + _closeOpenedFolder->setVisible(folder != nullptr); + if (folder) { + _openedFolderText.setText( + st::msgNameStyle, + folder->chatListName(), + Ui::DialogTextOptions()); + } refresh(); return true; } @@ -920,18 +935,25 @@ void DialogsInner::mousePressEvent(QMouseEvent *e) { } void DialogsInner::checkReorderPinnedStart(QPoint localPosition) { - if (_pressed != nullptr && !_dragging && _state == State::Default) { - if (qAbs(localPosition.y() - _dragStart.y()) >= ConvertScale(kStartReorderThreshold)) { - _dragging = _pressed; - if (updateReorderIndexGetCount() < 2) { - _dragging = nullptr; - } else { - _pinnedOrder = session().data().pinnedDialogsOrder(); - _pinnedRows[_draggingIndex].yadd = anim::value(0, localPosition.y() - _dragStart.y()); - _pinnedRows[_draggingIndex].animStartTime = crl::now(); - _pinnedShiftAnimation.start(); - } - } + if (!_pressed || _dragging || _state != State::Default) { + return; + } else if (qAbs(localPosition.y() - _dragStart.y()) + < ConvertScale(kStartReorderThreshold)) { + return; + } + _dragging = _pressed; + if (updateReorderIndexGetCount() < 2) { + _dragging = nullptr; + } else { + const auto folderId = _openedFolder ? _openedFolder->id() : 0; + const auto &order = session().data().pinnedChatsOrder(folderId); + _pinnedOnDragStart = base::flat_set{ + order.begin(), + order.end() + }; + _pinnedRows[_draggingIndex].yadd = anim::value(0, localPosition.y() - _dragStart.y()); + _pinnedRows[_draggingIndex].animStartTime = crl::now(); + _pinnedShiftAnimation.start(); } } @@ -967,16 +989,17 @@ int DialogsInner::countPinnedIndex(Dialogs::Row *ofRow) { } void DialogsInner::savePinnedOrder() { - const auto &newOrder = session().data().pinnedDialogsOrder(); - if (newOrder.size() != _pinnedOrder.size()) { + const auto folderId = _openedFolder ? _openedFolder->id() : 0; + const auto &newOrder = session().data().pinnedChatsOrder(folderId); + if (newOrder.size() != _pinnedOnDragStart.size()) { return; // Something has changed in the set of pinned chats. } - for (const auto &pinned : newOrder) { - if (!base::contains(_pinnedOrder, pinned)) { + for (const auto &key : newOrder) { + if (!_pinnedOnDragStart.contains(key)) { return; // Something has changed in the set of pinned chats. } } - session().api().savePinnedOrder(); + session().api().savePinnedOrder(folderId); } void DialogsInner::finishReorderPinned() { @@ -1769,7 +1792,7 @@ void DialogsInner::clearSearchResults(bool clearPeerSearchResults) { _searchResults.clear(); _searchedCount = _searchedMigratedCount = 0; _lastSearchDate = 0; - _lastSearchPeer = 0; + _lastSearchPeer = nullptr; _lastSearchId = _lastSearchMigratedId = 0; } @@ -2457,14 +2480,7 @@ bool DialogsInner::chooseRow() { ? ShowAtUnreadMsgId : chosen.message.fullId.msg)); } else if (const auto folder = chosen.key.folder()) { - clearSelection(); - _openedFolder = folder; - _closeOpenedFolder->show(); - _openedFolderText.setText( - st::msgNameStyle, - folder->chatListName(), - Ui::DialogTextOptions()); - refresh(); + openFolder(folder); } if (openSearchResult && !session().supportMode()) { emit clearSearchQuery(); diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h index 9a2e859113..9dace8978c 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h @@ -50,6 +50,7 @@ public: void activate(); + bool openFolder(not_null folder); bool cancelFolder(); void selectSkip(int32 direction); void selectSkipPage(int32 pixels, int32 direction); @@ -273,6 +274,8 @@ private: bool pinnedShiftAnimationCallback(crl::time now); void handleChatMigration(not_null chat); + bool changeOpenedFolder(Data::Folder *folder); + not_null _controller; bool _mouseSelection = false; @@ -298,7 +301,7 @@ private: }; std::vector _pinnedRows; Ui::Animations::Basic _pinnedShiftAnimation; - std::deque _pinnedOrder; + base::flat_set _pinnedOnDragStart; // Remember the last currently dragged row top shift for updating area. int _aboveTopShift = -1; diff --git a/Telegram/SourceFiles/dialogs/dialogs_pinned_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_pinned_list.cpp new file mode 100644 index 0000000000..fecabf373f --- /dev/null +++ b/Telegram/SourceFiles/dialogs/dialogs_pinned_list.cpp @@ -0,0 +1,109 @@ +/* +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_pinned_list.h" + +#include "dialogs/dialogs_key.h" +#include "dialogs/dialogs_entry.h" +#include "data/data_session.h" + +namespace Dialogs { + +PinnedList::PinnedList(int limit) : _limit(limit) { + Expects(limit > 0); +} + +void PinnedList::setLimit(int limit) { + Expects(limit > 0); + + if (_limit == limit) { + return; + } + _limit = limit; + applyLimit(_limit); +} + +void PinnedList::addPinned(const Key &key) { + Expects(key.entry()->folderKnown()); + + addPinnedGetPosition(key); +} + +int PinnedList::addPinnedGetPosition(const Key &key) { + const auto already = ranges::find(_data, key); + if (already != end(_data)) { + return already - begin(_data); + } + applyLimit(_limit - 1); + const auto position = int(_data.size()); + _data.push_back(key); + key.entry()->cachePinnedIndex(position); + return position; +} + +void PinnedList::setPinned(const Key &key, bool pinned) { + Expects(key.entry()->folderKnown()); + + if (pinned) { + const int position = addPinnedGetPosition(key); + if (position) { + 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); + } + } + } else if (const auto it = ranges::find(_data, key); it != end(_data)) { + const auto index = (it - begin(_data)); + _data.erase(it); + key.entry()->cachePinnedIndex(0); + for (auto i = index, count = int(size(_data)); i != count; ++i) { + _data[i].entry()->cachePinnedIndex(i + 1); + } + } +} + +void PinnedList::applyLimit(int limit) { + Expects(limit >= 0); + + while (_data.size() > limit) { + setPinned(_data.back(), false); + } +} + +void PinnedList::clear() { + applyLimit(0); +} + +void PinnedList::applyList( + not_null owner, + const QVector &list) { + clear(); + for (const auto &peer : ranges::view::reverse(list)) { + peer.match([&](const MTPDdialogPeer &data) { + if (const auto peerId = peerFromMTP(data.vpeer)) { + setPinned(owner->history(peerId), true); + } + }, [&](const MTPDdialogPeerFolder &data) { + const auto folderId = data.vfolder_id.v; + setPinned(owner->folder(folderId), true); + }); + } +} + +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); + Assert(index1 >= 0 && index1 < _data.size()); + 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); +} + +} // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/dialogs_pinned_list.h b/Telegram/SourceFiles/dialogs/dialogs_pinned_list.h new file mode 100644 index 0000000000..bda94285e1 --- /dev/null +++ b/Telegram/SourceFiles/dialogs/dialogs_pinned_list.h @@ -0,0 +1,51 @@ +/* +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 + +namespace Data { +class Session; +} // namespace Data + +namespace Dialogs { + +class Key; + +class PinnedList { +public: + explicit PinnedList(int limit); + + void setLimit(int limit); + + // Places on the last place in the list otherwise. + // Does nothing if already pinned. + void addPinned(const Key &key); + + // if (pinned) places on the first place in the list. + void setPinned(const Key &key, bool pinned); + + void clear(); + + void applyList( + not_null owner, + const QVector &list); + void reorder(const Key &key1, const Key &key2); + + const std::vector &order() const { + return _data; + } + +private: + int addPinnedGetPosition(const Key &key); + void applyLimit(int limit); + + int _limit = 0; + std::vector _data; + +}; + +} // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 1a6c9fa50c..bd9a5dcab5 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -247,7 +247,11 @@ DialogsWidget::DialogsWidget(QWidget *parent, not_null cont && !_searchFullMigrated))) { onSearchMore(); } else { - session().api().requestDialogs(); + const auto folder = _inner->shownFolder(); + const auto folderId = folder ? folder->id() : FolderId(0); + if (!folderId || !folder->chatsListLoaded()) { + session().api().requestDialogs(folderId); + } } }); _inner->listBottomReached( @@ -1063,14 +1067,6 @@ void DialogsWidget::onListScroll() { // Fix button rendering glitch, Qt bug with WA_OpaquePaintEvent widgets. _scrollToTop->update(); - - if (const auto folder = _inner->shownFolder()) { - if (!folder->chatsListLoaded() - && (scrollTop + height() * PreloadHeightsCount - >= _scroll->scrollTopMax())) { - session().api().requestFolderDialogs(folder->id()); - } - } } void DialogsWidget::applyFilterUpdate(bool force) { diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index b30ed56f8d..f9d0b4e1e3 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -391,6 +391,7 @@ struct Data { int32 StickersRecentLimit = 30; int32 StickersFavedLimit = 5; int32 PinnedDialogsCountMax = 5; + int32 PinnedDialogsInFolderMax = 100; QString InternalLinksDomain = qsl("https://t.me/"); int32 ChannelsReadMediaPeriod = 86400 * 7; int32 CallReceiveTimeoutMs = 20000; @@ -525,6 +526,7 @@ DefineVar(Global, bool, RevokePrivateInbox); DefineVar(Global, int32, StickersRecentLimit); DefineVar(Global, int32, StickersFavedLimit); DefineVar(Global, int32, PinnedDialogsCountMax); +DefineVar(Global, int32, PinnedDialogsInFolderMax); DefineVar(Global, QString, InternalLinksDomain); DefineVar(Global, int32, ChannelsReadMediaPeriod); DefineVar(Global, int32, CallReceiveTimeoutMs); diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 30475f939a..63503e766a 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -248,6 +248,7 @@ DeclareVar(bool, RevokePrivateInbox); DeclareVar(int32, StickersRecentLimit); DeclareVar(int32, StickersFavedLimit); DeclareVar(int32, PinnedDialogsCountMax); +DeclareVar(int32, PinnedDialogsInFolderMax); DeclareVar(QString, InternalLinksDomain); DeclareVar(int32, ChannelsReadMediaPeriod); DeclareVar(int32, CallReceiveTimeoutMs); diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 84ccec5cb1..48cb5373c2 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -1835,6 +1835,9 @@ void History::setFolderPointer(Data::Folder *folder) { if (_folder == folder) { return; } + if (isPinnedDialog()) { + owner().setChatPinned(this, false); + } const auto wasKnown = folderKnown(); const auto wasInAll = inChatList(Mode::All); const auto wasInImportant = wasInAll && inChatList(Mode::Important); @@ -1864,6 +1867,18 @@ void History::setFolderPointer(Data::Folder *folder) { } } +void History::applyPinnedUpdate(const MTPDupdateDialogPinned &data) { + const auto folderId = data.has_folder_id() ? data.vfolder_id.v : 0; + if (!folderKnown()) { + if (folderId) { + setFolder(owner().folder(folderId)); + } else { + clearFolder(); + } + } + owner().setChatPinned(this, data.is_pinned()); +} + TimeId History::adjustedChatListTimeId() const { const auto result = chatListTimeId(); if (const auto draft = cloudDraft()) { diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 9d4955ad6c..480e71664e 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -192,6 +192,7 @@ public: void unknownMessageDeleted(MsgId messageId); void applyDialogTopMessage(MsgId topMessageId); void applyDialog(FolderId requestFolderId, const MTPDdialog &data); + void applyPinnedUpdate(const MTPDupdateDialogPinned &data); void applyDialogFields( int unreadCount, MsgId maxInboxRead, diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index cd279f608c..53ad5ca1e1 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -2931,7 +2931,7 @@ void MainWidget::gotState(const MTPupdates_State &state) { _noUpdatesTimer.callOnce(kNoUpdatesTimeout); _ptsWaiter.setRequesting(false); - session().api().requestDialogs(); + session().api().requestDialogs(FolderId(0)); updateOnline(); } @@ -4269,7 +4269,11 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { case mtpc_updatePinnedDialogs: { const auto &d = update.c_updatePinnedDialogs(); - if (d.has_order()) { + const auto folderId = d.has_folder_id() ? d.vfolder_id.v : 0; + const auto done = [&] { + if (!d.has_order()) { + return false; + } const auto &order = d.vorder.v; const auto notLoaded = [&](const MTPDialogPeer &peer) { return peer.match([&](const MTPDdialogPeer &data) { @@ -4281,41 +4285,52 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { }; const auto allLoaded = ranges::find_if(order, notLoaded) == order.end(); - if (allLoaded) { - session().data().applyPinnedDialogs(order); - } else { - session().api().requestPinnedDialogs(); + if (!allLoaded) { + return false; } - } else { - session().api().requestPinnedDialogs(); + session().data().applyPinnedChats(folderId, order); + return true; + }(); + if (!done) { + session().api().requestPinnedDialogs(folderId); } } break; case mtpc_updateDialogPinned: { const auto &d = update.c_updateDialogPinned(); - d.vpeer.match([&](const MTPDdialogPeer &data) { + const auto folderId = d.has_folder_id() ? d.vfolder_id.v : 0; + const auto done = d.vpeer.match([&](const MTPDdialogPeer &data) { const auto id = peerFromMTP(data.vpeer); if (const auto history = session().data().historyLoaded(id)) { - session().data().setPinnedDialog(history, d.is_pinned()); - } else { - DEBUG_LOG(("API Error: " - "pinned chat not loaded for peer %1" - ).arg(id - )); - session().api().requestPinnedDialogs(); + history->applyPinnedUpdate(d); + return true; } + DEBUG_LOG(("API Error: " + "pinned chat not loaded for peer %1, folder: %2" + ).arg(id + ).arg(folderId + )); + return false; }, [&](const MTPDdialogPeerFolder &data) { + if (folderId != 0) { + DEBUG_LOG(("API Error: Nested folders updateDialogPinned.")); + return false; + } const auto id = data.vfolder_id.v; if (const auto folder = session().data().folderLoaded(id)) { - session().data().setPinnedDialog(folder, d.is_pinned()); - } else { - DEBUG_LOG(("API Error: " - "pinned folder not loaded for folderId %1" - ).arg(id - )); - session().api().requestPinnedDialogs(); + folder->applyPinnedUpdate(d); + return true; } + DEBUG_LOG(("API Error: " + "pinned folder not loaded for folderId %1, folder: %2" + ).arg(id + ).arg(folderId + )); + return false; }); + if (!done) { + session().api().requestPinnedDialogs(folderId); + } } break; case mtpc_updateChannel: { diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.cpp b/Telegram/SourceFiles/mtproto/mtp_instance.cpp index bba6ad0744..f18c3ba791 100644 --- a/Telegram/SourceFiles/mtproto/mtp_instance.cpp +++ b/Telegram/SourceFiles/mtproto/mtp_instance.cpp @@ -773,7 +773,10 @@ void Instance::Private::configLoadDone(const MTPConfig &result) { Global::SetRevokePrivateInbox(data.is_revoke_pm_inbox()); Global::SetStickersRecentLimit(data.vstickers_recent_limit.v); Global::SetStickersFavedLimit(data.vstickers_faved_limit.v); - Global::SetPinnedDialogsCountMax(data.vpinned_dialogs_count_max.v); + Global::SetPinnedDialogsCountMax( + std::max(data.vpinned_dialogs_count_max.v, 1)); + Global::SetPinnedDialogsInFolderMax( + std::max(data.vpinned_infolder_count_max.v, 1)); Core::App().setInternalLinkDomain(qs(data.vme_url_prefix)); Global::SetChannelsReadMediaPeriod(data.vchannels_read_media_period.v); Global::SetWebFileDcId(data.vwebfile_dc_id.v); @@ -801,6 +804,8 @@ void Instance::Private::configLoadDone(const MTPConfig &result) { ? data.vbase_lang_pack_version.v : 0)); + Core::App().configUpdated(); + if (data.has_autoupdate_url_prefix()) { Local::writeAutoupdatePrefix(qs(data.vautoupdate_url_prefix)); } diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index e0cb15598b..89085a940b 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -80,12 +80,12 @@ public: void fill(); private: - bool showInfo(); - void addPinToggle(); - void addInfo(); - void addSearch(); - void addNotifications(); - void addUngroup(); + //bool showInfo(); + //void addPinToggle(); + //void addInfo(); + //void addSearch(); + //void addNotifications(); + //void addUngroup(); not_null _controller; not_null _folder; @@ -94,8 +94,8 @@ private: }; -History *FindWastedPin() { - const auto &order = Auth().data().pinnedDialogsOrder(); +History *FindWastedPin(FolderId folderId) { + const auto &order = Auth().data().pinnedChatsOrder(folderId); for (const auto &pinned : order) { if (const auto history = pinned.history()) { if (history->peer->isChat() @@ -113,16 +113,20 @@ void AddChatMembers(not_null chat) { } bool PinnedLimitReached(Dialogs::Key key) { - const auto pinnedCount = Auth().data().pinnedDialogsCount(); - const auto pinnedMax = Global::PinnedDialogsCountMax(); + 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); if (pinnedCount < pinnedMax) { return false; } // Some old chat, that was converted, maybe is still pinned. - if (const auto wasted = FindWastedPin()) { - Auth().data().setPinnedDialog(wasted, false); - Auth().data().setPinnedDialog(key, true); - Auth().api().savePinnedOrder(); + if (const auto wasted = FindWastedPin(folderId)) { + Auth().data().setChatPinned(wasted, false); + Auth().data().setChatPinned(key, true); + Auth().api().savePinnedOrder(folderId); } else { auto errorText = lng_error_pinned_max( lt_count, @@ -133,32 +137,34 @@ bool PinnedLimitReached(Dialogs::Key key) { } void TogglePinnedDialog(Dialogs::Key key) { + if (!key.entry()->folderKnown()) { + return; + } const auto isPinned = !key.entry()->isPinnedDialog(); if (isPinned && PinnedLimitReached(key)) { return; } - Auth().data().setPinnedDialog(key, isPinned); - auto flags = MTPmessages_ToggleDialogPin::Flags(0); - if (isPinned) { - flags |= MTPmessages_ToggleDialogPin::Flag::f_pinned; - } - //MTP::send(MTPmessages_ToggleDialogPin( // #feed - // MTP_flags(flags), - // key.history() - // ? MTP_inputDialogPeer(key.history()->peer->input) - // : MTP_inputDialogPeerFeed(MTP_int(key.feed()->id())))); - if (key.history()) { - MTP::send(MTPmessages_ToggleDialogPin( + Auth().data().setChatPinned(key, isPinned); + 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))); + MTP_inputDialogPeer(key.history()->peer->input) + )).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( diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 5a2f52b677..daeea1868f 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_pinned_list.cpp +<(src_loc)/dialogs/dialogs_pinned_list.h <(src_loc)/dialogs/dialogs_row.cpp <(src_loc)/dialogs/dialogs_row.h <(src_loc)/dialogs/dialogs_search_from_controllers.cpp