From 37c7b0c6d1a8d5b059a2277a459c031cef951dde Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 21 Jan 2025 13:38:04 +0400 Subject: [PATCH] Allow exporting unique gifts to blockchain. --- Telegram/Resources/langs/lang.strings | 3 + .../SourceFiles/boxes/transfer_gift_box.cpp | 146 +++++++++++++++--- 2 files changed, 124 insertions(+), 25 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 026b82d49..a1d07c974 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -3365,6 +3365,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gift_transferred_about" = "{name} was successfully transferred to {recipient}."; "lng_gift_transfer_title" = "Transfer {name}"; "lng_gift_transfer_via_blockchain" = "Send via Blockchain"; +"lng_gift_transfer_password_title" = "Two-step verification"; +"lng_gift_transfer_password_description" = "Please enter your password to transfer."; +"lng_gift_transfer_password_about" = "You can withdraw only if you have:"; "lng_gift_transfer_unlocks_days#one" = "unlocks in {count} day"; "lng_gift_transfer_unlocks_days#other" = "unlocks in {count} days"; "lng_gift_transfer_unlocks_hours#one" = "unlocks in {count} hour"; diff --git a/Telegram/SourceFiles/boxes/transfer_gift_box.cpp b/Telegram/SourceFiles/boxes/transfer_gift_box.cpp index befba0e52..2334dc7f7 100644 --- a/Telegram/SourceFiles/boxes/transfer_gift_box.cpp +++ b/Telegram/SourceFiles/boxes/transfer_gift_box.cpp @@ -9,7 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "api/api_credits.h" +#include "api/api_cloud_password.h" #include "base/unixtime.h" +#include "boxes/passcode_box.h" #include "data/data_star_gift.h" #include "data/data_user.h" #include "boxes/filters/edit_filter_chats_list.h" // CreatePe...tionSubtitle. @@ -22,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/boxes/confirm_box.h" #include "ui/layers/generic_box.h" #include "ui/text/text_utilities.h" +#include "ui/basic_click_handlers.h" #include "ui/empty_userpic.h" #include "ui/painter.h" #include "ui/vertical_list.h" @@ -42,13 +45,15 @@ public: Controller( not_null window, std::shared_ptr gift, + Data::SavedStarGiftId savedId, Fn)> choose); + void initExport(not_null box); + void noSearchSubmit(); private: void prepareViewHook() override; - void setupExportOption(); bool overrideKeyboardNavigation( int direction, @@ -59,34 +64,115 @@ private: not_null user) override; void rowClicked(not_null row) override; + const not_null _window; const std::shared_ptr _gift; + const Data::SavedStarGiftId _savedId; const Fn)> _choose; ExportOption _exportOption; }; +void ExportOnBlockchain( + not_null window, + not_null parent, + Data::SavedStarGiftId giftId, + Fn waitFinished) { + struct State { + bool loading = false; + rpl::lifetime lifetime; + }; + const auto state = std::make_shared(); + const auto session = &window->session(); + const auto show = window->uiShow(); + session->api().cloudPassword().reload(); + session->api().request( + MTPpayments_GetStarGiftWithdrawalUrl( + Api::InputSavedStarGiftId(giftId), + MTP_inputCheckPasswordEmpty()) + ).fail([=](const MTP::Error &error) { + auto box = PrePasswordErrorBox( + error.type(), + session, + TextWithEntities{ + tr::lng_gift_transfer_password_about(tr::now), + }); + if (box) { + waitFinished(); + show->show(std::move(box)); + return; + } + state->lifetime = session->api().cloudPassword().state( + ) | rpl::take( + 1 + ) | rpl::start_with_next([=](const Core::CloudPasswordState &pass) { + waitFinished(); + + auto fields = PasscodeBox::CloudFields::From(pass); + fields.customTitle = tr::lng_gift_transfer_password_title(); + fields.customDescription + = tr::lng_gift_transfer_password_description(tr::now); + fields.customSubmitButton = tr::lng_passcode_submit(); + fields.customCheckCallback = crl::guard(parent, [=]( + const Core::CloudPasswordResult &result, + QPointer box) { + const auto done = [=](const QString &result) { + }; + using ExportUrl = MTPpayments_StarGiftWithdrawalUrl; + session->api().request( + MTPpayments_GetStarGiftWithdrawalUrl( + Api::InputSavedStarGiftId(giftId), + result.result) + ).done([=](const ExportUrl &result) { + UrlClickHandler::Open(qs(result.data().vurl())); + if (box) { + box->closeBox(); + } + }).fail([=](const MTP::Error &error) { + const auto message = error.type(); + if (box && !box->handleCustomCheckError(message)) { + show->showToast(message); + } + }).send(); + }); + show->show(Box(session, fields)); + }); + }).send(); +} + [[nodiscard]] ExportOption MakeExportOption( not_null window, + not_null parent, + Data::SavedStarGiftId giftId, TimeId when) { + struct State { + bool exporting = false; + }; + const auto state = std::make_shared(); const auto activate = [=] { const auto now = base::unixtime::now(); const auto left = (when > now) ? (when - now) : 0; const auto hours = left ? std::max((left + 1800) / 3600, 1) : 0; + if (!hours) { + if (state->exporting) { + return; + } + state->exporting = true; + ExportOnBlockchain(window, parent, giftId, [=] { + state->exporting = false; + }); + return; + } window->show(Ui::MakeInformBox({ - .text = (!hours - ? tr::lng_gift_transfer_unlocks_update_about() - : tr::lng_gift_transfer_unlocks_about( - lt_when, - ((hours >= 24) - ? tr::lng_gift_transfer_unlocks_when_days( - lt_count, - rpl::single((hours / 24) * 1.)) - : tr::lng_gift_transfer_unlocks_when_hours( - lt_count, - rpl::single(hours * 1.))))), - .title = (!hours - ? tr::lng_gift_transfer_unlocks_update_title() - : tr::lng_gift_transfer_unlocks_title()), + .text = tr::lng_gift_transfer_unlocks_about( + lt_when, + ((hours >= 24) + ? tr::lng_gift_transfer_unlocks_when_days( + lt_count, + rpl::single((hours / 24) * 1.)) + : tr::lng_gift_transfer_unlocks_when_hours( + lt_count, + rpl::single(hours * 1.)))), + .title = tr::lng_gift_transfer_unlocks_title(), })); }; @@ -131,10 +217,14 @@ private: const style::PeerListItem &computeSt( const style::PeerListItem &st) const override { - return _available ? st::recentPeersSpecialName : st; + _st = st; + _st.namePosition.setY( + st::recentPeersSpecialName.namePosition.y()); + return _available ? _st : st; } private: + mutable style::PeerListItem _st; bool _available = false; }; @@ -229,18 +319,26 @@ private: Controller::Controller( not_null window, std::shared_ptr gift, + Data::SavedStarGiftId savedId, Fn)> choose) : ContactsBoxController(&window->session()) +, _window(window) , _gift(std::move(gift)) +, _savedId(savedId) , _choose(std::move(choose)) { - if (const auto when = _gift->exportAt) { - _exportOption = MakeExportOption(window, when); - } - if (_exportOption.content) { + if (_gift->exportAt) { setStyleOverrides(&st::peerListSmallSkips); } } +void Controller::initExport(not_null box) { + if (const auto when = _gift->exportAt) { + _exportOption = MakeExportOption(_window, box, _savedId, when); + delegate()->peerListSetAboveWidget(std::move(_exportOption.content)); + delegate()->peerListRefreshRows(); + } +} + void Controller::noSearchSubmit() { if (const auto onstack = _exportOption.activate) { onstack(); @@ -259,11 +357,6 @@ void Controller::prepareViewHook() { delegate()->peerListSetTitle(tr::lng_gift_transfer_title( lt_name, rpl::single(UniqueGiftName(*_gift)))); - setupExportOption(); -} - -void Controller::setupExportOption() { - delegate()->peerListSetAboveWidget(std::move(_exportOption.content)); } std::unique_ptr Controller::createRow( @@ -400,11 +493,14 @@ void ShowTransferGiftBox( auto controller = std::make_unique( window, gift, + savedId, [=](not_null peer) { ShowTransferToBox(window, peer, gift, savedId); }); const auto controllerRaw = controller.get(); auto initBox = [=](not_null box) { + controllerRaw->initExport(box); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); box->noSearchSubmits() | rpl::start_with_next([=] {