Update API scheme to layer 191. Sold out gifts.

This commit is contained in:
John Preston 2024-10-11 12:48:02 +04:00
parent 7ee2e3d8bc
commit f2a92c9122
11 changed files with 129 additions and 67 deletions

View file

@ -2444,6 +2444,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_credits_box_history_entry_giveaway_name" = "Received Prize";
"lng_credits_box_history_entry_gift_sent" = "Sent Gift";
"lng_credits_box_history_entry_gift_converted" = "Converted Gift";
"lng_credits_box_history_entry_gift_unavailable" = "Unavailable";
"lng_credits_box_history_entry_gift_sold_out" = "This gift has sold out";
"lng_credits_box_history_entry_gift_out_about" = "With Stars, **{user}** will be able to unlock content and services on Telegram.\n{link}";
"lng_credits_box_history_entry_gift_in_about" = "Use Stars to unlock content and services on Telegram. {link}";
"lng_credits_box_history_entry_gift_about_link" = "See Examples {emoji}";
@ -2990,6 +2992,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_link_reason_unclaimed" = "Incomplete Giveaway";
"lng_gift_link_reason_chosen" = "You were selected by the channel";
"lng_gift_link_label_date" = "Date";
"lng_gift_link_label_first_sale" = "First Sale";
"lng_gift_link_label_last_sale" = "Last Sale";
"lng_gift_link_label_value" = "Value";
"lng_gift_link_also_send" = "You can also {link} to a friend as a gift.";
"lng_gift_link_also_send_link" = "send this link";
"lng_gift_link_use" = "Use Link";

View file

@ -776,6 +776,8 @@ std::optional<StarGift> FromTL(
.document = document,
.limitedLeft = remaining.value_or_empty(),
.limitedCount = total.value_or_empty(),
.firstSaleDate = data.vfirst_sale_date().value_or_empty(),
.lastSaleDate = data.vlast_sale_date().value_or_empty(),
};
}
@ -789,7 +791,7 @@ std::optional<UserStarGift> FromTL(
return {};
}
return UserStarGift{
.gift = std::move(*parsed),
.info = std::move(*parsed),
.message = (data.vmessage()
? TextWithEntities{
.text = qs(data.vmessage()->data().vtext()),

View file

@ -80,10 +80,16 @@ struct StarGift {
not_null<DocumentData*> document;
int limitedLeft = 0;
int limitedCount = 0;
TimeId firstSaleDate = 0;
TimeId lastSaleDate = 0;
friend inline bool operator==(
const StarGift &,
const StarGift &) = default;
};
struct UserStarGift {
StarGift gift;
StarGift info;
TextWithEntities message;
int64 convertStars = 0;
PeerId fromId = 0;

View file

@ -213,11 +213,14 @@ void AddTableRow(
not_null<Ui::FlatLabel*> AddTableRow(
not_null<Ui::TableLayout*> table,
rpl::producer<QString> label,
rpl::producer<TextWithEntities> value) {
rpl::producer<TextWithEntities> value,
const Fn<std::any(Fn<void()>)> &makeContext = nullptr) {
auto widget = object_ptr<Ui::FlatLabel>(
table,
std::move(value),
st::giveawayGiftCodeValue);
st::giveawayGiftCodeValue,
st::defaultPopupMenu,
std::move(makeContext));
const auto result = widget.data();
AddTableRow(
table,
@ -946,19 +949,49 @@ void AddStarGiftTable(
st::giveawayGiftCodeTable),
st::giveawayGiftCodeTableMargin);
const auto peerId = PeerId(entry.barePeerId);
const auto session = &controller->session();
const auto makeContext = [session](Fn<void()> update) {
return Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = std::move(update),
};
};
if (peerId) {
AddTableRow(
table,
tr::lng_credits_box_history_entry_peer_in(),
controller,
peerId);
} else {
} else if (!entry.soldOutInfo) {
AddTableRow(
table,
tr::lng_credits_box_history_entry_peer_in(),
MakeHiddenPeerTableValue(table, controller),
st::giveawayGiftCodePeerMargin);
}
if (!entry.firstSaleDate.isNull()) {
AddTableRow(
table,
tr::lng_gift_link_label_first_sale(),
rpl::single(Ui::Text::WithEntities(
langDateTime(entry.firstSaleDate))));
}
if (!entry.lastSaleDate.isNull()) {
AddTableRow(
table,
tr::lng_gift_link_label_last_sale(),
rpl::single(Ui::Text::WithEntities(
langDateTime(entry.lastSaleDate))));
}
{
auto star = session->data().customEmojiManager().creditsEmoji();
AddTableRow(
table,
tr::lng_gift_link_label_value(),
rpl::single(
star.append(Lang::FormatCountDecimal(entry.credits))),
makeContext);
}
if (!entry.date.isNull()) {
AddTableRow(
table,
@ -967,14 +1000,14 @@ void AddStarGiftTable(
}
if (entry.limitedCount > 0) {
auto amount = rpl::single(TextWithEntities{
QString::number(entry.limitedCount)
Lang::FormatCountDecimal(entry.limitedCount)
});
AddTableRow(
table,
tr::lng_gift_availability(),
((entry.limitedLeft > 0)
? tr::lng_gift_availability_left(
lt_count,
lt_count_decimal,
rpl::single(entry.limitedLeft * 1.),
lt_amount,
std::move(amount),

View file

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/event_filter.h"
#include "base/random.h"
#include "base/unixtime.h"
#include "api/api_premium.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/send_credits_box.h"
@ -19,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/tabbed_panel.h"
#include "chat_helpers/tabbed_selector.h"
#include "core/ui_integration.h"
#include "data/data_credits.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_session.h"
@ -40,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "payments/payments_checkout_process.h"
#include "payments/payments_non_panel_process.h"
#include "settings/settings_credits.h"
#include "settings/settings_credits_graphics.h"
#include "settings/settings_premium.h"
#include "ui/chat/chat_style.h"
#include "ui/chat/chat_theme.h"
@ -213,7 +216,7 @@ auto GenerateGiftMedia(
return tr::lng_action_gift_got_stars_text(
tr::now,
lt_count,
gift.convertStars,
gift.info.convertStars,
Ui::Text::RichLangValue);
});
auto description = data.text.empty()
@ -280,7 +283,7 @@ void ShowSentToast(
return tr::lng_gift_sent_about(
tr::now,
lt_count,
gift.stars,
gift.info.stars,
Ui::Text::RichLangValue);
});
const auto strong = window->showToast({
@ -338,7 +341,10 @@ void PreviewWrap::prepare(rpl::producer<GiftDetails> details) {
const auto cost = v::match(descriptor, [&](GiftTypePremium data) {
return FillAmountAndCurrency(data.cost, data.currency, true);
}, [&](GiftTypeStars data) {
return tr::lng_gift_stars_title(tr::now, lt_count, data.stars);
return tr::lng_gift_stars_title(
tr::now,
lt_count,
data.info.stars);
});
const auto text = tr::lng_action_gift_received(
tr::now,
@ -508,14 +514,7 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
const auto &gifts = api->starGifts();
list.reserve(gifts.size());
for (auto &gift : gifts) {
list.push_back({
.id = gift.id,
.stars = gift.stars,
.convertStars = gift.convertStars,
.document = gift.document,
.limitedCount = gift.limitedCount,
.limitedLeft = gift.limitedLeft,
});
list.push_back({ .info = gift });
}
auto &map = Map[session];
if (map.last != list) {
@ -587,7 +586,8 @@ struct GiftPriceTabs {
auto sameKey = 0;
for (const auto &gift : gifts) {
if (same) {
const auto key = gift.stars * (gift.limitedCount ? -1 : 1);
const auto key = gift.info.stars
* (gift.info.limitedCount ? -1 : 1);
if (!sameKey) {
sameKey = key;
} else if (sameKey != key) {
@ -595,12 +595,12 @@ struct GiftPriceTabs {
}
}
if (gift.limitedCount
if (gift.info.limitedCount
&& (result.size() < 2 || result[1] != kPriceTabLimited)) {
result.insert(begin(result) + 1, kPriceTabLimited);
}
if (!ranges::contains(result, gift.stars)) {
result.push_back(gift.stars);
if (!ranges::contains(result, gift.info.stars)) {
result.push_back(gift.info.stars);
}
}
if (same) {
@ -838,16 +838,38 @@ void SendGift(
const auto processNonPanelPaymentFormFactory
= Payments::ProcessNonPanelPaymentFormFactory(window, done);
Payments::CheckoutProcess::Start(Payments::InvoiceStarGift{
.giftId = gift.id,
.giftId = gift.info.id,
.randomId = details.randomId,
.message = details.text,
.user = peer->asUser(),
.limitedCount = gift.limitedCount,
.limitedCount = gift.info.limitedCount,
.anonymous = details.anonymous,
}, done, processNonPanelPaymentFormFactory);
});
}
void SoldOutBox(
not_null<Ui::GenericBox*> box,
not_null<Window::SessionController*> window,
const GiftTypeStars &gift) {
Settings::ReceiptCreditsBox(
box,
window,
Data::CreditsHistoryEntry{
.firstSaleDate = base::unixtime::parse(gift.info.firstSaleDate),
.lastSaleDate = base::unixtime::parse(gift.info.lastSaleDate),
.credits = uint64(gift.info.stars),
.bareGiftStickerId = gift.info.document->id,
.peerType = Data::CreditsHistoryEntry::PeerType::Peer,
.limitedCount = gift.info.limitedCount,
.limitedLeft = gift.info.limitedLeft,
.soldOutInfo = true,
.gift = true,
},
Data::SubscriptionEntry());
}
void SendGiftBox(
not_null<Ui::GenericBox*> box,
not_null<Window::SessionController*> window,
@ -873,7 +895,7 @@ void SendGiftBox(
};
}, [&](const GiftTypeStars &data) {
return Ui::CreditsEmojiSmall(session).append(
Lang::FormatCountDecimal(std::abs(data.stars)));
Lang::FormatCountDecimal(std::abs(data.info.stars)));
});
}());
@ -1076,15 +1098,10 @@ void SendGiftBox(
button->setClickedCallback([=] {
const auto star = std::get_if<GiftTypeStars>(&descriptor);
if (star && star->limitedCount && !star->limitedLeft) {
window->showToast({
.title = tr::lng_gift_sold_out_title(tr::now),
.text = tr::lng_gift_sold_out_text(
tr::now,
lt_count_decimal,
star->limitedCount,
Ui::Text::RichLangValue),
});
if (star
&& star->info.limitedCount
&& !star->info.limitedLeft) {
window->show(Box(SoldOutBox, window, *star));
} else {
window->show(
Box(SendGiftBox, window, peer, api, descriptor));
@ -1187,8 +1204,8 @@ void AddBlock(
) | rpl::map([=](std::vector<GiftTypeStars> &&gifts, int price) {
gifts.erase(ranges::remove_if(gifts, [&](const GiftTypeStars &gift) {
return (price == kPriceTabLimited)
? (!gift.limitedCount)
: (price && gift.stars != price);
? (!gift.info.limitedCount)
: (price && gift.info.stars != price);
}), end(gifts));
return GiftsDescriptor{
gifts | ranges::to<std::vector<GiftDescriptor>>(),

View file

@ -52,6 +52,8 @@ struct CreditsHistoryEntry final {
QString title;
TextWithEntities description;
QDateTime date;
QDateTime firstSaleDate;
QDateTime lastSaleDate;
PhotoId photoId = 0;
std::vector<CreditsHistoryMedia> extended;
uint64 credits = 0;
@ -70,6 +72,7 @@ struct CreditsHistoryEntry final {
bool anonymous = false;
bool savedToProfile = false;
bool fromGiftsList = false;
bool soldOutInfo = false;
bool reaction = false;
bool refunded = false;
bool pending = false;

View file

@ -86,12 +86,12 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor) {
{ 1., st::windowActiveTextFg->c },
});
}, [&](const GiftTypeStars &data) {
const auto soldOut = data.limitedCount
const auto soldOut = data.info.limitedCount
&& !data.userpic
&& !data.limitedLeft;
&& !data.info.limitedLeft;
_price.setMarkedText(
st::semiboldTextStyle,
_delegate->star().append(' ' + QString::number(data.stars)),
_delegate->star().append(' ' + QString::number(data.info.stars)),
kMarkupTextOptions,
_delegate->textContext());
_userpic = !data.userpic
@ -282,8 +282,8 @@ void GiftButton::paintEvent(QPaintEvent *e) {
}
return QString();
}, [&](const GiftTypeStars &data) {
if (const auto count = data.limitedCount) {
const auto soldOut = !data.userpic && !data.limitedLeft;
if (const auto count = data.info.limitedCount) {
const auto soldOut = !data.userpic && !data.info.limitedLeft;
p.setBrush(soldOut
? st::attentionButtonFg
: st::windowActiveTextFg);
@ -463,9 +463,7 @@ DocumentData *LookupGiftSticker(
return v::match(descriptor, [&](GiftTypePremium data) {
return packs.lookup(data.months);
}, [&](GiftTypeStars data) {
return data.document
? data.document
: packs.lookup(packs.monthsForStars(data.stars));
return data.info.document.get();
});
}

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "api/api_premium.h"
#include "ui/abstract_button.h"
#include "ui/effects/premium_stars_colored.h"
#include "ui/text/text.h"
@ -43,13 +44,8 @@ struct GiftTypePremium {
};
struct GiftTypeStars {
uint64 id = 0;
int64 stars = 0;
int64 convertStars = 0;
DocumentData *document = nullptr;
Api::StarGift info;
PeerData *from = nullptr;
int limitedCount = 0;
int limitedLeft = 0;
bool userpic = false;
bool hidden = false;
bool mine = false;

View file

@ -34,14 +34,10 @@ constexpr auto kPerPage = 50;
not_null<UserData*> to,
const Api::UserStarGift &gift) {
return GiftTypeStars{
.id = gift.gift.id,
.stars = gift.gift.stars,
.convertStars = gift.gift.convertStars,
.document = gift.gift.document,
.info = gift.info,
.from = ((gift.anonymous || !gift.fromId)
? nullptr
: to->owner().peer(gift.fromId).get()),
.limitedCount = gift.gift.limitedCount,
.userpic = true,
.hidden = gift.hidden,
.mine = to->isSelf(),

View file

@ -1862,7 +1862,7 @@ starsGiveawayOption#94ce852a flags:# extended:flags.0?true default:flags.1?true
starsGiveawayWinnersOption#54236209 flags:# default:flags.0?true users:int per_user_stars:long = StarsGiveawayWinnersOption;
starGift#aea174ee flags:# limited:flags.0?true id:long sticker:Document stars:long availability_remains:flags.0?int availability_total:flags.0?int convert_stars:long = StarGift;
starGift#49c577cd flags:# limited:flags.0?true sold_out:flags.1?true id:long sticker:Document stars:long availability_remains:flags.0?int availability_total:flags.0?int convert_stars:long first_sale_date:flags.1?int last_sale_date:flags.1?int = StarGift;
payments.starGiftsNotModified#a388a368 = payments.StarGifts;
payments.starGifts#901689ea hash:int gifts:Vector<StarGift> = payments.StarGifts;
@ -2565,4 +2565,4 @@ smsjobs.finishJob#4f1ebf24 flags:# job_id:string error:flags.0?string = Bool;
fragment.getCollectibleInfo#be1e85ba collectible:InputCollectible = fragment.CollectibleInfo;
// LAYER 190
// LAYER 191

View file

@ -731,7 +731,7 @@ void ReceiptCreditsBox(
const Data::SubscriptionEntry &s) {
const auto item = controller->session().data().message(
PeerId(e.barePeerId), MsgId(e.bareMsgId));
const auto isStarGift = (e.convertStars > 0);
const auto isStarGift = (e.convertStars > 0) || e.soldOutInfo;
const auto creditsHistoryStarGift = isStarGift && !e.id.isEmpty();
const auto sentStarGift = creditsHistoryStarGift && !e.in;
const auto convertedStarGift = creditsHistoryStarGift && e.converted;
@ -880,6 +880,8 @@ void ReceiptCreditsBox(
? tr::lng_credits_box_history_entry_subscription(tr::now)
: !e.title.isEmpty()
? e.title
: e.soldOutInfo
? tr::lng_credits_box_history_entry_gift_unavailable(tr::now)
: sentStarGift
? tr::lng_credits_box_history_entry_gift_sent(tr::now)
: convertedStarGift
@ -920,7 +922,11 @@ void ReceiptCreditsBox(
.session = session,
.customEmojiRepaint = [=] { amount->update(); },
};
if (s) {
if (e.soldOutInfo) {
text->setText(
st::defaultTextStyle,
tr::lng_credits_box_history_entry_gift_sold_out(tr::now));
} else if (s) {
text->setMarkedText(
st::defaultTextStyle,
tr::lng_credits_subscription_subtitle(
@ -960,7 +966,9 @@ void ReceiptCreditsBox(
amount->paintRequest(
) | rpl::start_with_next([=] {
auto p = Painter(amount);
p.setPen(s
p.setPen(e.soldOutInfo
? st::menuIconAttentionColor
: s
? st::windowSubTextFg
: e.pending
? st::creditsStroke
@ -1424,16 +1432,14 @@ void UserStarGiftBox(
Data::CreditsHistoryEntry{
.description = data.message,
.date = base::unixtime::parse(data.date),
.credits = uint64(data.gift.stars),
.credits = uint64(data.info.stars),
.bareMsgId = uint64(data.messageId.bare),
.barePeerId = data.fromId.value,
.bareGiftStickerId = (data.gift.document
? data.gift.document->id
: 0),
.bareGiftStickerId = data.info.document->id,
.peerType = Data::CreditsHistoryEntry::PeerType::Peer,
.limitedCount = data.gift.limitedCount,
.limitedLeft = data.gift.limitedLeft,
.convertStars = int(data.gift.convertStars),
.limitedCount = data.info.limitedCount,
.limitedLeft = data.info.limitedLeft,
.convertStars = int(data.info.convertStars),
.converted = false,
.anonymous = data.anonymous,
.savedToProfile = !data.hidden,