Update API scheme to layer 168. Giveaways.

This commit is contained in:
John Preston 2023-12-08 10:37:37 +04:00
parent f5b59c9456
commit e135f8954f
10 changed files with 258 additions and 58 deletions

View file

@ -2170,6 +2170,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_giveaway_maximum_users_error#other" = "You can select maximum {count} users.";
"lng_giveaway_channels_confirm_title" = "Channel is Private";
"lng_giveaway_channels_confirm_about" = "Are you sure you want to add a private channel? Users won't be able to join it without an invite link.";
"lng_giveaway_additional_prizes" = "Additional prizes";
"lng_giveaway_additional_about" = "Turn this on if you want to give the winners your own prizes in addition to Premium subscriptions.";
"lng_giveaway_additional_prizes_ph" = "Enter your prize";
"lng_giveaway_prizes_just_premium#one" = "All prizes: **{count}** Telegram Premium subscription {duration}.";
"lng_giveaway_prizes_just_premium#other" = "All prizes: **{count}** Telegram Premium subscriptions {duration}.";
"lng_giveaway_prizes_additional#one" = "All prizes: **{count}** {prize} with Telegram Premium subscription {duration}.";
"lng_giveaway_prizes_additional#other" = "All prizes: **{count}** {prize} with Telegram Premium subscriptions {duration}.";
"lng_giveaway_show_winners" = "Show winners";
"lng_giveaway_show_winners_about" = "Choose whether to make the list of winners public when the giveaway ends.";
"lng_giveaway_created_title" = "Giveaway created";
"lng_giveaway_created_body" = "Check your channels' {link} to see how this giveaway boosted your channel.";

View file

@ -420,6 +420,8 @@ const std::vector<int> &PremiumGiftCodeOptions::availablePresets() const {
}
[[nodiscard]] int PremiumGiftCodeOptions::monthsFromPreset(int monthsIndex) {
Expects(monthsIndex >= 0 && monthsIndex < _availablePresets.size());
return _optionsForOnePerson.months[monthsIndex];
}

View file

@ -221,11 +221,11 @@ void ApiWrap::setupSupportMode() {
void ApiWrap::requestChangelog(
const QString &sinceVersion,
Fn<void(const MTPUpdates &result)> callback) {
request(MTPhelp_GetAppChangelog(
MTP_string(sinceVersion)
)).done(
callback
).send();
//request(MTPhelp_GetAppChangelog(
// MTP_string(sinceVersion)
//)).done(
// callback
//).send();
}
void ApiWrap::refreshTopPromotion() {

View file

@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/toast/toast.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/continuous_sliders.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/labels.h"
#include "ui/wrap/slide_wrap.h"
#include "styles/style_giveaway.h"
@ -48,6 +49,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace {
constexpr auto kDoneTooltipDuration = 5 * crl::time(1000);
constexpr auto kAdditionalPrizeLengthMax = 128;
[[nodiscard]] QDateTime ThreeDaysAfterToday() {
auto dateNow = QDateTime::currentDateTime();
@ -257,6 +259,10 @@ void CreateGiveawayBox(
rpl::variable<TimeId> dateValue;
rpl::variable<std::vector<QString>> countriesValue;
rpl::variable<QString> additionalPrize;
rpl::variable<int> chosenMonths;
rpl::variable<bool> showWinners;
rpl::variable<bool> confirmButtonBusy = true;
};
const auto state = box->lifetime().make_state<State>(peer);
@ -669,6 +675,182 @@ void CreateGiveawayBox(
c->add(std::move(terms));
};
const auto durationGroup = std::make_shared<Ui::RadiobuttonGroup>(0);
durationGroup->setChangedCallback([=](int value) {
state->chosenMonths = state->apiOptions.monthsFromPreset(value);
});
const auto listOptionsRandom = randomWrap->entity()->add(
object_ptr<Ui::VerticalLayout>(box));
const auto listOptionsSpecific = contentWrap->entity()->add(
object_ptr<Ui::VerticalLayout>(box));
const auto rebuildListOptions = [=](GiveawayType type, int usersCount) {
if (prepaid) {
return;
}
while (listOptionsRandom->count()) {
delete listOptionsRandom->widgetAt(0);
}
while (listOptionsSpecific->count()) {
delete listOptionsSpecific->widgetAt(0);
}
const auto listOptions = (type == GiveawayType::SpecificUsers)
? listOptionsSpecific
: listOptionsRandom;
Ui::AddSubsectionTitle(
listOptions,
tr::lng_giveaway_duration_title(
lt_count,
rpl::single(usersCount) | tr::to_count()),
st::giveawayGiftCodeChannelsSubsectionPadding);
Ui::Premium::AddGiftOptions(
listOptions,
durationGroup,
state->apiOptions.options(usersCount),
st::giveawayGiftCodeGiftOption,
true);
Ui::AddSkip(listOptions);
auto termsContainer = object_ptr<Ui::VerticalLayout>(listOptions);
addTerms(termsContainer.data());
listOptions->add(object_ptr<Ui::DividerLabel>(
listOptions,
std::move(termsContainer),
st::defaultBoxDividerLabelPadding));
Ui::AddSkip(listOptions);
box->verticalLayout()->resizeToWidth(box->width());
};
if (!prepaid) {
rpl::combine(
state->sliderValue.value(),
state->typeValue.value()
) | rpl::start_with_next([=](int users, GiveawayType type) {
typeGroup->setValue(type);
rebuildListOptions(type, (type == GiveawayType::SpecificUsers)
? state->selectedToAward.size()
: users);
}, box->lifetime());
} else {
typeGroup->setValue(GiveawayType::Random);
}
{
const auto additionalWrap = randomWrap->entity()->add(
object_ptr<Ui::VerticalLayout>(randomWrap));
const auto additionalToggle = additionalWrap->add(
object_ptr<Ui::SettingsButton>(
additionalWrap,
tr::lng_giveaway_additional_prizes(),
st::defaultSettingsButton));
const auto additionalInner = additionalWrap->add(
object_ptr<Ui::SlideWrap<Ui::InputField>>(
additionalWrap,
object_ptr<Ui::InputField>(
additionalWrap,
st::giveawayGiftCodeAdditionalField,
Ui::InputField::Mode::SingleLine,
tr::lng_giveaway_additional_prizes_ph()),
st::giveawayGiftCodeAdditionalPaddingMin));
const auto additionalPadded = additionalInner->wrapped();
const auto additional = additionalInner->entity();
additionalInner->hide(anim::type::instant);
additional->setMaxLength(kAdditionalPrizeLengthMax);
const auto fillAdditionalPrizeValue = [=] {
state->additionalPrize = additional->getLastText().trimmed();
};
additionalToggle->toggleOn(rpl::single(false))->toggledChanges(
) | rpl::start_with_next([=](bool toggled) {
if (!toggled && Ui::InFocusChain(additional)) {
additionalWrap->setFocus();
state->additionalPrize = QString();
}
additionalInner->toggle(toggled, anim::type::normal);
if (toggled) {
additional->setFocusFast();
fillAdditionalPrizeValue();
}
}, additionalInner->lifetime());
additionalInner->finishAnimating();
additional->changes() | rpl::filter([=] {
return additionalInner->toggled();
}) | rpl::start_with_next(
fillAdditionalPrizeValue,
additional->lifetime());
Ui::AddSkip(additionalWrap);
auto monthsValue = prepaid
? (rpl::single(prepaid->months) | rpl::type_erased())
: state->chosenMonths.value();
const auto usersCountByType = [=](GiveawayType type) {
if (type != GiveawayType::SpecificUsers) {
return state->sliderValue.value() | rpl::type_erased();
}
return state->toAwardAmountChanged.events_starting_with_copy(
rpl::empty
) | rpl::map([=] {
return int(state->selectedToAward.size());
}) | rpl::type_erased();
};
auto usersCountValue = prepaid
? (rpl::single(prepaid->quantity) | rpl::type_erased())
: state->typeValue.value(
) | rpl::map(usersCountByType) | rpl::flatten_latest();
const auto additionalLabel = Ui::CreateChild<Ui::FlatLabel>(
additionalInner,
rpl::duplicate(usersCountValue) | rpl::map([](int count) {
return QString::number(count);
}),
st::giveawayGiftCodeAdditionalLabel);
additionalLabel->widthValue() | rpl::start_with_next([=](int width) {
const auto min = st::giveawayGiftCodeAdditionalPaddingMin;
const auto skip = st::giveawayGiftCodeAdditionalLabelSkip;
const auto added = std::max(width + skip - min.left(), 0);
const auto &field = st::giveawayGiftCodeAdditionalField;
const auto top = field.textMargins.top();
additionalLabel->moveToLeft(min.right(), min.top() + top);
additionalPadded->setPadding(min + QMargins(added, 0, 0, 0));
}, additionalLabel->lifetime());
auto additionalAbout = rpl::combine(
state->additionalPrize.value(),
std::move(monthsValue),
std::move(usersCountValue)
) | rpl::map([=](QString prize, int months, int users) {
const auto duration = ((months >= 12)
? tr::lng_premium_gift_duration_years
: tr::lng_premium_gift_duration_months)(
tr::now,
lt_count,
(months >= 12) ? (months / 12) : months);
if (prize.isEmpty()) {
return tr::lng_giveaway_prizes_just_premium(
tr::now,
lt_count,
users,
lt_duration,
TextWithEntities{ duration },
Ui::Text::RichLangValue);
}
return tr::lng_giveaway_prizes_additional(
tr::now,
lt_count,
users,
lt_prize,
TextWithEntities{ prize },
lt_duration,
TextWithEntities{ duration },
Ui::Text::RichLangValue);
});
Ui::AddDividerText(additionalWrap, std::move(additionalAbout));
Ui::AddSkip(additionalWrap);
}
{
const auto dateContainer = randomWrap->entity()->add(
object_ptr<Ui::VerticalLayout>(randomWrap));
@ -699,7 +881,7 @@ void CreateGiveawayBox(
.time = state->dateValue.current(),
.max = [=] {
return QDateTime::currentSecsSinceEpoch()
+ state->apiOptions.giveawayPeriodMax();;
+ state->apiOptions.giveawayPeriodMax();
},
});
}));
@ -721,6 +903,7 @@ void CreateGiveawayBox(
dateContainer,
std::move(terms),
st::defaultBoxDividerLabelPadding));
Ui::AddSkip(dateContainer);
} else {
Ui::AddDividerText(
dateContainer,
@ -731,53 +914,24 @@ void CreateGiveawayBox(
}
}
const auto durationGroup = std::make_shared<Ui::RadiobuttonGroup>(0);
const auto listOptions = contentWrap->entity()->add(
object_ptr<Ui::VerticalLayout>(box));
const auto rebuildListOptions = [=](int amountUsers) {
if (prepaid) {
return;
}
while (listOptions->count()) {
delete listOptions->widgetAt(0);
}
Ui::AddSubsectionTitle(
listOptions,
tr::lng_giveaway_duration_title(
lt_count,
rpl::single(amountUsers) | tr::to_count()),
st::giveawayGiftCodeChannelsSubsectionPadding);
Ui::Premium::AddGiftOptions(
listOptions,
durationGroup,
state->apiOptions.options(amountUsers),
st::giveawayGiftCodeGiftOption,
true);
{
const auto winnersWrap = randomWrap->entity()->add(
object_ptr<Ui::VerticalLayout>(randomWrap));
const auto winnersToggle = winnersWrap->add(
object_ptr<Ui::SettingsButton>(
winnersWrap,
tr::lng_giveaway_show_winners(),
st::defaultSettingsButton));
state->showWinners = winnersToggle->toggleOn(
rpl::single(false)
)->toggledValue();
Ui::AddSkip(winnersWrap);
Ui::AddSkip(listOptions);
auto termsContainer = object_ptr<Ui::VerticalLayout>(listOptions);
addTerms(termsContainer.data());
listOptions->add(object_ptr<Ui::DividerLabel>(
listOptions,
std::move(termsContainer),
st::defaultBoxDividerLabelPadding));
box->verticalLayout()->resizeToWidth(box->width());
};
if (!prepaid) {
rpl::combine(
state->sliderValue.value(),
state->typeValue.value()
) | rpl::start_with_next([=](int users, GiveawayType type) {
typeGroup->setValue(type);
rebuildListOptions((type == GiveawayType::SpecificUsers)
? state->selectedToAward.size()
: users);
}, box->lifetime());
} else {
typeGroup->setValue(GiveawayType::Random);
Ui::AddDividerText(
winnersWrap,
tr::lng_giveaway_show_winners_about());
}
{
using namespace Info::Statistics;
const auto &stButton = st::startGiveawayBox;
@ -862,9 +1016,11 @@ void CreateGiveawayBox(
return not_null{ p->asChannel() };
}) | ranges::to_vector,
.countries = state->countriesValue.current(),
.additionalPrize = state->additionalPrize.current(),
.untilDate = state->dateValue.current(),
.onlyNewSubscribers = (membersGroup->value()
== GiveawayType::OnlyNewMembers),
.showWinners = state->showWinners.current(),
};
}
state->confirmButtonBusy = true;
@ -960,7 +1116,10 @@ void CreateGiveawayBox(
loading->toggle(false, anim::type::instant);
state->confirmButtonBusy = false;
fillSliderContainer();
rebuildListOptions(1);
if (!prepaid) {
state->chosenMonths = state->apiOptions.monthsFromPreset(0);
}
rebuildListOptions(state->typeValue.current(), 1);
contentWrap->toggle(true, anim::type::instant);
contentWrap->resizeToWidth(box->width());
};

View file

@ -121,6 +121,13 @@ giveawayGiftCodeTypeDividerPadding: margins(0px, 7px, 0px, 5px);
giveawayGiftCodeSliderPadding: margins(0px, 24px, 0px, 10px);
giveawayGiftCodeSliderFloatSkip: 6px;
giveawayGiftCodeChannelsSubsectionPadding: margins(0px, -1px, 0px, -4px);
giveawayGiftCodeAdditionalPaddingMin: margins(50px, 4px, 22px, 0px);
giveawayGiftCodeAdditionalField: InputField(defaultMultiSelectSearchField) {
}
giveawayGiftCodeAdditionalLabel: FlatLabel(defaultFlatLabel) {
style: semiboldTextStyle;
}
giveawayGiftCodeAdditionalLabelSkip: 12px;
giveawayGiftCodeChannelsPeerList: PeerList(boostsListBox) {
padding: margins(0px, 7px, 0px, 0px);

View file

@ -131,7 +131,7 @@ messageMediaGeoLive#b940c666 flags:# geo:GeoPoint heading:flags.0?int period:int
messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia;
messageMediaDice#3f7ee58b value:int emoticon:string = MessageMedia;
messageMediaStory#68cb6283 flags:# via_mention:flags.1?true peer:Peer id:int story:flags.0?StoryItem = MessageMedia;
messageMediaGiveaway#58260664 flags:# only_new_subscribers:flags.0?true channels:Vector<long> countries_iso2:flags.1?Vector<string> quantity:int months:int until_date:int = MessageMedia;
messageMediaGiveaway#daad85b0 flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.2?true channels:Vector<long> countries_iso2:flags.1?Vector<string> prize_description:flags.3?string quantity:int months:int until_date:int = MessageMedia;
messageActionEmpty#b6aef7b0 = MessageAction;
messageActionChatCreate#bd47cbad title:string users:Vector<long> = MessageAction;
@ -1426,7 +1426,7 @@ help.premiumPromo#5334759c status_text:string status_entities:Vector<MessageEnti
inputStorePaymentPremiumSubscription#a6751e66 flags:# restore:flags.0?true upgrade:flags.1?true = InputStorePaymentPurpose;
inputStorePaymentGiftPremium#616f7fe8 user_id:InputUser currency:string amount:long = InputStorePaymentPurpose;
inputStorePaymentPremiumGiftCode#a3805f3f flags:# users:Vector<InputUser> boost_peer:flags.0?InputPeer currency:string amount:long = InputStorePaymentPurpose;
inputStorePaymentPremiumGiveaway#7c9375e6 flags:# only_new_subscribers:flags.0?true boost_peer:InputPeer additional_peers:flags.1?Vector<InputPeer> countries_iso2:flags.2?Vector<string> random_id:long until_date:int currency:string amount:long = InputStorePaymentPurpose;
inputStorePaymentPremiumGiveaway#160544ca flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.3?true boost_peer:InputPeer additional_peers:flags.1?Vector<InputPeer> countries_iso2:flags.2?Vector<string> prize_description:flags.4?string random_id:long until_date:int currency:string amount:long = InputStorePaymentPurpose;
premiumGiftOption#74c34319 flags:# months:int currency:string amount:long bot_url:string store_product:flags.0?string = PremiumGiftOption;
@ -1583,7 +1583,7 @@ premiumGiftCodeOption#257e962b flags:# users:int months:int store_product:flags.
payments.checkedGiftCode#b722f158 flags:# via_giveaway:flags.2?true from_id:Peer giveaway_msg_id:flags.3?int to_id:flags.0?long date:int months:int used_date:flags.1?int chats:Vector<Chat> users:Vector<User> = payments.CheckedGiftCode;
payments.giveawayInfo#4367daa0 flags:# participating:flags.0?true preparing_results:flags.3?true start_date:int joined_too_early_date:flags.1?int admin_disallowed_chat_id:flags.2?long disallowed_country:flags.4?string = payments.GiveawayInfo;
payments.giveawayInfoResults#cd5570 flags:# winner:flags.0?true refunded:flags.1?true start_date:int gift_code_slug:flags.0?string finish_date:int winners_count:int activated_count:int = payments.GiveawayInfo;
payments.giveawayInfoResults#8ac7e167 flags:# winner:flags.0?true refunded:flags.1?true start_date:int gift_code_slug:flags.0?string finish_date:int winners_count:int activated_count:int winners:flags.2?Vector<User> = payments.GiveawayInfo;
prepaidGiveaway#b2539d54 id:long months:int quantity:int date:int = PrepaidGiveaway;
@ -1619,6 +1619,10 @@ help.peerColorOption#135bd42f flags:# hidden:flags.0?true color_id:int colors:fl
help.peerColorsNotModified#2ba1f5ce = help.PeerColors;
help.peerColors#f8ed08 hash:int colors:Vector<help.PeerColorOption> = help.PeerColors;
storyPeerReaction#7decc433 peer_id:Peer date:int reaction:Reaction = StoryPeerReaction;
stories.storyReactionsList#d86c162a flags:# count:int reactions:Vector<StoryPeerReaction> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = stories.StoryReactionsList;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1986,7 +1990,6 @@ help.getNearestDc#1fb33026 = NearestDc;
help.getAppUpdate#522d5a7d source:string = help.AppUpdate;
help.getInviteText#4d392343 = help.InviteText;
help.getSupport#9cdf08cd = help.Support;
help.getAppChangelog#9010ef6f prev_app_version:string = Updates;
help.setBotUpdatesStatus#ec22cfcd pending_updates_count:int message:string = Bool;
help.getCdnConfig#52029342 = CdnConfig;
help.getRecentMeUrls#3dc0f114 referer:string = help.RecentMeUrls;
@ -2195,6 +2198,7 @@ stories.getAllReadPeerStories#9b5ae7f9 = Updates;
stories.getPeerMaxIDs#535983c3 id:Vector<InputPeer> = Vector<int>;
stories.getChatsToSend#a56a8b60 = messages.Chats;
stories.togglePeerStoriesHidden#bd0415c4 peer:InputPeer hidden:Bool = Bool;
stories.getStoryReactionsList#b9b2881f flags:# peer:InputPeer id:int reaction:flags.0?Reaction offset:flags.1?string limit:int = stories.StoryReactionsList;
premium.getBoostsList#60f67660 flags:# gifts:flags.0?true peer:InputPeer offset:string limit:int = premium.BoostsList;
premium.getMyBoosts#be77b4a = premium.MyBoosts;
@ -2202,4 +2206,4 @@ premium.applyBoost#6b7da746 flags:# slots:flags.0?Vector<int> peer:InputPeer = p
premium.getBoostsStatus#42f1f61 peer:InputPeer = premium.BoostsStatus;
premium.getUserBoosts#39854d1f peer:InputPeer user_id:InputUser = premium.BoostsList;
// LAYER 167
// LAYER 168

View file

@ -145,7 +145,13 @@ MTPinputStorePaymentPurpose InvoicePremiumGiftCodeGiveawayToTL(
: Flag::f_additional_peers)
| (giveaway.countries.empty()
? Flag()
: Flag::f_countries_iso2)),
: Flag::f_countries_iso2)
| (giveaway.showWinners
? Flag::f_winners_are_visible
: Flag())
| (giveaway.additionalPrize.isEmpty()
? Flag()
: Flag::f_prize_description)),
giveaway.boostPeer->input,
MTP_vector_from_range(ranges::views::all(
giveaway.additionalChannels
@ -157,6 +163,7 @@ MTPinputStorePaymentPurpose InvoicePremiumGiftCodeGiveawayToTL(
) | ranges::views::transform([](QString value) {
return MTP_string(value);
})),
MTP_string(giveaway.additionalPrize),
MTP_long(invoice.randomId),
MTP_int(giveaway.untilDate),
MTP_string(invoice.currency),

View file

@ -190,8 +190,10 @@ struct InvoicePremiumGiftCodeGiveaway {
not_null<ChannelData*> boostPeer;
std::vector<not_null<ChannelData*>> additionalChannels;
std::vector<QString> countries;
QString additionalPrize;
TimeId untilDate = 0;
bool onlyNewSubscribers = false;
bool showWinners = false;
};
struct InvoicePremiumGiftCodeUsers {

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/vertical_list.h"
#include "ui/text/text_utilities.h"
#include "ui/widgets/box_content_divider.h"
#include "ui/widgets/labels.h"
#include "ui/wrap/padding_wrap.h"
@ -30,6 +31,12 @@ void AddDivider(not_null<Ui::VerticalLayout*> container) {
void AddDividerText(
not_null<Ui::VerticalLayout*> container,
rpl::producer<QString> text) {
AddDividerText(container, std::move(text) | Ui::Text::ToWithEntities());
}
void AddDividerText(
not_null<Ui::VerticalLayout*> container,
rpl::producer<TextWithEntities> text) {
container->add(object_ptr<Ui::DividerLabel>(
container,
object_ptr<Ui::FlatLabel>(

View file

@ -22,6 +22,9 @@ void AddDivider(not_null<Ui::VerticalLayout*> container);
void AddDividerText(
not_null<Ui::VerticalLayout*> container,
rpl::producer<QString> text);
void AddDividerText(
not_null<Ui::VerticalLayout*> container,
rpl::producer<TextWithEntities> text);
not_null<Ui::FlatLabel*> AddSubsectionTitle(
not_null<Ui::VerticalLayout*> container,
rpl::producer<QString> text,