From bd2e3956b5fc4a2b645ec610ea570d49863beb35 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 16 May 2026 23:34:00 +0300 Subject: [PATCH] Switched top bar suggestion driver to spec table. --- .../dialogs/dialogs_top_bar_suggestion.cpp | 681 ++---------------- 1 file changed, 79 insertions(+), 602 deletions(-) diff --git a/Telegram/SourceFiles/dialogs/dialogs_top_bar_suggestion.cpp b/Telegram/SourceFiles/dialogs/dialogs_top_bar_suggestion.cpp index 7a8750c30c..c657ce428f 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_top_bar_suggestion.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_top_bar_suggestion.cpp @@ -8,51 +8,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "dialogs/dialogs_top_bar_suggestion.h" #include "api/api_authorizations.h" -#include "api/api_credits.h" -#include "api/api_peer_photo.h" -#include "api/api_premium.h" #include "apiwrap.h" #include "base/call_delayed.h" -#include "boxes/star_gift_box.h" // ShowStarGiftBox. -#include "boxes/star_gift_auction_box.h" -#include "boxes/unconfirmed_auth_denied_box.h" #include "core/application.h" -#include "core/click_handler_types.h" -#include "core/ui_integration.h" #include "data/components/gift_auctions.h" #include "data/components/promo_suggestions.h" -#include "data/data_birthday.h" -#include "data/data_changes.h" #include "data/data_peer_values.h" // Data::AmPremiumValue. -#include "data/data_session.h" -#include "data/data_user.h" +#include "dialogs/suggestions/suggestion.h" #include "dialogs/ui/dialogs_top_bar_suggestion_content.h" -#include "history/view/history_view_group_call_bar.h" -#include "info/profile/info_profile_values.h" -#include "lang/lang_keys.h" #include "main/main_session.h" -#include "settings/sections/settings_active_sessions.h" -#include "settings/settings_credits_graphics.h" -#include "settings/sections/settings_premium.h" -#include "ui/boxes/confirm_box.h" -#include "ui/controls/userpic_button.h" -#include "ui/effects/credits_graphics.h" -#include "ui/layers/generic_box.h" -#include "ui/rect.h" -#include "ui/text/format_values.h" -#include "ui/text/text_utilities.h" -#include "ui/toast/toast.h" #include "ui/ui_utility.h" -#include "ui/vertical_list.h" -#include "ui/wrap/fade_wrap.h" #include "ui/wrap/slide_wrap.h" #include "window/window_controller.h" #include "window/window_session_controller.h" -#include "styles/style_boxes.h" -#include "styles/style_chat.h" -#include "styles/style_chat_helpers.h" #include "styles/style_dialogs.h" -#include "styles/style_layers.h" namespace Dialogs { namespace { @@ -64,88 +33,6 @@ namespace { return window->sessionController(); } -[[nodiscard]] QString FormatAuthInfo(const Data::UnreviewedAuth &auth) { - const auto location = auth.location.isEmpty() - ? QString() - : "\U0001F30D " + auth.location; - const auto device = auth.device.isEmpty() - ? QString() - : "\U0001F4F1 " + auth.device; - - if (!location.isEmpty() && !device.isEmpty()) { - return location + " (" + device + ")"; - } else if (!location.isEmpty()) { - return location; - } else if (!device.isEmpty()) { - return device; - } - return QString(); -} - -void ShowAuthToast( - not_null parent, - not_null session, - const std::vector &list, - bool confirmed) { - if (confirmed) { - auto text = tr::lng_unconfirmed_auth_confirmed_message( - tr::now, - lt_link, - tr::link(tr::lng_settings_sessions_title(tr::now)), - tr::rich); - auto filter = [=]( - ClickHandlerPtr handler, - Qt::MouseButton button) { - if (const auto controller = FindSessionController(parent)) { - session->api().authorizations().reload(); - controller->showSettings(Settings::SessionsId()); - return false; - } - return true; - }; - Ui::Toast::Show(parent->window(), Ui::Toast::Config{ - .title = tr::lng_unconfirmed_auth_confirmed(tr::now), - .text = std::move(text), - .filter = std::move(filter), - .duration = crl::time(5000), - }); - } else { - auto messageText = QString(); - if (list.size() == 1) { - messageText = tr::lng_unconfirmed_auth_denied_single( - tr::now, - lt_country, - FormatAuthInfo(list.front())); - } else { - auto authList = QString('\n'); - for (auto i = 0; i < std::min(int(list.size()), 10); ++i) { - const auto info = FormatAuthInfo(list[i]); - if (!info.isEmpty()) { - authList += "• " + info + "\n"; - } - } - messageText = tr::lng_unconfirmed_auth_denied_multiple( - tr::now, - lt_country, - authList); - } - if (const auto controller = FindSessionController(parent)) { - const auto count = float64(list.size()); - controller->show(Box(ShowAuthDeniedBox, count, messageText)); - } - } -} - -constexpr auto kSugSetBirthday = "BIRTHDAY_SETUP"_cs; -constexpr auto kSugBirthdayContacts = "BIRTHDAY_CONTACTS_TODAY"_cs; -constexpr auto kSugPremiumAnnual = "PREMIUM_ANNUAL"_cs; -constexpr auto kSugPremiumUpgrade = "PREMIUM_UPGRADE"_cs; -constexpr auto kSugPremiumRestore = "PREMIUM_RESTORE"_cs; -constexpr auto kSugPremiumGrace = "PREMIUM_GRACE"_cs; -constexpr auto kSugSetUserpic = "USERPIC_SETUP"_cs; -constexpr auto kSugLowCreditsSubs = "STARS_SUBSCRIPTION_LOW_BALANCE"_cs; - - } // namespace rpl::producer*> TopBarSuggestionValue( @@ -169,21 +56,18 @@ rpl::producer*> TopBarSuggestionValue( struct State { TopBarSuggestionContent *content = nullptr; - UnconfirmedAuthWrap *unconfirmedWarning = nullptr; base::unique_qptr> wrap; rpl::variable desiredWrapToggle; rpl::variable outerWrapToggle; - rpl::lifetime birthdayLifetime; - rpl::lifetime premiumLifetime; - rpl::lifetime userpicLifetime; - rpl::lifetime creditsLifetime; - rpl::lifetime auctionsLifetime; - std::unique_ptr creditsHistory; + rpl::lifetime activeLifetime; + Fn prepareSnapshot; }; const auto state = lifetime.make_state(); state->outerWrapToggle = rpl::duplicate(outerWrapToggleValue); - const auto ensureContent = [=] { + + const auto ensureContent + = [=]() -> not_null { if (!state->content) { const auto window = FindSessionController(parent); state->content = Ui::CreateChild( @@ -193,497 +77,92 @@ rpl::producer*> TopBarSuggestionValue( state->content->setCollapseProgress( rpl::duplicate(childListShown)); } + return state->content; }; - const auto ensureWrap = [=](not_null child) { - if (!state->wrap) { - state->wrap - = base::make_unique_q>( - parent, - object_ptr::fromRaw(child)); - state->desiredWrapToggle.force_assign( - Toggle{ false, anim::type::instant }); - } + + const auto context = TopBarSuggestions::Context{ + .parent = parent, + .session = session, + .ensureContent = ensureContent, + .findController = [=]() + -> not_null { + return FindSessionController(parent); + }, + .childListShown = [=]() -> rpl::producer { + return rpl::duplicate(childListShown); + }, }; + const auto specs = lifetime.make_state>(TopBarSuggestions::AllSpecs()); + ranges::sort( + *specs, + std::greater<>(), + &TopBarSuggestions::Spec::priority); + const auto processCurrentSuggestion = [=](auto repeat) -> void { - state->birthdayLifetime.destroy(); - state->premiumLifetime.destroy(); - state->userpicLifetime.destroy(); - state->creditsLifetime.destroy(); - state->auctionsLifetime.destroy(); + state->activeLifetime.destroy(); - if (!session->api().authorizations().unreviewed().empty()) { - state->content = nullptr; - state->wrap = nullptr; - const auto &list - = session->api().authorizations().unreviewed(); - const auto hashes = ranges::views::all( - list - ) | ranges::views::transform([](const auto &auth) { - return auth.hash; - }) | ranges::to_vector; + const auto recompute = [=] { repeat(repeat); }; + const auto wonHere = std::make_shared(false); + const auto activated = std::make_shared(false); - const auto content = CreateUnconfirmedAuthContent( - parent, - list, - [=](bool confirmed) { - ShowAuthToast(parent, session, list, confirmed); - session->api().authorizations().review( - hashes, - confirmed); + for (auto i = 0; i < int(specs->size()); ++i) { + const auto &spec = (*specs)[i]; + if (!spec.available(context)) { + continue; + } + + *activated = true; + + auto args = TopBarSuggestions::ActivateArgs{ + .context = context, + .lifetime = &state->activeLifetime, + .done = [=]( + not_null widget, + Fn prepareSnapshot) { + if (state->wrap + && state->wrap->entity() != widget.get()) { + state->wrap = nullptr; + if (widget.get() != state->content) { + state->content = nullptr; + } + state->prepareSnapshot = nullptr; + } + if (!state->wrap) { + state->wrap = base::make_unique_q< + Ui::SlideWrap>( + parent, + object_ptr::fromRaw(widget)); + state->desiredWrapToggle.force_assign( + Toggle{ false, anim::type::instant }); + state->prepareSnapshot = std::move( + prepareSnapshot); + } + state->desiredWrapToggle.force_assign( + Toggle{ true, anim::type::normal }); + *wonHere = true; }, - rpl::duplicate(childListShown)); - ensureWrap(content); - const auto wasUnconfirmedWarning = state->unconfirmedWarning; - state->unconfirmedWarning = content; - state->desiredWrapToggle.force_assign(Toggle{ - true, - (state->unconfirmedWarning != wasUnconfirmedWarning) - ? anim::type::instant - : anim::type::normal, - }); - return; - } else { - if (state->unconfirmedWarning) { - state->unconfirmedWarning = nullptr; - state->wrap = nullptr; - } + .recompute = recompute, + }; + spec.activate(std::move(args)); + break; } - ensureContent(); - ensureWrap(state->content); - const auto content = state->content; + if (*wonHere || *activated) { + return; + } + + if (!state->wrap) { + return; + } const auto wrap = state->wrap.get(); - using RightIcon = TopBarSuggestionContent::RightIcon; - const auto promo = &session->promoSuggestions(); - const auto auctions = &session->giftAuctions(); - if (auctions->hasActive()) { - using namespace Data; - struct Button { - rpl::variable text; - Fn callback; - base::has_weak_ptr guard; - }; - auto &lifetime = state->auctionsLifetime; - const auto button = lifetime.template make_state