diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 4e53775e8..f27bc5471 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1641,6 +1641,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_star_ref_duration_about" = "Set the duration for which affiliates will earn commissions from referred users."; "lng_star_ref_existing_title" = "View existing programs"; "lng_star_ref_existing_about" = "Explore what other mini apps offer."; +"lng_star_ref_add_bot" = "Add {bot}"; "lng_star_ref_end" = "End Affiliate Program"; "lng_star_ref_start" = "Start Affiliate Program"; "lng_star_ref_start_disabled" = "Available in {time}"; @@ -1690,6 +1691,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_star_ref_one_about_for_months#other" = "for **{count} months**"; "lng_star_ref_one_about_for_years#one" = "for **{count} year**"; "lng_star_ref_one_about_for_years#other" = "for **{count} years**"; +"lng_star_ref_one_daily_revenue" = "Daily revenue per user: {amount}"; "lng_star_ref_one_join" = "Join Program"; "lng_star_ref_one_join_text" = "By joining this program, you agree to the {terms} of Affiliate Programs."; "lng_star_ref_joined_title" = "Program joined"; diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 71d5d27cb..09d72a03e 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -739,6 +739,9 @@ StarRefProgram ParseStarRefProgram(const MTPStarRefProgram *program) { const auto &data = program->data(); result.commission = data.vcommission_permille().v; result.durationMonths = data.vduration_months().value_or_empty(); + result.revenuePerUser = data.vdaily_revenue_per_user() + ? Data::FromTL(*data.vdaily_revenue_per_user()) + : StarsAmount(); result.endDate = data.vend_date().value_or_empty(); return result; } diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index 26256dedc..38a8c8d4b 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "core/stars_amount.h" #include "data/data_birthday.h" #include "data/data_peer.h" #include "data/data_chat_participant_status.h" @@ -20,6 +21,7 @@ struct BusinessDetails; } // namespace Data struct StarRefProgram { + StarsAmount revenuePerUser; TimeId endDate = 0; ushort commission = 0; uint8 durationMonths = 0; diff --git a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_common.cpp b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_common.cpp index db86edab7..fa3a32f61 100644 --- a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_common.cpp +++ b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_common.cpp @@ -9,7 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "boxes/peers/replace_boost_box.h" // CreateUserpicsTransfer. +#include "boxes/send_credits_box.h" // Ui::CreditsEmoji. #include "chat_helpers/stickers_lottie.h" +#include "core/ui_integration.h" #include "data/data_document.h" #include "data/data_session.h" #include "history/view/media/history_view_sticker.h" @@ -499,8 +501,32 @@ object_ptr StarRefLinkBox( st::starrefCenteredText), st::boxRowPadding); - Ui::AddSkip(box->verticalLayout(), st::defaultVerticalListSkip * 4); - + Ui::AddSkip(box->verticalLayout(), st::defaultVerticalListSkip * 3); + if (const auto average = program.revenuePerUser) { + const auto layout = box->verticalLayout(); + const auto session = &peer->session(); + const auto makeContext = [session](Fn update) { + return Core::MarkedTextContext{ + .session = session, + .customEmojiRepaint = std::move(update), + }; + }; + auto text = Ui::Text::Colorized(Ui::CreditsEmoji(session)); + text.append(Lang::FormatStarsAmountDecimal(average)); + layout->add( + object_ptr( + box, + tr::lng_star_ref_one_daily_revenue( + lt_amount, + rpl::single(Ui::Text::Wrapped(text, EntityType::Bold)), + Ui::Text::WithEntities), + st::starrefRevenueText, + st::defaultPopupMenu, + makeContext), + st::boxRowPadding); + Ui::AddSkip(layout, st::defaultVerticalListSkip); + } +#if 0 box->addRow( object_ptr( box, @@ -510,6 +536,7 @@ object_ptr StarRefLinkBox( box->addRow(object_ptr::fromRaw( MakePeerBubbleButton(box, peer).release() ))->setAttribute(Qt::WA_TransparentForMouseEvents); +#endif struct State { QPointer weak; diff --git a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_join_widget.cpp b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_join_widget.cpp index f43983073..2a91bfa04 100644 --- a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_join_widget.cpp +++ b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_join_widget.cpp @@ -57,6 +57,7 @@ constexpr auto kPerPage = 50; enum class JoinType { Joined, Suggested, + Existing, }; class ListController final @@ -79,12 +80,14 @@ public: [[nodiscard]] rpl::producer rowCountValue() const; [[nodiscard]] rpl::producer connected() const; + [[nodiscard]] rpl::producer<> addForBotRequests() const; void process(ConnectedBot row); private: [[nodiscard]] std::unique_ptr createRow(ConnectedBot bot); void open(not_null bot, ConnectedBotState state); + void setupAddForBot(); const not_null _controller; const not_null _peer; @@ -95,6 +98,7 @@ private: UserData *_openOnResolve = nullptr; rpl::event_stream _connected; + rpl::event_stream<> _addForBot; mtpRequestId _requestId = 0; TimeId _offsetDate = 0; @@ -171,15 +175,16 @@ void ListController::loadMoreRows() { return; } else if (_type == JoinType::Joined) { using Flag = MTPpayments_GetConnectedStarRefBots::Flag; - _requestId = session().api().request(MTPpayments_GetConnectedStarRefBots( - MTP_flags(Flag() - | (_offsetDate ? Flag::f_offset_date : Flag()) - | (_offsetThing.isEmpty() ? Flag() : Flag::f_offset_link)), - _peer->input, - MTP_int(_offsetDate), - MTP_string(_offsetThing), - MTP_int(kPerPage) - )).done([=](const MTPpayments_ConnectedStarRefBots &result) { + _requestId = session().api().request( + MTPpayments_GetConnectedStarRefBots( + MTP_flags(Flag() + | (_offsetDate ? Flag::f_offset_date : Flag()) + | (_offsetThing.isEmpty() ? Flag() : Flag::f_offset_link)), + _peer->input, + MTP_int(_offsetDate), + MTP_string(_offsetThing), + MTP_int(kPerPage)) + ).done([=](const MTPpayments_ConnectedStarRefBots &result) { const auto parsed = Parse(&session(), result); if (parsed.empty()) { _allLoaded = true; @@ -195,6 +200,9 @@ void ListController::loadMoreRows() { _requestId = 0; }).send(); } else { + if (_type == JoinType::Existing) { + setDescriptionText(tr::lng_contacts_loading(tr::now)); + } using Flag = MTPpayments_GetSuggestedStarRefBots::Flag; _requestId = session().api().request( MTPpayments_GetSuggestedStarRefBots( @@ -203,6 +211,9 @@ void ListController::loadMoreRows() { MTP_string(_offsetThing), MTP_int(kPerPage)) ).done([=](const MTPpayments_SuggestedStarRefBots &result) { + setDescriptionText(QString()); + setupAddForBot(); + const auto &data = result.data(); if (data.vnext_offset()) { _offsetThing = qs(*data.vnext_offset()); @@ -210,20 +221,13 @@ void ListController::loadMoreRows() { _allLoaded = true; } session().data().processUsers(data.vusers()); - for (const auto &bot : data.vsuggested_bots().v) { - const auto &data = bot.data(); - const auto botId = UserId(data.vbot_id()); - const auto commission = data.vcommission_permille().v; - const auto durationMonths - = data.vduration_months().value_or_empty(); + for (const auto &program : data.vsuggested_bots().v) { + const auto botId = UserId(program.data().vbot_id()); const auto user = session().data().user(botId); delegate()->peerListAppendRow(createRow({ .bot = user, .state = { - .program = { - .commission = ushort(commission), - .durationMonths = uchar(durationMonths), - }, + .program = Data::ParseStarRefProgram(&program), .unresolved = true, }, })); @@ -238,6 +242,46 @@ void ListController::loadMoreRows() { } } +void ListController::setupAddForBot() { + const auto user = _peer->asUser(); + if (_type != JoinType::Existing + || !user + || !user->isBot() + || user->botInfo->starRefProgram.commission > 0) { + return; + } + auto button = object_ptr>( + nullptr, + object_ptr( + nullptr, + tr::lng_star_ref_add_bot(lt_bot, rpl::single(user->name())), + st::inviteViaLinkButton), + style::margins(0, st::membersMarginTop, 0, 0)); + + const auto icon = Ui::CreateChild( + button->entity(), + st::starrefAddForBotIcon, + QPoint()); + button->entity()->heightValue( + ) | rpl::start_with_next([=](int height) { + icon->moveToLeft( + st::starrefAddForBotIconPosition.x(), + (height - st::starrefAddForBotIcon.height()) / 2); + }, icon->lifetime()); + + button->entity()->setClickedCallback([=] { + _addForBot.fire({}); + }); + button->entity()->events( + ) | rpl::filter([=](not_null e) { + return (e->type() == QEvent::Enter); + }) | rpl::start_with_next([=] { + delegate()->peerListMouseLeftGeometry(); + }, button->lifetime()); + delegate()->peerListSetAboveWidget(std::move(button)); + delegate()->peerListRefreshRows(); +} + rpl::producer ListController::rowCountValue() const { return _rowCount.value(); } @@ -246,6 +290,10 @@ rpl::producer ListController::connected() const { return _connected.events(); } +rpl::producer<> ListController::addForBotRequests() const { + return _addForBot.events(); +} + void ListController::process(ConnectedBot row) { if (!delegate()->peerListFindRow(PeerListRowId(row.bot->id.value))) { delegate()->peerListPrependRow(createRow(row)); @@ -720,19 +768,27 @@ std::shared_ptr Make(not_null peer) { } object_ptr ProgramsListBox( - not_null controller, + not_null window, not_null peer) { + const auto weak = std::make_shared>(); const auto initBox = [=](not_null box) { + *weak = box; box->addButton(tr::lng_close(), [=] { box->closeBox(); }); }; - return Box( - std::make_unique( - controller, - peer, - JoinType::Suggested), - initBox); + + auto controller = std::make_unique( + window, + peer, + JoinType::Existing); + controller->addForBotRequests() | rpl::start_with_next([=] { + if (const auto strong = weak->data()) { + strong->closeBox(); + } + }, controller->lifetime()); + + return Box(std::move(controller), initBox); } } // namespace Info::BotStarRef::Join diff --git a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_join_widget.h b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_join_widget.h index e3b9c868e..ef9afee14 100644 --- a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_join_widget.h +++ b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_join_widget.h @@ -83,7 +83,7 @@ private: [[nodiscard]] std::shared_ptr Make(not_null peer); [[nodiscard]] object_ptr ProgramsListBox( - not_null controller, + not_null window, not_null peer); } // namespace Info::BotStarRef::Join diff --git a/Telegram/SourceFiles/ui/effects/premium.style b/Telegram/SourceFiles/ui/effects/premium.style index 2483bf73d..28b730868 100644 --- a/Telegram/SourceFiles/ui/effects/premium.style +++ b/Telegram/SourceFiles/ui/effects/premium.style @@ -441,6 +441,11 @@ starrefCenteredText: FlatLabel(defaultFlatLabel) { starrefJoinFooter: FlatLabel(starrefCenteredText) { textFg: windowSubTextFg; } +starrefRevenueText: FlatLabel(starrefCenteredText) { + palette: TextPalette(defaultTextPalette) { + linkFg: creditsBg1; + } +} starrefInfoIconPosition: point(16px, 8px); starrefBottomButton: RoundButton(defaultActiveButton) { @@ -469,4 +474,7 @@ starrefLinkCountAdd: 6px; starrefLinkCountIcon: icon{{ "chat/mini_subscribers", historyPeerUserpicFg }}; starrefLinkCountIconPosition: point(0px, 1px); starrefLinkCountFont: font(10px bold); -starrefLinkCountPadding: margins(2px, 0px, 3px, 1px); \ No newline at end of file +starrefLinkCountPadding: margins(2px, 0px, 3px, 1px); + +starrefAddForBotIcon: icon {{ "menu/bot_add", lightButtonFg }}; +starrefAddForBotIconPosition: point(23px, 2px);