diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 2c5feb862..9ec116fd3 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1141,6 +1141,8 @@ PRIVATE menu/menu_mute.h menu/menu_send.cpp menu/menu_send.h + menu/menu_sponsored.cpp + menu/menu_sponsored.h menu/menu_ttl_validator.cpp menu/menu_ttl_validator.h mtproto/config_loader.cpp diff --git a/Telegram/Resources/icons/sponsored/large_about.png b/Telegram/Resources/icons/sponsored/large_about.png new file mode 100644 index 000000000..95fb27be4 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/large_about.png differ diff --git a/Telegram/Resources/icons/sponsored/large_about@2x.png b/Telegram/Resources/icons/sponsored/large_about@2x.png new file mode 100644 index 000000000..8af8a580f Binary files /dev/null and b/Telegram/Resources/icons/sponsored/large_about@2x.png differ diff --git a/Telegram/Resources/icons/sponsored/large_about@3x.png b/Telegram/Resources/icons/sponsored/large_about@3x.png new file mode 100644 index 000000000..94d8147c2 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/large_about@3x.png differ diff --git a/Telegram/Resources/icons/sponsored/privacy_about.png b/Telegram/Resources/icons/sponsored/privacy_about.png new file mode 100644 index 000000000..67fc01990 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/privacy_about.png differ diff --git a/Telegram/Resources/icons/sponsored/privacy_about@2x.png b/Telegram/Resources/icons/sponsored/privacy_about@2x.png new file mode 100644 index 000000000..e63d296dc Binary files /dev/null and b/Telegram/Resources/icons/sponsored/privacy_about@2x.png differ diff --git a/Telegram/Resources/icons/sponsored/privacy_about@3x.png b/Telegram/Resources/icons/sponsored/privacy_about@3x.png new file mode 100644 index 000000000..3b95f5bc6 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/privacy_about@3x.png differ diff --git a/Telegram/Resources/icons/sponsored/remove_about.png b/Telegram/Resources/icons/sponsored/remove_about.png new file mode 100644 index 000000000..06a33dc3c Binary files /dev/null and b/Telegram/Resources/icons/sponsored/remove_about.png differ diff --git a/Telegram/Resources/icons/sponsored/remove_about@2x.png b/Telegram/Resources/icons/sponsored/remove_about@2x.png new file mode 100644 index 000000000..996851dbc Binary files /dev/null and b/Telegram/Resources/icons/sponsored/remove_about@2x.png differ diff --git a/Telegram/Resources/icons/sponsored/remove_about@3x.png b/Telegram/Resources/icons/sponsored/remove_about@3x.png new file mode 100644 index 000000000..0863b33e8 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/remove_about@3x.png differ diff --git a/Telegram/Resources/icons/sponsored/revenue_split.png b/Telegram/Resources/icons/sponsored/revenue_split.png new file mode 100644 index 000000000..94390bce4 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/revenue_split.png differ diff --git a/Telegram/Resources/icons/sponsored/revenue_split@2x.png b/Telegram/Resources/icons/sponsored/revenue_split@2x.png new file mode 100644 index 000000000..23a23ff0a Binary files /dev/null and b/Telegram/Resources/icons/sponsored/revenue_split@2x.png differ diff --git a/Telegram/Resources/icons/sponsored/revenue_split@3x.png b/Telegram/Resources/icons/sponsored/revenue_split@3x.png new file mode 100644 index 000000000..4086a7714 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/revenue_split@3x.png differ diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 45a22a71b..d198a8be6 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -4656,6 +4656,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "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_sponsored_menu_revenued_about" = "About These Ads"; +"lng_sponsored_revenued_subtitle" = "Telegram Ads are very different from ads on other platforms. Ads such as this one:"; +"lng_sponsored_revenued_info1_title" = "Respect Your Privacy"; +"lng_sponsored_revenued_info1_description" = "Ads on Telegram do not use your personal information and are abased on the channel in which you see them."; +"lng_sponsored_revenued_info2_title" = "Help the Channel Creator"; +"lng_sponsored_revenued_info2_description" = "50% of the revenue from Telegram Ads goes to the owner of the channel where they are displayed."; +"lng_sponsored_revenued_info3_title" = "Can Be Removed"; +"lng_sponsored_revenued_info3_description#one" = "You can turn off ads by subscribing to {link}, and Level {count} channels can remove them for their subscribers."; +"lng_sponsored_revenued_info3_description#other" = "You can turn off ads by subscribing to {link}, and Level {count} channels can remove them for their subscribers."; +"lng_sponsored_revenued_footer_title" = "Can I Launch an Ad?"; +"lng_sponsored_revenued_footer_description" = "Anyone can create an ad to display in this channel — with minimal budgets. Check out the **Telegram Ad Platform** for details. {link}"; "lng_telegram_features_url" = "https://t.me/TelegramTips"; diff --git a/Telegram/SourceFiles/history/history_item_helpers.cpp b/Telegram/SourceFiles/history/history_item_helpers.cpp index 7841959ad..7b51f751c 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.cpp +++ b/Telegram/SourceFiles/history/history_item_helpers.cpp @@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_domain.h" #include "main/main_session.h" #include "main/main_session_settings.h" +#include "menu/menu_sponsored.h" #include "platform/platform_notifications_manager.h" #include "window/window_controller.h" #include "window/window_session_controller.h" @@ -367,8 +368,15 @@ ClickHandlerPtr HideSponsoredClickHandler() { }); } -ClickHandlerPtr ReportSponsoredClickHandler() { +ClickHandlerPtr ReportSponsoredClickHandler(not_null item) { return std::make_shared([=](ClickContext context) { + const auto my = context.other.value(); + if (const auto controller = my.sessionWindow.get()) { + Menu::ShowSponsored( + controller->widget(), + controller->uiShow(), + item); + } }); } diff --git a/Telegram/SourceFiles/history/history_item_helpers.h b/Telegram/SourceFiles/history/history_item_helpers.h index cdd9af7cd..3d67e29ec 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.h +++ b/Telegram/SourceFiles/history/history_item_helpers.h @@ -142,7 +142,8 @@ ClickHandlerPtr JumpToStoryClickHandler( not_null peer, StoryId storyId); [[nodiscard]] ClickHandlerPtr HideSponsoredClickHandler(); -[[nodiscard]] ClickHandlerPtr ReportSponsoredClickHandler(); +[[nodiscard]] ClickHandlerPtr ReportSponsoredClickHandler( + not_null item); [[nodiscard]] not_null GenerateJoinedMessage( not_null history, diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 2100599a0..100fff459 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -441,7 +441,7 @@ Message::Message( _rightAction = std::make_unique(); _rightAction->second = std::make_unique(); - _rightAction->second->link = ReportSponsoredClickHandler(); + _rightAction->second->link = ReportSponsoredClickHandler(data); } } } diff --git a/Telegram/SourceFiles/info/channel_statistics/earn/channel_earn.style b/Telegram/SourceFiles/info/channel_statistics/earn/channel_earn.style index 7f0815fc4..12bf06c42 100644 --- a/Telegram/SourceFiles/info/channel_statistics/earn/channel_earn.style +++ b/Telegram/SourceFiles/info/channel_statistics/earn/channel_earn.style @@ -108,3 +108,8 @@ channelEarnLearnDescription: FlatLabel(defaultFlatLabel) { minWidth: 280px; align: align(top); } + +sponsoredAboutTitleIcon: icon {{ "sponsored/large_about", activeButtonFg }}; +sponsoredAboutPrivacyIcon: icon {{ "sponsored/privacy_about", boxTextFg }}; +sponsoredAboutRemoveIcon: icon {{ "sponsored/remove_about", boxTextFg }}; +sponsoredAboutSplitIcon: icon {{ "sponsored/revenue_split", boxTextFg }}; diff --git a/Telegram/SourceFiles/menu/menu_sponsored.cpp b/Telegram/SourceFiles/menu/menu_sponsored.cpp new file mode 100644 index 000000000..a6c13a662 --- /dev/null +++ b/Telegram/SourceFiles/menu/menu_sponsored.cpp @@ -0,0 +1,235 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "menu/menu_sponsored.h" + +#include "core/ui_integration.h" // Core::MarkedTextContext. +#include "data/data_premium_limits.h" +#include "data/data_session.h" +#include "data/stickers/data_custom_emoji.h" +#include "history/history.h" +#include "history/history_item.h" +#include "lang/lang_keys.h" +#include "main/main_session.h" +#include "ui/layers/generic_box.h" +#include "ui/painter.h" +#include "ui/rect.h" +#include "ui/text/text_utilities.h" +#include "ui/vertical_list.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/popup_menu.h" +#include "styles/style_channel_earn.h" +#include "styles/style_chat.h" +#include "styles/style_layers.h" +#include "styles/style_menu_icons.h" +#include "styles/style_premium.h" +#include "styles/style_settings.h" + +namespace Menu { +namespace { + +void AboutBox( + not_null box, + not_null session) { + constexpr auto kUrl = "https://promote.telegram.org"_cs; + + box->setNoContentMargin(true); + + const auto content = box->verticalLayout().get(); + const auto levels = Data::LevelLimits(session) + .channelRestrictSponsoredLevelMin(); + + Ui::AddSkip(content); + Ui::AddSkip(content); + Ui::AddSkip(content); + { + const auto &icon = st::sponsoredAboutTitleIcon; + const auto rect = Rect(icon.size() * 1.4); + auto owned = object_ptr(content); + owned->resize(rect.size()); + const auto widget = box->addRow(object_ptr>( + content, + std::move(owned)))->entity(); + widget->paintRequest( + ) | rpl::start_with_next([=] { + auto p = Painter(widget); + p.setPen(Qt::NoPen); + p.setBrush(st::activeButtonBg); + p.drawEllipse(rect); + icon.paintInCenter(p, rect); + }, widget->lifetime()); + } + Ui::AddSkip(content); + Ui::AddSkip(content); + box->addRow(object_ptr>( + content, + object_ptr( + content, + tr::lng_sponsored_menu_revenued_about(), + st::boxTitle))); + Ui::AddSkip(content); + box->addRow(object_ptr>( + content, + object_ptr( + content, + tr::lng_sponsored_revenued_subtitle(), + st::channelEarnLearnDescription))); + Ui::AddSkip(content); + Ui::AddSkip(content); + { + const auto padding = QMargins( + st::settingsButton.padding.left(), + st::boxRowPadding.top(), + st::boxRowPadding.right(), + st::boxRowPadding.bottom()); + const auto addEntry = [&]( + rpl::producer title, + rpl::producer about, + const style::icon &icon) { + const auto top = content->add( + object_ptr( + content, + std::move(title), + st::channelEarnSemiboldLabel), + padding); + Ui::AddSkip(content, st::channelEarnHistoryThreeSkip); + content->add( + object_ptr( + content, + std::move(about), + st::channelEarnHistoryRecipientLabel), + padding); + const auto left = Ui::CreateChild( + box->verticalLayout().get()); + left->paintRequest( + ) | rpl::start_with_next([=] { + auto p = Painter(left); + icon.paint(p, 0, 0, left->width()); + }, left->lifetime()); + left->resize(icon.size()); + top->geometryValue( + ) | rpl::start_with_next([=](const QRect &g) { + left->moveToLeft( + (g.left() - left->width()) / 2, + g.top() + st::channelEarnHistoryThreeSkip); + }, left->lifetime()); + }; + addEntry( + tr::lng_sponsored_revenued_info1_title(), + tr::lng_sponsored_revenued_info1_description( + Ui::Text::RichLangValue), + st::sponsoredAboutPrivacyIcon); + Ui::AddSkip(content); + Ui::AddSkip(content); + addEntry( + tr::lng_sponsored_revenued_info2_title(), + tr::lng_sponsored_revenued_info2_description( + Ui::Text::RichLangValue), + st::sponsoredAboutSplitIcon); + Ui::AddSkip(content); + Ui::AddSkip(content); + addEntry( + tr::lng_sponsored_revenued_info3_title(), + tr::lng_sponsored_revenued_info3_description( + lt_count, + rpl::single(float64(levels)), + lt_link, + tr::lng_settings_privacy_premium_link( + ) | rpl::map([=](QString t) { + return Ui::Text::Link(t, kUrl.utf8()); + }), + Ui::Text::RichLangValue), + st::sponsoredAboutRemoveIcon); + Ui::AddSkip(content); + Ui::AddSkip(content); + } + Ui::AddSkip(content); + Ui::AddSkip(content); + { + box->addRow( + object_ptr>( + content, + object_ptr( + content, + tr::lng_sponsored_revenued_footer_title(), + st::boxTitle))); + } + Ui::AddSkip(content); + { + const auto arrow = Ui::Text::SingleCustomEmoji( + session->data().customEmojiManager().registerInternalEmoji( + st::topicButtonArrow, + st::channelEarnLearnArrowMargins, + false)); + const auto label = box->addRow( + object_ptr( + content, + st::channelEarnLearnDescription)); + tr::lng_sponsored_revenued_footer_description( + lt_link, + tr::lng_channel_earn_about_link( + lt_emoji, + rpl::single(arrow), + Ui::Text::RichLangValue + ) | rpl::map([=](TextWithEntities text) { + return Ui::Text::Link(std::move(text), kUrl.utf8()); + }), + Ui::Text::RichLangValue + ) | rpl::start_with_next([=, l = label]( + TextWithEntities t) { + l->setMarkedText( + std::move(t), + Core::MarkedTextContext{ + .session = session, + .customEmojiRepaint = [=] { l->update(); }, + }); + l->resizeToWidth(box->width() + - rect::m::sum::h(st::boxRowPadding)); + }, label->lifetime()); + } + Ui::AddSkip(content); + Ui::AddSkip(content); + { + const auto &st = st::premiumPreviewDoubledLimitsBox; + box->setStyle(st); + auto button = object_ptr( + box, + tr::lng_box_ok(), + st::defaultActiveButton); + button->resizeToWidth(box->width() + - st.buttonPadding.left() + - st.buttonPadding.left()); + button->setClickedCallback([=] { box->closeBox(); }); + box->addButton(std::move(button)); + } + +} + +} // namespace + +void ShowSponsored( + not_null parent, + std::shared_ptr show, + not_null item) { + Expects(item->isSponsored()); + + struct State final { + }; + const auto state = std::make_shared(); + + const auto menu = Ui::CreateChild( + parent.get(), + st::popupMenuWithIcons); + + menu->addAction(tr::lng_sponsored_menu_revenued_about(tr::now), [=] { + show->show(Box(AboutBox, &item->history()->session())); + }, &st::menuIconInfo); + + menu->popup(QCursor::pos()); +} + +} // namespace Menu diff --git a/Telegram/SourceFiles/menu/menu_sponsored.h b/Telegram/SourceFiles/menu/menu_sponsored.h new file mode 100644 index 000000000..ecdde20c4 --- /dev/null +++ b/Telegram/SourceFiles/menu/menu_sponsored.h @@ -0,0 +1,24 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +namespace Ui { +class RpWidget; +class Show; +} // namespace Ui + +class HistoryItem; + +namespace Menu { + +void ShowSponsored( + not_null parent, + std::shared_ptr show, + not_null item); + +} // namespace Menu