From 26f287fae0fdd5f9f84821be7be08d8b0564f752 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 5 Nov 2022 15:41:27 +0300 Subject: [PATCH] Added support of posts_between flag for sponsored messages. --- .../data/data_sponsored_messages.cpp | 114 +++++++++++++++++- .../data/data_sponsored_messages.h | 22 +++- Telegram/SourceFiles/history/history.cpp | 3 +- Telegram/SourceFiles/history/history.h | 3 + .../SourceFiles/history/history_message.cpp | 7 +- .../SourceFiles/history/history_message.h | 3 +- .../SourceFiles/history/history_widget.cpp | 33 ++++- Telegram/SourceFiles/history/history_widget.h | 2 + 8 files changed, 173 insertions(+), 14 deletions(-) diff --git a/Telegram/SourceFiles/data/data_sponsored_messages.cpp b/Telegram/SourceFiles/data/data_sponsored_messages.cpp index 909e9fbcd3..011ad072da 100644 --- a/Telegram/SourceFiles/data/data_sponsored_messages.cpp +++ b/Telegram/SourceFiles/data/data_sponsored_messages.cpp @@ -10,11 +10,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_text_entities.h" #include "apiwrap.h" #include "base/unixtime.h" -#include "data/data_user.h" #include "data/data_channel.h" #include "data/data_peer_id.h" #include "data/data_session.h" +#include "data/data_user.h" #include "history/history.h" +#include "history/history_message.h" +#include "history/view/history_view_element.h" #include "main/main_session.h" #include "ui/image/image_location_factory.h" @@ -61,7 +63,9 @@ bool SponsoredMessages::append(not_null history) { return false; } auto &list = it->second; - if (list.showedAll || !TooEarlyForRequest(list.received)) { + if (list.showedAll + || !TooEarlyForRequest(list.received) + || list.postsBetween) { return false; } @@ -81,11 +85,98 @@ bool SponsoredMessages::append(not_null history) { return true; } +void SponsoredMessages::inject( + not_null history, + MsgId injectAfterMsgId, + int betweenHeight, + int fallbackWidth) { + if (!canHaveFor(history)) { + return; + } + const auto it = _data.find(history); + if (it == end(_data)) { + return; + } + auto &list = it->second; + if (!list.postsBetween || (list.entries.size() == list.injectedCount)) { + return; + } + + while (true) { + const auto entryIt = ranges::find_if(list.entries, [](const auto &e) { + return e.item == nullptr; + }); + if (entryIt == end(list.entries)) { + list.showedAll = true; + return; + } + const auto lastView = (entryIt != begin(list.entries)) + ? (entryIt - 1)->item->mainView() + : (injectAfterMsgId == ShowAtUnreadMsgId) + ? history->firstUnreadMessage() + : [&] { + const auto message = history->peer->owner().message( + history->peer->id, + injectAfterMsgId); + return message ? message->mainView() : nullptr; + }(); + if (!lastView || !lastView->block()) { + return; + } + + auto summaryBetween = 0; + auto summaryHeight = 0; + + using BlockPtr = std::unique_ptr; + using ViewPtr = std::unique_ptr; + auto blockIt = ranges::find( + history->blocks, + lastView->block(), + &BlockPtr::get); + if (blockIt == end(history->blocks)) { + return; + } + const auto messages = [&]() -> const std::vector & { + return (*blockIt)->messages; + }; + auto lastViewIt = ranges::find(messages(), lastView, &ViewPtr::get); + while ((summaryBetween < list.postsBetween) + || (summaryHeight < betweenHeight)) { + lastViewIt++; + if (lastViewIt == end(messages())) { + blockIt++; + if (blockIt != end(history->blocks)) { + lastViewIt = begin(messages()); + } else { + return; + } + } + summaryBetween++; + const auto viewHeight = (*lastViewIt)->height(); + summaryHeight += viewHeight + ? viewHeight + : (*lastViewIt)->resizeGetHeight(fallbackWidth); + } + const auto makedMessage = history->makeMessage( + _session->data().nextLocalMessageId(), + entryIt->sponsored.from, + entryIt->sponsored.textWithEntities, + (*lastViewIt)->data()); + entryIt->item.reset(makedMessage.get()); + history->addNewInTheMiddle( + makedMessage.get(), + std::distance(begin(history->blocks), blockIt), + std::distance(begin(messages()), lastViewIt) + 1); + messages().back().get()->setPendingResize(); + list.injectedCount++; + } +} + bool SponsoredMessages::canHaveFor(not_null history) const { return history->peer->isChannel(); } -void SponsoredMessages::request(not_null history) { +void SponsoredMessages::request(not_null history, Fn done) { if (!canHaveFor(history)) { return; } @@ -113,6 +204,9 @@ void SponsoredMessages::request(not_null history) { channel->inputChannel) ).done([=](const MTPmessages_sponsoredMessages &result) { parse(history, result); + if (done) { + done(); + } }).fail([=] { _requests.remove(history); }).send(); @@ -138,7 +232,12 @@ void SponsoredMessages::parse( list.received = crl::now(); for (const auto &message : messages) { append(history, list, message); - break; + } + if (const auto postsBetween = data.vposts_between()) { + list.postsBetween = postsBetween->v; + list.state = State::InjectToMiddle; + } else { + list.state = State::AppendToEnd; } }, [](const MTPDmessages_sponsoredMessagesEmpty &) { }); @@ -240,6 +339,7 @@ void SponsoredMessages::clearItems(not_null history) { entry.item.reset(); } list.showedAll = false; + list.injectedCount = 0; } const SponsoredMessages::Entry *SponsoredMessages::find( @@ -301,4 +401,10 @@ SponsoredMessages::Details SponsoredMessages::lookupDetails( }; } +SponsoredMessages::State SponsoredMessages::state( + not_null history) const { + const auto it = _data.find(history); + return (it == end(_data)) ? State::None : it->second.state; +} + } // namespace Data diff --git a/Telegram/SourceFiles/data/data_sponsored_messages.h b/Telegram/SourceFiles/data/data_sponsored_messages.h index d61a06f9b9..e2a653968a 100644 --- a/Telegram/SourceFiles/data/data_sponsored_messages.h +++ b/Telegram/SourceFiles/data/data_sponsored_messages.h @@ -46,6 +46,11 @@ struct SponsoredMessage { class SponsoredMessages final { public: + enum class State { + None, + AppendToEnd, + InjectToMiddle, + }; struct Details { std::optional hash; PeerData *peer = nullptr; @@ -58,13 +63,21 @@ public: ~SponsoredMessages(); [[nodiscard]] bool canHaveFor(not_null history) const; - void request(not_null history); - [[nodiscard]] bool append(not_null history); + void request(not_null history, Fn done); void clearItems(not_null history); [[nodiscard]] Details lookupDetails(const FullMsgId &fullId) const; + [[nodiscard]] bool append(not_null history); + void inject( + not_null history, + MsgId injectAfterMsgId, + int betweenHeight, + int fallbackWidth); + void view(const FullMsgId &fullId); + [[nodiscard]] State state(not_null history) const; + private: using OwnedItem = std::unique_ptr; struct Entry { @@ -73,8 +86,13 @@ private: }; struct List { std::vector entries; + // Data between history displays. + size_t injectedCount = 0; bool showedAll = false; + // crl::time received = 0; + int postsBetween = 0; + State state = State::None; }; struct Request { mtpRequestId requestId = 0; diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 4437cac51b..5b74e069f0 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -764,7 +764,8 @@ not_null History::addNewLocalMessage( makeMessage( id, from, - textWithEntities), + textWithEntities, + nullptr), true); } diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 857dc7eeb1..6285dcbb3e 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -34,6 +34,7 @@ class Session; class Folder; class ChatFilter; struct SponsoredFrom; +class SponsoredMessages; enum class ForwardOptions { PreserveInfo, @@ -495,6 +496,8 @@ private: not_null addNewToBack( not_null item, bool unread); + + friend class Data::SponsoredMessages; not_null addNewInTheMiddle( not_null item, int blockIndex, diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index b72b713156..9a32deecf1 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -636,14 +636,17 @@ HistoryMessage::HistoryMessage( not_null history, MsgId id, Data::SponsoredFrom from, - const TextWithEntities &textWithEntities) + const TextWithEntities &textWithEntities, + HistoryItem *injectedAfter) : HistoryItem( history, id, ((history->peer->isChannel() ? MessageFlag::Post : MessageFlag(0)) //| (from.peer ? MessageFlag::HasFromId : MessageFlag(0)) | MessageFlag::Local), - HistoryItem::NewMessageDate(0), + HistoryItem::NewMessageDate(injectedAfter + ? injectedAfter->date() + : 0), /*from.peer ? from.peer->id : */PeerId(0)) { createComponentsHelper( _flags, diff --git a/Telegram/SourceFiles/history/history_message.h b/Telegram/SourceFiles/history/history_message.h index 2fd3a20958..37402441f9 100644 --- a/Telegram/SourceFiles/history/history_message.h +++ b/Telegram/SourceFiles/history/history_message.h @@ -130,7 +130,8 @@ public: not_null history, MsgId id, Data::SponsoredFrom from, - const TextWithEntities &textWithEntities); // sponsored + const TextWithEntities &textWithEntities, + HistoryItem *injectedAfter); // sponsored void refreshMedia(const MTPMessageMedia *media); void refreshSentMedia(const MTPMessageMedia *media); diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 8fe4ac6563..7af4eb5049 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -2288,9 +2288,25 @@ void HistoryWidget::showHistory( showAboutTopPromotion(); { - auto &sponsored = session().data().sponsoredMessages(); - sponsored.request(_history); - _scroll->setTrackingContent(sponsored.canHaveFor(_history)); + _scroll->setTrackingContent(false); + const auto checkState = crl::guard(this, [=] { + auto &sponsored = session().data().sponsoredMessages(); + using State = Data::SponsoredMessages::State; + const auto state = sponsored.state(_history); + if (state == State::AppendToEnd) { + _scroll->setTrackingContent( + sponsored.canHaveFor(_history)); + } else if (state == State::InjectToMiddle) { + if (_list) { + _list->setCanHaveFromUserpicsSponsored(true); + } + injectSponsoredMessages(); + } + }); + session().data().sponsoredMessages().request( + _history, + checkState); + checkState(); } } else { _chooseForReport = nullptr; @@ -2354,6 +2370,14 @@ void HistoryWidget::setHistory(History *history) { refreshAttachBotsMenu(); } +void HistoryWidget::injectSponsoredMessages() const { + session().data().sponsoredMessages().inject( + _history, + _showAtMsgId, + _scroll->height() * 2, + _scroll->width()); +} + void HistoryWidget::refreshAttachBotsMenu() { _attachBotsMenu = nullptr; if (!_history) { @@ -3268,7 +3292,7 @@ void HistoryWidget::loadMessagesDown() { auto loadMigrated = _migrated && !(_migrated->isEmpty() || _migrated->loadedAtBottom() || (!_history->isEmpty() && !_history->loadedAtTop())); auto from = loadMigrated ? _migrated : _history; if (from->loadedAtBottom()) { - session().data().sponsoredMessages().request(_history); + session().data().sponsoredMessages().request(_history, nullptr); return; } @@ -5659,6 +5683,7 @@ void HistoryWidget::addMessagesToBack( if (!_firstLoadRequest) { updateHistoryGeometry(false, true, { ScrollChangeNoJumpToBottom, 0 }); } + injectSponsoredMessages(); } void HistoryWidget::updateBotKeyboard(History *h, bool force) { diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index ce23a06b06..7939f91432 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -604,6 +604,8 @@ private: void refreshSendAsToggle(); void refreshAttachBotsMenu(); + void injectSponsoredMessages() const; + bool kbWasHidden() const; void searchInChat();