From fc6d4d66b7b828dc81d3a7702187d2b034795c3d Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 31 Oct 2023 20:43:07 +0300 Subject: [PATCH] Added ability to filter users by countries for giveaway to giveaway box. --- Telegram/Resources/langs/lang.strings | 10 ++- Telegram/SourceFiles/api/api_premium.cpp | 7 ++ Telegram/SourceFiles/api/api_premium.h | 1 + .../info/boosts/create_giveaway_box.cpp | 88 +++++++++++++++++++ .../boosts/giveaway/giveaway_type_row.cpp | 28 +++--- .../info/boosts/giveaway/giveaway_type_row.h | 5 +- 6 files changed, 127 insertions(+), 12 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 9a0a409a0..f1a46c481 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2089,8 +2089,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_giveaway_channels_about" = "Choose the channels the users need to join to take part in the giveaway."; "lng_giveaway_users_title" = "Users eligible for the giveaway"; "lng_giveaway_users_all" = "All subscribers"; +"lng_giveaway_users_from_all_countries" = "from all countries"; +"lng_giveaway_users_from_one_country" = "from {country}"; +"lng_giveaway_users_from_countries#one" = "from {count} country"; +"lng_giveaway_users_from_countries#other" = "from {count} countries"; "lng_giveaway_users_new" = "Only new subscribers"; -"lng_giveaway_users_about" = "Choose if you want to limit the giveaway only to the newly joined subscribers."; +"lng_giveaway_users_about" = "Choose if you want to limit the giveaway only to those who joined the channel after the giveaway started or to users from specific countries."; "lng_giveaway_start" = "Start Giveaway"; "lng_giveaway_award" = "Gift Premium"; "lng_giveaway_date_title" = "Date when giveaway ends"; @@ -2106,6 +2110,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_giveaway_channels_select#other" = "Select up to {count} channels"; "lng_giveaway_recipients_save" = "Save Recipients"; "lng_giveaway_recipients_deselect" = "Deselect All"; +"lng_giveaway_maximum_countries_error#one" = "You can select maximum {count} country."; +"lng_giveaway_maximum_countries_error#other" = "You can select maximum {count} countries."; +"lng_giveaway_maximum_channels_error#one" = "You can select maximum {count} channel."; +"lng_giveaway_maximum_channels_error#other" = "You can select maximum {count} channels."; "lng_prize_title" = "Congratulations!"; "lng_prize_about" = "You won a prize in a giveaway organized by {channel}."; diff --git a/Telegram/SourceFiles/api/api_premium.cpp b/Telegram/SourceFiles/api/api_premium.cpp index ba63898c6..185e2728c 100644 --- a/Telegram/SourceFiles/api/api_premium.cpp +++ b/Telegram/SourceFiles/api/api_premium.cpp @@ -442,4 +442,11 @@ Data::SubscriptionOptions PremiumGiftCodeOptions::options(int amount) { kFallbackCount); } +[[nodiscard]] int PremiumGiftCodeOptions::giveawayCountriesMax() const { + constexpr auto kFallbackCount = 10; + return _peer->session().account().appConfig().get( + u"giveaway_countries_max"_q, + kFallbackCount); +} + } // namespace Api diff --git a/Telegram/SourceFiles/api/api_premium.h b/Telegram/SourceFiles/api/api_premium.h index d6141cac0..cfc7c6ec1 100644 --- a/Telegram/SourceFiles/api/api_premium.h +++ b/Telegram/SourceFiles/api/api_premium.h @@ -157,6 +157,7 @@ public: int monthsIndex); [[nodiscard]] int giveawayBoostsPerPremium() const; + [[nodiscard]] int giveawayCountriesMax() const; private: struct Token final { diff --git a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp index d149cafe8..0e8b26133 100644 --- a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp +++ b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp @@ -8,12 +8,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/boosts/create_giveaway_box.h" #include "api/api_premium.h" +#include "base/call_delayed.h" #include "base/unixtime.h" #include "boxes/peers/edit_participants_box.h" // ParticipantsBoxController #include "data/data_peer.h" #include "data/data_subscription_option.h" #include "data/data_user.h" #include "info/boosts/giveaway/giveaway_type_row.h" +#include "info/boosts/giveaway/select_countries_box.h" #include "info/info_controller.h" #include "lang/lang_keys.h" #include "payments/payments_checkout_process.h" // Payments::CheckoutProcess @@ -25,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/layers/generic_box.h" #include "ui/text/format_values.h" #include "ui/text/text_utilities.h" +#include "ui/toast/toast.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/continuous_sliders.h" #include "ui/widgets/labels.h" @@ -99,6 +102,7 @@ void CreateGiveawayBox( rpl::variable typeValue; rpl::variable sliderValue; rpl::variable dateValue; + rpl::variable> countriesValue; bool confirmButtonBusy = false; }; @@ -308,6 +312,90 @@ void CreateGiveawayBox( Settings::AddSkip(dateContainer); } + const auto membersGroup = std::make_shared(); + { + const auto countriesContainer = randomWrap->entity()->add( + object_ptr(randomWrap)); + Settings::AddSubsectionTitle( + countriesContainer, + tr::lng_giveaway_users_title()); + + membersGroup->setValue(GiveawayType::AllMembers); + auto subtitle = state->countriesValue.value( + ) | rpl::map([=](const std::vector &list) { + return list.empty() + ? tr::lng_giveaway_users_from_all_countries() + : (list.size() == 1) + ? tr::lng_giveaway_users_from_one_country( + lt_country, + rpl::single(Countries::Instance().countryNameByISO2( + list.front()))) + : tr::lng_giveaway_users_from_countries( + lt_count, + rpl::single(list.size()) | tr::to_count()); + }) | rpl::flatten_latest(); + + const auto showBox = [=] { + auto done = [=](std::vector list) { + state->countriesValue = std::move(list); + }; + auto error = [=](int count) { + const auto max = state->apiOptions.giveawayCountriesMax(); + const auto error = (count >= max); + if (error) { + Ui::Toast::Show(tr::lng_giveaway_maximum_countries_error( + tr::now, + lt_count, + max)); + } + return error; + }; + + box->uiShow()->showBox(Box( + Ui::SelectCountriesBox, + state->countriesValue.current(), + std::move(done), + std::move(error))); + }; + + const auto createCallback = [=](GiveawayType type) { + return [=] { + const auto was = membersGroup->value(); + membersGroup->setValue(type); + const auto now = membersGroup->value(); + if (was == now) { + base::call_delayed( + st::defaultRippleAnimation.hideDuration, + box, + showBox); + } + }; + }; + + { + const auto row = countriesContainer->add( + object_ptr( + box, + GiveawayType::AllMembers, + rpl::duplicate(subtitle))); + row->addRadio(membersGroup); + row->setClickedCallback(createCallback(GiveawayType::AllMembers)); + } + const auto row = countriesContainer->add( + object_ptr( + box, + GiveawayType::OnlyNewMembers, + std::move(subtitle))); + row->addRadio(membersGroup); + row->setClickedCallback(createCallback(GiveawayType::OnlyNewMembers)); + + Settings::AddSkip(countriesContainer); + Settings::AddDividerText( + countriesContainer, + tr::lng_giveaway_users_about()); + Settings::AddSkip(countriesContainer); + } + const auto durationGroup = std::make_shared(0); { const auto listOptions = box->verticalLayout()->add( diff --git a/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.cpp b/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.cpp index 1779b91e1..25a71a00a 100644 --- a/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.cpp +++ b/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.cpp @@ -25,8 +25,10 @@ GiveawayTypeRow::GiveawayTypeRow( Type type, rpl::producer subtitle) : RippleButton(parent, st::defaultRippleAnimation) -, _st(st::giveawayTypeListItem) , _type(type) +, _st((_type == Type::SpecificUsers || _type == Type::Random) + ? st::giveawayTypeListItem + : st::defaultPeerListItem) , _userpic( Ui::EmptyUserpic::UserpicColor((_type == Type::SpecificUsers) ? kColorIndexSpecific @@ -36,7 +38,11 @@ GiveawayTypeRow::GiveawayTypeRow( _st.nameStyle, (type == Type::SpecificUsers) ? tr::lng_giveaway_award_option(tr::now) - : tr::lng_giveaway_create_option(tr::now), + : (type == Type::Random) + ? tr::lng_giveaway_create_option(tr::now) + : (type == Type::AllMembers) + ? tr::lng_giveaway_users_all(tr::now) + : tr::lng_giveaway_users_new(tr::now), Ui::NameTextOptions()) { std::move( subtitle @@ -56,18 +62,20 @@ void GiveawayTypeRow::paintEvent(QPaintEvent *e) { const auto skipRight = _st.photoPosition.x(); const auto outerWidth = width(); const auto isSpecific = (_type == Type::SpecificUsers); + const auto hasUserpic = (_type == Type::Random) || isSpecific; if (paintOver) { p.fillRect(e->rect(), _st.button.textBgOver); } Ui::RippleButton::paintRipple(p, 0, 0); - _userpic.paintCircle( - p, - _st.photoPosition.x(), - _st.photoPosition.y(), - outerWidth, - _st.photoSize); - { + if (hasUserpic) { + _userpic.paintCircle( + p, + _st.photoPosition.x(), + _st.photoPosition.y(), + outerWidth, + _st.photoSize); + const auto &userpic = isSpecific ? st::giveawayUserpicGroup : st::giveawayUserpic; @@ -91,7 +99,7 @@ void GiveawayTypeRow::paintEvent(QPaintEvent *e) { const auto statusy = _st.statusPosition.y(); const auto statusw = outerWidth - statusx - skipRight; p.setFont(st::contactsStatusFont); - p.setPen(isSpecific ? st::lightButtonFg : _st.statusFg); + p.setPen((isSpecific || !hasUserpic) ? st::lightButtonFg : _st.statusFg); _status.drawLeftElided(p, statusx, statusy, statusw, outerWidth); } diff --git a/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.h b/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.h index 440b037f6..0167b338e 100644 --- a/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.h +++ b/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.h @@ -22,6 +22,9 @@ public: enum class Type { Random, SpecificUsers, + + AllMembers, + OnlyNewMembers, }; GiveawayTypeRow( @@ -37,8 +40,8 @@ protected: int resizeGetHeight(int) override; private: - const style::PeerListItem _st; const Type _type; + const style::PeerListItem _st; Ui::EmptyUserpic _userpic; Ui::Text::String _status;