From 1aefada45d0db1bef0776f31a7036600e4b77f8f Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 26 Mar 2021 17:05:31 +0400 Subject: Improve checkout main page design. --- .../Resources/icons/payments/payment_address.png | Bin 0 -> 802 bytes .../icons/payments/payment_address@2x.png | Bin 0 -> 1638 bytes .../icons/payments/payment_address@3x.png | Bin 0 -> 2402 bytes Telegram/Resources/icons/payments/payment_card.png | Bin 0 -> 357 bytes .../Resources/icons/payments/payment_card@2x.png | Bin 0 -> 560 bytes .../Resources/icons/payments/payment_card@3x.png | Bin 0 -> 985 bytes .../Resources/icons/payments/payment_email.png | Bin 0 -> 949 bytes .../Resources/icons/payments/payment_email@2x.png | Bin 0 -> 1936 bytes .../Resources/icons/payments/payment_email@3x.png | Bin 0 -> 2860 bytes Telegram/Resources/icons/payments/payment_name.png | Bin 0 -> 473 bytes .../Resources/icons/payments/payment_name@2x.png | Bin 0 -> 884 bytes .../Resources/icons/payments/payment_name@3x.png | Bin 0 -> 1411 bytes .../Resources/icons/payments/payment_phone.png | Bin 0 -> 711 bytes .../Resources/icons/payments/payment_phone@2x.png | Bin 0 -> 1327 bytes .../Resources/icons/payments/payment_phone@3x.png | Bin 0 -> 2090 bytes .../Resources/icons/payments/payment_shipping.png | Bin 0 -> 526 bytes .../icons/payments/payment_shipping@2x.png | Bin 0 -> 1022 bytes .../icons/payments/payment_shipping@3x.png | Bin 0 -> 1469 bytes Telegram/Resources/langs/lang.strings | 27 +- .../payments/payments_checkout_process.cpp | 15 +- Telegram/SourceFiles/payments/payments_form.cpp | 120 +++++++- Telegram/SourceFiles/payments/payments_form.h | 31 ++- Telegram/SourceFiles/payments/ui/payments.style | 46 ++- .../payments/ui/payments_form_summary.cpp | 307 ++++++++++++++------- .../payments/ui/payments_form_summary.h | 11 +- .../SourceFiles/payments/ui/payments_panel.cpp | 23 +- Telegram/SourceFiles/payments/ui/payments_panel.h | 3 + .../SourceFiles/payments/ui/payments_panel_data.h | 9 + 28 files changed, 450 insertions(+), 142 deletions(-) create mode 100644 Telegram/Resources/icons/payments/payment_address.png create mode 100644 Telegram/Resources/icons/payments/payment_address@2x.png create mode 100644 Telegram/Resources/icons/payments/payment_address@3x.png create mode 100644 Telegram/Resources/icons/payments/payment_card.png create mode 100644 Telegram/Resources/icons/payments/payment_card@2x.png create mode 100644 Telegram/Resources/icons/payments/payment_card@3x.png create mode 100644 Telegram/Resources/icons/payments/payment_email.png create mode 100644 Telegram/Resources/icons/payments/payment_email@2x.png create mode 100644 Telegram/Resources/icons/payments/payment_email@3x.png create mode 100644 Telegram/Resources/icons/payments/payment_name.png create mode 100644 Telegram/Resources/icons/payments/payment_name@2x.png create mode 100644 Telegram/Resources/icons/payments/payment_name@3x.png create mode 100644 Telegram/Resources/icons/payments/payment_phone.png create mode 100644 Telegram/Resources/icons/payments/payment_phone@2x.png create mode 100644 Telegram/Resources/icons/payments/payment_phone@3x.png create mode 100644 Telegram/Resources/icons/payments/payment_shipping.png create mode 100644 Telegram/Resources/icons/payments/payment_shipping@2x.png create mode 100644 Telegram/Resources/icons/payments/payment_shipping@3x.png diff --git a/Telegram/Resources/icons/payments/payment_address.png b/Telegram/Resources/icons/payments/payment_address.png new file mode 100644 index 0000000000..dcc9b2f812 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_address.png differ diff --git a/Telegram/Resources/icons/payments/payment_address@2x.png b/Telegram/Resources/icons/payments/payment_address@2x.png new file mode 100644 index 0000000000..5b4cea3d39 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_address@2x.png differ diff --git a/Telegram/Resources/icons/payments/payment_address@3x.png b/Telegram/Resources/icons/payments/payment_address@3x.png new file mode 100644 index 0000000000..f155680197 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_address@3x.png differ diff --git a/Telegram/Resources/icons/payments/payment_card.png b/Telegram/Resources/icons/payments/payment_card.png new file mode 100644 index 0000000000..6e80687c6c Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_card.png differ diff --git a/Telegram/Resources/icons/payments/payment_card@2x.png b/Telegram/Resources/icons/payments/payment_card@2x.png new file mode 100644 index 0000000000..1fd98a77c0 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_card@2x.png differ diff --git a/Telegram/Resources/icons/payments/payment_card@3x.png b/Telegram/Resources/icons/payments/payment_card@3x.png new file mode 100644 index 0000000000..ae50fe67da Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_card@3x.png differ diff --git a/Telegram/Resources/icons/payments/payment_email.png b/Telegram/Resources/icons/payments/payment_email.png new file mode 100644 index 0000000000..41154157ba Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_email.png differ diff --git a/Telegram/Resources/icons/payments/payment_email@2x.png b/Telegram/Resources/icons/payments/payment_email@2x.png new file mode 100644 index 0000000000..d6b6ab1505 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_email@2x.png differ diff --git a/Telegram/Resources/icons/payments/payment_email@3x.png b/Telegram/Resources/icons/payments/payment_email@3x.png new file mode 100644 index 0000000000..5f113ae768 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_email@3x.png differ diff --git a/Telegram/Resources/icons/payments/payment_name.png b/Telegram/Resources/icons/payments/payment_name.png new file mode 100644 index 0000000000..fdeb96631e Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_name.png differ diff --git a/Telegram/Resources/icons/payments/payment_name@2x.png b/Telegram/Resources/icons/payments/payment_name@2x.png new file mode 100644 index 0000000000..2bcacb3746 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_name@2x.png differ diff --git a/Telegram/Resources/icons/payments/payment_name@3x.png b/Telegram/Resources/icons/payments/payment_name@3x.png new file mode 100644 index 0000000000..23d2ee2435 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_name@3x.png differ diff --git a/Telegram/Resources/icons/payments/payment_phone.png b/Telegram/Resources/icons/payments/payment_phone.png new file mode 100644 index 0000000000..87b185d8c6 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_phone.png differ diff --git a/Telegram/Resources/icons/payments/payment_phone@2x.png b/Telegram/Resources/icons/payments/payment_phone@2x.png new file mode 100644 index 0000000000..d047fb5b4b Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_phone@2x.png differ diff --git a/Telegram/Resources/icons/payments/payment_phone@3x.png b/Telegram/Resources/icons/payments/payment_phone@3x.png new file mode 100644 index 0000000000..ff201d3e80 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_phone@3x.png differ diff --git a/Telegram/Resources/icons/payments/payment_shipping.png b/Telegram/Resources/icons/payments/payment_shipping.png new file mode 100644 index 0000000000..aa618f5fb6 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_shipping.png differ diff --git a/Telegram/Resources/icons/payments/payment_shipping@2x.png b/Telegram/Resources/icons/payments/payment_shipping@2x.png new file mode 100644 index 0000000000..3315d9a9cc Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_shipping@2x.png differ diff --git a/Telegram/Resources/icons/payments/payment_shipping@3x.png b/Telegram/Resources/icons/payments/payment_shipping@3x.png new file mode 100644 index 0000000000..8b08421d09 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_shipping@3x.png differ diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 11bb8041cb..6be8d6bd0b 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1865,24 +1865,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_payments_pay_amount" = "Pay {amount}"; "lng_payments_payment_method" = "Payment Method"; "lng_payments_new_card" = "New Card..."; -"lng_payments_payment_method_ph" = "Enter your card details"; -"lng_payments_shipping_address" = "Shipping Information"; -"lng_payments_shipping_address_ph" = "Enter your shipping information"; +"lng_payments_shipping_address" = "Shipping Address"; +"lng_payments_receiver_information" = "Receiver"; +"lng_payments_address_street1" = "Address 1"; +"lng_payments_address_street2" = "Address 2"; +"lng_payments_address_city" = "City"; +"lng_payments_address_state" = "State"; +"lng_payments_address_country" = "Country"; +"lng_payments_address_postcode" = "Postcode"; + "lng_payments_shipping_method" = "Shipping Method"; -"lng_payments_shipping_method_ph" = "Choose your shipping method"; "lng_payments_info_name" = "Name"; -"lng_payments_info_name_ph" = "Enter your name"; "lng_payments_info_email" = "Email"; -"lng_payments_info_email_ph" = "Enter your email"; "lng_payments_info_phone" = "Phone"; -"lng_payments_info_phone_ph" = "Enter your phone number"; "lng_payments_shipping_address_title" = "Shipping Address"; "lng_payments_save_shipping_about" = "You can save your shipping information for future use."; -"lng_payments_payment_card" = "Payment Card"; -"lng_payments_cardholder_title" = "Cardholder"; -"lng_payments_cardholder_about" = "Cardholder Name"; -"lng_payments_billing_address" = "Billing Address"; -"lng_payments_zip_code" = "Zip Code"; +"lng_payments_card_title" = "New Card"; +"lng_payments_card_number" = "Card Number"; +"lng_payments_card_holder" = "Cardholder name"; +"lng_payments_billing_address" = "Billing Information"; +"lng_payments_billing_country" = "Country"; +"lng_payments_billing_zip_code" = "Zip Code"; "lng_payments_save_payment_about" = "You can save your payment information for future use."; "lng_payments_save_information" = "Save Information"; diff --git a/Telegram/SourceFiles/payments/payments_checkout_process.cpp b/Telegram/SourceFiles/payments/payments_checkout_process.cpp index d6ae27bb5a..5b7287066f 100644 --- a/Telegram/SourceFiles/payments/payments_checkout_process.cpp +++ b/Telegram/SourceFiles/payments/payments_checkout_process.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/storage_domain.h" #include "history/history_item.h" #include "history/history.h" +#include "data/data_user.h" // UserData::isBot. #include "core/local_url_handlers.h" // TryConvertUrlToLocal. #include "core/file_utilities.h" // File::OpenUrl. #include "apiwrap.h" @@ -105,6 +106,8 @@ void CheckoutProcess::handleFormUpdate(const FormUpdate &update) { if (!_initialSilentValidation) { showForm(); } + }, [&](const ThumbnailUpdated &data) { + _panel->updateFormThumbnail(data.thumbnail); }, [&](const ValidateFinished &) { if (_initialSilentValidation) { _initialSilentValidation = false; @@ -114,16 +117,16 @@ void CheckoutProcess::handleFormUpdate(const FormUpdate &update) { _submitState = SubmitState::Validated; panelSubmit(); } - }, [&](const PaymentMethodUpdate&) { + }, [&](const PaymentMethodUpdate &) { showForm(); - }, [&](const VerificationNeeded &info) { - if (!_panel->showWebview(info.url, false)) { - File::OpenUrl(info.url); + }, [&](const VerificationNeeded &data) { + if (!_panel->showWebview(data.url, false)) { + File::OpenUrl(data.url); panelCloseSure(); } - }, [&](const PaymentFinished &result) { + }, [&](const PaymentFinished &data) { const auto weak = base::make_weak(this); - _session->api().applyUpdates(result.updates); + _session->api().applyUpdates(data.updates); if (weak) { panelCloseSure(); } diff --git a/Telegram/SourceFiles/payments/payments_form.cpp b/Telegram/SourceFiles/payments/payments_form.cpp index b6ad2c95e6..6114bcb651 100644 --- a/Telegram/SourceFiles/payments/payments_form.cpp +++ b/Telegram/SourceFiles/payments/payments_form.cpp @@ -9,10 +9,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "data/data_session.h" -#include "apiwrap.h" +#include "data/data_media_types.h" +#include "data/data_user.h" +#include "data/data_photo.h" +#include "data/data_photo_media.h" +#include "data/data_file_origin.h" +#include "history/history_item.h" #include "stripe/stripe_api_client.h" #include "stripe/stripe_error.h" #include "stripe/stripe_token.h" +#include "ui/image/image.h" +#include "apiwrap.h" +#include "styles/style_payments.h" // paymentsThumbnailSize. #include #include @@ -82,15 +90,110 @@ namespace { Form::Form(not_null session, FullMsgId itemId) : _session(session) , _api(&_session->mtp()) -, _msgId(itemId.msg) { +, _msgId(itemId) { + fillInvoiceFromMessage(); requestForm(); } Form::~Form() = default; +void Form::fillInvoiceFromMessage() { + if (const auto item = _session->data().message(_msgId)) { + if (const auto media = item->media()) { + if (const auto invoice = media->invoice()) { + _invoice.cover = Ui::Cover{ + .title = invoice->title, + .description = invoice->description, + }; + if (const auto photo = invoice->photo) { + loadThumbnail(photo); + } + } + } + } +} + +void Form::loadThumbnail(not_null photo) { + Expects(!_thumbnailLoadProcess); + + auto view = photo->createMediaView(); + if (auto good = prepareGoodThumbnail(view); !good.isNull()) { + _invoice.cover.thumbnail = std::move(good); + return; + } + _thumbnailLoadProcess = std::make_unique(); + if (auto blurred = prepareBlurredThumbnail(view); !blurred.isNull()) { + _invoice.cover.thumbnail = std::move(blurred); + _thumbnailLoadProcess->blurredSet = true; + } else { + _invoice.cover.thumbnail = prepareEmptyThumbnail(); + } + _thumbnailLoadProcess->view = std::move(view); + photo->load(Data::PhotoSize::Thumbnail, _msgId); + _session->downloaderTaskFinished( + ) | rpl::start_with_next([=] { + const auto &view = _thumbnailLoadProcess->view; + if (auto good = prepareGoodThumbnail(view); !good.isNull()) { + _invoice.cover.thumbnail = std::move(good); + _thumbnailLoadProcess = nullptr; + } else if (_thumbnailLoadProcess->blurredSet) { + return; + } else if (auto blurred = prepareBlurredThumbnail(view) + ; !blurred.isNull()) { + _invoice.cover.thumbnail = std::move(blurred); + _thumbnailLoadProcess->blurredSet = true; + } else { + return; + } + _updates.fire(ThumbnailUpdated{ _invoice.cover.thumbnail }); + }, _thumbnailLoadProcess->lifetime); +} + +QImage Form::prepareGoodThumbnail( + const std::shared_ptr &view) const { + using Size = Data::PhotoSize; + if (const auto large = view->image(Size::Large)) { + return prepareThumbnail(large); + } else if (const auto thumbnail = view->image(Size::Thumbnail)) { + return prepareThumbnail(thumbnail); + } + return QImage(); +} + +QImage Form::prepareBlurredThumbnail( + const std::shared_ptr &view) const { + if (const auto small = view->image(Data::PhotoSize::Small)) { + return prepareThumbnail(small, true); + } else if (const auto blurred = view->thumbnailInline()) { + return prepareThumbnail(blurred, true); + } + return QImage(); +} + +QImage Form::prepareThumbnail( + not_null image, + bool blurred) const { + auto result = image->original().scaled( + st::paymentsThumbnailSize * cIntRetinaFactor(), + Qt::KeepAspectRatio, + Qt::SmoothTransformation); + Images::prepareRound(result, ImageRoundRadius::Large); + result.setDevicePixelRatio(cRetinaFactor()); + return result; +} + +QImage Form::prepareEmptyThumbnail() const { + auto result = QImage( + st::paymentsThumbnailSize * cIntRetinaFactor(), + QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(cRetinaFactor()); + result.fill(Qt::transparent); + return result; +} + void Form::requestForm() { _api.request(MTPpayments_GetPaymentForm( - MTP_int(_msgId) + MTP_int(_msgId.msg) )).done([=](const MTPpayments_PaymentForm &result) { result.match([&](const auto &data) { processForm(data); @@ -123,6 +226,8 @@ void Form::processForm(const MTPDpayments_paymentForm &data) { void Form::processInvoice(const MTPDinvoice &data) { _invoice = Ui::Invoice{ + .cover = std::move(_invoice.cover), + .prices = ParsePrices(data.vprices()), .currency = qs(data.vcurrency()), @@ -154,6 +259,11 @@ void Form::processDetails(const MTPDpayments_paymentForm &data) { .canSaveCredentials = data.is_can_save_credentials(), .passwordMissing = data.is_password_missing(), }; + if (_details.botId) { + if (const auto bot = _session->data().userLoaded(_details.botId)) { + _invoice.cover.seller = bot->name; + } + } } void Form::processSavedInformation(const MTPDpaymentRequestedInfo &data) { @@ -240,7 +350,7 @@ void Form::submit() { | (_shippingOptions.selectedId.isEmpty() ? Flag(0) : Flag::f_shipping_option_id)), - MTP_int(_msgId), + MTP_int(_msgId.msg), MTP_string(_requestedInformationId), MTP_string(_shippingOptions.selectedId), MTP_inputPaymentCredentials( @@ -267,7 +377,7 @@ void Form::validateInformation(const Ui::RequestedInformation &information) { _validatedInformation = information; _validateRequestId = _api.request(MTPpayments_ValidateRequestedInfo( MTP_flags(0), // #TODO payments save information - MTP_int(_msgId), + MTP_int(_msgId.msg), Serialize(information) )).done([=](const MTPpayments_ValidatedRequestedInfo &result) { _validateRequestId = 0; diff --git a/Telegram/SourceFiles/payments/payments_form.h b/Telegram/SourceFiles/payments/payments_form.h index 7fbbe6c2a3..803dd4b3fa 100644 --- a/Telegram/SourceFiles/payments/payments_form.h +++ b/Telegram/SourceFiles/payments/payments_form.h @@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/weak_ptr.h" #include "mtproto/sender.h" +class Image; + namespace Stripe { class APIClient; } // namespace Stripe @@ -19,6 +21,10 @@ namespace Main { class Session; } // namespace Main +namespace Data { +class PhotoMedia; +} // namespace Data + namespace Payments { struct FormDetails { @@ -38,6 +44,12 @@ struct FormDetails { } }; +struct ThumbnailLoadProcess { + std::shared_ptr view; + bool blurredSet = false; + rpl::lifetime lifetime; +}; + struct SavedCredentials { QString id; QString title; @@ -88,6 +100,9 @@ struct PaymentMethod { }; struct FormReady {}; +struct ThumbnailUpdated { + QImage thumbnail; +}; struct ValidateFinished {}; struct PaymentMethodUpdate {}; struct VerificationNeeded { @@ -109,6 +124,7 @@ struct Error { struct FormUpdate : std::variant< FormReady, + ThumbnailUpdated, ValidateFinished, PaymentMethodUpdate, VerificationNeeded, @@ -149,6 +165,18 @@ public: void submit(); private: + void fillInvoiceFromMessage(); + + void loadThumbnail(not_null photo); + [[nodiscard]] QImage prepareGoodThumbnail( + const std::shared_ptr &view) const; + [[nodiscard]] QImage prepareBlurredThumbnail( + const std::shared_ptr &view) const; + [[nodiscard]] QImage prepareThumbnail( + not_null image, + bool blurred = false) const; + [[nodiscard]] QImage prepareEmptyThumbnail() const; + void requestForm(); void processForm(const MTPDpayments_paymentForm &data); void processInvoice(const MTPDinvoice &data); @@ -167,9 +195,10 @@ private: const not_null _session; MTP::Sender _api; - MsgId _msgId = 0; + FullMsgId _msgId; Ui::Invoice _invoice; + std::unique_ptr _thumbnailLoadProcess; FormDetails _details; Ui::RequestedInformation _savedInformation; PaymentMethod _paymentMethod; diff --git a/Telegram/SourceFiles/payments/ui/payments.style b/Telegram/SourceFiles/payments/ui/payments.style index d331d30336..811f70160b 100644 --- a/Telegram/SourceFiles/payments/ui/payments.style +++ b/Telegram/SourceFiles/payments/ui/payments.style @@ -7,12 +7,52 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ using "ui/basic.style"; -using "passport/passport.style"; +using "info/info.style"; -paymentsFormPricePadding: margins(22px, 7px, 22px, 6px); -paymentsPanelSubmit: RoundButton(passportPasswordSubmit) { +paymentsPanelSubmit: RoundButton(defaultActiveButton) { width: 0px; height: 49px; padding: margins(0px, -3px, 0px, 0px); textTop: 16px; } + +paymentsCoverPadding: margins(26px, 0px, 26px, 13px); +paymentsDescription: FlatLabel(defaultFlatLabel) { + minWidth: 160px; + textFg: windowFg; +} +paymentsTitle: FlatLabel(paymentsDescription) { + style: semiboldTextStyle; +} +paymentsSeller: FlatLabel(paymentsDescription) { + textFg: windowSubTextFg; +} +paymentsPriceLabel: paymentsDescription; +paymentsPriceAmount: defaultFlatLabel; +paymentsFullPriceLabel: paymentsTitle; +paymentsFullPriceAmount: FlatLabel(defaultFlatLabel) { + style: semiboldTextStyle; +} + +paymentsTitleTop: 0px; +paymentsDescriptionTop: 3px; +paymentsSellerTop: 4px; + +paymentsThumbnailSize: size(80px, 80px); +paymentsThumbnailSkip: 18px; + +paymentsPricesTopSkip: 12px; +paymentsPricesBottomSkip: 13px; +paymentsPricePadding: margins(28px, 6px, 28px, 5px); + +paymentsSectionsTopSkip: 11px; +paymentsSectionButton: SettingsButton(infoProfileButton) { + padding: margins(68px, 11px, 14px, 9px); +} + +paymentsIconPaymentMethod: icon {{ "payments/payment_card", menuIconFg }}; +paymentsIconShippingAddress: icon {{ "payments/payment_address", menuIconFg }}; +paymentsIconName: icon {{ "payments/payment_name", menuIconFg }}; +paymentsIconEmail: icon {{ "payments/payment_email", menuIconFg }}; +paymentsIconPhone: icon {{ "payments/payment_phone", menuIconFg }}; +paymentsIconShippingMethod: icon {{ "payments/payment_shipping", menuIconFg }}; diff --git a/Telegram/SourceFiles/payments/ui/payments_form_summary.cpp b/Telegram/SourceFiles/payments/ui/payments_form_summary.cpp index dfcee629e4..0f63805bee 100644 --- a/Telegram/SourceFiles/payments/ui/payments_form_summary.cpp +++ b/Telegram/SourceFiles/payments/ui/payments_form_summary.cpp @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "payments/ui/payments_form_summary.h" #include "payments/ui/payments_panel_delegate.h" -#include "passport/ui/passport_form_row.h" +#include "settings/settings_common.h" #include "ui/widgets/scroll_area.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" @@ -22,7 +22,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Payments::Ui { using namespace ::Ui; -using namespace Passport::Ui; class PanelDelegate; @@ -45,16 +44,24 @@ FormSummary::FormSummary( this, tr::lng_payments_pay_amount( lt_amount, - rpl::single(computeTotalAmount())), + rpl::single(formatAmount(computeTotalAmount()))), st::paymentsPanelSubmit) { setupControls(); } -QString FormSummary::computeAmount(int64 amount) const { - return FillAmountAndCurrency(amount, _invoice.currency); +void FormSummary::updateThumbnail(const QImage &thumbnail) { + _invoice.cover.thumbnail = thumbnail; + _thumbnails.fire_copy(thumbnail); } -QString FormSummary::computeTotalAmount() const { +QString FormSummary::formatAmount(int64 amount) const { + const auto base = FillAmountAndCurrency( + std::abs(amount), + _invoice.currency); + return (amount > 0) ? base : (QString::fromUtf8("\xe2\x88\x92") + base); +} + +int64 FormSummary::computeTotalAmount() const { const auto total = ranges::accumulate( _invoice.prices, int64(0), @@ -71,7 +78,7 @@ QString FormSummary::computeTotalAmount() const { std::plus<>(), &LabeledPrice::price) : int64(0); - return computeAmount(total + shipping); + return total + shipping; } void FormSummary::setupControls() { @@ -92,22 +99,125 @@ void FormSummary::setupControls() { _1 + _2 < _3)); } -not_null FormSummary::setupContent() { - const auto inner = _scroll->setOwnedWidget( - object_ptr(this)); +void FormSummary::setupCover(not_null layout) { + struct State { + QImage thumbnail; + FlatLabel *title = nullptr; + FlatLabel *description = nullptr; + FlatLabel *seller = nullptr; + }; - _scroll->widthValue( - ) | rpl::start_with_next([=](int width) { - inner->resizeToWidth(width); - }, inner->lifetime()); + const auto cover = layout->add(object_ptr(layout)); + const auto state = cover->lifetime().make_state(); + state->title = CreateChild( + cover, + _invoice.cover.title, + st::paymentsTitle); + state->description = CreateChild( + cover, + _invoice.cover.description, + st::paymentsDescription); + state->seller = CreateChild( + cover, + _invoice.cover.seller, + st::paymentsSeller); + cover->paintRequest( + ) | rpl::start_with_next([=](QRect clip) { + if (state->thumbnail.isNull()) { + return; + } + const auto &padding = st::paymentsCoverPadding; + const auto thumbnailSkip = st::paymentsThumbnailSize.width() + + st::paymentsThumbnailSkip; + const auto left = padding.left(); + const auto top = padding.top(); + const auto rect = QRect( + QPoint(left, top), + state->thumbnail.size() / state->thumbnail.devicePixelRatio()); + if (rect.intersects(clip)) { + QPainter(cover).drawImage(rect, state->thumbnail); + } + }, cover->lifetime()); + rpl::combine( + cover->widthValue(), + _thumbnails.events_starting_with_copy(_invoice.cover.thumbnail) + ) | rpl::start_with_next([=](int width, QImage &&thumbnail) { + const auto &padding = st::paymentsCoverPadding; + const auto thumbnailSkip = st::paymentsThumbnailSize.width() + + st::paymentsThumbnailSkip; + const auto left = padding.left() + + (thumbnail.isNull() ? 0 : thumbnailSkip); + const auto available = width + - padding.left() + - padding.right() + - (thumbnail.isNull() ? 0 : thumbnailSkip); + state->title->resizeToNaturalWidth(available); + state->title->moveToLeft( + left, + padding.top() + st::paymentsTitleTop); + state->description->resizeToNaturalWidth(available); + state->description->moveToLeft( + left, + (state->title->y() + + state->title->height() + + st::paymentsDescriptionTop)); + state->seller->resizeToNaturalWidth(available); + state->seller->moveToLeft( + left, + (state->description->y() + + state->description->height() + + st::paymentsSellerTop)); + const auto thumbnailHeight = padding.top() + + (thumbnail.isNull() + ? 0 + : int(thumbnail.height() / thumbnail.devicePixelRatio())) + + padding.bottom(); + const auto height = state->seller->y() + + state->seller->height() + + padding.bottom(); + cover->resize(width, std::max(thumbnailHeight, height)); + state->thumbnail = std::move(thumbnail); + cover->update(); + }, cover->lifetime()); +} +void FormSummary::setupPrices(not_null layout) { + Settings::AddSkip(layout, st::paymentsPricesTopSkip); + const auto add = [&]( + const QString &label, + int64 amount, + bool full = false) { + const auto &st = full + ? st::paymentsFullPriceAmount + : st::paymentsPriceAmount; + const auto right = CreateChild( + layout.get(), + formatAmount(amount), + st); + const auto &padding = st::paymentsPricePadding; + const auto left = layout->add( + object_ptr( + layout, + label, + (full + ? st::paymentsFullPriceLabel + : st::paymentsPriceLabel)), + style::margins( + padding.left(), + padding.top(), + (padding.right() + + right->naturalWidth() + + 2 * st.style.font->spacew), + padding.bottom())); + rpl::combine( + left->topValue(), + layout->widthValue() + ) | rpl::start_with_next([=](int top, int width) { + right->moveToRight(st::paymentsPricePadding.right(), top, width); + }, right->lifetime()); + }; for (const auto &price : _invoice.prices) { - inner->add( - object_ptr( - inner, - price.label + ": " + computeAmount(price.price), - st::passportFormPolicy), - st::paymentsFormPricePadding); + add(price.label, price.price); } const auto selected = ranges::find( _options.list, @@ -115,44 +225,35 @@ not_null FormSummary::setupContent() { &ShippingOption::id); if (selected != end(_options.list)) { for (const auto &price : selected->prices) { - inner->add( - object_ptr( - inner, - price.label + ": " + computeAmount(price.price), - st::passportFormPolicy), - st::paymentsFormPricePadding); + add(price.label, price.price); } } - inner->add( - object_ptr( - inner, - "Total: " + computeTotalAmount(), - st::passportFormHeader), - st::passportFormHeaderPadding); - - inner->add( - object_ptr( - inner, - st::passportFormDividerHeight), - { 0, 0, 0, st::passportFormHeaderPadding.top() }); - - const auto method = inner->add(object_ptr(inner)); - method->addClickHandler([=] { - _delegate->panelEditPaymentMethod(); - }); - method->updateContent( - tr::lng_payments_payment_method(tr::now), - (_method.ready - ? _method.title - : tr::lng_payments_payment_method_ph(tr::now)), - _method.ready, - false, - anim::type::instant); + add(tr::lng_payments_total_label(tr::now), computeTotalAmount(), true); + Settings::AddSkip(layout, st::paymentsPricesBottomSkip); +} + +void FormSummary::setupSections(not_null layout) { + Settings::AddSkip(layout, st::paymentsSectionsTopSkip); + + const auto add = [&]( + rpl::producer title, + const QString &label, + const style::icon *icon, + Fn handler) { + Settings::AddButtonWithLabel( + layout, + std::move(title), + rpl::single(label), + st::paymentsSectionButton, + icon + )->addClickHandler(std::move(handler)); + }; + add( + tr::lng_payments_payment_method(), + _method.title, + &st::paymentsIconPaymentMethod, + [=] { _delegate->panelEditPaymentMethod(); }); if (_invoice.isShippingAddressRequested) { - const auto info = inner->add(object_ptr(inner)); - info->addClickHandler([=] { - _delegate->panelEditShippingInformation(); - }); auto list = QStringList(); const auto push = [&](const QString &value) { if (!value.isEmpty()) { @@ -165,65 +266,61 @@ not_null FormSummary::setupContent() { push(_information.shippingAddress.state); push(_information.shippingAddress.countryIso2); push(_information.shippingAddress.postcode); - info->updateContent( - tr::lng_payments_shipping_address(tr::now), - (list.isEmpty() - ? tr::lng_payments_shipping_address_ph(tr::now) - : list.join(", ")), - !list.isEmpty(), - false, - anim::type::instant); + add( + tr::lng_payments_shipping_address(), + list.join(", "), + &st::paymentsIconShippingAddress, + [=] { _delegate->panelEditShippingInformation(); }); } if (!_options.list.empty()) { - const auto options = inner->add(object_ptr(inner)); - options->addClickHandler([=] { - _delegate->panelChooseShippingOption(); - }); - options->updateContent( - tr::lng_payments_shipping_method(tr::now), - (selected != end(_options.list) - ? selected->title - : tr::lng_payments_shipping_method_ph(tr::now)), - (selected != end(_options.list)), - false, - anim::type::instant); + const auto selected = ranges::find( + _options.list, + _options.selectedId, + &ShippingOption::id); + add( + tr::lng_payments_shipping_method(), + (selected != end(_options.list)) ? selected->title : QString(), + &st::paymentsIconShippingMethod, + [=] { _delegate->panelChooseShippingOption(); }); } if (_invoice.isNameRequested) { - const auto name = inner->add(object_ptr(inner)); - name->addClickHandler([=] { _delegate->panelEditName(); }); - name->updateContent( - tr::lng_payments_info_name(tr::now), - (_information.name.isEmpty() - ? tr::lng_payments_info_name_ph(tr::now) - : _information.name), - !_information.name.isEmpty(), - false, - anim::type::instant); + add( + tr::lng_payments_info_name(), + _information.name, + &st::paymentsIconName, + [=] { _delegate->panelEditName(); }); } if (_invoice.isEmailRequested) { - const auto email = inner->add(object_ptr(inner)); - email->addClickHandler([=] { _delegate->panelEditEmail(); }); - email->updateContent( - tr::lng_payments_info_email(tr::now), - (_information.email.isEmpty() - ? tr::lng_payments_info_email_ph(tr::now) - : _information.email), - !_information.email.isEmpty(), - false, - anim::type::instant); + add( + tr::lng_payments_info_email(), + _information.email, + &st::paymentsIconEmail, + [=] { _delegate->panelEditEmail(); }); } if (_invoice.isPhoneRequested) { - const auto phone = inner->add(object_ptr(inner)); - phone->addClickHandler([=] { _delegate->panelEditPhone(); }); - phone->updateContent( - tr::lng_payments_info_phone(tr::now), - (_information.phone.isEmpty() - ? tr::lng_payments_info_phone_ph(tr::now) - : _information.phone), - !_information.phone.isEmpty(), - false, - anim::type::instant); + add( + tr::lng_payments_info_phone(), + _information.phone, + &st::paymentsIconPhone, + [=] { _delegate->panelEditPhone(); }); } + Settings::AddSkip(layout, st::paymentsSectionsTopSkip); +} + +not_null FormSummary::setupContent() { + const auto inner = _scroll->setOwnedWidget( + object_ptr(this)); + + _scroll->widthValue( + ) | rpl::start_with_next([=](int width) { + inner->resizeToWidth(width); + }, inner->lifetime()); + + setupCover(inner); + Settings::AddDivider(inner); + setupPrices(inner); + Settings::AddDivider(inner); + setupSections(inner); return inner; } diff --git a/Telegram/SourceFiles/payments/ui/payments_form_summary.h b/Telegram/SourceFiles/payments/ui/payments_form_summary.h index 38f30ff0fd..8ce6bcd4c9 100644 --- a/Telegram/SourceFiles/payments/ui/payments_form_summary.h +++ b/Telegram/SourceFiles/payments/ui/payments_form_summary.h @@ -15,6 +15,7 @@ namespace Ui { class ScrollArea; class FadeShadow; class RoundButton; +class VerticalLayout; } // namespace Ui namespace Payments::Ui { @@ -33,15 +34,20 @@ public: const ShippingOptions &options, not_null delegate); + void updateThumbnail(const QImage &thumbnail); + private: void resizeEvent(QResizeEvent *e) override; void setupControls(); [[nodiscard]] not_null setupContent(); + void setupCover(not_null layout); + void setupPrices(not_null layout); + void setupSections(not_null layout); void updateControlsGeometry(); - [[nodiscard]] QString computeAmount(int64 amount) const; - [[nodiscard]] QString computeTotalAmount() const; + [[nodiscard]] QString formatAmount(int64 amount) const; + [[nodiscard]] int64 computeTotalAmount() const; const not_null _delegate; Invoice _invoice; @@ -52,6 +58,7 @@ private: object_ptr _topShadow; object_ptr _bottomShadow; object_ptr _submit; + rpl::event_stream _thumbnails; }; diff --git a/Telegram/SourceFiles/payments/ui/payments_panel.cpp b/Telegram/SourceFiles/payments/ui/payments_panel.cpp index 97c652e12a..1ae7054ec8 100644 --- a/Telegram/SourceFiles/payments/ui/payments_panel.cpp +++ b/Telegram/SourceFiles/payments/ui/payments_panel.cpp @@ -52,17 +52,24 @@ void Panel::showForm( const RequestedInformation ¤t, const PaymentMethodDetails &method, const ShippingOptions &options) { - _widget->showInner( - base::make_unique_q( - _widget.get(), - invoice, - current, - method, - options, - _delegate)); + auto form = base::make_unique_q( + _widget.get(), + invoice, + current, + method, + options, + _delegate); + _weakFormSummary = form.get(); + _widget->showInner(std::move(form)); _widget->setBackAllowed(false); } +void Panel::updateFormThumbnail(const QImage &thumbnail) { + if (_weakFormSummary) { + _weakFormSummary->updateThumbnail(thumbnail); + } +} + void Panel::showEditInformation( const Invoice &invoice, const RequestedInformation ¤t, diff --git a/Telegram/SourceFiles/payments/ui/payments_panel.h b/Telegram/SourceFiles/payments/ui/payments_panel.h index a8a694a895..e07703ba9c 100644 --- a/Telegram/SourceFiles/payments/ui/payments_panel.h +++ b/Telegram/SourceFiles/payments/ui/payments_panel.h @@ -28,6 +28,7 @@ struct RequestedInformation; struct ShippingOptions; enum class InformationField; enum class CardField; +class FormSummary; class EditInformation; class EditCard; struct PaymentMethodDetails; @@ -45,6 +46,7 @@ public: const RequestedInformation ¤t, const PaymentMethodDetails &method, const ShippingOptions &options); + void updateFormThumbnail(const QImage &thumbnail); void showEditInformation( const Invoice &invoice, const RequestedInformation ¤t, @@ -78,6 +80,7 @@ private: const not_null _delegate; std::unique_ptr _widget; std::unique_ptr _webview; + QPointer _weakFormSummary; QPointer _weakEditInformation; QPointer _weakEditCard; diff --git a/Telegram/SourceFiles/payments/ui/payments_panel_data.h b/Telegram/SourceFiles/payments/ui/payments_panel_data.h index 7c3964c298..03f37aa4e6 100644 --- a/Telegram/SourceFiles/payments/ui/payments_panel_data.h +++ b/Telegram/SourceFiles/payments/ui/payments_panel_data.h @@ -14,7 +14,16 @@ struct LabeledPrice { int64 price = 0; }; +struct Cover { + QString title; + QString description; + QString seller; + QImage thumbnail; +}; + struct Invoice { + Cover cover; + std::vector prices; QString currency; -- cgit v1.2.3