From 31ea4cfe8047ebc91999418354484fa96ccfc89c Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 28 Nov 2025 18:21:50 +0400 Subject: [PATCH] Process offers with accept / reject. --- Telegram/Resources/langs/lang.strings | 7 + Telegram/SourceFiles/api/api_suggest_post.cpp | 25 +++- .../SourceFiles/boxes/transfer_gift_box.cpp | 140 ++++++++++++++++++ .../SourceFiles/boxes/transfer_gift_box.h | 11 ++ Telegram/SourceFiles/history/history_item.cpp | 17 ++- 5 files changed, 197 insertions(+), 3 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 7ec20df0ac..a6153979b0 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2291,8 +2291,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_action_proximity_distance_km#other" = "{count} km"; "lng_action_webview_data_done" = "Data from the \"{text}\" button was transferred to the bot."; "lng_action_gift_received" = "{user} sent you a gift for {cost}"; +"lng_action_gift_received_sold" = "{user} sold you a gift for {cost}"; "lng_action_gift_unique_received" = "{user} sent you a unique collectible item"; "lng_action_gift_sent" = "You sent a gift for {cost}"; +"lng_action_gift_sent_sold" = "You sold a gift for {cost}"; "lng_action_gift_unique_sent" = "You sent a unique collectible item"; "lng_action_gift_upgraded" = "{user} turned the gift from you into a unique collectible"; "lng_action_gift_upgraded_channel" = "{user} turned this gift to {channel} into a unique collectible"; @@ -3959,6 +3961,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "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_offer_confirm_reject" = "Do you want to reject the offer?"; +"lng_gift_offer_confirm_accept" = "Do you want to sell {name} to {user} for {cost}?"; +"lng_gift_offer_you_get" = "You will receive {cost} after fees."; +"lng_gift_offer_value_higher" = "{percent} higher"; +"lng_gift_offer_sell_for" = "Sell for {price}"; "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"; diff --git a/Telegram/SourceFiles/api/api_suggest_post.cpp b/Telegram/SourceFiles/api/api_suggest_post.cpp index f973c4fcaa..cb5aa52e3c 100644 --- a/Telegram/SourceFiles/api/api_suggest_post.cpp +++ b/Telegram/SourceFiles/api/api_suggest_post.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "base/unixtime.h" +#include "boxes/transfer_gift_box.h" #include "chat_helpers/message_field.h" #include "core/click_handler_types.h" #include "data/components/credits.h" @@ -504,6 +505,20 @@ void SuggestApprovalPrice( }, SuggestMode::Change); } +void ConfirmGiftSaleAccept( + not_null window, + not_null item, + not_null suggestion) { + ShowGiftSaleAcceptBox(window, item, suggestion); +} + +void ConfirmGiftSaleDecline( + not_null window, + not_null item, + not_null suggestion) { + ShowGiftSaleRejectBox(window, item, suggestion); +} + } // namespace std::shared_ptr AcceptClickHandler( @@ -524,6 +539,8 @@ std::shared_ptr AcceptClickHandler( const auto suggestion = item->Get(); if (!suggestion) { return; + } else if (suggestion->gift) { + ConfirmGiftSaleAccept(controller, item, suggestion); } else if (!suggestion->date) { RequestApprovalDate(show, item); } else { @@ -546,8 +563,12 @@ std::shared_ptr DeclineClickHandler( if (!item) { return; } - - RequestDeclineComment(controller->uiShow(), item); + const auto suggestion = item->Get(); + if (suggestion && suggestion->gift) { + ConfirmGiftSaleDecline(controller, item, suggestion); + } else { + RequestDeclineComment(controller->uiShow(), item); + } }); } diff --git a/Telegram/SourceFiles/boxes/transfer_gift_box.cpp b/Telegram/SourceFiles/boxes/transfer_gift_box.cpp index f5102b6f62..f5cbf61b0e 100644 --- a/Telegram/SourceFiles/boxes/transfer_gift_box.cpp +++ b/Telegram/SourceFiles/boxes/transfer_gift_box.cpp @@ -24,7 +24,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_star_gift.h" #include "data/data_thread.h" #include "data/data_user.h" +#include "history/history.h" +#include "history/history_item.h" +#include "history/history_item_components.h" #include "lang/lang_keys.h" +#include "main/main_app_config.h" #include "main/main_session.h" #include "payments/payments_checkout_process.h" #include "ui/boxes/confirm_box.h" @@ -492,6 +496,26 @@ void TransferGift( } } +void ResolveGiftSaleOffer( + not_null window, + MsgId id, + bool accept, + Fn done) { + using Flag = MTPpayments_ResolveStarGiftOffer::Flag; + const auto session = &window->session(); + const auto show = window->uiShow(); + session->api().request(MTPpayments_ResolveStarGiftOffer( + MTP_flags(accept ? Flag() : Flag::f_decline), + MTP_int(id.bare) + )).done([=](const MTPUpdates &result) { + session->api().applyUpdates(result); + done(true); + }).fail([=](const MTP::Error &error) { + show->showToast(error.type()); + done(false); + }).send(); +} + void BuyResaleGift( std::shared_ptr show, not_null to, @@ -687,6 +711,122 @@ void ShowTransferGiftBox( Ui::LayerOption::KeepOther); } +void ShowGiftSaleAcceptBox( + not_null controller, + not_null item, + not_null suggestion) { + const auto id = item->id; + const auto peer = item->history()->peer; + const auto gift = suggestion->gift; + const auto price = suggestion->price; + + const auto &appConfig = controller->session().appConfig(); + const auto starsThousandths = appConfig.giftResaleStarsThousandths(); + const auto nanoTonThousandths = appConfig.giftResaleNanoTonThousandths(); + + controller->show(Box([=](not_null box) { + auto button = tr::lng_gift_offer_sell_for( + lt_price, + rpl::single(Ui::Text::IconEmoji( + &st::starIconEmoji + ).append(Lang::FormatCreditsAmountDecimal( + price + ))), + tr::marked); + + struct State { + bool sent = false; + }; + const auto state = std::make_shared(); + auto callback = [=] { + if (state->sent) { + return; + } + state->sent = true; + const auto weak = base::make_weak(controller); + const auto weakBox = base::make_weak(box); + ResolveGiftSaleOffer(controller, id, true, [=](bool ok) { + state->sent = false; + if (ok) { + if (const auto strong = weak.get()) { + strong->showPeerHistory(peer->id); + } + if (const auto strong = weakBox.get()) { + strong->closeBox(); + } + } + }); + }; + + const auto receive = price.ton() + ? ((price.value() * nanoTonThousandths) / 1000.) + : ((int64(price.value()) * starsThousandths) / 1000); + + box->addRow( + CreateGiftTransfer(box->verticalLayout(), gift, peer), + QMargins(0, st::boxPadding.top(), 0, 0)); + + Ui::ConfirmBox(box, { + .text = tr::lng_gift_offer_confirm_accept( + tr::now, + lt_name, + tr::bold(UniqueGiftName(*gift)), + lt_user, + tr::bold(peer->shortName()), + lt_cost, + tr::bold(PrepareCreditsAmountText(price)), + tr::marked + ).append(u"\n\n"_q).append(tr::lng_gift_offer_you_get( + tr::now, + lt_cost, + tr::bold(price.stars() + ? tr::lng_action_gift_for_stars( + tr::now, + lt_count_decimal, + receive) + : tr::lng_action_gift_for_ton( + tr::now, + lt_count_decimal, + receive)), + tr::marked)), + .confirmed = std::move(callback), + .confirmText = std::move(button), + }); + + const auto show = controller->uiShow(); + AddTransferGiftTable(show, box->verticalLayout(), gift); + })); +} + +void ShowGiftSaleRejectBox( + not_null controller, + not_null item, + not_null suggestion) { + struct State { + bool sent = false; + }; + const auto id = item->id; + const auto state = std::make_shared(); + auto callback = [=](Fn close) { + if (state->sent) { + return; + } + state->sent = true; + const auto weak = base::make_weak(controller); + ResolveGiftSaleOffer(controller, id, false, [=](bool ok) { + state->sent = false; + if (ok) { + close(); + } + }); + }; + controller->show(Ui::MakeConfirmBox({ + .text = tr::lng_gift_offer_confirm_reject(), + .confirmed = std::move(callback), + .confirmText = tr::lng_action_gift_offer_decline(), + })); +} + void SetThemeFromUniqueGift( not_null window, std::shared_ptr unique) { diff --git a/Telegram/SourceFiles/boxes/transfer_gift_box.h b/Telegram/SourceFiles/boxes/transfer_gift_box.h index ec074e4d70..e1929260e9 100644 --- a/Telegram/SourceFiles/boxes/transfer_gift_box.h +++ b/Telegram/SourceFiles/boxes/transfer_gift_box.h @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +struct HistoryMessageSuggestion; + namespace Window { class SessionController; } // namespace Window @@ -36,6 +38,15 @@ void ShowTransferGiftBox( std::shared_ptr gift, Data::SavedStarGiftId savedId); +void ShowGiftSaleAcceptBox( + not_null controller, + not_null item, + not_null suggestion); +void ShowGiftSaleRejectBox( + not_null controller, + not_null item, + not_null suggestion); + void ShowBuyResaleGiftBox( std::shared_ptr show, std::shared_ptr gift, diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index fd42b4ba93..6d9f56d2ce 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -6202,6 +6202,7 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) { auto result = PreparedServiceText(); const auto isSelf = _from->isSelf(); const auto resale = CreditsAmountFromTL(action.vresale_amount()); + const auto fromOffer = action.is_from_offer(); const auto resaleCost = !resale ? tr::marked() : tr::marked(PrepareCreditsAmountText(resale)); @@ -6283,7 +6284,21 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) { if (!resale || !isSelf) { result.links.push_back(from->createOpenLink()); } - result.text = resale + result.text = fromOffer + ? (isSelf + ? tr::lng_action_gift_sent_sold( + tr::now, + lt_cost, + resaleCost, + Ui::Text::WithEntities) + : tr::lng_action_gift_received_sold( + tr::now, + lt_user, + Ui::Text::Link(peer->shortName(), 1), // Link 1. + lt_cost, + resaleCost, + Ui::Text::WithEntities)) + : resale ? (isSelf ? tr::lng_action_gift_sent( tr::now,