From 287b3509ab01dd3e52c9a77e4bf27fa1e2484d1a Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 16 Jan 2019 13:01:38 +0400 Subject: [PATCH] Optimize getPeerDialog requests. --- Telegram/SourceFiles/apiwrap.cpp | 222 +++++++++--------- Telegram/SourceFiles/apiwrap.h | 11 +- .../dialogs/dialogs_inner_widget.cpp | 19 +- .../SourceFiles/dialogs/dialogs_widget.cpp | 49 ++-- Telegram/SourceFiles/history/history.cpp | 13 +- .../feed/info_feed_channels_controllers.cpp | 2 +- Telegram/SourceFiles/mainwidget.cpp | 47 ++-- Telegram/SourceFiles/mainwidget.h | 4 - 8 files changed, 174 insertions(+), 193 deletions(-) diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index d5f264327c..7ad12c2354 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -257,26 +257,23 @@ void ApiWrap::getProxyPromotionDelayed(TimeId now, TimeId next) { }; void ApiWrap::proxyPromotionDone(const MTPhelp_ProxyData &proxy) { - if (proxy.type() == mtpc_help_proxyDataEmpty) { - const auto &data = proxy.c_help_proxyDataEmpty(); - const auto next = _proxyPromotionNextRequestTime = data.vexpires.v; - getProxyPromotionDelayed(unixtime(), next); - _session->data().setProxyPromoted(nullptr); - return; - } - Assert(proxy.type() == mtpc_help_proxyDataPromo); - const auto &data = proxy.c_help_proxyDataPromo(); - const auto next = _proxyPromotionNextRequestTime = data.vexpires.v; - getProxyPromotionDelayed(unixtime(), next); + _proxyPromotionNextRequestTime = proxy.match([&](const auto &data) { + return data.vexpires.v; + }); + getProxyPromotionDelayed(unixtime(), _proxyPromotionNextRequestTime); - App::feedChats(data.vchats); - App::feedUsers(data.vusers); - const auto peerId = peerFromMTP(data.vpeer); - const auto peer = App::peer(peerId); - _session->data().setProxyPromoted(peer); - if (const auto history = App::historyLoaded(peer)) { - requestDialogEntry(history); - } + proxy.match([&](const MTPDhelp_proxyDataEmpty &data) { + _session->data().setProxyPromoted(nullptr); + }, [&](const MTPDhelp_proxyDataPromo &data) { + App::feedChats(data.vchats); + App::feedUsers(data.vusers); + const auto peerId = peerFromMTP(data.vpeer); + const auto peer = _session->data().peer(peerId); + _session->data().setProxyPromoted(peer); + if (const auto history = App::historyLoaded(peer)) { + requestDialogEntry(history); + } + }); } void ApiWrap::requestDeepLinkInfo( @@ -687,69 +684,78 @@ void ApiWrap::requestDialogEntry(not_null feed) { void ApiWrap::requestDialogEntry( not_null history, Fn callback) { - const auto[i, ok] = _dialogRequests.try_emplace(history); + const auto i = _dialogRequests.find(history); + if (i != end(_dialogRequests)) { + if (callback) { + i->second.push_back(std::move(callback)); + } + return; + } + + const auto [j, ok] = _dialogRequestsPending.try_emplace(history); if (callback) { - i->second.push_back(std::move(callback)); + j->second.push_back(std::move(callback)); } if (!ok) { return; } + if (_dialogRequestsPending.size() > 1) { + return; + } + Core::App().postponeCall(crl::guard(_session, [=] { + sendDialogRequests(); + })); +} + +void ApiWrap::sendDialogRequests() { + if (_dialogRequestsPending.empty()) { + return; + } + auto histories = std::vector>(); + ranges::transform( + _dialogRequestsPending, + ranges::back_inserter(histories), + [](const auto &pair) { return pair.first; }); + auto peers = QVector(); + const auto dialogPeer = [](not_null history) { + return MTP_inputDialogPeer(history->peer->input); + }; + ranges::transform( + histories, + ranges::back_inserter(peers), + dialogPeer); + for (auto &[history, callbacks] : base::take(_dialogRequestsPending)) { + _dialogRequests.emplace(history, std::move(callbacks)); + } + const auto finalize = [=] { - if (const auto callbacks = _dialogRequests.take(history)) { - for (const auto &callback : *callbacks) { - callback(); - } + for (const auto history : histories) { + dialogEntryApplied(history); + history->updateChatListExistence(); } }; - auto peers = QVector( - 1, - MTP_inputDialogPeer(history->peer->input)); request(MTPmessages_GetPeerDialogs( MTP_vector(std::move(peers)) )).done([=](const MTPmessages_PeerDialogs &result) { applyPeerDialogs(result); - history->dialogEntryApplied(); finalize(); }).fail([=](const RPCError &error) { finalize(); }).send(); } -void ApiWrap::requestDialogEntries( - std::vector> histories) { - const auto already = [&](not_null history) { - const auto [i, ok] = _dialogRequests.try_emplace(history); - return !ok; - }; - histories.erase(ranges::remove_if(histories, already), end(histories)); - if (histories.empty()) { - return; - } - auto peers = QVector(); - peers.reserve(histories.size()); - for (const auto history : histories) { - peers.push_back(MTP_inputDialogPeer(history->peer->input)); - } - const auto finalize = [=](std::vector> histories) { - for (const auto history : histories) { - if (const auto callbacks = _dialogRequests.take(history)) { - for (const auto &callback : *callbacks) { - callback(); - } - } +void ApiWrap::dialogEntryApplied(not_null history) { + history->dialogEntryApplied(); + if (const auto callbacks = _dialogRequestsPending.take(history)) { + for (const auto &callback : *callbacks) { + callback(); } - }; - request(MTPmessages_GetPeerDialogs( - MTP_vector(std::move(peers)) - )).done([=](const MTPmessages_PeerDialogs &result) { - applyPeerDialogs(result); - for (const auto history : histories) { - history->dialogEntryApplied(); + } + if (const auto callbacks = _dialogRequests.take(history)) { + for (const auto &callback : *callbacks) { + callback(); } - finalize(histories); - }).fail([=](const RPCError &error) { - finalize(histories); - }).send(); + } } void ApiWrap::applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs) { @@ -1490,7 +1496,7 @@ void ApiWrap::applyLastParticipantsList( Notify::peerUpdatedDelayed(update); channel->mgInfo->botStatus = botStatus; - if (App::main()) fullPeerUpdated().notify(channel); + fullPeerUpdated().notify(channel); } void ApiWrap::applyBotsList( @@ -1532,7 +1538,7 @@ void ApiWrap::applyBotsList( } channel->mgInfo->botStatus = botStatus; - if (App::main()) fullPeerUpdated().notify(channel); + fullPeerUpdated().notify(channel); } void ApiWrap::applyAdminsList( @@ -1568,58 +1574,58 @@ void ApiWrap::applyAdminsList( } } -void ApiWrap::requestSelfParticipant(ChannelData *channel) { +void ApiWrap::requestSelfParticipant(not_null channel) { if (_selfParticipantRequests.contains(channel)) { return; } - auto requestId = request(MTPchannels_GetParticipant( + const auto finalize = [=](UserId inviter, TimeId inviteDate) { + channel->inviter = inviter; + channel->inviteDate = inviteDate; + if (const auto history = App::historyLoaded(channel)) { + if (history->lastMessageKnown()) { + history->checkJoinedMessage(true); + history->owner().sendHistoryChangeNotifications(); + } else { + requestDialogEntry(history); + } + } + }; + _selfParticipantRequests.emplace(channel); + request(MTPchannels_GetParticipant( channel->inputChannel, MTP_inputUserSelf() - )).done([this, channel](const MTPchannels_ChannelParticipant &result) { - _selfParticipantRequests.remove(channel); - if (result.type() != mtpc_channels_channelParticipant) { - LOG(("API Error: unknown type in gotSelfParticipant (%1)").arg(result.type())); - channel->inviter = -1; - if (App::main()) App::main()->onSelfParticipantUpdated(channel); - return; - } + )).done([=](const MTPchannels_ChannelParticipant &result) { + _selfParticipantRequests.erase(channel); + result.match([&](const MTPDchannels_channelParticipant &data) { + App::feedUsers(data.vusers); - auto &p = result.c_channels_channelParticipant(); - App::feedUsers(p.vusers); - - switch (p.vparticipant.type()) { - case mtpc_channelParticipantSelf: { - auto &d = p.vparticipant.c_channelParticipantSelf(); - channel->inviter = d.vinviter_id.v; - channel->inviteDate = d.vdate.v; - } break; - case mtpc_channelParticipantCreator: { - auto &d = p.vparticipant.c_channelParticipantCreator(); - channel->inviter = _session->userId(); - channel->inviteDate = channel->date; - if (channel->mgInfo) { - channel->mgInfo->creator = _session->user(); - } - } break; - case mtpc_channelParticipantAdmin: { - auto &d = p.vparticipant.c_channelParticipantAdmin(); - channel->inviter = (d.is_self() && d.has_inviter_id()) - ? d.vinviter_id.v - : 0; - channel->inviteDate = d.vdate.v; - } break; - } - - if (App::main()) App::main()->onSelfParticipantUpdated(channel); - }).fail([this, channel](const RPCError &error) { - _selfParticipantRequests.remove(channel); - if (error.type() == qstr("USER_NOT_PARTICIPANT")) { - channel->inviter = -1; - } + const auto &participant = data.vparticipant; + participant.match([&](const MTPDchannelParticipantSelf &data) { + finalize(data.vinviter_id.v, data.vdate.v); + }, [&](const MTPDchannelParticipantCreator &) { + if (channel->mgInfo) { + channel->mgInfo->creator = _session->user(); + } + finalize(_session->userId(), channel->date); + }, [&](const MTPDchannelParticipantAdmin &data) { + const auto inviter = (data.is_self() + && data.has_inviter_id()) + ? data.vinviter_id.v + : -1; + finalize(inviter, data.vdate.v); + }, [&](const MTPDchannelParticipantBanned &data) { + LOG(("API Error: Got self banned participant.")); + finalize(-1, 0); + }, [&](const MTPDchannelParticipant &data) { + LOG(("API Error: Got self regular participant.")); + finalize(-1, 0); + }); + }); + }).fail([=](const RPCError &error) { + _selfParticipantRequests.erase(channel); + finalize(-1, 0); }).afterDelay(kSmallDelayMs).send(); - - _selfParticipantRequests.insert(channel, requestId); } void ApiWrap::kickParticipant( diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 7342b8d97a..690bc46ad4 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -83,7 +83,7 @@ public: void requestDialogEntry( not_null history, Fn callback = nullptr); - void requestDialogEntries(std::vector> histories); + void dialogEntryApplied(not_null history); //void applyFeedSources(const MTPDchannels_feedSources &data); // #feed //void setFeedChannels( // not_null feed, @@ -146,7 +146,7 @@ public: void markMediaRead(const base::flat_set> &items); void markMediaRead(not_null item); - void requestSelfParticipant(ChannelData *channel); + void requestSelfParticipant(not_null channel); void kickParticipant(not_null chat, not_null user); void kickParticipant( not_null channel, @@ -592,6 +592,8 @@ private: not_null channel); void migrateFail(not_null peer, const RPCError &error); + void sendDialogRequests(); + not_null _session; MessageDataRequests _messageDataRequests; @@ -624,7 +626,7 @@ private: not_null, mtpRequestId> _defaultRestrictionsRequests; - QMap _selfParticipantRequests; + base::flat_set> _selfParticipantRequests; base::flat_map< not_null, @@ -666,6 +668,9 @@ private: base::flat_map< not_null, std::vector>> _dialogRequests; + base::flat_map< + not_null, + std::vector>> _dialogRequestsPending; base::flat_set> _fakeChatListRequests; base::flat_map, mtpRequestId> _unreadMentionsRequests; diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 76095b323b..7fcd01ec86 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -1298,14 +1298,14 @@ void DialogsInner::createDialog(Dialogs::Key key) { const auto from = dialogsOffset() + changed.movedFrom * st::dialogsRowHeight; const auto to = dialogsOffset() + changed.movedTo * st::dialogsRowHeight; - if (!_dragging) { + if (!_dragging && from != to) { // Don't jump in chats list scroll position while dragging. emit dialogMoved(from, to); } if (creating) { refresh(); - } else if (_state == State::Default && changed.movedFrom != changed.movedTo) { + } else if (_state == State::Default && from != to) { update(0, qMin(from, to), getFullWidth(), qAbs(from - to) + st::dialogsRowHeight); } } @@ -1948,7 +1948,6 @@ bool DialogsInner::searchReceived( auto isGlobalSearch = (type == DialogsSearchFromStart || type == DialogsSearchFromOffset); auto isMigratedSearch = (type == DialogsSearchMigratedFromStart || type == DialogsSearchMigratedFromOffset); - auto unknownUnreadCounts = std::vector>(); TimeId lastDateFound = 0; for (const auto &message : messages) { auto msgId = IdFromMessage(message); @@ -1966,7 +1965,7 @@ bool DialogsInner::searchReceived( _searchInChat, item)); if (uniquePeers && !history->unreadCountKnown()) { - unknownUnreadCounts.push_back(history); + history->session().api().requestDialogEntry(history); } } lastDateFound = lastDate; @@ -2001,9 +2000,6 @@ bool DialogsInner::searchReceived( refresh(); - if (!unknownUnreadCounts.empty()) { - Auth().api().requestDialogEntries(std::move(unknownUnreadCounts)); - } return lastDateFound != 0; } @@ -2123,16 +2119,15 @@ void DialogsInner::notify_historyMuteUpdated(History *history) { return; } - int from = dialogsOffset() + changed.movedFrom * st::dialogsRowHeight; - int to = dialogsOffset() + changed.movedTo * st::dialogsRowHeight; - if (!_dragging) { + const auto from = dialogsOffset() + changed.movedFrom * st::dialogsRowHeight; + const auto to = dialogsOffset() + changed.movedTo * st::dialogsRowHeight; + if (!_dragging && from != to) { // Don't jump in chats list scroll position while dragging. emit dialogMoved(from, to); } - if (creating) { refresh(); - } else if (_state == State::Default && changed.movedFrom != changed.movedTo) { + } else if (_state == State::Default && from != to) { update(0, qMin(from, to), getFullWidth(), qAbs(from - to) + st::dialogsRowHeight); } } diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index b39b16ec3e..efd816da11 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -481,36 +481,29 @@ void DialogsWidget::updateDialogsOffset( auto lastDate = TimeId(0); auto lastPeer = PeerId(0); auto lastMsgId = MsgId(0); - const auto fillFromDialog = [&](const auto &dialog) { - const auto peer = peerFromMTP(dialog.vpeer); - const auto msgId = dialog.vtop_message.v; - if (!peer || !msgId) { - return; - } - if (!lastPeer) { - lastPeer = peer; - } - if (!lastMsgId) { - lastMsgId = msgId; - } - for (auto j = messages.size(); j != 0;) { - const auto &message = messages[--j]; - if (IdFromMessage(message) == msgId - && PeerFromMessage(message) == peer) { - if (const auto date = DateFromMessage(message)) { - lastDate = date; - } + for (const auto &dialog : ranges::view::reverse(dialogs)) { + dialog.match([&](const auto &dialog) { + const auto peer = peerFromMTP(dialog.vpeer); + const auto messageId = dialog.vtop_message.v; + if (!peer || !messageId) { return; } - } - }; - for (auto i = dialogs.size(); i != 0;) { - const auto &dialog = dialogs[--i]; - switch (dialog.type()) { - case mtpc_dialog: fillFromDialog(dialog.c_dialog()); break; -// case mtpc_dialogFeed: fillFromDialog(dialog.c_dialogFeed()); break; // #feed - default: Unexpected("Type in DialogsWidget::updateDialogsOffset"); - } + if (!lastPeer) { + lastPeer = peer; + } + if (!lastMsgId) { + lastMsgId = messageId; + } + for (const auto &message : ranges::view::reverse(messages)) { + if (IdFromMessage(message) == messageId + && PeerFromMessage(message) == peer) { + if (const auto date = DateFromMessage(message)) { + lastDate = date; + } + return; + } + } + }); if (lastDate) { break; } diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 3c51c2069d..07b2e8ce46 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "apiwrap.h" #include "mainwidget.h" +#include "application.h" #include "mainwindow.h" #include "storage/localstorage.h" #include "observer_peer.h" @@ -2178,7 +2179,9 @@ void History::setChatListMessageFromLast() { void History::requestChatListMessage() { if (!lastMessageKnown()) { - session().api().requestDialogEntry(this); + session().api().requestDialogEntry(this, [=] { + requestChatListMessage(); + }); return; } else if (chatListMessageKnown()) { return; @@ -2336,13 +2339,16 @@ void History::applyDialog(const MTPDdialog &data) { if (data.has_draft() && data.vdraft.type() == mtpc_draftMessage) { Data::applyPeerCloudDraft(peer->id, data.vdraft.c_draftMessage()); } + session().api().dialogEntryApplied(this); } void History::dialogEntryApplied() { if (!lastMessageKnown()) { setLastMessage(nullptr); } - if (!chatListMessageKnown()) { + if (peer->migrateTo()) { + return; + } else if (!chatListMessageKnown()) { requestChatListMessage(); return; } @@ -2371,7 +2377,7 @@ void History::dialogEntryApplied() { if (chatListTimeId() != 0 && loadedAtBottom()) { if (const auto channel = peer->asChannel()) { const auto inviter = channel->inviter; - if (inviter != 0 + if (inviter > 0 && chatListTimeId() <= channel->inviteDate && channel->amIn()) { if (const auto from = App::userLoaded(inviter)) { @@ -2380,7 +2386,6 @@ void History::dialogEntryApplied() { } } } - updateChatListExistence(); } bool History::clearUnreadOnClientSide() const { diff --git a/Telegram/SourceFiles/info/feed/info_feed_channels_controllers.cpp b/Telegram/SourceFiles/info/feed/info_feed_channels_controllers.cpp index 58566f4449..8dedec3843 100644 --- a/Telegram/SourceFiles/info/feed/info_feed_channels_controllers.cpp +++ b/Telegram/SourceFiles/info/feed/info_feed_channels_controllers.cpp @@ -306,7 +306,7 @@ void NotificationsController::applyFeedDialogs( const auto history = App::history(peerId); const auto channel = history->peer->asChannel(); history->applyDialog(dialog.c_dialog()); - channels.push_back(channel); + channels.emplace_back(channel); } else { LOG(("API Error: " "Unexpected non-channel in feed dialogs list.")); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index ea1c282637..27a059aff9 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -3305,24 +3305,6 @@ void MainWidget::openPeerByName( } } -void MainWidget::onSelfParticipantUpdated(ChannelData *channel) { - auto history = App::historyLoaded(channel->id); - if (_updatedChannels.contains(channel)) { - _updatedChannels.remove(channel); - if (!history) { - history = App::history(channel); - } - if (history->isEmpty()) { - Auth().api().requestDialogEntry(history); - } else { - history->checkJoinedMessage(true); - } - } else if (history) { - history->checkJoinedMessage(); - } - Auth().data().sendHistoryChangeNotifications(); -} - bool MainWidget::contentOverlapped(const QRect &globalRect) { return (_history->contentOverlapped(globalRect) || _playerPanel->overlaps(globalRect) @@ -4461,22 +4443,21 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { auto &d = update.c_updateChannel(); if (const auto channel = App::channelLoaded(d.vchannel_id.v)) { channel->inviter = UserId(0); - if (channel->amIn() - && !channel->amCreator() - && App::history(channel->id)) { - _updatedChannels.insert(channel, true); - Auth().api().requestSelfParticipant(channel); - } - if (const auto feed = channel->feed()) { - feed->requestChatListMessage(); - if (!feed->unreadCountKnown()) { - feed->session().api().requestDialogEntry(feed); + if (channel->amIn()) { + const auto history = App::history(channel); + if (const auto feed = channel->feed()) { + feed->requestChatListMessage(); + if (!feed->unreadCountKnown()) { + feed->session().api().requestDialogEntry(feed); + } + } else { + history->requestChatListMessage(); + if (!history->unreadCountKnown()) { + history->session().api().requestDialogEntry(history); + } } - } else if (channel->amIn()) { - const auto history = App::history(channel->id); - history->requestChatListMessage(); - if (!history->unreadCountKnown()) { - history->session().api().requestDialogEntry(history); + if (!channel->amCreator()) { + Auth().api().requestSelfParticipant(channel); } } } diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 97bb26d044..6abab911e4 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -253,7 +253,6 @@ public: void scheduleViewIncrement(HistoryItem *item); - void onSelfParticipantUpdated(ChannelData *channel); void feedChannelDifference(const MTPDupdates_channelDifference &data); // Mayde public for ApiWrap, while it is still here. @@ -537,9 +536,6 @@ private: int _cachedY = 0; SingleTimer _cacheBackgroundTimer; - typedef QMap UpdatedChannels; - UpdatedChannels _updatedChannels; - PhotoData *_deletingPhoto = nullptr; typedef QMap ViewsIncrementMap;