From 43aa8825a5d7ea8208616593b96d8030a71aebd6 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 7 Nov 2023 23:32:48 +0300 Subject: [PATCH] Added badge and loading state to confirm button in giveaway box. --- .../info/boosts/create_giveaway_box.cpp | 34 +++++- .../info/boosts/giveaway/boost_badge.cpp | 106 ++++++++++++++++++ .../info/boosts/giveaway/boost_badge.h | 14 +++ .../info/boosts/giveaway/giveaway.style | 16 +++ 4 files changed, 166 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp index 1c830c08f..ac22dc934 100644 --- a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp +++ b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unixtime.h" #include "countries/countries_instance.h" #include "data/data_peer.h" +#include "info/boosts/giveaway/boost_badge.h" #include "info/boosts/giveaway/giveaway_list_controllers.h" #include "info/boosts/giveaway/giveaway_type_row.h" #include "info/boosts/giveaway/select_countries_box.h" @@ -253,7 +254,7 @@ void CreateGiveawayBox( rpl::variable dateValue; rpl::variable> countriesValue; - bool confirmButtonBusy = false; + rpl::variable confirmButtonBusy = true; }; const auto state = box->lifetime().make_state(peer); const auto typeGroup = std::make_shared(); @@ -726,17 +727,41 @@ void CreateGiveawayBox( }, box->lifetime()); } { - // TODO mini-icon. + using namespace Info::Statistics; const auto &stButton = st::startGiveawayBox; box->setStyle(stButton); auto button = object_ptr( box, + rpl::never(), + st::giveawayGiftCodeStartButton); + + AddLabelWithBadgeToButton( + button, rpl::conditional( state->typeValue.value( ) | rpl::map(rpl::mappers::_1 == GiveawayType::Random), tr::lng_giveaway_start(), tr::lng_giveaway_award()), - st::giveawayGiftCodeStartButton); + state->sliderValue.value( + ) | rpl::map([=](int v) -> int { + return state->apiOptions.giveawayBoostsPerPremium() * v; + }), + state->confirmButtonBusy.value() | rpl::map(!rpl::mappers::_1)); + + { + const auto loadingAnimation = InfiniteRadialAnimationWidget( + button, + st::giveawayGiftCodeStartButton.height / 2); + button->sizeValue( + ) | rpl::start_with_next([=](const QSize &s) { + const auto size = loadingAnimation->size(); + loadingAnimation->moveToLeft( + (s.width() - size.width()) / 2, + (s.height() - size.height()) / 2); + }, loadingAnimation->lifetime()); + loadingAnimation->showOn(state->confirmButtonBusy.value()); + } + button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform); state->typeValue.value( ) | rpl::start_with_next([=, raw = button.data()] { @@ -745,7 +770,7 @@ void CreateGiveawayBox( - stButton.buttonPadding.right()); }, button->lifetime()); button->setClickedCallback([=] { - if (state->confirmButtonBusy) { + if (state->confirmButtonBusy.current()) { return; } const auto type = typeGroup->value(); @@ -847,6 +872,7 @@ void CreateGiveawayBox( }, [=] { state->lifetimeApi.destroy(); loading->toggle(false, anim::type::instant); + state->confirmButtonBusy = false; fillSliderContainer(); rebuildListOptions(1); contentWrap->toggle(true, anim::type::instant); diff --git a/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.cpp b/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.cpp index 778257f55..13e8fb192 100644 --- a/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.cpp +++ b/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.cpp @@ -7,11 +7,51 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "info/boosts/giveaway/boost_badge.h" +#include "ui/effects/radial_animation.h" #include "ui/painter.h" #include "ui/rect.h" +#include "ui/rp_widget.h" +#include "ui/widgets/labels.h" +#include "styles/style_giveaway.h" +#include "styles/style_statistics.h" +#include "styles/style_widgets.h" namespace Info::Statistics { +not_null InfiniteRadialAnimationWidget( + not_null parent, + int size) { + class Widget final : public Ui::RpWidget { + public: + Widget(not_null p, int size) + : Ui::RpWidget(p) + , _animation([=] { update(); }, st::startGiveawayButtonLoading) { + resize(size, size); + shownValue() | rpl::start_with_next([=](bool v) { + return v + ? _animation.start() + : _animation.stop(anim::type::instant); + }, lifetime()); + } + + protected: + void paintEvent(QPaintEvent *e) override { + auto p = QPainter(this); + p.setPen(st::activeButtonFg); + p.setBrush(st::activeButtonFg); + const auto r = rect() + - Margins(st::startGiveawayButtonLoading.thickness); + _animation.draw(p, r.topLeft(), r.size(), width()); + } + + private: + Ui::InfiniteRadialAnimation _animation; + + }; + + return Ui::CreateChild(parent.get(), size); +} + QImage CreateBadge( const style::TextStyle &textStyle, const QString &text, @@ -64,4 +104,70 @@ QImage CreateBadge( return result; } +void AddLabelWithBadgeToButton( + not_null parent, + rpl::producer text, + rpl::producer number, + rpl::producer shown) { + struct State { + QImage badge; + }; + const auto state = parent->lifetime().make_state(); + const auto label = Ui::CreateChild( + parent.get(), + st::startGiveawayButtonLabelSimple); + std::move( + text + ) | rpl::start_with_next([=](const QString &s) { + label->setText(s); + }, label->lifetime()); + const auto count = Ui::CreateChild(parent.get()); + count->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(count); + p.drawImage(0, 0, state->badge); + }, count->lifetime()); + std::move( + number + ) | rpl::start_with_next([=](int c) { + state->badge = Info::Statistics::CreateBadge( + st::startGiveawayButtonTextStyle, + QString::number(c), + st::boostsListBadgeHeight, + st::startGiveawayButtonBadgeTextPadding, + st::activeButtonFg, + st::activeButtonBg, + 1., + st::boostsListMiniIconPadding, + st::startGiveawayButtonMiniIcon); + count->resize(state->badge.size() / style::DevicePixelRatio()); + count->update(); + }, count->lifetime()); + + std::move( + shown + ) | rpl::start_with_next([=](bool shown) { + count->setVisible(shown); + label->setVisible(shown); + }, count->lifetime()); + + rpl::combine( + parent->sizeValue(), + label->sizeValue(), + count->sizeValue() + ) | rpl::start_with_next([=]( + const QSize &s, + const QSize &s1, + const QSize &s2) { + const auto sum = st::startGiveawayButtonMiniIconSkip + + s1.width() + + s2.width(); + const auto contentLeft = (s.width() - sum) / 2; + label->moveToLeft(contentLeft, (s.height() - s1.height()) / 2); + count->moveToLeft( + contentLeft + sum - s2.width(), + (s.height() - s2.height()) / 2 + st::boostsListMiniIconSkip); + }, parent->lifetime()); +} + } // namespace Info::Statistics diff --git a/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.h b/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.h index 3d31df608..fe2427e9c 100644 --- a/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.h +++ b/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.h @@ -11,6 +11,10 @@ namespace style { struct TextStyle; } // namespace style +namespace Ui { +class RpWidget; +} // namespace Ui + namespace Info::Statistics { [[nodiscard]] QImage CreateBadge( @@ -24,4 +28,14 @@ namespace Info::Statistics { const style::margins &iconPadding, const style::icon &icon); +[[nodiscard]] not_null InfiniteRadialAnimationWidget( + not_null parent, + int size); + +void AddLabelWithBadgeToButton( + not_null parent, + rpl::producer text, + rpl::producer number, + rpl::producer shown); + } // namespace Info::Statistics diff --git a/Telegram/SourceFiles/info/boosts/giveaway/giveaway.style b/Telegram/SourceFiles/info/boosts/giveaway/giveaway.style index 211087975..4f04fdabb 100644 --- a/Telegram/SourceFiles/info/boosts/giveaway/giveaway.style +++ b/Telegram/SourceFiles/info/boosts/giveaway/giveaway.style @@ -178,3 +178,19 @@ startGiveawayCover: PremiumCover(giveawayGiftCodeCover) { bg: boxDividerBg; additionalShadowForDarkThemes: false; } + +startGiveawayButtonLabelSimple: LabelSimple { + font: semiboldFont; + textFg: activeButtonFg; +} +startGiveawayButtonMiniIcon: icon{{ "boosts/boost_mini2", activeButtonBg }}; +startGiveawayButtonMiniIconSkip: 5px; +startGiveawayButtonBadgeTextPadding: margins(16px, -1px, 6px, 0px); +startGiveawayButtonTextStyle: TextStyle(defaultTextStyle) { + font: semiboldFont; +} + +startGiveawayButtonLoading: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) { + color: activeButtonFg; + thickness: 2px; +}