Added support of posts_between flag for sponsored messages.
This commit is contained in:
parent
fb33951c94
commit
26f287fae0
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -764,7 +764,8 @@ not_null<HistoryItem*> History::addNewLocalMessage(
|
||||||
makeMessage(
|
makeMessage(
|
||||||
id,
|
id,
|
||||||
from,
|
from,
|
||||||
textWithEntities),
|
textWithEntities,
|
||||||
|
nullptr),
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in New Issue