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<TimeId> dateValue;
 		rpl::variable<std::vector<QString>> countriesValue;
 
-		bool confirmButtonBusy = false;
+		rpl::variable<bool> confirmButtonBusy = true;
 	};
 	const auto state = box->lifetime().make_state<State>(peer);
 	const auto typeGroup = std::make_shared<GiveawayGroup>();
@@ -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<Ui::RoundButton>(
 			box,
+			rpl::never<QString>(),
+			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<Ui::RpWidget*> InfiniteRadialAnimationWidget(
+		not_null<Ui::RpWidget*> parent,
+		int size) {
+	class Widget final : public Ui::RpWidget {
+	public:
+		Widget(not_null<Ui::RpWidget*> 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<Widget>(parent.get(), size);
+}
+
 QImage CreateBadge(
 		const style::TextStyle &textStyle,
 		const QString &text,
@@ -64,4 +104,70 @@ QImage CreateBadge(
 	return result;
 }
 
+void AddLabelWithBadgeToButton(
+		not_null<Ui::RpWidget*> parent,
+		rpl::producer<QString> text,
+		rpl::producer<int> number,
+		rpl::producer<bool> shown) {
+	struct State {
+		QImage badge;
+	};
+	const auto state = parent->lifetime().make_state<State>();
+	const auto label = Ui::CreateChild<Ui::LabelSimple>(
+		parent.get(),
+		st::startGiveawayButtonLabelSimple);
+	std::move(
+		text
+	) | rpl::start_with_next([=](const QString &s) {
+		label->setText(s);
+	}, label->lifetime());
+	const auto count = Ui::CreateChild<Ui::RpWidget>(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<Ui::RpWidget*> InfiniteRadialAnimationWidget(
+	not_null<Ui::RpWidget*> parent,
+	int size);
+
+void AddLabelWithBadgeToButton(
+	not_null<Ui::RpWidget*> parent,
+	rpl::producer<QString> text,
+	rpl::producer<int> number,
+	rpl::producer<bool> 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;
+}