Added support of posts_between flag for sponsored messages.

This commit is contained in:
23rd 2022-11-05 15:41:27 +03:00
parent fb33951c94
commit 26f287fae0
8 changed files with 173 additions and 14 deletions

View File

@ -10,11 +10,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_text_entities.h" #include "api/api_text_entities.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "base/unixtime.h" #include "base/unixtime.h"
#include "data/data_user.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_peer_id.h" #include "data/data_peer_id.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_user.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_message.h"
#include "history/view/history_view_element.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "ui/image/image_location_factory.h" #include "ui/image/image_location_factory.h"
@ -61,7 +63,9 @@ bool SponsoredMessages::append(not_null<History*> history) {
return false; return false;
} }
auto &list = it->second; auto &list = it->second;
if (list.showedAll || !TooEarlyForRequest(list.received)) { if (list.showedAll
|| !TooEarlyForRequest(list.received)
|| list.postsBetween) {
return false; return false;
} }
@ -81,11 +85,98 @@ bool SponsoredMessages::append(not_null<History*> history) {
return true; return true;
} }
void SponsoredMessages::inject(
not_null<History*> 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<HistoryBlock>;
using ViewPtr = std::unique_ptr<HistoryView::Element>;
auto blockIt = ranges::find(
history->blocks,
lastView->block(),
&BlockPtr::get);
if (blockIt == end(history->blocks)) {
return;
}
const auto messages = [&]() -> const std::vector<ViewPtr> & {
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*> history) const { bool SponsoredMessages::canHaveFor(not_null<History*> history) const {
return history->peer->isChannel(); return history->peer->isChannel();
} }
void SponsoredMessages::request(not_null<History*> history) { void SponsoredMessages::request(not_null<History*> history, Fn<void()> done) {
if (!canHaveFor(history)) { if (!canHaveFor(history)) {
return; return;
} }
@ -113,6 +204,9 @@ void SponsoredMessages::request(not_null<History*> history) {
channel->inputChannel) channel->inputChannel)
).done([=](const MTPmessages_sponsoredMessages &result) { ).done([=](const MTPmessages_sponsoredMessages &result) {
parse(history, result); parse(history, result);
if (done) {
done();
}
}).fail([=] { }).fail([=] {
_requests.remove(history); _requests.remove(history);
}).send(); }).send();
@ -138,7 +232,12 @@ void SponsoredMessages::parse(
list.received = crl::now(); list.received = crl::now();
for (const auto &message : messages) { for (const auto &message : messages) {
append(history, list, message); 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 &) { }, [](const MTPDmessages_sponsoredMessagesEmpty &) {
}); });
@ -240,6 +339,7 @@ void SponsoredMessages::clearItems(not_null<History*> history) {
entry.item.reset(); entry.item.reset();
} }
list.showedAll = false; list.showedAll = false;
list.injectedCount = 0;
} }
const SponsoredMessages::Entry *SponsoredMessages::find( const SponsoredMessages::Entry *SponsoredMessages::find(
@ -301,4 +401,10 @@ SponsoredMessages::Details SponsoredMessages::lookupDetails(
}; };
} }
SponsoredMessages::State SponsoredMessages::state(
not_null<History*> history) const {
const auto it = _data.find(history);
return (it == end(_data)) ? State::None : it->second.state;
}
} // namespace Data } // namespace Data

View File

@ -46,6 +46,11 @@ struct SponsoredMessage {
class SponsoredMessages final { class SponsoredMessages final {
public: public:
enum class State {
None,
AppendToEnd,
InjectToMiddle,
};
struct Details { struct Details {
std::optional<QString> hash; std::optional<QString> hash;
PeerData *peer = nullptr; PeerData *peer = nullptr;
@ -58,13 +63,21 @@ public:
~SponsoredMessages(); ~SponsoredMessages();
[[nodiscard]] bool canHaveFor(not_null<History*> history) const; [[nodiscard]] bool canHaveFor(not_null<History*> history) const;
void request(not_null<History*> history); void request(not_null<History*> history, Fn<void()> done);
[[nodiscard]] bool append(not_null<History*> history);
void clearItems(not_null<History*> history); void clearItems(not_null<History*> history);
[[nodiscard]] Details lookupDetails(const FullMsgId &fullId) const; [[nodiscard]] Details lookupDetails(const FullMsgId &fullId) const;
[[nodiscard]] bool append(not_null<History*> history);
void inject(
not_null<History*> history,
MsgId injectAfterMsgId,
int betweenHeight,
int fallbackWidth);
void view(const FullMsgId &fullId); void view(const FullMsgId &fullId);
[[nodiscard]] State state(not_null<History*> history) const;
private: private:
using OwnedItem = std::unique_ptr<HistoryItem, HistoryItem::Destroyer>; using OwnedItem = std::unique_ptr<HistoryItem, HistoryItem::Destroyer>;
struct Entry { struct Entry {
@ -73,8 +86,13 @@ private:
}; };
struct List { struct List {
std::vector<Entry> entries; std::vector<Entry> entries;
// Data between history displays.
size_t injectedCount = 0;
bool showedAll = false; bool showedAll = false;
//
crl::time received = 0; crl::time received = 0;
int postsBetween = 0;
State state = State::None;
}; };
struct Request { struct Request {
mtpRequestId requestId = 0; mtpRequestId requestId = 0;

View File

@ -764,7 +764,8 @@ not_null<HistoryItem*> History::addNewLocalMessage(
makeMessage( makeMessage(
id, id,
from, from,
textWithEntities), textWithEntities,
nullptr),
true); true);
} }

View File

@ -34,6 +34,7 @@ class Session;
class Folder; class Folder;
class ChatFilter; class ChatFilter;
struct SponsoredFrom; struct SponsoredFrom;
class SponsoredMessages;
enum class ForwardOptions { enum class ForwardOptions {
PreserveInfo, PreserveInfo,
@ -495,6 +496,8 @@ private:
not_null<HistoryItem*> addNewToBack( not_null<HistoryItem*> addNewToBack(
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
bool unread); bool unread);
friend class Data::SponsoredMessages;
not_null<HistoryItem*> addNewInTheMiddle( not_null<HistoryItem*> addNewInTheMiddle(
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
int blockIndex, int blockIndex,

View File

@ -636,14 +636,17 @@ HistoryMessage::HistoryMessage(
not_null<History*> history, not_null<History*> history,
MsgId id, MsgId id,
Data::SponsoredFrom from, Data::SponsoredFrom from,
const TextWithEntities &textWithEntities) const TextWithEntities &textWithEntities,
HistoryItem *injectedAfter)
: HistoryItem( : HistoryItem(
history, history,
id, id,
((history->peer->isChannel() ? MessageFlag::Post : MessageFlag(0)) ((history->peer->isChannel() ? MessageFlag::Post : MessageFlag(0))
//| (from.peer ? MessageFlag::HasFromId : MessageFlag(0)) //| (from.peer ? MessageFlag::HasFromId : MessageFlag(0))
| MessageFlag::Local), | MessageFlag::Local),
HistoryItem::NewMessageDate(0), HistoryItem::NewMessageDate(injectedAfter
? injectedAfter->date()
: 0),
/*from.peer ? from.peer->id : */PeerId(0)) { /*from.peer ? from.peer->id : */PeerId(0)) {
createComponentsHelper( createComponentsHelper(
_flags, _flags,

View File

@ -130,7 +130,8 @@ public:
not_null<History*> history, not_null<History*> history,
MsgId id, MsgId id,
Data::SponsoredFrom from, Data::SponsoredFrom from,
const TextWithEntities &textWithEntities); // sponsored const TextWithEntities &textWithEntities,
HistoryItem *injectedAfter); // sponsored
void refreshMedia(const MTPMessageMedia *media); void refreshMedia(const MTPMessageMedia *media);
void refreshSentMedia(const MTPMessageMedia *media); void refreshSentMedia(const MTPMessageMedia *media);

View File

@ -2288,9 +2288,25 @@ void HistoryWidget::showHistory(
showAboutTopPromotion(); showAboutTopPromotion();
{ {
auto &sponsored = session().data().sponsoredMessages(); _scroll->setTrackingContent(false);
sponsored.request(_history); const auto checkState = crl::guard(this, [=] {
_scroll->setTrackingContent(sponsored.canHaveFor(_history)); 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 { } else {
_chooseForReport = nullptr; _chooseForReport = nullptr;
@ -2354,6 +2370,14 @@ void HistoryWidget::setHistory(History *history) {
refreshAttachBotsMenu(); refreshAttachBotsMenu();
} }
void HistoryWidget::injectSponsoredMessages() const {
session().data().sponsoredMessages().inject(
_history,
_showAtMsgId,
_scroll->height() * 2,
_scroll->width());
}
void HistoryWidget::refreshAttachBotsMenu() { void HistoryWidget::refreshAttachBotsMenu() {
_attachBotsMenu = nullptr; _attachBotsMenu = nullptr;
if (!_history) { if (!_history) {
@ -3268,7 +3292,7 @@ void HistoryWidget::loadMessagesDown() {
auto loadMigrated = _migrated && !(_migrated->isEmpty() || _migrated->loadedAtBottom() || (!_history->isEmpty() && !_history->loadedAtTop())); auto loadMigrated = _migrated && !(_migrated->isEmpty() || _migrated->loadedAtBottom() || (!_history->isEmpty() && !_history->loadedAtTop()));
auto from = loadMigrated ? _migrated : _history; auto from = loadMigrated ? _migrated : _history;
if (from->loadedAtBottom()) { if (from->loadedAtBottom()) {
session().data().sponsoredMessages().request(_history); session().data().sponsoredMessages().request(_history, nullptr);
return; return;
} }
@ -5659,6 +5683,7 @@ void HistoryWidget::addMessagesToBack(
if (!_firstLoadRequest) { if (!_firstLoadRequest) {
updateHistoryGeometry(false, true, { ScrollChangeNoJumpToBottom, 0 }); updateHistoryGeometry(false, true, { ScrollChangeNoJumpToBottom, 0 });
} }
injectSponsoredMessages();
} }
void HistoryWidget::updateBotKeyboard(History *h, bool force) { void HistoryWidget::updateBotKeyboard(History *h, bool force) {

View File

@ -604,6 +604,8 @@ private:
void refreshSendAsToggle(); void refreshSendAsToggle();
void refreshAttachBotsMenu(); void refreshAttachBotsMenu();
void injectSponsoredMessages() const;
bool kbWasHidden() const; bool kbWasHidden() const;
void searchInChat(); void searchInChat();