| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463 |
- /*
- 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 "boxes/premium_preview_box.h"
- #include "chat_helpers/compose/compose_show.h"
- #include "core/ui_integration.h" // TextContext
- #include "data/components/sponsored_messages.h"
- #include "data/data_premium_limits.h"
- #include "data/data_session.h"
- #include "data/stickers/data_custom_emoji.h"
- #include "history/history.h"
- #include "lang/lang_keys.h"
- #include "main/main_session.h"
- #include "ui/boxes/report_box_graphics.h" // AddReportOptionButton.
- #include "ui/layers/generic_box.h"
- #include "ui/painter.h"
- #include "ui/rect.h"
- #include "ui/text/text_utilities.h"
- #include "ui/toast/toast.h"
- #include "ui/vertical_list.h"
- #include "ui/widgets/buttons.h"
- #include "ui/widgets/label_with_custom_emoji.h"
- #include "ui/widgets/menu/menu_add_action_callback.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 "styles/style_channel_earn.h"
- #include "styles/style_chat.h"
- #include "styles/style_info.h"
- #include "styles/style_layers.h"
- #include "styles/style_media_view.h"
- #include "styles/style_menu_icons.h"
- #include "styles/style_premium.h"
- #include "styles/style_settings.h"
- namespace Menu {
- namespace {
- void AboutBox(
- not_null<Ui::GenericBox*> box,
- std::shared_ptr<ChatHelpers::Show> show,
- const FullMsgId &fullId) {
- constexpr auto kUrl = "https://promote.telegram.org"_cs;
- box->setNoContentMargin(true);
- const auto isChannel = peerIsChannel(fullId.peer);
- const auto session = &show->session();
- 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<Ui::RpWidget>(content);
- owned->resize(rect.size());
- const auto widget = box->addRow(object_ptr<Ui::CenterWrap<>>(
- content,
- std::move(owned)))->entity();
- widget->paintRequest(
- ) | rpl::start_with_next([=] {
- auto p = Painter(widget);
- auto hq = PainterHighQualityEnabler(p);
- 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<Ui::CenterWrap<>>(
- content,
- object_ptr<Ui::FlatLabel>(
- content,
- tr::lng_sponsored_menu_revenued_about(),
- st::boxTitle)));
- Ui::AddSkip(content);
- box->addRow(object_ptr<Ui::CenterWrap<>>(
- content,
- object_ptr<Ui::FlatLabel>(
- 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<QString> title,
- rpl::producer<TextWithEntities> about,
- const style::icon &icon) {
- const auto top = content->add(
- object_ptr<Ui::FlatLabel>(
- content,
- std::move(title),
- st::channelEarnSemiboldLabel),
- padding);
- Ui::AddSkip(content, st::channelEarnHistoryThreeSkip);
- const auto label = content->add(
- object_ptr<Ui::FlatLabel>(
- content,
- std::move(about),
- st::channelEarnHistoryRecipientLabel),
- padding);
- const auto left = Ui::CreateChild<Ui::RpWidget>(
- 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());
- return label;
- };
- addEntry(
- tr::lng_sponsored_revenued_info1_title(),
- (isChannel
- ? tr::lng_sponsored_revenued_info1_description
- : tr::lng_sponsored_revenued_info1_bot_description)(
- Ui::Text::RichLangValue),
- st::sponsoredAboutPrivacyIcon);
- Ui::AddSkip(content);
- Ui::AddSkip(content);
- addEntry(
- (isChannel
- ? tr::lng_sponsored_revenued_info2_title
- : tr::lng_sponsored_revenued_info2_bot_title)(),
- (isChannel
- ? tr::lng_sponsored_revenued_info2_description
- : tr::lng_sponsored_revenued_info2_bot_description)(
- Ui::Text::RichLangValue),
- st::sponsoredAboutSplitIcon);
- Ui::AddSkip(content);
- Ui::AddSkip(content);
- auto link = tr::lng_settings_privacy_premium_link(
- ) | rpl::map([](QString t) {
- return Ui::Text::Link(std::move(t), u"internal:"_q);
- });
- addEntry(
- tr::lng_sponsored_revenued_info3_title(),
- isChannel
- ? tr::lng_sponsored_revenued_info3_description(
- lt_count,
- rpl::single(float64(levels)),
- lt_link,
- std::move(link),
- Ui::Text::RichLangValue)
- : tr::lng_sponsored_revenued_info3_bot_description(
- lt_link,
- std::move(link),
- Ui::Text::RichLangValue),
- st::sponsoredAboutRemoveIcon)->setClickHandlerFilter([=](
- const auto &...) {
- ShowPremiumPreviewBox(show, PremiumFeature::NoAds);
- return true;
- });
- Ui::AddSkip(content);
- Ui::AddSkip(content);
- }
- Ui::AddSkip(content);
- Ui::AddSkip(content);
- {
- box->addRow(
- object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
- content,
- object_ptr<Ui::FlatLabel>(
- content,
- tr::lng_sponsored_revenued_footer_title(),
- st::boxTitle)));
- }
- Ui::AddSkip(content);
- {
- const auto arrow = Ui::Text::IconEmoji(&st::textMoreIconEmoji);
- const auto available = box->width()
- - rect::m::sum::h(st::boxRowPadding);
- box->addRow(
- Ui::CreateLabelWithCustomEmoji(
- content,
- (isChannel
- ? tr::lng_sponsored_revenued_footer_description
- : tr::lng_sponsored_revenued_footer_bot_description)(
- lt_link,
- tr::lng_channel_earn_about_link(
- lt_emoji,
- rpl::single(arrow),
- Ui::Text::RichLangValue
- ) | rpl::map([=](TextWithEntities t) {
- return Ui::Text::Link(std::move(t), kUrl.utf16());
- }),
- Ui::Text::RichLangValue),
- Core::TextContext({ .session = session }),
- st::channelEarnLearnDescription))->resizeToWidth(available);
- }
- Ui::AddSkip(content);
- Ui::AddSkip(content);
- {
- const auto &st = st::premiumPreviewDoubledLimitsBox;
- box->setStyle(st);
- auto button = object_ptr<Ui::RoundButton>(
- 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));
- }
- if (!isChannel) {
- const auto top = Ui::CreateChild<Ui::IconButton>(
- box,
- st::infoTopBarMenu);
- box->widthValue(
- ) | rpl::start_with_next([=](int width) {
- top->raise();
- top->moveToLeft(
- width - top->width() - st::defaultScrollArea.width,
- 0);
- }, top->lifetime());
- using MenuPtr = base::unique_qptr<Ui::PopupMenu>;
- const auto menu = top->lifetime().make_state<MenuPtr>();
- top->setClickedCallback([=] {
- *menu = base::make_unique_q<Ui::PopupMenu>(
- box->window(),
- st::popupMenuWithIcons);
- const auto raw = menu->get();
- raw->animatePhaseValue(
- ) | rpl::start_with_next([=](Ui::PopupMenu::AnimatePhase phase) {
- top->setForceRippled(phase == Ui::PopupMenu::AnimatePhase::Shown
- || phase == Ui::PopupMenu::AnimatePhase::StartShow);
- }, top->lifetime());
- FillSponsored(
- top,
- Ui::Menu::CreateAddActionCallback(menu->get()),
- show,
- fullId,
- false,
- true);
- const auto global = top->mapToGlobal(
- QPoint(top->width() / 4 * 3, top->height() / 2));
- raw->setForcedOrigin(Ui::PanelAnimation::Origin::TopRight);
- raw->popup(
- QPoint(
- global.x(),
- std::max(global.y(), QCursor::pos().y())));
- return true;
- });
- }
- }
- void ShowReportSponsoredBox(
- std::shared_ptr<ChatHelpers::Show> show,
- const FullMsgId &fullId) {
- auto &sponsoredMessages = show->session().sponsoredMessages();
- const auto report = sponsoredMessages.createReportCallback(fullId);
- const auto guideLink = Ui::Text::Link(
- tr::lng_report_sponsored_reported_link(tr::now),
- u"https://promote.telegram.org/guidelines"_q);
- auto performRequest = [=](
- const auto &repeatRequest,
- Data::SponsoredReportResult::Id id) -> void {
- report(id, [=](const Data::SponsoredReportResult &result) {
- if (!result.error.isEmpty()) {
- show->showToast(result.error);
- }
- if (!result.options.empty()) {
- show->show(Box([=](not_null<Ui::GenericBox*> box) {
- box->setTitle(rpl::single(result.title));
- for (const auto &option : result.options) {
- const auto button = Ui::AddReportOptionButton(
- box->verticalLayout(),
- option.text,
- nullptr);
- button->setClickedCallback([=] {
- repeatRequest(repeatRequest, option.id);
- });
- }
- if (!id.isNull()) {
- box->addLeftButton(
- tr::lng_create_group_back(),
- [=] { box->closeBox(); });
- } else {
- const auto container = box->verticalLayout();
- Ui::AddSkip(container);
- container->add(object_ptr<Ui::DividerLabel>(
- container,
- object_ptr<Ui::FlatLabel>(
- container,
- tr::lng_report_sponsored_reported_learn(
- lt_link,
- rpl::single(guideLink),
- Ui::Text::WithEntities),
- st::boxDividerLabel),
- st::defaultBoxDividerLabelPadding,
- RectPart::Top | RectPart::Bottom));
- }
- box->addButton(
- tr::lng_close(),
- [=] { show->hideLayer(); });
- }));
- } else {
- constexpr auto kToastDuration = crl::time(4000);
- switch (result.result) {
- case Data::SponsoredReportResult::FinalStep::Hidden: {
- show->showToast(
- tr::lng_report_sponsored_hidden(tr::now),
- kToastDuration);
- } break;
- case Data::SponsoredReportResult::FinalStep::Reported: {
- auto text = tr::lng_report_sponsored_reported(
- tr::now,
- lt_link,
- guideLink,
- Ui::Text::WithEntities);
- show->showToast({
- .text = std::move(text),
- .duration = kToastDuration,
- });
- } break;
- case Data::SponsoredReportResult::FinalStep::Premium: {
- ShowPremiumPreviewBox(show, PremiumFeature::NoAds);
- } break;
- }
- show->hideLayer();
- }
- });
- };
- performRequest(performRequest, Data::SponsoredReportResult::Id());
- }
- } // namespace
- void FillSponsored(
- not_null<Ui::RpWidget*> parent,
- const Ui::Menu::MenuCallback &addAction,
- std::shared_ptr<ChatHelpers::Show> show,
- const FullMsgId &fullId,
- bool mediaViewer,
- bool skipAbout) {
- const auto session = &show->session();
- const auto details = session->sponsoredMessages().lookupDetails(fullId);
- const auto &info = details.info;
- if (!mediaViewer && !info.empty()) {
- auto fillSubmenu = [&](not_null<Ui::PopupMenu*> 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 = [=] {
- TextUtilities::SetClipboardText({ allText });
- show->showToast(tr::lng_text_copied(tr::now));
- };
- for (const auto &i : info) {
- auto item = base::make_unique_q<Ui::Menu::MultilineAction>(
- menu,
- st::defaultMenu,
- st::historySponsorInfoItem,
- st::historyHasCustomEmojiPosition,
- base::duplicate(i));
- item->clicks(
- ) | rpl::start_with_next(callback, menu->lifetime());
- menu->addAction(std::move(item));
- if (i != details.info.back()) {
- menu->addSeparator();
- }
- }
- };
- addAction({
- .text = tr::lng_sponsored_info_menu(tr::now),
- .handler = nullptr,
- .icon = &st::menuIconChannel,
- .fillSubmenu = std::move(fillSubmenu),
- });
- addAction({
- .separatorSt = &st::expandedMenuSeparator,
- .isSeparator = true,
- });
- }
- if (details.canReport) {
- if (!skipAbout) {
- addAction(tr::lng_sponsored_menu_revenued_about(tr::now), [=] {
- show->show(Box(AboutBox, show, fullId));
- }, (mediaViewer ? &st::mediaMenuIconInfo : &st::menuIconInfo));
- }
- addAction(tr::lng_sponsored_menu_revenued_report(tr::now), [=] {
- ShowReportSponsoredBox(show, fullId);
- }, (mediaViewer ? &st::mediaMenuIconBlock : &st::menuIconBlock));
- addAction({
- .separatorSt = (mediaViewer
- ? &st::mediaviewMenuSeparator
- : &st::expandedMenuSeparator),
- .isSeparator = true,
- });
- }
- addAction(tr::lng_sponsored_hide_ads(tr::now), [=] {
- if (session->premium()) {
- using Result = Data::SponsoredReportResult;
- session->sponsoredMessages().createReportCallback(
- fullId)(Result::Id("-1"), [](const auto &) {});
- } else {
- ShowPremiumPreviewBox(show, PremiumFeature::NoAds);
- }
- }, (mediaViewer ? &st::mediaMenuIconCancel : &st::menuIconCancel));
- }
- void ShowSponsored(
- not_null<Ui::RpWidget*> parent,
- std::shared_ptr<ChatHelpers::Show> show,
- const FullMsgId &fullId) {
- const auto menu = Ui::CreateChild<Ui::PopupMenu>(
- parent.get(),
- st::popupMenuWithIcons);
- FillSponsored(
- parent,
- Ui::Menu::CreateAddActionCallback(menu),
- show,
- fullId,
- false);
- menu->popup(QCursor::pos());
- }
- void ShowSponsoredAbout(
- std::shared_ptr<ChatHelpers::Show> show,
- const FullMsgId &fullId) {
- show->showBox(Box([=](not_null<Ui::GenericBox*> box) {
- AboutBox(box, show, fullId);
- }));
- }
- } // namespace Menu
|