Added support of simplified constructor for MTP sponsored message.

This commit is contained in:
23rd 2024-04-12 02:42:18 +03:00 committed by John Preston
parent ac15990b48
commit 72b274a2bf
8 changed files with 85 additions and 229 deletions

View file

@ -9,8 +9,9 @@ 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 "data/data_bot_app.h" #include "core/click_handler_types.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_photo.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "history/history.h" #include "history/history.h"
@ -257,94 +258,23 @@ void SponsoredMessages::append(
const MTPSponsoredMessage &message) { const MTPSponsoredMessage &message) {
const auto &data = message.data(); const auto &data = message.data();
const auto randomId = data.vrandom_id().v; const auto randomId = data.vrandom_id().v;
const auto hash = qs(data.vchat_invite_hash().value_or_empty()); const auto from = SponsoredFrom{
const auto makeFrom = [&]( .title = qs(data.vtitle()),
not_null<PeerData*> peer, .link = qs(data.vurl()),
bool exactPost = false) { .buttonText = qs(data.vbutton_text()),
const auto channel = peer->asChannel(); .photoId = data.vphoto()
return SponsoredFrom{ ? history->session().data().processPhoto(*data.vphoto())->id
.peer = peer, : PhotoId(0),
.title = peer->name(), .backgroundEmojiId = data.vcolor().has_value()
.isBroadcast = (channel && channel->isBroadcast()), ? data.vcolor()->data().vbackground_emoji_id().value_or_empty()
.isMegagroup = (channel && channel->isMegagroup()), : uint64(0),
.isChannel = (channel != nullptr), .colorIndex = uint8(data.vcolor().has_value()
.isPublic = (channel && channel->isPublic()), ? data.vcolor()->data().vcolor().value_or_empty()
.isExactPost = exactPost, : 0),
.isRecommended = data.is_recommended(), .isLinkInternal = !UrlRequiresConfirmation(qs(data.vurl())),
.isForceUserpicDisplay = data.is_show_peer_photo(), .isRecommended = data.is_recommended(),
.buttonText = qs(data.vbutton_text().value_or_empty()), .canReport = data.is_can_report(),
.canReport = data.is_can_report(),
};
}; };
const auto externalLink = data.vwebpage()
? qs(data.vwebpage()->data().vurl())
: QString();
const auto from = [&]() -> SponsoredFrom {
if (const auto webpage = data.vwebpage()) {
const auto &data = webpage->data();
const auto photoId = data.vphoto()
? _session->data().processPhoto(*data.vphoto())->id
: PhotoId(0);
return SponsoredFrom{
.title = qs(data.vsite_name()),
.externalLink = externalLink,
.webpageOrBotPhotoId = photoId,
.isForceUserpicDisplay = message.data().is_show_peer_photo(),
.canReport = message.data().is_can_report(),
};
} else if (const auto fromId = data.vfrom_id()) {
const auto peerId = peerFromMTP(*fromId);
auto result = makeFrom(
_session->data().peer(peerId),
(data.vchannel_post() != nullptr));
const auto user = result.peer->asUser();
if (user && user->isBot()) {
const auto botAppData = data.vapp()
? _session->data().processBotApp(peerId, *data.vapp())
: nullptr;
result.botLinkInfo = Window::PeerByLinkInfo{
.usernameOrId = user->username(),
.resolveType = botAppData
? Window::ResolveType::BotApp
: data.vstart_param()
? Window::ResolveType::BotStart
: Window::ResolveType::Default,
.startToken = qs(data.vstart_param().value_or_empty()),
.botAppName = botAppData
? botAppData->shortName
: QString(),
};
result.webpageOrBotPhotoId = (botAppData && botAppData->photo)
? botAppData->photo->id
: PhotoId(0);
}
return result;
}
Assert(data.vchat_invite());
return data.vchat_invite()->match([&](const MTPDchatInvite &data) {
return SponsoredFrom{
.title = qs(data.vtitle()),
.isBroadcast = data.is_broadcast(),
.isMegagroup = data.is_megagroup(),
.isChannel = data.is_channel(),
.isPublic = data.is_public(),
.isForceUserpicDisplay = message.data().is_show_peer_photo(),
.canReport = message.data().is_can_report(),
};
}, [&](const MTPDchatInviteAlready &data) {
const auto chat = _session->data().processChat(data.vchat());
if (const auto channel = chat->asChannel()) {
channel->clearInvitePeek();
}
return makeFrom(chat);
}, [&](const MTPDchatInvitePeek &data) {
const auto chat = _session->data().processChat(data.vchat());
if (const auto channel = chat->asChannel()) {
channel->setInvitePeek(hash, data.vexpires().v);
}
return makeFrom(chat);
});
}();
auto sponsorInfo = data.vsponsor_info() auto sponsorInfo = data.vsponsor_info()
? tr::lng_sponsored_info_submenu( ? tr::lng_sponsored_info_submenu(
tr::now, tr::now,
@ -364,9 +294,7 @@ void SponsoredMessages::append(
data.ventities().value_or_empty()), data.ventities().value_or_empty()),
}, },
.history = history, .history = history,
.msgId = data.vchannel_post().value_or_empty(), .link = from.link,
.chatInviteHash = hash,
.externalLink = externalLink,
.sponsorInfo = std::move(sponsorInfo), .sponsorInfo = std::move(sponsorInfo),
.additionalInfo = std::move(additionalInfo), .additionalInfo = std::move(additionalInfo),
}; };
@ -438,7 +366,6 @@ SponsoredMessages::Details SponsoredMessages::lookupDetails(
return {}; return {};
} }
const auto &data = entryPtr->sponsored; const auto &data = entryPtr->sponsored;
const auto &hash = data.chatInviteHash;
using InfoList = std::vector<TextWithEntities>; using InfoList = std::vector<TextWithEntities>;
auto info = (!data.sponsorInfo.text.isEmpty() auto info = (!data.sponsorInfo.text.isEmpty()
@ -450,20 +377,13 @@ SponsoredMessages::Details SponsoredMessages::lookupDetails(
? InfoList{ data.additionalInfo } ? InfoList{ data.additionalInfo }
: InfoList{}; : InfoList{};
return { return {
.hash = hash.isEmpty() ? std::nullopt : std::make_optional(hash),
.peer = data.from.peer,
.msgId = data.msgId,
.info = std::move(info), .info = std::move(info),
.externalLink = data.externalLink, .link = data.link,
.isForceUserpicDisplay = data.from.isForceUserpicDisplay, .photoId = data.from.photoId,
.buttonText = !data.from.buttonText.isEmpty() .buttonText = data.from.buttonText,
? data.from.buttonText .backgroundEmojiId = data.from.backgroundEmojiId,
: !data.externalLink.isEmpty() .colorIndex = data.from.colorIndex,
? tr::lng_view_button_external_link(tr::now) .isLinkInternal = data.from.isLinkInternal,
: data.from.botLinkInfo
? tr::lng_view_button_bot(tr::now)
: QString(),
.botLinkInfo = data.from.botLinkInfo,
.canReport = data.from.canReport, .canReport = data.from.canReport,
}; };
} }

View file

@ -40,19 +40,14 @@ struct SponsoredReportResult final {
}; };
struct SponsoredFrom { struct SponsoredFrom {
PeerData *peer = nullptr;
QString title; QString title;
bool isBroadcast = false; QString link;
bool isMegagroup = false;
bool isChannel = false;
bool isPublic = false;
std::optional<Window::PeerByLinkInfo> botLinkInfo;
bool isExactPost = false;
bool isRecommended = false;
QString externalLink;
PhotoId webpageOrBotPhotoId = PhotoId(0);
bool isForceUserpicDisplay = false;
QString buttonText; QString buttonText;
PhotoId photoId = PhotoId(0);
uint64 backgroundEmojiId = 0;
uint8 colorIndex : 6 = 0;
bool isLinkInternal = false;
bool isRecommended = false;
bool canReport = false; bool canReport = false;
}; };
@ -61,9 +56,7 @@ struct SponsoredMessage {
SponsoredFrom from; SponsoredFrom from;
TextWithEntities textWithEntities; TextWithEntities textWithEntities;
History *history = nullptr; History *history = nullptr;
MsgId msgId; QString link;
QString chatInviteHash;
QString externalLink;
TextWithEntities sponsorInfo; TextWithEntities sponsorInfo;
TextWithEntities additionalInfo; TextWithEntities additionalInfo;
}; };
@ -76,14 +69,13 @@ public:
InjectToMiddle, InjectToMiddle,
}; };
struct Details { struct Details {
std::optional<QString> hash;
PeerData *peer = nullptr;
MsgId msgId;
std::vector<TextWithEntities> info; std::vector<TextWithEntities> info;
QString externalLink; QString link;
bool isForceUserpicDisplay = false;
QString buttonText; QString buttonText;
std::optional<Window::PeerByLinkInfo> botLinkInfo; PhotoId photoId = PhotoId(0);
uint64 backgroundEmojiId = 0;
uint8 colorIndex : 6 = 0;
bool isLinkInternal = false;
bool canReport = false; bool canReport = false;
}; };
using RandomId = QByteArray; using RandomId = QByteArray;

View file

@ -641,32 +641,18 @@ HistoryItem::HistoryItem(
? injectedAfter->date() ? injectedAfter->date()
: 0), : 0),
}) { }) {
const auto webPageType = !from.externalLink.isEmpty()
? WebPageType::None
: from.isExactPost
? WebPageType::Message
: (from.botLinkInfo && !from.botLinkInfo->botAppName.isEmpty())
? WebPageType::BotApp
: from.botLinkInfo
? WebPageType::Bot
: from.isBroadcast
? WebPageType::Channel
: (from.peer && from.peer->isUser())
? WebPageType::User
: WebPageType::Group;
const auto webpage = history->peer->owner().webpage( const auto webpage = history->peer->owner().webpage(
history->peer->owner().nextLocalMessageId().bare, history->peer->owner().nextLocalMessageId().bare,
webPageType, WebPageType::None,
from.externalLink, from.link,
from.externalLink, from.link,
from.isRecommended from.isRecommended
? tr::lng_recommended_message_title(tr::now) ? tr::lng_recommended_message_title(tr::now)
: tr::lng_sponsored_message_title(tr::now), : tr::lng_sponsored_message_title(tr::now),
from.title, from.title,
textWithEntities, textWithEntities,
(from.webpageOrBotPhotoId (from.photoId
? history->owner().photo(from.webpageOrBotPhotoId).get() ? history->owner().photo(from.photoId).get()
: nullptr), : nullptr),
nullptr, nullptr,
WebPageCollage(), WebPageCollage(),

View file

@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "history/view/history_view_element.h" #include "history/view/history_view_element.h"
#include "api/api_chat_invite.h"
#include "history/view/history_view_service_message.h" #include "history/view/history_view_service_message.h"
#include "history/view/history_view_message.h" #include "history/view/history_view_message.h"
#include "history/view/media/history_view_media_grouped.h" #include "history/view/media/history_view_media_grouped.h"
@ -27,10 +26,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h" #include "core/application.h"
#include "core/core_settings.h" #include "core/core_settings.h"
#include "core/click_handler_types.h" #include "core/click_handler_types.h"
#include "core/file_utilities.h"
#include "core/ui_integration.h" #include "core/ui_integration.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "main/main_domain.h"
#include "chat_helpers/stickers_emoji_pack.h" #include "chat_helpers/stickers_emoji_pack.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "ui/effects/path_shift_gradient.h" #include "ui/effects/path_shift_gradient.h"
@ -1099,29 +1096,7 @@ ClickHandlerPtr Element::fromLink() const {
return _fromLink; return _fromLink;
} }
const auto item = data(); const auto item = data();
if (item->isSponsored()) { if (const auto from = item->displayFrom()) {
const auto session = &item->history()->session();
_fromLink = std::make_shared<LambdaClickHandler>([=](
ClickContext context) {
if (context.button != Qt::LeftButton) {
return;
}
const auto my = context.other.value<ClickHandlerContext>();
if (const auto window = ContextOrSessionWindow(my, session)) {
auto &sponsored = session->sponsoredMessages();
const auto itemId = my.itemId ? my.itemId : item->fullId();
const auto details = sponsored.lookupDetails(itemId);
if (!details.externalLink.isEmpty()) {
File::OpenUrl(details.externalLink);
} else if (const auto &hash = details.hash) {
Api::CheckChatInvite(window, *hash);
} else if (const auto peer = details.peer) {
window->showPeerInfo(peer);
}
}
});
return _fromLink;
} else if (const auto from = item->displayFrom()) {
_fromLink = std::make_shared<LambdaClickHandler>([=]( _fromLink = std::make_shared<LambdaClickHandler>([=](
ClickContext context) { ClickContext context) {
if (context.button != Qt::LeftButton) { if (context.button != Qt::LeftButton) {

View file

@ -7,53 +7,32 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "history/view/history_view_sponsored_click_handler.h" #include "history/view/history_view_sponsored_click_handler.h"
#include "api/api_chat_invite.h"
#include "core/click_handler_types.h" #include "core/click_handler_types.h"
#include "core/file_utilities.h"
#include "data/components/sponsored_messages.h"
#include "data/data_session.h"
#include "main/main_session.h"
#include "window/window_session_controller.h"
namespace HistoryView { namespace HistoryView {
ClickHandlerPtr SponsoredLink(const QString &externalLink) { ClickHandlerPtr SponsoredLink(const QString &link, bool isInternal) {
if (!externalLink.isEmpty()) { class ClickHandler final : public UrlClickHandler {
class ClickHandler : public UrlClickHandler { public:
public: ClickHandler(const QString &link, bool isInternal)
using UrlClickHandler::UrlClickHandler; : UrlClickHandler(link, false)
, _isInternal(isInternal) {
}
QString copyToClipboardContextItemText() const override { QString copyToClipboardContextItemText() const override final {
return QString(); return QString();
} }
}; QString tooltip() const override final {
return _isInternal ? QString() : url();
}
return std::make_shared<ClickHandler>(externalLink, false); private:
} else { const bool _isInternal;
return std::make_shared<LambdaClickHandler>([](ClickContext context) {
const auto my = context.other.value<ClickHandlerContext>(); };
const auto controller = my.sessionWindow.get();
if (!controller) { return std::make_shared<ClickHandler>(link, isInternal);
return;
}
const auto &session = controller->session();
const auto details = session.sponsoredMessages().lookupDetails(
my.itemId);
if (!details.externalLink.isEmpty()) {
File::OpenUrl(details.externalLink);
} else if (details.hash) {
Api::CheckChatInvite(controller, *details.hash);
} else if (details.botLinkInfo) {
controller->showPeerByLink(*details.botLinkInfo);
} else if (details.peer) {
controller->showPeerHistory(
details.peer,
Window::SectionShow::Way::Forward,
details.msgId);
}
});
}
} }
} // namespace HistoryView } // namespace HistoryView

View file

@ -12,6 +12,7 @@ class ClickHandler;
namespace HistoryView { namespace HistoryView {
[[nodiscard]] std::shared_ptr<ClickHandler> SponsoredLink( [[nodiscard]] std::shared_ptr<ClickHandler> SponsoredLink(
const QString &externalLink); const QString &link,
bool isInternal);
} // namespace HistoryView } // namespace HistoryView

View file

@ -232,17 +232,10 @@ WebPage::WebPage(
_parent->data()->fullId()); _parent->data()->fullId());
auto result = std::make_optional<SponsoredData>(); auto result = std::make_optional<SponsoredData>();
result->buttonText = details.buttonText; result->buttonText = details.buttonText;
result->hasExternalLink = (details.externalLink == _data->url); result->isLinkInternal = details.isLinkInternal;
result->backgroundEmojiId = details.backgroundEmojiId;
result->colorIndex = details.colorIndex;
result->canReport = details.canReport; result->canReport = details.canReport;
#ifdef _DEBUG
if (details.peer) {
#else
if (details.isForceUserpicDisplay && details.peer) {
#endif
result->peer = details.peer;
result->userpicView = details.peer->createUserpicView();
details.peer->loadUserpic();
}
return result; return result;
}()) }())
, _siteName(st::msgMinWidth - _st.padding.left() - _st.padding.right()) , _siteName(st::msgMinWidth - _st.padding.left() - _st.padding.right())
@ -345,9 +338,9 @@ QSize WebPage::countOptimalSize() {
_parent->data()->fullId()); _parent->data()->fullId());
} }
if (_sponsoredData) { if (_sponsoredData) {
_openl = SponsoredLink(_sponsoredData->hasExternalLink _openl = SponsoredLink(
? _data->url _data->url,
: QString()); _sponsoredData->isLinkInternal);
if (_sponsoredData->canReport) { if (_sponsoredData->canReport) {
_sponsoredData->hintLink = AboutSponsoredClickHandler(); _sponsoredData->hintLink = AboutSponsoredClickHandler();
@ -683,14 +676,23 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
auto tshift = inner.top(); auto tshift = inner.top();
auto paintw = inner.width(); auto paintw = inner.width();
const auto asSponsored = (!!_sponsoredData);
const auto selected = context.selected(); const auto selected = context.selected();
const auto view = parent(); const auto view = parent();
const auto from = view->data()->contentColorsFrom(); const auto from = view->data()->contentColorsFrom();
const auto colorIndex = from ? from->colorIndex() : view->colorIndex(); const auto colorIndex = (asSponsored && _sponsoredData->colorIndex)
? _sponsoredData->colorIndex
: from
? from->colorIndex()
: view->colorIndex();
const auto cache = context.outbg const auto cache = context.outbg
? stm->replyCache[st->colorPatternIndex(colorIndex)].get() ? stm->replyCache[st->colorPatternIndex(colorIndex)].get()
: st->coloredReplyCache(selected, colorIndex).get(); : st->coloredReplyCache(selected, colorIndex).get();
const auto backgroundEmojiId = from const auto backgroundEmojiId = (asSponsored
&& _sponsoredData->backgroundEmojiId)
? _sponsoredData->backgroundEmojiId
: from
? from->backgroundEmojiId() ? from->backgroundEmojiId()
: DocumentId(); : DocumentId();
const auto backgroundEmoji = backgroundEmojiId const auto backgroundEmoji = backgroundEmojiId
@ -724,8 +726,6 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
} }
} }
const auto asSponsored = (!!_sponsoredData);
auto lineHeight = UnitedLineHeight(); auto lineHeight = UnitedLineHeight();
if (asArticle()) { if (asArticle()) {
ensurePhotoMediaCreated(); ensurePhotoMediaCreated();

View file

@ -134,7 +134,10 @@ private:
PeerData *peer = nullptr; PeerData *peer = nullptr;
Ui::PeerUserpicView userpicView; Ui::PeerUserpicView userpicView;
QString buttonText; QString buttonText;
bool hasExternalLink = false; bool isLinkInternal = false;
uint64 backgroundEmojiId = 0;
uint8 colorIndex : 6 = 0;
bool canReport = false; bool canReport = false;
QSize hintSize; QSize hintSize;