From 7ad5520b82d0447cb97cea66e2e612c8315bf76f Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 23 Jun 2023 19:36:40 +0400 Subject: [PATCH] Support external links sponsored messages. --- Telegram/Resources/langs/lang.strings | 1 + .../data/data_sponsored_messages.cpp | 51 +++++++++++++------ .../data/data_sponsored_messages.h | 3 ++ Telegram/SourceFiles/history/history_item.cpp | 4 +- .../history/history_item_components.h | 1 + .../history/view/history_view_view_button.cpp | 20 +++++++- 6 files changed, 62 insertions(+), 18 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 281718c8f..425dc2843 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -3707,6 +3707,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_view_button_voice_chat" = "Voice chat"; "lng_view_button_voice_chat_channel" = "Live stream"; "lng_view_button_request_join" = "Request to Join"; +"lng_view_button_external_link" = "Open link"; "lng_sponsored_hide_ads" = "Hide"; "lng_sponsored_title" = "What are sponsored messages?"; diff --git a/Telegram/SourceFiles/data/data_sponsored_messages.cpp b/Telegram/SourceFiles/data/data_sponsored_messages.cpp index 9f4f992ed..c586b219e 100644 --- a/Telegram/SourceFiles/data/data_sponsored_messages.cpp +++ b/Telegram/SourceFiles/data/data_sponsored_messages.cpp @@ -270,28 +270,45 @@ void SponsoredMessages::append( .isForceUserpicDisplay = data.is_show_peer_photo(), }; }; + const auto externalLink = data.vwebpage() + ? qs(data.vwebpage()->data().vurl()) + : QString(); + const auto userpicFromPhoto = [&](const MTPphoto &photo) { + return photo.match([&](const MTPDphoto &data) { + for (const auto &size : data.vsizes().v) { + const auto result = Images::FromPhotoSize( + _session, + data, + size); + if (result.location.valid()) { + return result; + } + } + return ImageWithLocation{}; + }, [](const MTPDphotoEmpty &) { + return ImageWithLocation{}; + }); + }; const auto from = [&]() -> SponsoredFrom { - if (data.vfrom_id()) { + if (const auto webpage = data.vwebpage()) { + const auto &data = webpage->data(); + auto userpic = data.vphoto() + ? userpicFromPhoto(*data.vphoto()) + : ImageWithLocation{}; + return SponsoredFrom{ + .title = qs(data.vsite_name()), + .isExternalLink = true, + .userpic = std::move(userpic), + .isForceUserpicDisplay = message.data().is_show_peer_photo(), + }; + } else if (const auto fromId = data.vfrom_id()) { return makeFrom( - _session->data().peer(peerFromMTP(*data.vfrom_id())), + _session->data().peer(peerFromMTP(*fromId)), (data.vchannel_post() != nullptr)); } Assert(data.vchat_invite()); return data.vchat_invite()->match([&](const MTPDchatInvite &data) { - auto userpic = data.vphoto().match([&](const MTPDphoto &data) { - for (const auto &size : data.vsizes().v) { - const auto result = Images::FromPhotoSize( - _session, - data, - size); - if (result.location.valid()) { - return result; - } - } - return ImageWithLocation{}; - }, [](const MTPDphotoEmpty &) { - return ImageWithLocation{}; - }); + auto userpic = userpicFromPhoto(data.vphoto()); return SponsoredFrom{ .title = qs(data.vtitle()), .isBroadcast = data.is_broadcast(), @@ -336,6 +353,7 @@ void SponsoredMessages::append( .history = history, .msgId = data.vchannel_post().value_or_empty(), .chatInviteHash = hash, + .externalLink = externalLink, .sponsorInfo = std::move(sponsorInfo), .additionalInfo = std::move(additionalInfo), }; @@ -423,6 +441,7 @@ SponsoredMessages::Details SponsoredMessages::lookupDetails( .peer = data.from.peer, .msgId = data.msgId, .info = std::move(info), + .externalLink = data.externalLink, }; } diff --git a/Telegram/SourceFiles/data/data_sponsored_messages.h b/Telegram/SourceFiles/data/data_sponsored_messages.h index 7abbe2ea3..9c309ade3 100644 --- a/Telegram/SourceFiles/data/data_sponsored_messages.h +++ b/Telegram/SourceFiles/data/data_sponsored_messages.h @@ -31,6 +31,7 @@ struct SponsoredFrom { bool isBot = false; bool isExactPost = false; bool isRecommended = false; + bool isExternalLink = false; ImageWithLocation userpic; bool isForceUserpicDisplay = false; }; @@ -42,6 +43,7 @@ struct SponsoredMessage { History *history = nullptr; MsgId msgId; QString chatInviteHash; + QString externalLink; TextWithEntities sponsorInfo; TextWithEntities additionalInfo; }; @@ -58,6 +60,7 @@ public: PeerData *peer = nullptr; MsgId msgId; std::vector info; + QString externalLink; }; using RandomId = QByteArray; explicit SponsoredMessages(not_null owner); diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index e2bee8c06..039a7bc8d 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -3201,7 +3201,9 @@ void HistoryItem::setSponsoredFrom(const Data::SponsoredFrom &from) { } using Type = HistoryMessageSponsored::Type; - sponsored->type = from.isExactPost + sponsored->type = from.isExternalLink + ? Type::ExternalLink + : from.isExactPost ? Type::Post : from.isBot ? Type::Bot diff --git a/Telegram/SourceFiles/history/history_item_components.h b/Telegram/SourceFiles/history/history_item_components.h index 57d38514e..5a8db07c9 100644 --- a/Telegram/SourceFiles/history/history_item_components.h +++ b/Telegram/SourceFiles/history/history_item_components.h @@ -139,6 +139,7 @@ struct HistoryMessageSponsored : public RuntimeComponent sender; Type type = Type::User; diff --git a/Telegram/SourceFiles/history/view/history_view_view_button.cpp b/Telegram/SourceFiles/history/view/history_view_view_button.cpp index d7d406f4b..71b258ed9 100644 --- a/Telegram/SourceFiles/history/view/history_view_view_button.cpp +++ b/Telegram/SourceFiles/history/view/history_view_view_button.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_chat_invite.h" #include "core/application.h" #include "core/click_handler_types.h" +#include "core/file_utilities.h" #include "data/data_cloud_themes.h" #include "data/data_session.h" #include "data/data_sponsored_messages.h" @@ -42,6 +43,8 @@ inline auto SponsoredPhrase(SponsoredType type) { case SponsoredType::Broadcast: return tr::lng_view_button_channel; case SponsoredType::Post: return tr::lng_view_button_message; case SponsoredType::Bot: return tr::lng_view_button_bot; + case SponsoredType::ExternalLink: + return tr::lng_view_button_external_link; } Unexpected("SponsoredType in SponsoredPhrase."); }(); @@ -115,6 +118,7 @@ struct ViewButton::Inner { const ClickHandlerPtr link; const Fn updateCallback; bool belowInfo = true; + bool externalLink = false; int lastWidth = 0; QPoint lastPoint; std::unique_ptr ripple; @@ -158,7 +162,9 @@ ViewButton::Inner::Inner( const auto &data = controller->session().data(); const auto itemId = my.itemId; const auto details = data.sponsoredMessages().lookupDetails(itemId); - if (details.hash) { + if (!details.externalLink.isEmpty()) { + File::OpenUrl(details.externalLink); + } else if (details.hash) { Api::CheckChatInvite(controller, *details.hash); } else if (details.peer) { controller->showPeerHistory( @@ -169,6 +175,7 @@ ViewButton::Inner::Inner( } })) , updateCallback(std::move(updateCallback)) +, externalLink(sponsored->type == SponsoredType::ExternalLink) , text(st::historyViewButtonTextStyle, SponsoredPhrase(sponsored->type)) { } @@ -260,6 +267,17 @@ void ViewButton::draw( r.width(), 1, style::al_center); + + if (_inner->externalLink) { + const auto &icon = st::msgBotKbUrlIcon; + const auto padding = st::msgBotKbIconPadding; + icon.paint( + p, + r.left() + r.width() - icon.width() - padding, + r.top() + padding, + r.width(), + stm->fwdTextPalette.linkFg->c); + } } p.restore(); if (_inner->lastWidth != r.width()) {