diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index ec4ea2a6fe..bed1be00e6 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -139,6 +139,8 @@ PRIVATE api/api_updates.h api/api_user_privacy.cpp api/api_user_privacy.h + api/api_views.cpp + api/api_views.h api/api_who_read.cpp api/api_who_read.h boxes/filters/edit_filter_box.cpp diff --git a/Telegram/SourceFiles/api/api_views.cpp b/Telegram/SourceFiles/api/api_views.cpp new file mode 100644 index 0000000000..def381d805 --- /dev/null +++ b/Telegram/SourceFiles/api/api_views.cpp @@ -0,0 +1,134 @@ +/* +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 "api/api_views.h" + +#include "apiwrap.h" +#include "data/data_peer.h" +#include "data/data_peer_id.h" +#include "data/data_session.h" +#include "history/history.h" +#include "history/history_item.h" +#include "main/main_session.h" + +namespace Api { +namespace { + +// Send channel views each second. +constexpr auto kSendViewsTimeout = crl::time(1000); + +} // namespace + +ViewsManager::ViewsManager(not_null api) +: _session(&api->session()) +, _api(&api->instance()) { +} + +void ViewsManager::scheduleIncrement(not_null item) { + auto peer = item->history()->peer; + auto i = _incremented.find(peer); + if (i != _incremented.cend()) { + if (i->second.contains(item->id)) { + return; + } + } else { + i = _incremented.emplace(peer).first; + } + i->second.emplace(item->id); + auto j = _toIncrement.find(peer); + if (j == _toIncrement.cend()) { + j = _toIncrement.emplace(peer).first; + _incrementTimer.callOnce(kSendViewsTimeout); + } + j->second.emplace(item->id); +} + +void ViewsManager::removeIncremented(not_null peer) { + _incremented.remove(peer); +} + +void ViewsManager::viewsIncrement() { + for (auto i = _toIncrement.begin(); i != _toIncrement.cend();) { + if (_incrementRequests.contains(i->first)) { + ++i; + continue; + } + + QVector ids; + ids.reserve(i->second.size()); + for (const auto msgId : i->second) { + ids.push_back(MTP_int(msgId)); + } + const auto requestId = _api.request(MTPmessages_GetMessagesViews( + i->first->input, + MTP_vector(ids), + MTP_bool(true) + )).done([=]( + const MTPmessages_MessageViews &result, + mtpRequestId requestId) { + done(ids, result, requestId); + }).fail([=](const MTP::Error &error, mtpRequestId requestId) { + fail(error, requestId); + }).afterDelay(5).send(); + + _incrementRequests.emplace(i->first, requestId); + i = _toIncrement.erase(i); + } +} + +void ViewsManager::done( + QVector ids, + const MTPmessages_MessageViews &result, + mtpRequestId requestId) { + const auto &data = result.c_messages_messageViews(); + auto &owner = _session->data(); + owner.processUsers(data.vusers()); + owner.processChats(data.vchats()); + auto &v = data.vviews().v; + if (ids.size() == v.size()) { + for (const auto &[peer, id] : _incrementRequests) { + if (id != requestId) { + continue; + } + const auto channel = peerToChannel(peer->id); + for (auto j = 0, l = int(ids.size()); j < l; ++j) { + if (const auto item = owner.message(channel, ids[j].v)) { + v[j].match([&](const MTPDmessageViews &data) { + if (const auto views = data.vviews()) { + item->setViewsCount(views->v); + } + if (const auto forwards = data.vforwards()) { + item->setForwardsCount(forwards->v); + } + if (const auto replies = data.vreplies()) { + item->setReplies(*replies); + } + }); + } + } + _incrementRequests.erase(peer); + break; + } + } + if (!_toIncrement.empty() && !_incrementTimer.isActive()) { + _incrementTimer.callOnce(kSendViewsTimeout); + } +} + +void ViewsManager::fail(const MTP::Error &error, mtpRequestId requestId) { + for (const auto &[peer, id] : _incrementRequests) { + if (id == requestId) { + _incrementRequests.erase(peer); + break; + } + } + if (!_toIncrement.empty() && !_incrementTimer.isActive()) { + _incrementTimer.callOnce(kSendViewsTimeout); + } +} + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_views.h b/Telegram/SourceFiles/api/api_views.h new file mode 100644 index 0000000000..d63b60f8c5 --- /dev/null +++ b/Telegram/SourceFiles/api/api_views.h @@ -0,0 +1,49 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "mtproto/sender.h" +#include "base/timer.h" + +class ApiWrap; +class PeerData; + +namespace Main { +class Session; +} // namespace Main + +namespace Api { + +class ViewsManager final { +public: + explicit ViewsManager(not_null api); + + void scheduleIncrement(not_null item); + void removeIncremented(not_null peer); + +private: + void viewsIncrement(); + + void done( + QVector ids, + const MTPmessages_MessageViews &result, + mtpRequestId requestId); + void fail(const MTP::Error &error, mtpRequestId requestId); + + const not_null _session; + MTP::Sender _api; + + base::flat_map, base::flat_set> _incremented; + base::flat_map, base::flat_set> _toIncrement; + base::flat_map, mtpRequestId> _incrementRequests; + base::flat_map> _incrementByRequest; + base::Timer _incrementTimer; + +}; + +} // namespace Api diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 0f9f034b5b..d892918ccf 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_global_privacy.h" #include "api/api_updates.h" #include "api/api_user_privacy.h" +#include "api/api_views.h" #include "data/stickers/data_stickers.h" #include "data/data_drafts.h" #include "data/data_changes.h" @@ -141,7 +142,8 @@ ApiWrap::ApiWrap(not_null session) , _sensitiveContent(std::make_unique(this)) , _globalPrivacy(std::make_unique(this)) , _userPrivacy(std::make_unique(this)) -, _inviteLinks(std::make_unique(this)) { +, _inviteLinks(std::make_unique(this)) +, _views(std::make_unique(this)) { crl::on_main(session, [=] { // You can't use _session->lifetime() in the constructor, // only queued, because it is not constructed yet. @@ -4733,6 +4735,10 @@ Api::InviteLinks &ApiWrap::inviteLinks() { return *_inviteLinks; } +Api::ViewsManager &ApiWrap::views() { + return *_views; +} + void ApiWrap::createPoll( const PollData &data, const SendAction &action, diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 9c9aa45803..afbfa4f458 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -62,6 +62,7 @@ class SensitiveContent; class GlobalPrivacy; class UserPrivacy; class InviteLinks; +class ViewsManager; namespace details { @@ -400,6 +401,7 @@ public: [[nodiscard]] Api::GlobalPrivacy &globalPrivacy(); [[nodiscard]] Api::UserPrivacy &userPrivacy(); [[nodiscard]] Api::InviteLinks &inviteLinks(); + [[nodiscard]] Api::ViewsManager &views(); void createPoll( const PollData &data, @@ -720,6 +722,7 @@ private: const std::unique_ptr _globalPrivacy; const std::unique_ptr _userPrivacy; const std::unique_ptr _inviteLinks; + const std::unique_ptr _views; base::flat_map _pollVotesRequestIds; base::flat_map _pollCloseRequestIds; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index bcf22db8ab..2736db6f01 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -58,6 +58,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_attached_stickers.h" #include "api/api_toggling_media.h" #include "api/api_who_read.h" +#include "api/api_views.h" #include "lang/lang_keys.h" #include "data/data_session.h" #include "data/data_media_types.h" @@ -677,7 +678,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { view->draw(p, context); if (item->hasViews()) { - _controller->content()->scheduleViewIncrement(item); + session().api().views().scheduleIncrement(item); } if (item->isUnreadMention() && !item->isUnreadMedia()) { readMentions.insert(item); @@ -737,7 +738,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { if (_visibleAreaBottom >= middle && _visibleAreaTop <= middle) { if (item->hasViews()) { - _controller->content()->scheduleViewIncrement(item); + session().api().views().scheduleIncrement(item); } if (item->isUnreadMention() && !item->isUnreadMedia()) { readMentions.insert(item); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index bb1e5d76fe..e241ba8fc8 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwidget.h" #include "api/api_updates.h" +#include "api/api_views.h" #include "data/data_photo.h" #include "data/data_document.h" #include "data/data_document_media.h" @@ -116,13 +117,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include -namespace { - -// Send channel views each second. -constexpr auto kSendViewsTimeout = crl::time(1000); - -} // namespace - enum StackItemType { HistoryStackItem, SectionStackItem, @@ -235,7 +229,6 @@ MainWidget::MainWidget( , _dialogs(this, _controller) , _history(this, _controller) , _playerPlaylist(this, _controller) -, _viewsIncrementTimer([=] { viewsIncrement(); }) , _changelogs(Core::Changelogs::Create(&controller->session())) { setupConnectingWidget(); @@ -1221,100 +1214,6 @@ void MainWidget::setInnerFocus() { } } -void MainWidget::scheduleViewIncrement(HistoryItem *item) { - PeerData *peer = item->history()->peer; - auto i = _viewsIncremented.find(peer); - if (i != _viewsIncremented.cend()) { - if (i->second.contains(item->id)) return; - } else { - i = _viewsIncremented.emplace(peer).first; - } - i->second.emplace(item->id); - auto j = _viewsToIncrement.find(peer); - if (j == _viewsToIncrement.cend()) { - j = _viewsToIncrement.emplace(peer).first; - _viewsIncrementTimer.callOnce(kSendViewsTimeout); - } - j->second.emplace(item->id); -} - -void MainWidget::viewsIncrement() { - for (auto i = _viewsToIncrement.begin(); i != _viewsToIncrement.cend();) { - if (_viewsIncrementRequests.contains(i->first)) { - ++i; - continue; - } - - QVector ids; - ids.reserve(i->second.size()); - for (const auto msgId : i->second) { - ids.push_back(MTP_int(msgId)); - } - const auto requestId = _api.request(MTPmessages_GetMessagesViews( - i->first->input, - MTP_vector(ids), - MTP_bool(true) - )).done([=](const MTPmessages_MessageViews &result, mtpRequestId requestId) { - viewsIncrementDone(ids, result, requestId); - }).fail([=](const MTP::Error &error, mtpRequestId requestId) { - viewsIncrementFail(error, requestId); - }).afterDelay(5).send(); - - _viewsIncrementRequests.emplace(i->first, requestId); - i = _viewsToIncrement.erase(i); - } -} - -void MainWidget::viewsIncrementDone( - QVector ids, - const MTPmessages_MessageViews &result, - mtpRequestId requestId) { - const auto &data = result.c_messages_messageViews(); - session().data().processUsers(data.vusers()); - session().data().processChats(data.vchats()); - auto &v = data.vviews().v; - if (ids.size() == v.size()) { - for (auto i = _viewsIncrementRequests.begin(); i != _viewsIncrementRequests.cend(); ++i) { - if (i->second == requestId) { - const auto peer = i->first; - const auto channel = peerToChannel(peer->id); - for (int32 j = 0, l = ids.size(); j < l; ++j) { - if (const auto item = session().data().message(channel, ids[j].v)) { - v[j].match([&](const MTPDmessageViews &data) { - if (const auto views = data.vviews()) { - item->setViewsCount(views->v); - } - if (const auto forwards = data.vforwards()) { - item->setForwardsCount(forwards->v); - } - if (const auto replies = data.vreplies()) { - item->setReplies(*replies); - } - }); - } - } - _viewsIncrementRequests.erase(i); - break; - } - } - } - if (!_viewsToIncrement.empty() && !_viewsIncrementTimer.isActive()) { - _viewsIncrementTimer.callOnce(kSendViewsTimeout); - } -} - -void MainWidget::viewsIncrementFail(const MTP::Error &error, mtpRequestId requestId) { - for (auto i = _viewsIncrementRequests.begin(); i != _viewsIncrementRequests.cend(); ++i) { - if (i->second == requestId) { - _viewsIncrementRequests.erase(i); - break; - } - } - if (!_viewsToIncrement.empty() && !_viewsIncrementTimer.isActive()) { - _viewsIncrementTimer.callOnce(kSendViewsTimeout); - } -} - void MainWidget::choosePeer(PeerId peerId, MsgId showAtMsgId) { if (selectingPeer()) { _hider->offerPeer(peerId); @@ -1505,7 +1404,7 @@ void MainWidget::ui_showPeerHistory( } else { const auto nowActivePeer = _controller->activeChatCurrent().peer(); if (nowActivePeer && nowActivePeer != wasActivePeer) { - _viewsIncremented.remove(nowActivePeer); + session().api().views().removeIncremented(nowActivePeer); } if (isOneColumn() && !_dialogs->isHidden()) { _dialogs->hide(); diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 2aa0e42efe..458e289871 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "base/timer.h" #include "base/weak_ptr.h" #include "chat_helpers/bot_command.h" #include "ui/rp_widget.h" @@ -201,8 +200,6 @@ public: void ctrlEnterSubmitUpdated(); void setInnerFocus(); - void scheduleViewIncrement(HistoryItem *item); - bool contentOverlapped(const QRect &globalRect); void searchInChat(Dialogs::Key chat); @@ -245,8 +242,6 @@ protected: bool eventFilter(QObject *o, QEvent *e) override; private: - void viewsIncrement(); - void animationCallback(); void handleAdaptiveLayoutUpdate(); void updateWindowAdaptiveLayout(); @@ -313,12 +308,6 @@ private: void floatPlayerDoubleClickEvent( not_null item) override; - void viewsIncrementDone( - QVector ids, - const MTPmessages_MessageViews &result, - mtpRequestId requestId); - void viewsIncrementFail(const MTP::Error &error, mtpRequestId requestId); - void refreshResizeAreas(); template void createResizeArea( @@ -389,12 +378,6 @@ private: PhotoData *_deletingPhoto = nullptr; - base::flat_map, base::flat_set> _viewsIncremented; - base::flat_map, base::flat_set> _viewsToIncrement; - base::flat_map, mtpRequestId> _viewsIncrementRequests; - base::flat_map> _viewsIncrementByRequest; - base::Timer _viewsIncrementTimer; - struct SettingBackground; std::unique_ptr _background;