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 "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*> 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*> history) {
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 {
return history->peer->isChannel();
}
void SponsoredMessages::request(not_null<History*> history) {
void SponsoredMessages::request(not_null<History*> history, Fn<void()> done) {
if (!canHaveFor(history)) {
return;
}
@ -113,6 +204,9 @@ void SponsoredMessages::request(not_null<History*> 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*> 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*> history) const {
const auto it = _data.find(history);
return (it == end(_data)) ? State::None : it->second.state;
}
} // namespace Data

View File

@ -46,6 +46,11 @@ struct SponsoredMessage {
class SponsoredMessages final {
public:
enum class State {
None,
AppendToEnd,
InjectToMiddle,
};
struct Details {
std::optional<QString> hash;
PeerData *peer = nullptr;
@ -58,13 +63,21 @@ public:
~SponsoredMessages();
[[nodiscard]] bool canHaveFor(not_null<History*> history) const;
void request(not_null<History*> history);
[[nodiscard]] bool append(not_null<History*> history);
void request(not_null<History*> history, Fn<void()> done);
void clearItems(not_null<History*> history);
[[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);
[[nodiscard]] State state(not_null<History*> history) const;
private:
using OwnedItem = std::unique_ptr<HistoryItem, HistoryItem::Destroyer>;
struct Entry {
@ -73,8 +86,13 @@ private:
};
struct List {
std::vector<Entry> 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;

View File

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

View File

@ -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<HistoryItem*> addNewToBack(
not_null<HistoryItem*> item,
bool unread);
friend class Data::SponsoredMessages;
not_null<HistoryItem*> addNewInTheMiddle(
not_null<HistoryItem*> item,
int blockIndex,

View File

@ -636,14 +636,17 @@ HistoryMessage::HistoryMessage(
not_null<History*> 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,

View File

@ -130,7 +130,8 @@ public:
not_null<History*> 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);

View File

@ -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) {

View File

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