Added ability to filter users by countries for giveaway to giveaway box.

This commit is contained in:
23rd 2023-10-31 20:43:07 +03:00 committed by John Preston
parent 2d097ca9ae
commit fc6d4d66b7
6 changed files with 127 additions and 12 deletions

View file

@ -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}.";

View file

@ -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<int>(
u"giveaway_countries_max"_q,
kFallbackCount);
}
} // namespace Api

View file

@ -157,6 +157,7 @@ public:
int monthsIndex);
[[nodiscard]] int giveawayBoostsPerPremium() const;
[[nodiscard]] int giveawayCountriesMax() const;
private:
struct Token final {

View file

@ -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<GiveawayType> typeValue;
rpl::variable<int> sliderValue;
rpl::variable<TimeId> dateValue;
rpl::variable<std::vector<QString>> countriesValue;
bool confirmButtonBusy = false;
};
@ -308,6 +312,90 @@ void CreateGiveawayBox(
Settings::AddSkip(dateContainer);
}
const auto membersGroup = std::make_shared<GiveawayGroup>();
{
const auto countriesContainer = randomWrap->entity()->add(
object_ptr<Ui::VerticalLayout>(randomWrap));
Settings::AddSubsectionTitle(
countriesContainer,
tr::lng_giveaway_users_title());
membersGroup->setValue(GiveawayType::AllMembers);
auto subtitle = state->countriesValue.value(
) | rpl::map([=](const std::vector<QString> &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<QString> 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<Giveaway::GiveawayTypeRow>(
box,
GiveawayType::AllMembers,
rpl::duplicate(subtitle)));
row->addRadio(membersGroup);
row->setClickedCallback(createCallback(GiveawayType::AllMembers));
}
const auto row = countriesContainer->add(
object_ptr<Giveaway::GiveawayTypeRow>(
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<Ui::RadiobuttonGroup>(0);
{
const auto listOptions = box->verticalLayout()->add(

View file

@ -25,8 +25,10 @@ GiveawayTypeRow::GiveawayTypeRow(
Type type,
rpl::producer<QString> 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);
}

View file

@ -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;