diff --git a/Telegram/SourceFiles/api/api_views.cpp b/Telegram/SourceFiles/api/api_views.cpp index dda9d7e695..6c8ec8df7f 100644 --- a/Telegram/SourceFiles/api/api_views.cpp +++ b/Telegram/SourceFiles/api/api_views.cpp @@ -20,13 +20,16 @@ namespace { // Send channel views each second. constexpr auto kSendViewsTimeout = crl::time(1000); +constexpr auto kPollExtendedMediaPeriod = 30 * crl::time(1000); +constexpr auto kMaxPollPerRequest = 100; } // namespace ViewsManager::ViewsManager(not_null api) : _session(&api->session()) , _api(&api->instance()) -, _incrementTimer([=] { viewsIncrement(); }) { +, _incrementTimer([=] { viewsIncrement(); }) +, _pollTimer([=] { sendPollRequests(); }) { } void ViewsManager::scheduleIncrement(not_null item) { @@ -52,6 +55,25 @@ void ViewsManager::removeIncremented(not_null peer) { _incremented.remove(peer); } +void ViewsManager::pollExtendedMedia(not_null item) { + if (!item->isRegular()) { + return; + } + const auto id = item->id; + const auto peer = item->history()->peer; + auto &request = _pollRequests[peer]; + if (request.ids.contains(id) || request.sent.contains(id)) { + return; + } + request.ids.emplace(id); + if (!request.id && !request.when) { + request.when = crl::now() + kPollExtendedMediaPeriod; + } + if (!_pollTimer.isActive()) { + _pollTimer.callOnce(kPollExtendedMediaPeriod); + } +} + void ViewsManager::viewsIncrement() { for (auto i = _toIncrement.begin(); i != _toIncrement.cend();) { if (_incrementRequests.contains(i->first)) { @@ -81,6 +103,88 @@ void ViewsManager::viewsIncrement() { } } +void ViewsManager::sendPollRequests() { + const auto now = crl::now(); + auto toRequest = base::flat_map, QVector>(); + auto nearest = crl::time(); + for (auto &[peer, request] : _pollRequests) { + if (request.id) { + continue; + } else if (request.when <= now) { + Assert(request.sent.empty()); + auto &list = toRequest[peer]; + const auto count = int(request.ids.size()); + if (count < kMaxPollPerRequest) { + request.sent = base::take(request.ids); + } else { + const auto from = begin(request.ids); + const auto end = from + kMaxPollPerRequest; + request.sent = { from, end }; + request.ids.erase(from, end); + } + list.reserve(request.sent.size()); + for (const auto &id : request.sent) { + list.push_back(MTP_int(id.bare)); + } + if (!request.ids.empty()) { + nearest = now; + } + } else if (!nearest || nearest > request.when) { + nearest = request.when; + } + } + sendPollRequests(toRequest); + if (nearest) { + _pollTimer.callOnce(std::max(nearest - now, crl::time(1))); + } +} + +void ViewsManager::sendPollRequests( + const base::flat_map< + not_null, + QVector> &batched) { + for (auto &[peer, list] : batched) { + const auto finish = [=, list = list](mtpRequestId id) { + const auto now = crl::now(); + const auto owner = &_session->data(); + for (auto i = begin(_pollRequests); i != end(_pollRequests);) { + if (i->second.id == id) { + const auto peer = i->first->id; + for (const auto &itemId : i->second.sent) { + if (const auto item = owner->message(peer, itemId)) { + owner->requestItemRepaint(item); + } + } + i->second.sent.clear(); + i->second.id = 0; + if (i->second.ids.empty()) { + i = _pollRequests.erase(i); + } else { + i->second.when = now + kPollExtendedMediaPeriod; + if (!_pollTimer.isActive()) { + _pollTimer.callOnce(kPollExtendedMediaPeriod); + } + ++i; + } + } else { + ++i; + } + } + }; + const auto requestId = _api.request(MTPmessages_GetExtendedMedia( + peer->input, + MTP_vector(list) + )).done([=](const MTPUpdates &result, mtpRequestId id) { + _session->api().applyUpdates(result); + finish(id); + }).fail([=](const MTP::Error &error, mtpRequestId id) { + finish(id); + }).send(); + + _pollRequests[peer].id = requestId; + } +} + void ViewsManager::done( QVector ids, const MTPmessages_MessageViews &result, diff --git a/Telegram/SourceFiles/api/api_views.h b/Telegram/SourceFiles/api/api_views.h index d63b60f8c5..759d3c6a8a 100644 --- a/Telegram/SourceFiles/api/api_views.h +++ b/Telegram/SourceFiles/api/api_views.h @@ -26,8 +26,22 @@ public: void scheduleIncrement(not_null item); void removeIncremented(not_null peer); + void pollExtendedMedia(not_null item); + private: + struct PollExtendedMediaRequest { + crl::time when = 0; + mtpRequestId id = 0; + base::flat_set ids; + base::flat_set sent; + }; + void viewsIncrement(); + void sendPollRequests(); + void sendPollRequests( + const base::flat_map< + not_null, + QVector> &prepared); void done( QVector ids, @@ -44,6 +58,11 @@ private: base::flat_map> _incrementByRequest; base::Timer _incrementTimer; + base::flat_map< + not_null, + PollExtendedMediaRequest> _pollRequests; + base::Timer _pollTimer; + }; } // namespace Api diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index eb3c88cf0b..1c9b7ade8e 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -1611,7 +1611,7 @@ std::unique_ptr MediaInvoice::createView( message, realParent, replacing); - } else if (!_invoice.extendedPreview.dimensions.isEmpty()) { + } else if (_invoice.extendedPreview) { return std::make_unique( message, &_invoice); diff --git a/Telegram/SourceFiles/data/data_media_types.h b/Telegram/SourceFiles/data/data_media_types.h index 7d6e02cb8e..b9c240c3d9 100644 --- a/Telegram/SourceFiles/data/data_media_types.h +++ b/Telegram/SourceFiles/data/data_media_types.h @@ -63,6 +63,13 @@ struct ExtendedPreview { QByteArray inlineThumbnailBytes; QSize dimensions; TimeId videoDuration = -1; + + [[nodiscard]] bool empty() const { + return dimensions.isEmpty(); + } + explicit operator bool() const { + return !empty(); + } }; class Media; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 409b8cba0a..298810a369 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -997,6 +997,9 @@ void HistoryInner::paintEvent(QPaintEvent *e) { } } session().data().reactions().poll(item, now); + if (item->hasExtendedMediaPreview()) { + session().api().views().pollExtendedMedia(item); + } _reactionsManager->recordCurrentReactionEffect( item->fullId(), QPoint(0, top)); diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 0c2df7c8f7..297186e506 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -1169,6 +1169,15 @@ bool HistoryItem::isRegular() const { return isHistoryEntry() && !isLocal(); } +bool HistoryItem::hasExtendedMediaPreview() const { + if (const auto media = _media.get()) { + if (const auto invoice = media->invoice()) { + return (invoice->extendedPreview && !invoice->extendedMedia); + } + } + return false; +} + void HistoryItem::sendFailed() { Expects(_flags & MessageFlag::BeingSent); Expects(!(_flags & MessageFlag::SendingFailed)); diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index ced1e9c87a..57d76bb108 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -239,6 +239,7 @@ public: [[nodiscard]] virtual bool externalReply() const { return false; } + [[nodiscard]] bool hasExtendedMediaPreview() const; [[nodiscard]] virtual MsgId repliesInboxReadTill() const { return MsgId(0); diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp index 4d51f87b1a..fbf214956e 100644 --- a/Telegram/SourceFiles/history/history_item_components.cpp +++ b/Telegram/SourceFiles/history/history_item_components.cpp @@ -917,7 +917,7 @@ void HistoryMessageReplyMarkup::updateData( bool HistoryMessageReplyMarkup::hiddenBy(Data::Media *media) const { if (media && (data.flags & ReplyMarkupFlag::OnlyBuyButton)) { if (const auto invoice = media->invoice()) { - if (!invoice->extendedPreview.dimensions.isEmpty() + if (invoice->extendedPreview && (!invoice->extendedMedia || !invoice->receiptMsgId)) { return true; }