From 6d8012f13a594f341bdc0c854378017d41ac3924 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 14 Jun 2022 13:42:43 +0400 Subject: [PATCH] Pause premium settings animations. --- Telegram/Resources/langs/lang.strings | 4 +- .../SourceFiles/boxes/premium_preview_box.cpp | 91 +++++++-- .../SourceFiles/boxes/premium_preview_box.h | 5 + .../SourceFiles/settings/settings_premium.cpp | 182 +++++++++++------- .../SourceFiles/settings/settings_premium.h | 15 ++ .../ui/effects/premium_graphics.cpp | 8 +- .../ui/widgets/gradient_round_button.cpp | 6 +- .../ui/widgets/gradient_round_button.h | 4 +- 8 files changed, 218 insertions(+), 97 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 29a83debc..8fc2c693d 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1672,6 +1672,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_premium" = "Premium"; "lng_premium_free" = "Free"; "lng_premium_more_about" = "More About Telegram Premium"; +"lng_premium_unlock_reactions" = "Unlock Premium Reactions"; +"lng_premium_unlock_stickers" = "Unlock Premium Stickers"; "lng_premium_summary_title" = "Telegram Premium"; "lng_premium_summary_top_about" = "Go **beyond the limits**, get **exclusive features** and support us by subscribing to **Telegram Premium**."; @@ -1739,7 +1741,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_premium_double_limits_about_folder_chats#other" = "Add up to {count} chats into each of your folders"; "lng_premium_double_limits_subtitle_accounts" = "Connected Accounts"; -"lng_premium_double_limits_about_accounts#one" = "Connect {count} accounts with different mobile numbers"; +"lng_premium_double_limits_about_accounts#one" = "Connect {count} account with different mobile numbers"; "lng_premium_double_limits_about_accounts#other" = "Connect {count} accounts with different mobile numbers"; // diff --git a/Telegram/SourceFiles/boxes/premium_preview_box.cpp b/Telegram/SourceFiles/boxes/premium_preview_box.cpp index ffc91a556..13d3a3c8e 100644 --- a/Telegram/SourceFiles/boxes/premium_preview_box.cpp +++ b/Telegram/SourceFiles/boxes/premium_preview_box.cpp @@ -61,12 +61,15 @@ struct Descriptor { PremiumPreview section = PremiumPreview::Stickers; DocumentData *requestedSticker = nullptr; base::flat_map disabled; + bool fromSettings = false; + Fn hiddenCallback; }; bool operator==(const Descriptor &a, const Descriptor &b) { return (a.section == b.section) && (a.requestedSticker == b.requestedSticker) - && (a.disabled == b.disabled); + && (a.disabled == b.disabled) + && (a.fromSettings == b.fromSettings); } bool operator!=(const Descriptor &a, const Descriptor &b) { @@ -1146,27 +1149,27 @@ void ReactionPreview::paintEffect(QPainter &p) { } } -[[nodiscard]] object_ptr CreateGradientButton( +[[nodiscard]] object_ptr CreateGradientButton( QWidget *parent, QGradientStops stops) { return object_ptr(parent, std::move(stops)); } -[[nodiscard]] object_ptr CreatePremiumButton( +[[nodiscard]] object_ptr CreatePremiumButton( QWidget *parent) { return CreateGradientButton(parent, Ui::Premium::ButtonGradientStops()); } -[[nodiscard]] object_ptr CreateUnlockButton( +[[nodiscard]] object_ptr CreateUnlockButton( QWidget *parent, - int width) { + rpl::producer text) { auto result = CreatePremiumButton(parent); const auto &st = st::premiumPreviewBox.button; - result->resize(width, st.height); + result->resize(result->width(), st.height); const auto label = Ui::CreateChild( result.data(), - tr::lng_premium_more_about(), + std::move(text), st::premiumPreviewButtonLabel); label->setAttribute(Qt::WA_TransparentForMouseEvents); rpl::combine( @@ -1250,6 +1253,8 @@ void PreviewBox( bool stickersPreloadReady = false; Ui::RpWidget *reactionsPreload = nullptr; bool reactionsPreloadReady = false; + bool preloadScheduled = false; + bool showFinished = false; Ui::Animations::Simple animation; Fn preload; std::vector hiding; @@ -1259,6 +1264,10 @@ void PreviewBox( state->selected = descriptor.section; state->preload = [=] { + if (!state->showFinished) { + state->preloadScheduled = true; + return; + } const auto now = state->selected.current(); if (now != PremiumPreview::Stickers && !state->stickersPreload) { const auto ready = [=] { @@ -1294,12 +1303,17 @@ void PreviewBox( switch (descriptor.section) { case PremiumPreview::Stickers: - Assert(media != nullptr); - state->content = StickerPreview( - outer, - controller, - media, - state->preload); + state->content = media + ? StickerPreview( + outer, + controller, + media, + state->preload) + : GenericPreview( + outer, + controller, + descriptor.section, + state->preload); break; case PremiumPreview::Reactions: state->content = ReactionsPreview( @@ -1428,11 +1442,39 @@ void PreviewBox( const auto width = size.width() - buttonPadding.left() - buttonPadding.right(); - auto button = CreateUnlockButton(box, width); + const auto computeRef = [=] { + return Settings::LookupPremiumRef(state->selected.current()); + }; + auto unlock = state->selected.value( + ) | rpl::map([=](PremiumPreview section) { + return (section == PremiumPreview::Reactions) + ? tr::lng_premium_unlock_reactions() + : (section == PremiumPreview::Stickers) + ? tr::lng_premium_unlock_stickers() + : tr::lng_premium_more_about(); + }) | rpl::flatten_latest(); + auto button = descriptor.fromSettings + ? object_ptr::fromRaw( + Settings::CreateSubscribeButton(controller, box, computeRef)) + : CreateUnlockButton(box, std::move(unlock)); + button->resizeToWidth(width); button->setClickedCallback([=] { - Settings::ShowPremium(controller, "premium_stickers"); + Settings::ShowPremium( + controller, + Settings::LookupPremiumRef(state->selected.current())); + }); + box->setShowFinishedCallback([=, raw = button.data()] { + state->showFinished = true; + if (base::take(state->preloadScheduled)) { + state->preload(); + } + raw->startGlareAnimation(); }); box->addButton(std::move(button)); + + if (const auto &hidden = descriptor.hiddenCallback) { + box->boxClosing() | rpl::start_with_next(hidden, box->lifetime()); + } } void Show( @@ -1573,6 +1615,17 @@ void ShowPremiumPreviewBox( }); } +void ShowPremiumPreviewToBuy( + not_null controller, + PremiumPreview section, + Fn hiddenCallback) { + Show(controller, Descriptor{ + .section = section, + .fromSettings = true, + .hiddenCallback = std::move(hiddenCallback), + }); +} + void PremiumUnavailableBox(not_null box) { Ui::ConfirmBox(box, { .text = tr::lng_premium_unavailable( @@ -1744,15 +1797,19 @@ void DoubledLimitsPreviewBox( premium, }); } + const auto now = session->domain().accounts().size(); + const auto till = (now + 1 >= Main::Domain::kPremiumMaxAccounts) + ? QString::number(Main::Domain::kPremiumMaxAccounts) + : (QString::number(now + 1) + QChar('+')); entries.push_back(Ui::Premium::ListEntry{ tr::lng_premium_double_limits_subtitle_accounts(), tr::lng_premium_double_limits_about_accounts( lt_count, - rpl::single(float64(Main::Domain::kMaxAccounts)), + rpl::single(float64(Main::Domain::kPremiumMaxAccounts)), Ui::Text::RichLangValue), Main::Domain::kMaxAccounts, Main::Domain::kPremiumMaxAccounts, - QString::number(Main::Domain::kMaxAccounts + 1) + QChar('+'), + till, }); Ui::Premium::ShowListBox(box, std::move(entries)); } diff --git a/Telegram/SourceFiles/boxes/premium_preview_box.h b/Telegram/SourceFiles/boxes/premium_preview_box.h index 17f26f9dc..0cd6fa341 100644 --- a/Telegram/SourceFiles/boxes/premium_preview_box.h +++ b/Telegram/SourceFiles/boxes/premium_preview_box.h @@ -53,4 +53,9 @@ void ShowPremiumPreviewBox( PremiumPreview section, const base::flat_map &disabled = {}); +void ShowPremiumPreviewToBuy( + not_null controller, + PremiumPreview section, + Fn hiddenCallback); + void PremiumUnavailableBox(not_null box); diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp index 17ce38ba3..01b271f55 100644 --- a/Telegram/SourceFiles/settings/settings_premium.cpp +++ b/Telegram/SourceFiles/settings/settings_premium.cpp @@ -178,49 +178,6 @@ using Order = std::vector; }; } -[[nodiscard]] not_null CreateSubscribeButton( - not_null controller, - not_null parent, - Fn callback) { - const auto result = Ui::CreateChild( - parent.get(), - Ui::Premium::ButtonGradientStops()); - - result->setClickedCallback(std::move(callback)); - - const auto &st = st::premiumPreviewBox.button; - result->resize(parent->width(), st.height); - - const auto premium = &controller->session().api().premium(); - const auto computeCost = [=] { - const auto amount = premium->monthlyAmount(); - const auto currency = premium->monthlyCurrency(); - const auto valid = (amount > 0) && !currency.isEmpty(); - return Ui::FillAmountAndCurrency( - valid ? amount : 500, - valid ? currency : "USD"); - }; - - const auto label = Ui::CreateChild( - result, - tr::lng_premium_summary_button( - lt_cost, - premium->statusTextValue() | rpl::map(computeCost)), - st::premiumPreviewButtonLabel); - label->setAttribute(Qt::WA_TransparentForMouseEvents); - rpl::combine( - result->widthValue(), - label->widthValue() - ) | rpl::start_with_next([=](int outer, int width) { - label->moveToLeft( - (outer - width) / 2, - st::premiumPreviewBox.button.textTop, - outer); - }, label->lifetime()); - - return result; -} - void SendAppLog( not_null session, const QString &type, @@ -278,6 +235,7 @@ public: MiniStars(Fn updateCallback); void paint(Painter &p, const QRectF &rect); + void setPaused(bool paused); private: struct MiniStar { @@ -317,6 +275,7 @@ private: std::vector _ministars; crl::time _nextBirthTime = 0; + bool _paused = false; QRect _rectToUpdate; @@ -339,7 +298,7 @@ MiniStars::MiniStars(Fn updateCallback) , _distanceProgressStart(0.5) , _sprite(u":/gui/icons/settings/starmini.svg"_q) , _animation([=](crl::time now) { - if (now > _nextBirthTime) { + if (now > _nextBirthTime && !_paused) { createStar(now); _nextBirthTime = now + randomInterval(_lifeLength); } @@ -423,6 +382,10 @@ void MiniStars::paint(Painter &p, const QRectF &rect) { p.setOpacity(opacity); } +void MiniStars::setPaused(bool paused) { + _paused = paused; +} + int MiniStars::angle() const { const auto &interval = _availableAngles[ base::RandomIndex(_availableAngles.size())]; @@ -456,6 +419,7 @@ public: rpl::producer title, rpl::producer about); + void setPaused(bool paused); void setRoundEdges(bool value); void setTextPosition(int x, int y); @@ -523,6 +487,10 @@ TopBar::TopBar( }); } +void TopBar::setPaused(bool paused) { + _ministars.setPaused(paused); +} + void TopBar::setRoundEdges(bool value) { _roundEdges = value; update(); @@ -578,17 +546,6 @@ void TopBar::paintEvent(QPaintEvent *e) { p.fillRect(e->rect(), Qt::transparent); const auto r = rect(); - auto pathTop = QPainterPath(); - if (_roundEdges) { - pathTop.addRoundedRect(r, st::boxRadius, st::boxRadius); - } else { - pathTop.addRect(r); - } - auto pathBottom = QPainterPath(); - pathBottom.addRect( - QRect( - QPoint(r.x(), r.y() + r.height() - st::boxRadius), - QSize(r.width(), st::boxRadius))); const auto gradientPointTop = r.height() / 3. * 2.; auto gradient = QLinearGradient( @@ -599,7 +556,16 @@ void TopBar::paintEvent(QPaintEvent *e) { gradient.setColorAt(1., st::premiumButtonBg3->c); PainterHighQualityEnabler hq(p); - p.fillPath(pathTop + pathBottom, gradient); + if (_roundEdges) { + const auto radius = st::boxRadius; + p.setBrush(gradient); + p.drawRoundedRect( + r + QMargins{ 0, 0, 0, radius + 1 }, + radius, + radius); + } else { + p.fillRect(r, gradient); + } p.setOpacity(_progress.body); p.translate(_starRect.center()); @@ -663,10 +629,12 @@ private: const not_null _controller; const QString _ref; + QPointer _subscribe; base::unique_qptr> _back; base::unique_qptr _close; rpl::variable _backToggles; rpl::variable _wrap; + Fn _setPaused; rpl::event_stream<> _showBack; rpl::event_stream<> _showFinished; @@ -791,21 +759,30 @@ void Premium::setupContent() { const auto section = entry.section; button->setClickedCallback([=, controller = _controller] { + _setPaused(true); + const auto hidden = crl::guard(this, [=] { + _setPaused(false); + }); + if (section) { - ShowPremiumPreviewBox(controller, *section); + ShowPremiumPreviewToBuy(controller, *section, hidden); return; } controller->show(Box([=](not_null box) { DoubledLimitsPreviewBox(box, &controller->session()); - auto callback = [=] { - SendScreenAccept(controller); - StartPremiumPayment(controller, _ref); - }; const auto button = CreateSubscribeButton( controller, box, - std::move(callback)); + [] { return u"double_limits"_q; }); + + box->boxClosing( + ) | rpl::start_with_next(hidden, box->lifetime()); + + box->setShowFinishedCallback([=] { + button->startGlareAnimation(); + }); + box->setStyle(st::premiumPreviewDoubledLimitsBox); box->widthValue( ) | rpl::start_with_next([=](int width) { @@ -925,6 +902,10 @@ QPointer Premium::createPinnedToTop( _controller, std::move(title), std::move(about)); + _setPaused = [=](bool paused) { + content->setPaused(paused); + _subscribe->setGlarePaused(paused); + }; _wrap.value( ) | rpl::start_with_next([=](Info::Wrap wrap) { @@ -988,25 +969,24 @@ QPointer Premium::createPinnedToBottom( not_null parent) { const auto content = Ui::CreateChild(parent.get()); - const auto button = CreateSubscribeButton(_controller, content, [=] { - SendScreenAccept(_controller); - StartPremiumPayment(_controller, _ref); + _subscribe = CreateSubscribeButton(_controller, content, [=] { + return _ref; }); _showFinished.events( ) | rpl::take(1) | rpl::start_with_next([=] { - button->startGlareAnimation(); - }, button->lifetime()); + _subscribe->startGlareAnimation(); + }, _subscribe->lifetime()); content->widthValue( ) | rpl::start_with_next([=](int width) { const auto padding = st::settingsPremiumButtonPadding; - button->resizeToWidth(width - padding.left() - padding.right()); - }, button->lifetime()); + _subscribe->resizeToWidth(width - padding.left() - padding.right()); + }, _subscribe->lifetime()); const auto session = &_controller->session(); rpl::combine( - button->heightValue(), + _subscribe->heightValue(), Data::AmPremiumValue(session), session->premiumPossibleValue() ) | rpl::start_with_next([=]( @@ -1020,9 +1000,9 @@ QPointer Premium::createPinnedToBottom( ? (padding.top() + buttonHeight + padding.bottom()) : 0; content->resize(content->width(), finalHeight); - button->moveToLeft(padding.left(), padding.top()); - button->setVisible(!premium && premiumPossible); - }, button->lifetime()); + _subscribe->moveToLeft(padding.left(), padding.top()); + _subscribe->setVisible(!premium && premiumPossible); + }, _subscribe->lifetime()); return Ui::MakeWeak(not_null{ content }); } @@ -1082,4 +1062,60 @@ void StartPremiumPayment( } } +QString LookupPremiumRef(PremiumPreview section) { + for (const auto &[ref, entry] : EntryMap()) { + if (entry.section == section) { + return ref; + } + } + return QString(); +} + +not_null CreateSubscribeButton( + not_null controller, + not_null parent, + Fn computeRef) { + const auto result = Ui::CreateChild( + parent.get(), + Ui::Premium::ButtonGradientStops()); + + result->setClickedCallback([=] { + SendScreenAccept(controller); + StartPremiumPayment(controller, computeRef()); + }); + + const auto &st = st::premiumPreviewBox.button; + result->resize(parent->width(), st.height); + + const auto premium = &controller->session().api().premium(); + premium->reload(); + const auto computeCost = [=] { + const auto amount = premium->monthlyAmount(); + const auto currency = premium->monthlyCurrency(); + const auto valid = (amount > 0) && !currency.isEmpty(); + return Ui::FillAmountAndCurrency( + valid ? amount : 500, + valid ? currency : "USD"); + }; + + const auto label = Ui::CreateChild( + result, + tr::lng_premium_summary_button( + lt_cost, + premium->statusTextValue() | rpl::map(computeCost)), + st::premiumPreviewButtonLabel); + label->setAttribute(Qt::WA_TransparentForMouseEvents); + rpl::combine( + result->widthValue(), + label->widthValue() + ) | rpl::start_with_next([=](int outer, int width) { + label->moveToLeft( + (outer - width) / 2, + st::premiumPreviewBox.button.textTop, + outer); + }, label->lifetime()); + + return result; +} + } // namespace Settings diff --git a/Telegram/SourceFiles/settings/settings_premium.h b/Telegram/SourceFiles/settings/settings_premium.h index e640a2d3f..2737b02f8 100644 --- a/Telegram/SourceFiles/settings/settings_premium.h +++ b/Telegram/SourceFiles/settings/settings_premium.h @@ -9,6 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_type.h" +enum class PremiumPreview; + +namespace Ui { +class RpWidget; +class GradientButton; +} // namespace Ui + namespace Main { class Session; } // namespace Main @@ -30,5 +37,13 @@ void StartPremiumPayment( not_null controller, const QString &ref); +[[nodiscard]] QString LookupPremiumRef(PremiumPreview section); + +[[nodiscard]] not_null CreateSubscribeButton( + not_null controller, + not_null parent, + Fn computeRef); + + } // namespace Settings diff --git a/Telegram/SourceFiles/ui/effects/premium_graphics.cpp b/Telegram/SourceFiles/ui/effects/premium_graphics.cpp index 7baf72aa5..5a12aa9ae 100644 --- a/Telegram/SourceFiles/ui/effects/premium_graphics.cpp +++ b/Telegram/SourceFiles/ui/effects/premium_graphics.cpp @@ -484,10 +484,10 @@ Line::Line( TextFactory textFactory, int min) : Ui::RpWidget(parent) -, _leftText(st::defaultTextStyle, tr::lng_premium_free(tr::now)) -, _rightText(st::defaultTextStyle, tr::lng_premium(tr::now)) -, _rightLabel(st::defaultTextStyle, max ? textFactory(max) : QString()) -, _leftLabel(st::defaultTextStyle, min ? textFactory(min) : QString()) { +, _leftText(st::semiboldTextStyle, tr::lng_premium_free(tr::now)) +, _rightText(st::semiboldTextStyle, tr::lng_premium(tr::now)) +, _rightLabel(st::semiboldTextStyle, max ? textFactory(max) : QString()) +, _leftLabel(st::semiboldTextStyle, min ? textFactory(min) : QString()) { resize(width(), st::requestsAcceptButton.height); sizeValue( diff --git a/Telegram/SourceFiles/ui/widgets/gradient_round_button.cpp b/Telegram/SourceFiles/ui/widgets/gradient_round_button.cpp index 7b9f5e94c..5221ffa81 100644 --- a/Telegram/SourceFiles/ui/widgets/gradient_round_button.cpp +++ b/Telegram/SourceFiles/ui/widgets/gradient_round_button.cpp @@ -81,6 +81,10 @@ void GradientButton::validateBg() { _bg = Images::Round(std::move(_bg), ImageRoundRadius::Large); } +void GradientButton::setGlarePaused(bool paused) { + _glare.paused = paused; +} + void GradientButton::validateGlare() { if (anim::Disabled()) { return; @@ -88,7 +92,7 @@ void GradientButton::validateGlare() { _glare.width = st::gradientButtonGlareWidth; _glare.animation.init([=](crl::time now) { if (const auto diff = (now - _glare.glare.deathTime); diff > 0) { - if (diff > st::gradientButtonGlareTimeout) { + if (diff > st::gradientButtonGlareTimeout && !_glare.paused) { _glare.glare = Glare{ .birthTime = now, .deathTime = now + st::gradientButtonGlareDuration, diff --git a/Telegram/SourceFiles/ui/widgets/gradient_round_button.h b/Telegram/SourceFiles/ui/widgets/gradient_round_button.h index c6453a756..0c0e5e277 100644 --- a/Telegram/SourceFiles/ui/widgets/gradient_round_button.h +++ b/Telegram/SourceFiles/ui/widgets/gradient_round_button.h @@ -16,6 +16,7 @@ public: GradientButton(QWidget *widget, QGradientStops stops); void startGlareAnimation(); + void setGlarePaused(bool paused); private: void paintEvent(QPaintEvent *e); @@ -35,7 +36,8 @@ private: Ui::Animations::Basic animation; Glare glare; QPixmap pixmap; - int width; + int width = 0; + bool paused = false; } _glare; };