From ae5f2add0e6b10c2925d371dc5673322fd10ee9b Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 1 Mar 2023 16:28:06 +0300 Subject: [PATCH] Added possible info about sponsors to menu from sponsored messages. --- Telegram/Resources/langs/lang.strings | 2 + .../data/data_sponsored_messages.cpp | 31 +++++++- .../data/data_sponsored_messages.h | 3 + .../history/history_inner_widget.cpp | 77 +++++++++++++++++-- Telegram/SourceFiles/ui/chat/chat.style | 2 + 5 files changed, 105 insertions(+), 10 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 287fc9485..1dcc3d5fd 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -3552,6 +3552,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_sponsored_title" = "What are sponsored messages?"; "lng_sponsored_info_description1" = "Unlike other apps, Telegram never uses your private data to target ads. Sponsored messages on Telegram are based solely on the topic of the public channels in which they are shown. This means that no user data is mined or analyzed to display ads, and every user viewing a channel on Telegram sees the same sponsored messages.\n\nUnlike other apps, Telegram doesn't track whether you tapped on a sponsored message and doesn't profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties can’t spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that.\n\nTelegram offers a free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible advertisers at:"; "lng_sponsored_info_description2" = "Sponsored Messages are currently in test mode. Once they are fully launched and allow Telegram to cover its basic costs, we will start sharing ad revenue with the owners of public channels in which sponsored messages are displayed.\n\nOnline ads should no longer be synonymous with abuse of user privacy. Let us redefine how a tech company should operate – together."; +"lng_sponsored_info_menu" = "About this ad"; +"lng_sponsored_info_submenu" = "Advertiser: {text}"; "lng_telegram_features_url" = "https://t.me/TelegramTips"; diff --git a/Telegram/SourceFiles/data/data_sponsored_messages.cpp b/Telegram/SourceFiles/data/data_sponsored_messages.cpp index f6f0a7b4b..9f4f992ed 100644 --- a/Telegram/SourceFiles/data/data_sponsored_messages.cpp +++ b/Telegram/SourceFiles/data/data_sponsored_messages.cpp @@ -17,8 +17,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_item.h" #include "history/view/history_view_element.h" +#include "lang/lang_keys.h" #include "main/main_session.h" #include "ui/image/image_location_factory.h" +#include "ui/text/text_utilities.h" // Ui::Text::RichLangValue. namespace Data { namespace { @@ -313,6 +315,15 @@ void SponsoredMessages::append( return makeFrom(chat); }); }(); + auto sponsorInfo = data.vsponsor_info() + ? tr::lng_sponsored_info_submenu( + tr::now, + lt_text, + { .text = qs(*data.vsponsor_info()) }, + Ui::Text::RichLangValue) + : TextWithEntities(); + auto additionalInfo = TextWithEntities::Simple( + data.vadditional_info() ? qs(*data.vadditional_info()) : QString()); auto sharedMessage = SponsoredMessage{ .randomId = randomId, .from = from, @@ -325,6 +336,8 @@ void SponsoredMessages::append( .history = history, .msgId = data.vchannel_post().value_or_empty(), .chatInviteHash = hash, + .sponsorInfo = std::move(sponsorInfo), + .additionalInfo = std::move(additionalInfo), }; list.entries.push_back({ nullptr, std::move(sharedMessage) }); } @@ -393,11 +406,23 @@ SponsoredMessages::Details SponsoredMessages::lookupDetails( if (!entryPtr) { return {}; } - const auto &hash = entryPtr->sponsored.chatInviteHash; + const auto &data = entryPtr->sponsored; + const auto &hash = data.chatInviteHash; + + using InfoList = std::vector; + const auto info = (!data.sponsorInfo.text.isEmpty() + && !data.additionalInfo.text.isEmpty()) + ? InfoList{ data.sponsorInfo, data.additionalInfo } + : !data.sponsorInfo.text.isEmpty() + ? InfoList{ data.sponsorInfo } + : !data.additionalInfo.text.isEmpty() + ? InfoList{ data.additionalInfo } + : InfoList{}; return { .hash = hash.isEmpty() ? std::nullopt : std::make_optional(hash), - .peer = entryPtr->sponsored.from.peer, - .msgId = entryPtr->sponsored.msgId, + .peer = data.from.peer, + .msgId = data.msgId, + .info = std::move(info), }; } diff --git a/Telegram/SourceFiles/data/data_sponsored_messages.h b/Telegram/SourceFiles/data/data_sponsored_messages.h index e2a653968..7abbe2ea3 100644 --- a/Telegram/SourceFiles/data/data_sponsored_messages.h +++ b/Telegram/SourceFiles/data/data_sponsored_messages.h @@ -42,6 +42,8 @@ struct SponsoredMessage { History *history = nullptr; MsgId msgId; QString chatInviteHash; + TextWithEntities sponsorInfo; + TextWithEntities additionalInfo; }; class SponsoredMessages final { @@ -55,6 +57,7 @@ public: std::optional hash; PeerData *peer = nullptr; MsgId msgId; + std::vector info; }; using RandomId = QByteArray; explicit SponsoredMessages(not_null owner); diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 121b3084f..16bb719ec 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -28,6 +28,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item_components.h" #include "history/history_item_text.h" #include "ui/chat/chat_style.h" +#include "ui/widgets/menu/menu_add_action_callback_factory.h" +#include "ui/widgets/menu/menu_multiline_action.h" #include "ui/widgets/popup_menu.h" #include "ui/image/image.h" #include "ui/toasts/common_toasts.h" @@ -130,6 +132,72 @@ int BinarySearchBlocksOrItems(const T &list, int edge) { return start; } +void FillSponsoredMessagesMenu( + not_null controller, + FullMsgId itemId, + not_null menu) { + const auto &data = controller->session().data().sponsoredMessages(); + const auto info = data.lookupDetails(itemId).info; + const auto toastParent = Window::Show(controller).toastParent(); + if (!info.empty()) { + auto fillSubmenu = [&](not_null menu) { + const auto allText = ranges::accumulate( + info, + TextWithEntities(), + [](TextWithEntities a, TextWithEntities b) { + return a.text.isEmpty() ? b : a.append('\n').append(b); + }).text; + const auto callback = [=] { + QGuiApplication::clipboard()->setText(allText); + Ui::ShowMultilineToast({ + .parentOverride = toastParent, + .text = { tr::lng_text_copied(tr::now) }, + }); + }; + for (const auto &i : info) { + auto item = base::make_unique_q( + menu, + st::defaultMenu, + st::historyHasCustomEmoji, + st::historyHasCustomEmojiPosition, + base::duplicate(i)); + item->clicks( + ) | rpl::start_with_next(callback, menu->lifetime()); + menu->addAction(std::move(item)); + if (i != info.back()) { + menu->addSeparator(); + } + } + }; + using namespace Ui::Menu; + CreateAddActionCallback(menu)(MenuCallback::Args{ + .text = tr::lng_sponsored_info_menu(tr::now), + .handler = nullptr, + .icon = nullptr, + .fillSubmenu = std::move(fillSubmenu), + }); + menu->addSeparator(); + } + { + auto item = base::make_unique_q( + menu, + st::menuWithIcons, + st::historyHasCustomEmoji, + st::historySponsoredAboutMenuLabelPosition, + TextWithEntities{ tr::lng_sponsored_title(tr::now) }, + &st::menuIconInfo); + item->clicks( + ) | rpl::start_with_next([=] { + controller->show(Box(Ui::AboutSponsoredBox)); + }, item->lifetime()); + menu->addAction(std::move(item)); + } + menu->addSeparator(); + menu->addAction(tr::lng_sponsored_hide_ads(tr::now), [=] { + Settings::ShowPremium(controller, "no_ads"); + }, &st::menuIconBlock); +} + } // namespace // flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html @@ -2415,13 +2483,8 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } if (item->isSponsored()) { - _menu->addAction(tr::lng_sponsored_title({}), [=] { - _controller->show(Box(Ui::AboutSponsoredBox)); - }, &st::menuIconInfo); - _menu->addSeparator(); - _menu->addAction(tr::lng_sponsored_hide_ads({}), [=] { - Settings::ShowPremium(_controller, "no_ads"); - }, &st::menuIconBlock); + const auto itemId = item->fullId(); + FillSponsoredMessagesMenu(controller, itemId, _menu); } if (!item->isService() && view && actionText.isEmpty()) { if (!hasCopyRestriction(item) diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 6cf89a6d0..a83726419 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -1251,6 +1251,8 @@ historyTranslateSettings: IconButton(defaultIconButton) { } historyTranslateMenuPosition: point(-6px, 30px); +historySponsoredAboutMenuLabelPosition: point(54px, 4px); + historySendDisabled: FlatLabel(defaultFlatLabel) { minWidth: 10px; maxHeight: 20px;