From 640db8af7dddba633887f1da4aa34ea308dad379 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 5 Apr 2025 19:17:25 +0300 Subject: [PATCH] Added suggestion for annual premium to top bar in dialogs. --- Telegram/Resources/langs/lang.strings | 2 + .../SourceFiles/api/api_premium_option.cpp | 1 + .../data/data_premium_subscription_option.h | 1 + Telegram/SourceFiles/dialogs/dialogs.style | 7 ++ .../dialogs/dialogs_top_bar_suggestion.cpp | 46 +++++++++++++ .../SourceFiles/dialogs/dialogs_widget.cpp | 4 +- Telegram/SourceFiles/dialogs/dialogs_widget.h | 2 +- .../ui/dialogs_top_bar_suggestion_content.cpp | 65 ++++++++++++++----- .../ui/dialogs_top_bar_suggestion_content.h | 10 +++ 9 files changed, 118 insertions(+), 20 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index bc008b1fba..02dd7a92fd 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -3865,6 +3865,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_dialogs_top_bar_suggestions_birthday_title" = "Add your birthday! 🎂"; "lng_dialogs_top_bar_suggestions_birthday_about" = "Let your contacts know when you’re celebrating."; +"lng_dialogs_top_bar_suggestions_premium_annual_title" = "Telegram Premium with a {text} discount"; +"lng_dialogs_top_bar_suggestions_premium_annual_about" = "Sign up for the annual payment plan for Telegram Premium now to get the discount."; "lng_about_random" = "Send a {emoji} emoji to any chat to try your luck."; "lng_about_random_send" = "Send"; diff --git a/Telegram/SourceFiles/api/api_premium_option.cpp b/Telegram/SourceFiles/api/api_premium_option.cpp index d3c67e23b5..c4346dbe07 100644 --- a/Telegram/SourceFiles/api/api_premium_option.cpp +++ b/Telegram/SourceFiles/api/api_premium_option.cpp @@ -25,6 +25,7 @@ Data::PremiumSubscriptionOption CreateSubscriptionOption( * kDiscountDivider; }(); return { + .months = months, .duration = Ui::FormatTTL(months * 86400 * 31), .discount = (discount > 0) ? QString::fromUtf8("\xe2\x88\x92%1%").arg(discount) diff --git a/Telegram/SourceFiles/data/data_premium_subscription_option.h b/Telegram/SourceFiles/data/data_premium_subscription_option.h index 6d5bd5e733..df2c4a8c4e 100644 --- a/Telegram/SourceFiles/data/data_premium_subscription_option.h +++ b/Telegram/SourceFiles/data/data_premium_subscription_option.h @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Data { struct PremiumSubscriptionOption { + int months = 0; QString duration; QString discount; QString costPerMonth; diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 9d4323f19c..0269c5c1a8 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -813,3 +813,10 @@ dialogsSponsoredButton: DialogRightButton(dialogRowOpenBot) { } margin: margins(0px, 9px, 10px, 0px); } + +dialogsTopBarSuggestionTitleStyle: TextStyle(defaultTextStyle) { + font: font(semibold 12px); +} +dialogsTopBarSuggestionAboutStyle: TextStyle(defaultTextStyle) { + font: font(11px); +} diff --git a/Telegram/SourceFiles/dialogs/dialogs_top_bar_suggestion.cpp b/Telegram/SourceFiles/dialogs/dialogs_top_bar_suggestion.cpp index 656686b25b..9589cba5a7 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_top_bar_suggestion.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_top_bar_suggestion.cpp @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "dialogs/dialogs_top_bar_suggestion.h" +#include "api/api_premium.h" +#include "apiwrap.h" #include "base/call_delayed.h" #include "core/application.h" #include "core/click_handler_types.h" @@ -17,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "main/main_app_config.h" #include "main/main_session.h" +#include "settings/settings_premium.h" #include "ui/text/text_utilities.h" #include "ui/wrap/slide_wrap.h" #include "window/window_controller.h" @@ -32,6 +35,7 @@ namespace { } constexpr auto kSugSetBirthday = "BIRTHDAY_SETUP"_cs; +constexpr auto kSugPremiumAnnual = "PREMIUM_ANNUAL"_cs; } // namespace @@ -46,12 +50,14 @@ object_ptr> CreateTopBarSuggestion( wrap->toggle(false, anim::type::instant); struct State { rpl::lifetime birthdayLifetime; + rpl::lifetime premiumLifetime; }; const auto state = content->lifetime().make_state(); const auto processCurrentSuggestion = [=](auto repeat) -> void { if (session->appConfig().suggestionCurrent(kSugSetBirthday.utf8()) && !Data::IsBirthdayToday(session->user()->birthday())) { + content->setRightIcon(TopBarSuggestionContent::RightIcon::Close); content->setClickedCallback([=] { const auto controller = FindSessionController(parent); if (!controller) { @@ -86,6 +92,46 @@ object_ptr> CreateTopBarSuggestion( tr::now, TextWithEntities::Simple)); wrap->toggle(true, anim::type::normal); + } else if (session->premiumPossible() + && !session->premium() + && session->appConfig().suggestionCurrent( + kSugPremiumAnnual.utf8())) { + content->setRightIcon(TopBarSuggestionContent::RightIcon::Arrow); + const auto api = &session->api().premium(); + const auto set = [=](QString discount) { + constexpr auto kMinus = QChar(0x2212); + content->setContent( + tr::lng_dialogs_top_bar_suggestions_premium_annual_title( + tr::now, + lt_text, + { discount.replace(kMinus, QChar()) }, + Ui::Text::Bold), + tr::lng_dialogs_top_bar_suggestions_premium_annual_about( + tr::now, + TextWithEntities::Simple)); + content->setClickedCallback([=] { + const auto controller = FindSessionController(parent); + if (!controller) { + return; + } + Settings::ShowPremium(controller, "dialogs_hint"); + session->appConfig().dismissSuggestion( + kSugPremiumAnnual.utf8()); + repeat(repeat); + }); + wrap->toggle(true, anim::type::normal); + }; + api->statusTextValue( + ) | rpl::start_with_next([=] { + for (const auto &option : api->subscriptionOptions()) { + if (option.months == 12) { + set(option.discount); + state->premiumLifetime.destroy(); + return; + } + } + }, state->premiumLifetime); + api->reload(); } else { wrap->toggle(false, anim::type::normal); base::call_delayed(st::slideWrapDuration * 2, wrap, [=] { diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 56a7893ce0..ef1913176c 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -392,7 +392,9 @@ Widget::Widget( ) | rpl::start_with_next([=](int desiredHeight, float64 shown) { const auto newHeight = desiredHeight * (1. - shown); _topBarSuggestion->entity()->setMaximumHeight(newHeight); - _topBarSuggestion->entity()->setMinimumWidth(width()); + _topBarSuggestion->entity()->setMinimumWidth((shown > 0) + ? width() + : 0); _topBarSuggestion->entity()->resize(width(), newHeight); }, _topBarSuggestion->lifetime()); } diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.h b/Telegram/SourceFiles/dialogs/dialogs_widget.h index 5f2699cb47..2a6e8d4aca 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.h @@ -301,7 +301,7 @@ private: base::Timer _chooseByDragTimer; const Layout _layout = Layout::Main; - int _narrowWidth = 0; + const int _narrowWidth = 0; std::unique_ptr _frozenAccountBar; diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_top_bar_suggestion_content.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_top_bar_suggestion_content.cpp index 64fc212f5a..c085d9c8e8 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_top_bar_suggestion_content.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_top_bar_suggestion_content.cpp @@ -6,27 +6,57 @@ For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "dialogs/ui/dialogs_top_bar_suggestion_content.h" +#include "ui/ui_rpl_filter.h" #include "styles/style_chat.h" #include "styles/style_chat_helpers.h" #include "styles/style_dialogs.h" +#include "styles/style_settings.h" namespace Dialogs { -namespace { -} // namespace TopBarSuggestionContent::TopBarSuggestionContent(not_null p) : Ui::RippleButton(p, st::defaultRippleAnimationBgOver) , _titleSt(st::semiboldTextStyle) -, _contentTitleSt(st::semiboldTextStyle) -, _contentTextSt(st::defaultTextStyle) -, _rightHide( - base::make_unique_q( - this, - st::dialogsCancelSearchInPeer)) { - const auto rightHide = _rightHide.get(); - sizeValue() | rpl::start_with_next([=](const QSize &s) { - rightHide->moveToRight(st::buttonRadius, st::lineWidth); - }, rightHide->lifetime()); +, _contentTitleSt(st::dialogsTopBarSuggestionTitleStyle) +, _contentTextSt(st::dialogsTopBarSuggestionAboutStyle) { + setRightIcon(RightIcon::Close); +} + +void TopBarSuggestionContent::setRightIcon(RightIcon icon) { + if (icon == _rightIcon) { + return; + } + _rightHide = nullptr; + _rightArrow = nullptr; + _rightIcon = icon; + if (icon == RightIcon::Close) { + _rightHide = base::make_unique_q( + this, + st::dialogsCancelSearchInPeer); + const auto rightHide = _rightHide.get(); + sizeValue() | rpl::filter_size( + ) | rpl::start_with_next([=](const QSize &s) { + rightHide->moveToRight(st::buttonRadius, st::lineWidth); + }, rightHide->lifetime()); + rightHide->show(); + } else if (icon == RightIcon::Arrow) { + _rightArrow = base::make_unique_q( + this, + st::backButton); + const auto arrow = _rightArrow.get(); + arrow->setIconOverride( + &st::settingsPremiumArrow, + &st::settingsPremiumArrowOver); + arrow->setAttribute(Qt::WA_TransparentForMouseEvents); + sizeValue() | rpl::filter_size( + ) | rpl::start_with_next([=](const QSize &s) { + const auto &point = st::settingsPremiumArrowShift; + arrow->moveToLeft( + s.width() - arrow->width(), + point.y() + (s.height() - arrow->height()) / 2); + }, arrow->lifetime()); + arrow->show(); + } } void TopBarSuggestionContent::draw(QPainter &p) { @@ -41,28 +71,26 @@ void TopBarSuggestionContent::draw(QPainter &p) { const auto rightPadding = st::msgReplyBarSkip; const auto topPadding = st::msgReplyPadding.top(); const auto availableWidthNoPhoto = r.width() + - (_rightArrow ? _rightArrow->width() / 2 : 0) // Takes full height. - leftPadding - rightPadding; const auto availableWidth = availableWidthNoPhoto - (_rightHide ? _rightHide->width() : 0); - const auto titleRight = leftPadding - + _titleSt.font->spacew * 2; + const auto titleRight = leftPadding; const auto hasSecondLineTitle = (titleRight > (availableWidth - _contentTitle.maxWidth())); p.setPen(st::windowActiveTextFg); p.setPen(st::windowFg); { const auto left = leftPadding; - const auto top = hasSecondLineTitle - ? (topPadding + _titleSt.font->height) - : topPadding; + const auto top = topPadding; _contentTitle.draw(p, { .position = QPoint(left, top), .outerWidth = hasSecondLineTitle ? availableWidth : (availableWidth - titleRight), .availableWidth = availableWidth, - .elisionLines = 1, + .elisionLines = hasSecondLineTitle ? 2 : 1, }); } { @@ -94,6 +122,7 @@ void TopBarSuggestionContent::draw(QPainter &p) { : availableWidth, }; }; + p.setPen(st::windowSubTextFg); _contentText.draw(p, { .position = QPoint(left, top), .outerWidth = availableWidth, diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_top_bar_suggestion_content.h b/Telegram/SourceFiles/dialogs/ui/dialogs_top_bar_suggestion_content.h index 65190aca86..fc9868cf7c 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_top_bar_suggestion_content.h +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_top_bar_suggestion_content.h @@ -18,6 +18,12 @@ namespace Dialogs { class TopBarSuggestionContent : public Ui::RippleButton { public: + enum class RightIcon { + None, + Close, + Arrow, + }; + TopBarSuggestionContent(not_null); void setContent( @@ -27,6 +33,7 @@ public: [[nodiscard]] rpl::producer desiredHeightValue() const override; void setHideCallback(Fn); + void setRightIcon(RightIcon); protected: void paintEvent(QPaintEvent *) override; @@ -44,8 +51,11 @@ private: rpl::variable _lastPaintedContentTop = 0; base::unique_qptr _rightHide; + base::unique_qptr _rightArrow; Fn _hideCallback; + RightIcon _rightIcon = RightIcon::None; + std::shared_ptr _rightPhoto; QImage _rightPhotoImage;