Update API scheme to layer 220.

Allow offering to buy gifts.
This commit is contained in:
John Preston
2025-11-28 11:37:32 +04:00
parent 4439cbf553
commit 23880ac6c1
15 changed files with 281 additions and 74 deletions

View File

@@ -2348,6 +2348,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_gift_premium_about" = "Subscription for exclusive Telegram features.";
"lng_action_gift_refunded" = "This gift was downgraded because a request to refund the payment related to this gift was made, and the money was returned.";
"lng_action_gift_got_ton" = "Use TON to suggest posts to channels.";
"lng_action_gift_offer" = "{user} offered you {cost} for {name}.";
"lng_action_gift_offer_you" = "You offered {cost} for {name}.";
"lng_action_gift_offer_state_expires" = "This offer expires in {time}.";
"lng_action_gift_offer_state_accepted" = "This offer was accepted.";
"lng_action_gift_offer_state_rejected" = "This offer was rejected.";
"lng_action_gift_offer_state_expired" = "This offer has expired.";
"lng_action_gift_offer_sold" = "{user} sold {name} for {cost}.";
"lng_action_gift_offer_sold_you" = "You sold {name} for {cost}.";
"lng_action_gift_offer_decline" = "Reject";
"lng_action_gift_offer_accept" = "Accept";
"lng_action_gift_offer_expired" = "The offer from {user} to buy your {name} for {cost} has expired.";
"lng_action_gift_offer_expired_you" = "Your offer to buy {name} for {cost} has expired.";
"lng_action_gift_offer_declined" = "{user} rejected your offer to buy {name} for {cost}.";
"lng_action_gift_offer_declined_you" = "You rejected {user}'s offer to buy your {name} for {cost}.";
"lng_action_suggested_photo_me" = "You suggested this photo for {user}'s Telegram profile.";
"lng_action_suggested_photo" = "{user} suggests this photo for your Telegram profile.";
"lng_action_suggested_photo_button" = "View Photo";
@@ -3936,6 +3950,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_transfer_unlist" = "Unlist";
"lng_gift_transfer_locked_title" = "Action Locked";
"lng_gift_transfer_locked_text" = "Transfer this gift to your Telegram account on Fragment to unlock this action.";
"lng_gift_offer_button" = "Offer to Buy";
"lng_gift_offer_title" = "Offer to Buy";
"lng_gift_offer_stars_about" = "Choose how many Stars you'd like to offer for {name}.";
"lng_gift_offer_ton_about" = "Choose how many TON you'd like to offer for {name}.";
"lng_gift_offer_duration" = "Offer Duration";
"lng_gift_offer_duration_about" = "Choose how long {user} can accept your offer. When the time expires, the amount will be refunded.";
"lng_gift_offer_cost_button" = "Offer {cost}";
"lng_gift_sell_unlist_title" = "Unlist {name}";
"lng_gift_sell_unlist_sure" = "Are you sure you want to unlist your gift?";
"lng_gift_sell_title" = "Price in Stars";

View File

@@ -930,6 +930,7 @@ std::optional<Data::StarGift> FromTL(
.themeUser = themeUser,
.nanoTonForResale = FindTonForResale(data.vresell_amount()),
.starsForResale = FindStarsForResale(data.vresell_amount()),
.starsMinOffer = data.voffer_min_stars().value_or(-1),
.number = data.vnum().v,
.onlyAcceptTon = data.is_resale_ton_only(),
.canBeTheme = data.is_theme_available(),

View File

@@ -3560,6 +3560,63 @@ void ShowUniqueGiftSellBox(
});
}
void SendOfferBuyGift(
std::shared_ptr<ChatHelpers::Show> show,
std::shared_ptr<Data::UniqueGift> unique,
SuggestPostOptions options,
Fn<void()> done) {
const auto randomId = base::RandomValue<uint64>();
const auto owner = show->session().data().peer(unique->ownerId);
using Flag = MTPpayments_SendStarGiftOffer::Flag;
show->session().api().request(MTPpayments_SendStarGiftOffer(
MTP_flags(Flag() | Flag()),//Flag::f_allow_paid_stars)
owner->input,
MTP_string(unique->slug),
StarsAmountToTL(options.price()),
MTP_int(options.offerDuration),
MTP_long(randomId),
MTP_long(0) // allow_paid_stars
)).done([=](const MTPUpdates &result) {
show->session().api().applyUpdates(result);
done();
}).fail([=](const MTP::Error &error) {
if (error.type() == u""_q) {
} else {
show->showToast(error.type());
}
}).send();
}
void ShowOfferBuyBox(
std::shared_ptr<ChatHelpers::Show> show,
std::shared_ptr<Data::UniqueGift> unique) {
Expects(unique->starsMinOffer >= 0);
const auto weak = std::make_shared<base::weak_qptr<Ui::BoxContent>>();
const auto done = [=](SuggestPostOptions result) {
SendOfferBuyGift(show, unique, result, [=] {
if (const auto strong = weak->get()) {
strong->closeBox();
}
});
};
using namespace HistoryView;
const auto options = SuggestPostOptions{
.exists = 1,
.priceWhole = uint32(unique->starsMinOffer),
};
auto priceBox = Box(ChooseSuggestPriceBox, SuggestPriceBoxArgs{
.peer = show->session().data().peer(unique->ownerId),
.done = done,
.value = options,
.mode = SuggestMode::Gift,
});
*weak = priceBox.data();
show->show(std::move(priceBox));
}
void GiftReleasedByHandler(not_null<PeerData*> peer) {
const auto session = &peer->session();
const auto window = session->tryResolveWindow(peer);

View File

@@ -95,6 +95,10 @@ void ShowUniqueGiftSellBox(
Data::SavedStarGiftId savedId,
Settings::GiftWearBoxStyleOverride st);
void ShowOfferBuyBox(
std::shared_ptr<ChatHelpers::Show> show,
std::shared_ptr<Data::UniqueGift> unique);
void GiftReleasedByHandler(not_null<PeerData*> peer);
struct StarGiftUpgradeArgs {

View File

@@ -213,6 +213,7 @@ struct SuggestPostOptions {
uint32 priceNano : 31 = 0;
uint32 ton : 1 = 0;
TimeId date = 0;
TimeId offerDuration = 0;
[[nodiscard]] CreditsAmount price() const {
return CreditsAmount(

View File

@@ -76,6 +76,7 @@ struct UniqueGift {
int64 nanoTonForResale = -1;
int starsForResale = -1;
int starsForTransfer = -1;
int starsMinOffer = -1;
int number = 0;
bool onlyAcceptTon = false;
bool canBeTheme = false;

View File

@@ -905,6 +905,20 @@ UserpicsSlice ParseUserpicsSlice(
return result;
}
[[nodiscard]] ActionStarGift ParseStarGift(const MTPStarGift &gift) {
return gift.match([&](const MTPDstarGift &gift) {
return ActionStarGift{
.giftId = uint64(gift.vid().v),
.stars = int64(gift.vstars().v),
.limited = gift.is_limited(),
};
}, [&](const MTPDstarGiftUnique &gift) {
return ActionStarGift{
.giftId = uint64(gift.vid().v),
};
});
}
File &Story::file() {
return media.file();
}
@@ -1744,41 +1758,16 @@ ServiceAction ParseServiceAction(
.isUnclaimed = data.is_unclaimed(),
};
}, [&](const MTPDmessageActionStarGift &data) {
data.vgift().match([&](const MTPDstarGift &gift) {
result.content = ActionStarGift{
.giftId = uint64(gift.vid().v),
.stars = int64(gift.vstars().v),
.text = (data.vmessage()
? ParseText(
data.vmessage()->data().vtext(),
data.vmessage()->data().ventities().v)
: std::vector<TextPart>()),
.anonymous = data.is_name_hidden(),
.limited = gift.is_limited(),
};
}, [&](const MTPDstarGiftUnique &gift) {
result.content = ActionStarGift{
.giftId = uint64(gift.vid().v),
.text = (data.vmessage()
? ParseText(
data.vmessage()->data().vtext(),
data.vmessage()->data().ventities().v)
: std::vector<TextPart>()),
.anonymous = data.is_name_hidden(),
};
});
auto content = ParseStarGift(data.vgift());
content.text = (data.vmessage()
? ParseText(
data.vmessage()->data().vtext(),
data.vmessage()->data().ventities().v)
: std::vector<TextPart>());
content.anonymous = data.is_name_hidden();
result.content = content;
}, [&](const MTPDmessageActionStarGiftUnique &data) {
data.vgift().match([&](const MTPDstarGift &gift) {
result.content = ActionStarGift{
.giftId = uint64(gift.vid().v),
.stars = int64(gift.vstars().v),
.limited = gift.is_limited(),
};
}, [&](const MTPDstarGiftUnique &gift) {
result.content = ActionStarGift{
.giftId = uint64(gift.vid().v),
};
});
result.content = ParseStarGift(data.vgift());
}, [&](const MTPDmessageActionPaidMessagesRefunded &data) {
result.content = ActionPaidMessagesRefunded{
.messages = data.vcount().v,
@@ -1844,6 +1833,21 @@ ServiceAction ParseServiceAction(
fields.vmonth().v,
fields.vyear().value_or_empty());
result.content = content;
}, [&](const MTPDmessageActionStarGiftPurchaseOffer &data) {
auto content = ParseStarGift(data.vgift());
content.offer = true;
content.offerPrice = CreditsAmountFromTL(data.vprice());
content.offerExpireAt = data.vexpires_at().v;
content.offerAccepted = data.is_accepted();
content.offerDeclined = data.is_declined();
result.content = content;
}, [&](const MTPDmessageActionStarGiftPurchaseOfferDeclined &data) {
auto content = ParseStarGift(data.vgift());
content.offer = true;
content.offerDeclined = true;
content.offerExpired = data.is_expired();
content.offerPrice = CreditsAmountFromTL(data.vprice());
result.content = content;
}, [](const MTPDmessageActionEmpty &data) {});
return result;
}

View File

@@ -690,6 +690,13 @@ struct ActionStarGift {
std::vector<TextPart> text;
bool anonymous = false;
bool limited = false;
CreditsAmount offerPrice;
TimeId offerExpireAt = 0;
bool offer = false;
bool offerAccepted = false;
bool offerDeclined = false;
bool offerExpired = false;
};
struct ActionPaidMessagesRefunded {

View File

@@ -6393,7 +6393,7 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
Unexpected("PhoneCall type in setServiceMessageFromMtp.");
};
auto prepareSuggestBirthday = [this](const MTPDmessageActionSuggestBirthday &action) {
auto prepareSuggestBirthday = [&](const MTPDmessageActionSuggestBirthday &action) {
auto result = PreparedServiceText{};
const auto isSelf = (_from->id == _from->session().userPeerId());
const auto peer = isSelf ? history()->peer : _from;
@@ -6412,6 +6412,16 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
return result;
};
auto prepareStarGiftPurchaseOffer = [&](const MTPDmessageActionStarGiftPurchaseOffer &action) {
auto result = PreparedServiceText{};
return result;
};
auto prepareStarGiftPurchaseOfferDeclined = [&](const MTPDmessageActionStarGiftPurchaseOfferDeclined &action) {
auto result = PreparedServiceText{};
return result;
};
setServiceText(action.match(
prepareChatAddUserText,
prepareChatJoinedByLink,
@@ -6469,6 +6479,8 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
prepareSuggestedPostSuccess,
prepareSuggestedPostRefund,
prepareSuggestBirthday,
prepareStarGiftPurchaseOffer,
prepareStarGiftPurchaseOfferDeclined,
PrepareEmptyText<MTPDmessageActionRequestedPeerSentMe>,
PrepareErrorText<MTPDmessageActionEmpty>));

View File

@@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h"
#include "ui/boxes/choose_date_time.h"
#include "ui/boxes/single_choice_box.h"
#include "ui/controls/ton_common.h"
#include "ui/widgets/fields/number_input.h"
#include "ui/widgets/fields/input_field.h"
@@ -401,6 +402,7 @@ void ChooseSuggestPriceBox(
rpl::event_stream<> fieldsChanges;
rpl::variable<CreditsAmount> price;
rpl::variable<TimeId> date;
rpl::variable<TimeId> offerDuration;
rpl::variable<bool> ton;
Fn<std::optional<CreditsAmount>()> computePrice;
Fn<void()> save;
@@ -413,6 +415,7 @@ void ChooseSuggestPriceBox(
state->price = args.value.price();
const auto peer = args.peer;
const auto mode = args.mode;
const auto admin = peer->amMonoforumAdmin();
const auto broadcast = peer->monoforumBroadcast();
const auto usePeer = broadcast ? broadcast : peer;
@@ -426,7 +429,9 @@ void ChooseSuggestPriceBox(
box->setStyle(st::suggestPriceBox);
auto title = (args.mode == SuggestMode::New)
auto title = (mode == SuggestMode::Gift)
? tr::lng_gift_offer_title()
: (mode == SuggestMode::New)
? tr::lng_suggest_options_title()
: tr::lng_suggest_options_change();
if (admin) {
@@ -567,17 +572,27 @@ void ChooseSuggestPriceBox(
auto starsAbout = admin
? rpl::combine(
youGet(StarsPriceValue(state->price.value()), true),
tr::lng_suggest_options_stars_warning(Ui::Text::RichLangValue)
tr::lng_suggest_options_stars_warning(tr::rich)
) | rpl::map([=](const QString &t1, const TextWithEntities &t2) {
return TextWithEntities{ t1 }.append("\n\n").append(t2);
})
: tr::lng_suggest_options_stars_price_about(Ui::Text::WithEntities);
: (mode == SuggestMode::Gift)
? tr::lng_gift_offer_stars_about(
lt_name,
rpl::single(tr::marked(args.giftName)),
tr::rich)
: tr::lng_suggest_options_stars_price_about(tr::rich);
auto tonAbout = admin
? youGet(
TonPriceValue(state->price.value()),
false
) | Ui::Text::ToWithEntities()
: tr::lng_suggest_options_ton_price_about(Ui::Text::WithEntities);
) | rpl::map(tr::rich)
: (mode == SuggestMode::Gift)
? tr::lng_gift_offer_ton_about(
lt_name,
rpl::single(tr::marked(args.giftName)),
tr::rich)
: tr::lng_suggest_options_ton_price_about(tr::rich);
auto priceInput = AddStarsTonPriceInput(container, {
.session = session,
.showTon = state->ton.value(),
@@ -595,39 +610,94 @@ void ChooseSuggestPriceBox(
Ui::AddSkip(container);
const auto time = Settings::AddButtonWithLabel(
container,
tr::lng_suggest_options_date(),
state->date.value() | rpl::map([](TimeId date) {
return date
? langDateTime(base::unixtime::parse(date))
: tr::lng_suggest_options_date_any(tr::now);
}),
st::settingsButtonNoIcon);
time->setClickedCallback([=] {
const auto weak = std::make_shared<base::weak_qptr<Ui::BoxContent>>();
const auto parentWeak = base::make_weak(box);
const auto done = [=](TimeId result) {
if (parentWeak) {
state->date = result;
}
if (const auto strong = weak->get()) {
strong->closeBox();
}
if (args.mode == SuggestMode::Gift) {
const auto day = 86400;
auto durations = std::vector{
day / 4,
day / 2,
day,
day + day / 2,
day * 2,
day * 3,
};
auto dateBox = Box(ChooseSuggestTimeBox, SuggestTimeBoxArgs{
.session = session,
.done = done,
.value = state->date.current(),
.mode = args.mode,
});
*weak = dateBox.data();
box->uiShow()->show(std::move(dateBox));
});
if (peer->session().isTestMode()) {
durations.insert(begin(durations), 120);
}
const auto durationToText = [](TimeId date) {
return (date >= 3600)
? tr::lng_hours(tr::now, lt_count, date / 3600)
: tr::lng_minutes(tr::now, lt_count, date / 60);
};
state->offerDuration = day;
const auto duration = Settings::AddButtonWithLabel(
container,
tr::lng_gift_offer_duration(),
state->offerDuration.value() | rpl::map(durationToText),
st::settingsButtonNoIcon);
Ui::AddSkip(container);
Ui::AddDividerText(container, tr::lng_suggest_options_date_about());
duration->setClickedCallback([=] {
box->uiShow()->show(Box([=](not_null<Ui::GenericBox*> box) {
const auto save = [=](int index) {
state->offerDuration = durations[index];
};
auto options = durations
| ranges::views::transform(durationToText)
| ranges::to_vector;
const auto selected = ranges::find(
durations,
state->offerDuration.current());
SingleChoiceBox(box, {
.title = tr::lng_settings_angle_backend(),
.options = options,
.initialSelection = int(selected - begin(durations)),
.callback = save,
});
}));
});
Ui::AddSkip(container);
Ui::AddDividerText(
container,
tr::lng_gift_offer_duration_about(
lt_user,
rpl::single(peer->shortName())));
} else {
const auto time = Settings::AddButtonWithLabel(
container,
tr::lng_suggest_options_date(),
state->date.value() | rpl::map([](TimeId date) {
return date
? langDateTime(base::unixtime::parse(date))
: tr::lng_suggest_options_date_any(tr::now);
}),
st::settingsButtonNoIcon);
time->setClickedCallback([=] {
const auto weak = std::make_shared<
base::weak_qptr<Ui::BoxContent>
>();
const auto parentWeak = base::make_weak(box);
const auto done = [=](TimeId result) {
if (parentWeak) {
state->date = result;
}
if (const auto strong = weak->get()) {
strong->closeBox();
}
};
auto dateBox = Box(ChooseSuggestTimeBox, SuggestTimeBoxArgs{
.session = session,
.done = done,
.value = state->date.current(),
.mode = args.mode,
});
*weak = dateBox.data();
box->uiShow()->show(std::move(dateBox));
});
Ui::AddSkip(container);
Ui::AddDividerText(container, tr::lng_suggest_options_date_about());
}
state->save = [=] {
const auto ton = uint32(state->ton.current() ? 1 : 0);
@@ -674,6 +744,7 @@ void ChooseSuggestPriceBox(
.priceNano = uint32(value.nano()),
.ton = ton,
.date = state->date.current(),
.offerDuration = state->offerDuration.current(),
});
};

View File

@@ -34,6 +34,7 @@ enum class SuggestMode {
New,
Change,
Publish,
Gift,
};
struct SuggestTimeBoxArgs {
@@ -91,6 +92,7 @@ struct SuggestPriceBoxArgs {
Fn<void(SuggestPostOptions)> done;
SuggestPostOptions value;
SuggestMode mode = SuggestMode::New;
QString giftName;
};
void ChooseSuggestPriceBox(
not_null<Ui::GenericBox*> box,

View File

@@ -235,6 +235,7 @@ darkGiftHide: icon {{ "menu/stealth", groupCallMembersFg }};
darkGiftShow: icon {{ "menu/show_in_chat", groupCallMembersFg }};
darkGiftPin: icon {{ "menu/pin", groupCallMembersFg }};
darkGiftUnpin: icon {{ "menu/unpin", groupCallMembersFg }};
darkGiftOffer: icon {{ "menu/earn", groupCallMembersFg }};
darkGiftPalette: TextPalette(defaultTextPalette) {
linkFg: mediaviewTextLinkFg;
monoFg: groupCallMembersFg;

View File

@@ -187,7 +187,7 @@ messageActionPaymentRefunded#41b3e202 flags:# peer:Peer currency:string total_am
messageActionGiftStars#45d5b021 flags:# currency:string amount:long stars:long crypto_currency:flags.0?string crypto_amount:flags.0?long transaction_id:flags.1?string = MessageAction;
messageActionPrizeStars#b00c47a2 flags:# unclaimed:flags.0?true stars:long transaction_id:string boost_peer:Peer giveaway_msg_id:int = MessageAction;
messageActionStarGift#db596550 flags:# name_hidden:flags.0?true saved:flags.2?true converted:flags.3?true upgraded:flags.5?true refunded:flags.9?true can_upgrade:flags.10?true prepaid_upgrade:flags.13?true upgrade_separate:flags.16?true auction_acquired:flags.17?true gift:StarGift message:flags.1?TextWithEntities convert_stars:flags.4?long upgrade_msg_id:flags.5?int upgrade_stars:flags.8?long from_id:flags.11?Peer peer:flags.12?Peer saved_id:flags.12?long prepaid_upgrade_hash:flags.14?string gift_msg_id:flags.15?int to_id:flags.18?Peer = MessageAction;
messageActionStarGiftUnique#95728543 flags:# upgrade:flags.0?true transferred:flags.1?true saved:flags.2?true refunded:flags.5?true prepaid_upgrade:flags.11?true assigned:flags.13?true gift:StarGift can_export_at:flags.3?int transfer_stars:flags.4?long from_id:flags.6?Peer peer:flags.7?Peer saved_id:flags.7?long resale_amount:flags.8?StarsAmount can_transfer_at:flags.9?int can_resell_at:flags.10?int drop_original_details_stars:flags.12?long = MessageAction;
messageActionStarGiftUnique#95728543 flags:# upgrade:flags.0?true transferred:flags.1?true saved:flags.2?true refunded:flags.5?true prepaid_upgrade:flags.11?true assigned:flags.13?true from_offer:flags.14?true gift:StarGift can_export_at:flags.3?int transfer_stars:flags.4?long from_id:flags.6?Peer peer:flags.7?Peer saved_id:flags.7?long resale_amount:flags.8?StarsAmount can_transfer_at:flags.9?int can_resell_at:flags.10?int drop_original_details_stars:flags.12?long = MessageAction;
messageActionPaidMessagesRefunded#ac1f1fcd count:int stars:long = MessageAction;
messageActionPaidMessagesPrice#84b88578 flags:# broadcast_messages_allowed:flags.0?true stars:long = MessageAction;
messageActionConferenceCall#2ffe2f7a flags:# missed:flags.0?true active:flags.1?true video:flags.4?true call_id:long duration:flags.2?int other_participants:flags.3?Vector<Peer> = MessageAction;
@@ -198,6 +198,8 @@ messageActionSuggestedPostSuccess#95ddcf69 price:StarsAmount = MessageAction;
messageActionSuggestedPostRefund#69f916f8 flags:# payer_initiated:flags.0?true = MessageAction;
messageActionGiftTon#a8a3c699 flags:# currency:string amount:long crypto_currency:string crypto_amount:long transaction_id:flags.0?string = MessageAction;
messageActionSuggestBirthday#2c8f2a25 birthday:Birthday = MessageAction;
messageActionStarGiftPurchaseOffer#774278d4 flags:# accepted:flags.0?true declined:flags.1?true gift:StarGift price:StarsAmount expires_at:int = MessageAction;
messageActionStarGiftPurchaseOfferDeclined#73ada76b flags:# expired:flags.0?true gift:StarGift price:StarsAmount = MessageAction;
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
@@ -1915,7 +1917,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#1b9a4d7f flags:# limited:flags.0?true sold_out:flags.1?true birthday:flags.2?true require_premium:flags.7?true limited_per_user:flags.8?true peer_color_available:flags.10?true auction:flags.11?true id:long sticker:Document stars:long availability_remains:flags.0?int availability_total:flags.0?int availability_resale:flags.4?long convert_stars:long first_sale_date:flags.1?int last_sale_date:flags.1?int upgrade_stars:flags.3?long resell_min_stars:flags.4?long title:flags.5?string released_by:flags.6?Peer per_user_total:flags.8?int per_user_remains:flags.8?int locked_until_date:flags.9?int auction_slug:flags.11?string gifts_per_round:flags.11?int = StarGift;
starGiftUnique#b0bf741b flags:# require_premium:flags.6?true resale_ton_only:flags.7?true theme_available:flags.9?true id:long gift_id:long title:string slug:string num:int owner_id:flags.0?Peer owner_name:flags.1?string owner_address:flags.2?string attributes:Vector<StarGiftAttribute> availability_issued:int availability_total:int gift_address:flags.3?string resell_amount:flags.4?Vector<StarsAmount> released_by:flags.5?Peer value_amount:flags.8?long value_currency:flags.8?string theme_peer:flags.10?Peer peer_color:flags.11?PeerColor host_id:flags.12?Peer = StarGift;
starGiftUnique#9f2fb096 flags:# require_premium:flags.6?true resale_ton_only:flags.7?true theme_available:flags.9?true id:long gift_id:long title:string slug:string num:int owner_id:flags.0?Peer owner_name:flags.1?string owner_address:flags.2?string attributes:Vector<StarGiftAttribute> availability_issued:int availability_total:int gift_address:flags.3?string resell_amount:flags.4?Vector<StarsAmount> released_by:flags.5?Peer value_amount:flags.8?long value_currency:flags.8?string theme_peer:flags.10?Peer peer_color:flags.11?PeerColor host_id:flags.12?Peer offer_min_stars:flags.13?int = StarGift;
payments.starGiftsNotModified#a388a368 = payments.StarGifts;
payments.starGifts#2ed82995 hash:int gifts:Vector<StarGift> chats:Vector<Chat> users:Vector<User> = payments.StarGifts;
@@ -2732,6 +2734,8 @@ payments.checkCanSendGift#c0c4edc9 gift_id:long = payments.CheckCanSendGiftResul
payments.getStarGiftAuctionState#5c9ff4d6 auction:InputStarGiftAuction version:int = payments.StarGiftAuctionState;
payments.getStarGiftAuctionAcquiredGifts#6ba2cbec gift_id:long = payments.StarGiftAuctionAcquiredGifts;
payments.getStarGiftActiveAuctions#a5d0514d hash:long = payments.StarGiftActiveAuctions;
payments.resolveStarGiftOffer#e9ce781c flags:# decline:flags.0?true offer_msg_id:int = Updates;
payments.sendStarGiftOffer#8fb86b41 flags:# peer:InputPeer slug:string price:StarsAmount duration:int random_id:long allow_paid_stars:flags.0?long = Updates;
stickers.createStickerSet#9021ab67 flags:# masks:flags.0?true emojis:flags.5?true text_color:flags.6?true user_id:InputUser title:string short_name:string thumb:flags.2?InputDocument stickers:Vector<InputStickerSetItem> software:flags.3?string = messages.StickerSet;
stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet;
@@ -2867,4 +2871,4 @@ smsjobs.finishJob#4f1ebf24 flags:# job_id:string error:flags.0?string = Bool;
fragment.getCollectibleInfo#be1e85ba collectible:InputCollectible = fragment.CollectibleInfo;
// LAYER 219
// LAYER 220

View File

@@ -964,6 +964,21 @@ void ProcessReceivedSubscriptions(
// (owner->isChannel() && owner->asChannel()->canTransferGifts());
}
[[nodiscard]] bool ShowOfferBuyButton(
not_null<Main::Session*> session,
const Data::CreditsHistoryEntry &e) {
const auto unique = e.uniqueGift.get();
const auto owner = (unique && unique->ownerId)
? session->data().peer(unique->ownerId).get()
: nullptr;
return owner
&& owner->isUser()
&& !owner->isSelf()
&& (unique->starsMinOffer >= 0);
// Currently we're not making offers for channel gifts.
// (owner->isChannel() && !owner->asChannel()->canTransferGifts());
}
void FillUniqueGiftMenu(
std::shared_ptr<ChatHelpers::Show> show,
not_null<Ui::PopupMenu*> menu,
@@ -1158,6 +1173,10 @@ void FillUniqueGiftMenu(
}));
}, st.unlist ? st.unlist : &st::menuIconTagRemove);
}
} else if (ShowOfferBuyButton(&show->session(), e)) {
menu->addAction(tr::lng_gift_offer_button(tr::now), [=] {
ShowOfferBuyBox(show, unique);
}, st.offer ? st.offer : &st::menuIconEarn);
}
}
@@ -1193,6 +1212,7 @@ CreditsEntryBoxStyleOverrides DarkCreditsEntryBoxStyle() {
.hide = &st::darkGiftHide,
.pin = &st::darkGiftPin,
.unpin = &st::darkGiftUnpin,
.offer = &st::darkGiftOffer,
.shareBox = std::make_shared<ShareBoxStyleOverrides>(
DarkShareBoxStyle()),
.giftWearBox = std::make_shared<GiftWearBoxStyleOverride>(

View File

@@ -127,6 +127,7 @@ struct CreditsEntryBoxStyleOverrides {
const style::icon *hide = nullptr;
const style::icon *pin = nullptr;
const style::icon *unpin = nullptr;
const style::icon *offer = nullptr;
std::shared_ptr<ShareBoxStyleOverrides> shareBox;
std::shared_ptr<GiftWearBoxStyleOverride> giftWearBox;
};