From 47fdef1e38478a5cb203e19655d0d2a501842ea8 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 26 Mar 2021 19:23:12 +0400 Subject: [PATCH] Improve checkout information / card page design. --- Telegram/Resources/langs/lang.strings | 3 +- .../payments/payments_checkout_process.cpp | 26 +- .../payments/payments_checkout_process.h | 2 + .../SourceFiles/payments/ui/payments.style | 11 + .../payments/ui/payments_edit_card.cpp | 199 ++++++-------- .../payments/ui/payments_edit_card.h | 24 +- .../payments/ui/payments_edit_information.cpp | 251 +++++++----------- .../payments/ui/payments_edit_information.h | 33 ++- .../payments/ui/payments_field.cpp | 140 ++++++++++ .../SourceFiles/payments/ui/payments_field.h | 62 +++++ .../payments/ui/payments_form_summary.cpp | 15 +- .../payments/ui/payments_panel.cpp | 19 +- Telegram/cmake/td_ui.cmake | 2 + 13 files changed, 468 insertions(+), 319 deletions(-) create mode 100644 Telegram/SourceFiles/payments/ui/payments_field.cpp create mode 100644 Telegram/SourceFiles/payments/ui/payments_field.h diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 6be8d6bd0b..3f49c3076d 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1866,7 +1866,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_payments_payment_method" = "Payment Method"; "lng_payments_new_card" = "New Card..."; "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"; @@ -1878,7 +1877,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_payments_info_name" = "Name"; "lng_payments_info_email" = "Email"; "lng_payments_info_phone" = "Phone"; -"lng_payments_shipping_address_title" = "Shipping Address"; +"lng_payments_shipping_address_title" = "Shipping Information"; "lng_payments_save_shipping_about" = "You can save your shipping information for future use."; "lng_payments_card_title" = "New Card"; "lng_payments_card_number" = "Card Number"; diff --git a/Telegram/SourceFiles/payments/payments_checkout_process.cpp b/Telegram/SourceFiles/payments/payments_checkout_process.cpp index 5b7287066f..aba6d5e5a2 100644 --- a/Telegram/SourceFiles/payments/payments_checkout_process.cpp +++ b/Telegram/SourceFiles/payments/payments_checkout_process.cpp @@ -87,6 +87,7 @@ CheckoutProcess::CheckoutProcess( ) | rpl::start_with_next([=] { showForm(); }, _panel->lifetime()); + showForm(); } CheckoutProcess::~CheckoutProcess() { @@ -156,7 +157,7 @@ void CheckoutProcess::handleError(const Error &error) { showToast({ "Error: " + id }); } break; - case Error::Type::Validate: + case Error::Type::Validate: { if (_submitState == SubmitState::Validation) { _submitState = SubmitState::None; } @@ -189,7 +190,21 @@ void CheckoutProcess::handleError(const Error &error) { } else { showToast({ "Error: " + id }); } - break; + } break; + case Error::Type::Stripe: { + using Field = Ui::CardField; + if (id == u"InvalidNumber"_q || id == u"IncorrectNumber"_q) { + showCardError(Field::Number); + } else if (id == u"InvalidCVC"_q || id == u"IncorrectCVC"_q) { + showCardError(Field::CVC); + } else if (id == u"InvalidExpiryMonth"_q + || id == u"InvalidExpiryYear"_q + || id == u"ExpiredCard"_q) { + showCardError(Field::ExpireDate); + } else { + showToast({ "Error: " + id }); + } + } break; case Error::Type::Send: if (_submitState == SubmitState::Finishing) { _submitState = SubmitState::None; @@ -375,6 +390,13 @@ void CheckoutProcess::showInformationError(Ui::InformationField field) { field); } +void CheckoutProcess::showCardError(Ui::CardField field) { + if (_submitState != SubmitState::None) { + return; + } + _panel->showCardError(_form->paymentMethod().ui.native, field); +} + void CheckoutProcess::chooseShippingOption() { _panel->chooseShippingOption(_form->shippingOptions()); } diff --git a/Telegram/SourceFiles/payments/payments_checkout_process.h b/Telegram/SourceFiles/payments/payments_checkout_process.h index 636915071e..de9d37724b 100644 --- a/Telegram/SourceFiles/payments/payments_checkout_process.h +++ b/Telegram/SourceFiles/payments/payments_checkout_process.h @@ -19,6 +19,7 @@ class Session; namespace Payments::Ui { class Panel; enum class InformationField; +enum class CardField; } // namespace Payments::Ui namespace Payments { @@ -58,6 +59,7 @@ private: void showForm(); void showEditInformation(Ui::InformationField field); void showInformationError(Ui::InformationField field); + void showCardError(Ui::CardField field); void chooseShippingOption(); void editPaymentMethod(); diff --git a/Telegram/SourceFiles/payments/ui/payments.style b/Telegram/SourceFiles/payments/ui/payments.style index 811f70160b..20e44354a4 100644 --- a/Telegram/SourceFiles/payments/ui/payments.style +++ b/Telegram/SourceFiles/payments/ui/payments.style @@ -56,3 +56,14 @@ paymentsIconName: icon {{ "payments/payment_name", menuIconFg }}; paymentsIconEmail: icon {{ "payments/payment_email", menuIconFg }}; paymentsIconPhone: icon {{ "payments/payment_phone", menuIconFg }}; paymentsIconShippingMethod: icon {{ "payments/payment_shipping", menuIconFg }}; + +paymentsField: defaultInputField; +paymentsFieldPadding: margins(28px, 0px, 28px, 2px); +paymentsExpireCvcSkip: 34px; + +paymentsBillingInformationTitle: FlatLabel(defaultFlatLabel) { + style: semiboldTextStyle; + textFg: windowActiveTextFg; + minWidth: 240px; +} +paymentsBillingInformationTitlePadding: margins(28px, 26px, 28px, 1px); diff --git a/Telegram/SourceFiles/payments/ui/payments_edit_card.cpp b/Telegram/SourceFiles/payments/ui/payments_edit_card.cpp index 7d5918e9f1..8d647a174f 100644 --- a/Telegram/SourceFiles/payments/ui/payments_edit_card.cpp +++ b/Telegram/SourceFiles/payments/ui/payments_edit_card.cpp @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "payments/ui/payments_edit_card.h" #include "payments/ui/payments_panel_delegate.h" -#include "passport/ui/passport_details_row.h" +#include "payments/ui/payments_field.h" #include "ui/widgets/scroll_area.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" @@ -52,16 +52,24 @@ EditCard::EditCard( void EditCard::setFocus(CardField field) { _focusField = field; - if (const auto control = controlForField(field)) { - _scroll->ensureWidgetVisible(control); + if (const auto control = lookupField(field)) { + _scroll->ensureWidgetVisible(control->widget()); + control->setFocus(); + } +} + +void EditCard::setFocusFast(CardField field) { + _focusField = field; + if (const auto control = lookupField(field)) { + _scroll->ensureWidgetVisible(control->widget()); control->setFocusFast(); } } void EditCard::showError(CardField field) { - if (const auto control = controlForField(field)) { - _scroll->ensureWidgetVisible(control); - control->showError(QString()); + if (const auto control = lookupField(field)) { + _scroll->ensureWidgetVisible(control->widget()); + control->showError(); } } @@ -95,102 +103,69 @@ not_null EditCard::setupContent() { const auto showBox = [=](object_ptr box) { _delegate->panelShowBox(std::move(box)); }; - using Type = Passport::Ui::PanelDetailsType; - auto maxLabelWidth = 0; - accumulate_max( - maxLabelWidth, - Row::LabelWidth("Card Number")); - accumulate_max( - maxLabelWidth, - Row::LabelWidth("CVC")); - accumulate_max( - maxLabelWidth, - Row::LabelWidth("MM/YY")); + const auto add = [&](FieldConfig &&config) { + auto result = std::make_unique(inner, std::move(config)); + inner->add(result->ownedWidget(), st::paymentsFieldPadding); + return result; + }; + _number = add({ + .type = FieldType::CardNumber, + .placeholder = tr::lng_payments_card_number(), + .required = true, + }); if (_native.needCardholderName) { - accumulate_max( - maxLabelWidth, - Row::LabelWidth("Cardholder Name")); + _name = add({ + .type = FieldType::CardNumber, + .placeholder = tr::lng_payments_card_holder(), + .required = true, + }); + } + auto container = inner->add( + object_ptr( + inner, + _number->widget()->height()), + st::paymentsFieldPadding); + _expire = std::make_unique(container, FieldConfig{ + .type = FieldType::CardExpireDate, + .placeholder = rpl::single(u"MM / YY"_q), + .required = true, + }); + _cvc = std::make_unique(container, FieldConfig{ + .type = FieldType::CardCVC, + .placeholder = rpl::single(u"CVC"_q), + .required = true, + }); + container->widthValue( + ) | rpl::start_with_next([=](int width) { + const auto left = (width - st::paymentsExpireCvcSkip) / 2; + const auto right = width - st::paymentsExpireCvcSkip - left; + _expire->widget()->resizeToWidth(left); + _cvc->widget()->resizeToWidth(right); + _expire->widget()->moveToLeft(0, 0, width); + _cvc->widget()->moveToRight(0, 0, width); + }, container->lifetime()); + if (_native.needCountry || _native.needZip) { + inner->add( + object_ptr( + inner, + tr::lng_payments_billing_address(), + st::paymentsBillingInformationTitle), + st::paymentsBillingInformationTitlePadding); } if (_native.needCountry) { - accumulate_max( - maxLabelWidth, - Row::LabelWidth("Billing Country")); + _country = add({ + .type = FieldType::Country, + .placeholder = tr::lng_payments_billing_country(), + .required = true, + }); } if (_native.needZip) { - accumulate_max( - maxLabelWidth, - Row::LabelWidth("Billing Zip")); - } - _number = inner->add( - Row::Create( - inner, - showBox, - QString(), - Type::Text, - "Card Number", - maxLabelWidth, - QString(), - QString(), - 1024)); - _cvc = inner->add( - Row::Create( - inner, - showBox, - QString(), - Type::Text, - "CVC", - maxLabelWidth, - QString(), - QString(), - 1024)); - _expire = inner->add( - Row::Create( - inner, - showBox, - QString(), - Type::Text, - "MM/YY", - maxLabelWidth, - QString(), - QString(), - 1024)); - if (_native.needCardholderName) { - _name = inner->add( - Row::Create( - inner, - showBox, - QString(), - Type::Text, - "Cardholder Name", - maxLabelWidth, - QString(), - QString(), - 1024)); - } - if (_native.needCountry) { - _country = inner->add( - Row::Create( - inner, - showBox, - QString(), - Type::Country, - "Billing Country", - maxLabelWidth, - QString(), - QString())); - } - if (_native.needZip) { - _zip = inner->add( - Row::Create( - inner, - showBox, - QString(), - Type::Postcode, - "Billing Zip Code", - maxLabelWidth, - QString(), - QString(), - kMaxPostcodeSize)); + _zip = add({ + .type = FieldType::Text, + .placeholder = tr::lng_payments_billing_zip_code(), + .maxLength = kMaxPostcodeSize, + .required = true, + }); } return inner; } @@ -200,7 +175,7 @@ void EditCard::resizeEvent(QResizeEvent *e) { } void EditCard::focusInEvent(QFocusEvent *e) { - if (const auto control = controlForField(_focusField)) { + if (const auto control = lookupField(_focusField)) { control->setFocusFast(); } } @@ -218,27 +193,27 @@ void EditCard::updateControlsGeometry() { _scroll->updateBars(); } -auto EditCard::controlForField(CardField field) const -> Row* { +auto EditCard::lookupField(CardField field) const -> Field* { switch (field) { - case CardField::Number: return _number; - case CardField::CVC: return _cvc; - case CardField::ExpireDate: return _expire; - case CardField::Name: return _name; - case CardField::AddressCountry: return _country; - case CardField::AddressZip: return _zip; + case CardField::Number: return _number.get(); + case CardField::CVC: return _cvc.get(); + case CardField::ExpireDate: return _expire.get(); + case CardField::Name: return _name.get(); + case CardField::AddressCountry: return _country.get(); + case CardField::AddressZip: return _zip.get(); } Unexpected("Unknown field in EditCard::controlForField."); } UncheckedCardDetails EditCard::collect() const { return { - .number = _number ? _number->valueCurrent() : QString(), - .cvc = _cvc ? _cvc->valueCurrent() : QString(), - .expireYear = _expire ? ExtractYear(_expire->valueCurrent()) : 0, - .expireMonth = _expire ? ExtractMonth(_expire->valueCurrent()) : 0, - .cardholderName = _name ? _name->valueCurrent() : QString(), - .addressCountry = _country ? _country->valueCurrent() : QString(), - .addressZip = _zip ? _zip->valueCurrent() : QString(), + .number = _number ? _number->value() : QString(), + .cvc = _cvc ? _cvc->value() : QString(), + .expireYear = _expire ? ExtractYear(_expire->value()) : 0, + .expireMonth = _expire ? ExtractMonth(_expire->value()) : 0, + .cardholderName = _name ? _name->value() : QString(), + .addressCountry = _country ? _country->value() : QString(), + .addressZip = _zip ? _zip->value() : QString(), }; } diff --git a/Telegram/SourceFiles/payments/ui/payments_edit_card.h b/Telegram/SourceFiles/payments/ui/payments_edit_card.h index 157e13edc9..742bdbf341 100644 --- a/Telegram/SourceFiles/payments/ui/payments_edit_card.h +++ b/Telegram/SourceFiles/payments/ui/payments_edit_card.h @@ -17,15 +17,12 @@ class FadeShadow; class RoundButton; } // namespace Ui -namespace Passport::Ui { -class PanelDetailsRow; -} // namespace Passport::Ui - namespace Payments::Ui { using namespace ::Ui; class PanelDelegate; +class Field; class EditCard final : public RpWidget { public: @@ -35,19 +32,18 @@ public: CardField field, not_null delegate); - void showError(CardField field); void setFocus(CardField field); + void setFocusFast(CardField field); + void showError(CardField field); private: - using Row = Passport::Ui::PanelDetailsRow; - void resizeEvent(QResizeEvent *e) override; void focusInEvent(QFocusEvent *e) override; void setupControls(); [[nodiscard]] not_null setupContent(); void updateControlsGeometry(); - [[nodiscard]] Row *controlForField(CardField field) const; + [[nodiscard]] Field *lookupField(CardField field) const; [[nodiscard]] UncheckedCardDetails collect() const; @@ -59,12 +55,12 @@ private: object_ptr _bottomShadow; object_ptr _done; - Row *_number = nullptr; - Row *_cvc = nullptr; - Row *_expire = nullptr; - Row *_name = nullptr; - Row *_country = nullptr; - Row *_zip = nullptr; + std::unique_ptr _number; + std::unique_ptr _cvc; + std::unique_ptr _expire; + std::unique_ptr _name; + std::unique_ptr _country; + std::unique_ptr _zip; CardField _focusField = CardField::Number; diff --git a/Telegram/SourceFiles/payments/ui/payments_edit_information.cpp b/Telegram/SourceFiles/payments/ui/payments_edit_information.cpp index eff78b29ba..8445c38156 100644 --- a/Telegram/SourceFiles/payments/ui/payments_edit_information.cpp +++ b/Telegram/SourceFiles/payments/ui/payments_edit_information.cpp @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "payments/ui/payments_edit_information.h" #include "payments/ui/payments_panel_delegate.h" -#include "passport/ui/passport_details_row.h" +#include "payments/ui/payments_field.h" #include "ui/widgets/scroll_area.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" @@ -48,18 +48,28 @@ EditInformation::EditInformation( setupControls(); } +EditInformation::~EditInformation() = default; + void EditInformation::setFocus(InformationField field) { _focusField = field; - if (const auto control = controlForField(field)) { - _scroll->ensureWidgetVisible(control); + if (const auto control = lookupField(field)) { + _scroll->ensureWidgetVisible(control->widget()); + control->setFocus(); + } +} + +void EditInformation::setFocusFast(InformationField field) { + _focusField = field; + if (const auto control = lookupField(field)) { + _scroll->ensureWidgetVisible(control->widget()); control->setFocusFast(); } } void EditInformation::showError(InformationField field) { - if (const auto control = controlForField(field)) { - _scroll->ensureWidgetVisible(control); - control->showError(QString()); + if (const auto control = lookupField(field)) { + _scroll->ensureWidgetVisible(control->widget()); + control->showError(); } } @@ -93,106 +103,44 @@ not_null EditInformation::setupContent() { const auto showBox = [=](object_ptr box) { _delegate->panelShowBox(std::move(box)); }; - using Type = Passport::Ui::PanelDetailsType; - auto maxLabelWidth = 0; + const auto add = [&](FieldConfig &&config) { + auto result = std::make_unique(inner, std::move(config)); + inner->add(result->ownedWidget(), st::paymentsFieldPadding); + return result; + }; if (_invoice.isShippingAddressRequested) { - accumulate_max( - maxLabelWidth, - Row::LabelWidth(tr::lng_passport_street(tr::now))); - accumulate_max( - maxLabelWidth, - Row::LabelWidth(tr::lng_passport_city(tr::now))); - accumulate_max( - maxLabelWidth, - Row::LabelWidth(tr::lng_passport_state(tr::now))); - accumulate_max( - maxLabelWidth, - Row::LabelWidth(tr::lng_passport_country(tr::now))); - accumulate_max( - maxLabelWidth, - Row::LabelWidth(tr::lng_passport_postcode(tr::now))); - } - if (_invoice.isNameRequested) { - accumulate_max( - maxLabelWidth, - Row::LabelWidth(tr::lng_payments_info_name(tr::now))); - } - if (_invoice.isEmailRequested) { - accumulate_max( - maxLabelWidth, - Row::LabelWidth(tr::lng_payments_info_email(tr::now))); - } - if (_invoice.isPhoneRequested) { - accumulate_max( - maxLabelWidth, - Row::LabelWidth(tr::lng_payments_info_phone(tr::now))); - } - if (_invoice.isShippingAddressRequested) { - _street1 = inner->add( - Row::Create( - inner, - showBox, - QString(), - Type::Text, - tr::lng_passport_street(tr::now), - maxLabelWidth, - _information.shippingAddress.address1, - QString(), - kMaxStreetSize)); - _street2 = inner->add( - Row::Create( - inner, - showBox, - QString(), - Type::Text, - tr::lng_passport_street(tr::now), - maxLabelWidth, - _information.shippingAddress.address2, - QString(), - kMaxStreetSize)); - _city = inner->add( - Row::Create( - inner, - showBox, - QString(), - Type::Text, - tr::lng_passport_city(tr::now), - maxLabelWidth, - _information.shippingAddress.city, - QString(), - kMaxStreetSize)); - _state = inner->add( - Row::Create( - inner, - showBox, - QString(), - Type::Text, - tr::lng_passport_state(tr::now), - maxLabelWidth, - _information.shippingAddress.state, - QString(), - kMaxStreetSize)); - _country = inner->add( - Row::Create( - inner, - showBox, - QString(), - Type::Country, - tr::lng_passport_country(tr::now), - maxLabelWidth, - _information.shippingAddress.countryIso2, - QString())); - _postcode = inner->add( - Row::Create( - inner, - showBox, - QString(), - Type::Postcode, - tr::lng_passport_postcode(tr::now), - maxLabelWidth, - _information.shippingAddress.postcode, - QString(), - kMaxPostcodeSize)); + _street1 = add({ + .placeholder = tr::lng_payments_address_street1(), + .value = _information.shippingAddress.address1, + .maxLength = kMaxStreetSize, + .required = true, + }); + _street2 = add({ + .placeholder = tr::lng_payments_address_street2(), + .value = _information.shippingAddress.address2, + .maxLength = kMaxStreetSize, + }); + _city = add({ + .placeholder = tr::lng_payments_address_city(), + .value = _information.shippingAddress.city, + .required = true, + }); + _state = add({ + .placeholder = tr::lng_payments_address_state(), + .value = _information.shippingAddress.state, + }); + _country = add({ + .type = FieldType::Country, + .placeholder = tr::lng_payments_address_country(), + .value = _information.shippingAddress.countryIso2, + .required = true, + }); + _postcode = add({ + .placeholder = tr::lng_payments_address_postcode(), + .value = _information.shippingAddress.postcode, + .maxLength = kMaxPostcodeSize, + .required = true, + }); //StreetValidate, // #TODO payments //CityValidate, //CountryValidate, @@ -200,43 +148,28 @@ not_null EditInformation::setupContent() { //PostcodeValidate, } if (_invoice.isNameRequested) { - _name = inner->add( - Row::Create( - inner, - showBox, - QString(), - Type::Text, - tr::lng_payments_info_name(tr::now), - maxLabelWidth, - _information.name, - QString(), - kMaxNameSize)); + _name = add({ + .placeholder = tr::lng_payments_info_name(), + .value = _information.name, + .maxLength = kMaxNameSize, + .required = true, + }); } if (_invoice.isEmailRequested) { - _email = inner->add( - Row::Create( - inner, - showBox, - QString(), - Type::Text, - tr::lng_payments_info_email(tr::now), - maxLabelWidth, - _information.email, - QString(), - kMaxEmailSize)); + _email = add({ + .placeholder = tr::lng_payments_info_email(), + .value = _information.email, + .maxLength = kMaxEmailSize, + .required = true, + }); } if (_invoice.isPhoneRequested) { - _phone = inner->add( - Row::Create( - inner, - showBox, - QString(), - Type::Text, - tr::lng_payments_info_phone(tr::now), - maxLabelWidth, - _information.phone, - QString(), - kMaxPhoneSize)); + _phone = add({ + .placeholder = tr::lng_payments_info_phone(), + .value = _information.phone, + .maxLength = kMaxPhoneSize, + .required = true, + }); } return inner; } @@ -246,8 +179,8 @@ void EditInformation::resizeEvent(QResizeEvent *e) { } void EditInformation::focusInEvent(QFocusEvent *e) { - if (const auto control = controlForField(_focusField)) { - control->setFocusFast(); + if (const auto control = lookupField(_focusField)) { + control->setFocus(); } } @@ -264,32 +197,32 @@ void EditInformation::updateControlsGeometry() { _scroll->updateBars(); } -auto EditInformation::controlForField(InformationField field) const -> Row* { +auto EditInformation::lookupField(InformationField field) const -> Field* { switch (field) { - case InformationField::ShippingStreet: return _street1; - case InformationField::ShippingCity: return _city; - case InformationField::ShippingState: return _state; - case InformationField::ShippingCountry: return _country; - case InformationField::ShippingPostcode: return _postcode; - case InformationField::Name: return _name; - case InformationField::Email: return _email; - case InformationField::Phone: return _phone; + case InformationField::ShippingStreet: return _street1.get(); + case InformationField::ShippingCity: return _city.get(); + case InformationField::ShippingState: return _state.get(); + case InformationField::ShippingCountry: return _country.get(); + case InformationField::ShippingPostcode: return _postcode.get(); + case InformationField::Name: return _name.get(); + case InformationField::Email: return _email.get(); + case InformationField::Phone: return _phone.get(); } - Unexpected("Unknown field in EditInformation::controlForField."); + Unexpected("Unknown field in EditInformation::lookupField."); } RequestedInformation EditInformation::collect() const { return { - .name = _name ? _name->valueCurrent() : QString(), - .phone = _phone ? _phone->valueCurrent() : QString(), - .email = _email ? _email->valueCurrent() : QString(), + .name = _name ? _name->value() : QString(), + .phone = _phone ? _phone->value() : QString(), + .email = _email ? _email->value() : QString(), .shippingAddress = { - .address1 = _street1 ? _street1->valueCurrent() : QString(), - .address2 = _street2 ? _street2->valueCurrent() : QString(), - .city = _city ? _city->valueCurrent() : QString(), - .state = _state ? _state->valueCurrent() : QString(), - .countryIso2 = _country ? _country->valueCurrent() : QString(), - .postcode = _postcode ? _postcode->valueCurrent() : QString(), + .address1 = _street1 ? _street1->value() : QString(), + .address2 = _street2 ? _street2->value() : QString(), + .city = _city ? _city->value() : QString(), + .state = _state ? _state->value() : QString(), + .countryIso2 = _country ? _country->value() : QString(), + .postcode = _postcode ? _postcode->value() : QString(), }, }; } diff --git a/Telegram/SourceFiles/payments/ui/payments_edit_information.h b/Telegram/SourceFiles/payments/ui/payments_edit_information.h index d4f8ee78e8..978b72b433 100644 --- a/Telegram/SourceFiles/payments/ui/payments_edit_information.h +++ b/Telegram/SourceFiles/payments/ui/payments_edit_information.h @@ -15,17 +15,16 @@ namespace Ui { class ScrollArea; class FadeShadow; class RoundButton; +class InputField; +class MaskedInputField; } // namespace Ui -namespace Passport::Ui { -class PanelDetailsRow; -} // namespace Passport::Ui - namespace Payments::Ui { using namespace ::Ui; class PanelDelegate; +class Field; class EditInformation final : public RpWidget { public: @@ -35,20 +34,20 @@ public: const RequestedInformation ¤t, InformationField field, not_null delegate); + ~EditInformation(); - void showError(InformationField field); void setFocus(InformationField field); + void setFocusFast(InformationField field); + void showError(InformationField field); private: - using Row = Passport::Ui::PanelDetailsRow; - void resizeEvent(QResizeEvent *e) override; void focusInEvent(QFocusEvent *e) override; void setupControls(); [[nodiscard]] not_null setupContent(); void updateControlsGeometry(); - [[nodiscard]] Row *controlForField(InformationField field) const; + [[nodiscard]] Field *lookupField(InformationField field) const; [[nodiscard]] RequestedInformation collect() const; @@ -61,15 +60,15 @@ private: object_ptr _bottomShadow; object_ptr _done; - Row *_street1 = nullptr; - Row *_street2 = nullptr; - Row *_city = nullptr; - Row *_state = nullptr; - Row *_country = nullptr; - Row *_postcode = nullptr; - Row *_name = nullptr; - Row *_email = nullptr; - Row *_phone = nullptr; + std::unique_ptr _street1; + std::unique_ptr _street2; + std::unique_ptr _city; + std::unique_ptr _state; + std::unique_ptr _country; + std::unique_ptr _postcode; + std::unique_ptr _name; + std::unique_ptr _email; + std::unique_ptr _phone; InformationField _focusField = InformationField::ShippingStreet; diff --git a/Telegram/SourceFiles/payments/ui/payments_field.cpp b/Telegram/SourceFiles/payments/ui/payments_field.cpp new file mode 100644 index 0000000000..2161772f91 --- /dev/null +++ b/Telegram/SourceFiles/payments/ui/payments_field.cpp @@ -0,0 +1,140 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "payments/ui/payments_field.h" + +#include "ui/widgets/input_fields.h" +#include "styles/style_payments.h" + +namespace Payments::Ui { +namespace { + +[[nodiscard]] bool UseMaskedField(FieldType type) { + switch (type) { + case FieldType::Text: + case FieldType::Email: + return false; + case FieldType::CardNumber: + case FieldType::CardExpireDate: + case FieldType::CardCVC: + case FieldType::Country: + case FieldType::Phone: + return true; + } + Unexpected("FieldType in Payments::Ui::UseMaskedField."); +} + +[[nodiscard]] base::unique_qptr CreateWrap( + QWidget *parent, + FieldConfig &config) { + switch (config.type) { + case FieldType::Text: + case FieldType::Email: + return base::make_unique_q( + parent, + st::paymentsField, + std::move(config.placeholder), + config.value); + case FieldType::CardNumber: + case FieldType::CardExpireDate: + case FieldType::CardCVC: + case FieldType::Country: + case FieldType::Phone: + return base::make_unique_q(parent); + } + Unexpected("FieldType in Payments::Ui::CreateWrap."); +} + +[[nodiscard]] InputField *LookupInputField( + not_null wrap, + FieldConfig &config) { + return UseMaskedField(config.type) + ? nullptr + : static_cast(wrap.get()); +} + +[[nodiscard]] MaskedInputField *LookupMaskedField( + not_null wrap, + FieldConfig &config) { + if (!UseMaskedField(config.type)) { + return nullptr; + } + switch (config.type) { + case FieldType::Text: + case FieldType::Email: + return nullptr; + case FieldType::CardNumber: + case FieldType::CardExpireDate: + case FieldType::CardCVC: + case FieldType::Country: + case FieldType::Phone: + return CreateChild( + wrap.get(), + st::paymentsField, + std::move(config.placeholder), + config.value); + } + Unexpected("FieldType in Payments::Ui::LookupMaskedField."); +} + +} // namespace + +Field::Field(QWidget *parent, FieldConfig &&config) +: _type(config.type) +, _wrap(CreateWrap(parent, config)) +, _input(LookupInputField(_wrap.get(), config)) +, _masked(LookupMaskedField(_wrap.get(), config)) { + if (_masked) { + _wrap->resize(_masked->size()); + _wrap->widthValue( + ) | rpl::start_with_next([=](int width) { + _masked->resize(width, _masked->height()); + }, _masked->lifetime()); + _masked->heightValue( + ) | rpl::start_with_next([=](int height) { + _wrap->resize(_wrap->width(), height); + }, _masked->lifetime()); + } +} + +RpWidget *Field::widget() const { + return _wrap.get(); +} + +object_ptr Field::ownedWidget() const { + return object_ptr::fromRaw(_wrap.get()); +} + +[[nodiscard]] QString Field::value() const { + return _input ? _input->getLastText() : _masked->getLastText(); +} + +void Field::setFocus() { + if (_input) { + _input->setFocus(); + } else { + _masked->setFocus(); + } +} + +void Field::setFocusFast() { + if (_input) { + _input->setFocusFast(); + } else { + _masked->setFocusFast(); + } +} + +void Field::showError() { + if (_input) { + _input->showError(); + } else { + _masked->showError(); + } +} + +} // namespace Payments::Ui diff --git a/Telegram/SourceFiles/payments/ui/payments_field.h b/Telegram/SourceFiles/payments/ui/payments_field.h new file mode 100644 index 0000000000..92277ac360 --- /dev/null +++ b/Telegram/SourceFiles/payments/ui/payments_field.h @@ -0,0 +1,62 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "base/object_ptr.h" +#include "base/unique_qptr.h" + +namespace Ui { +class RpWidget; +class InputField; +class MaskedInputField; +} // namespace Ui + +namespace Payments::Ui { + +using namespace ::Ui; + +enum class FieldType { + Text, + CardNumber, + CardExpireDate, + CardCVC, + Country, + Phone, + Email, +}; + +struct FieldConfig { + FieldType type = FieldType::Text; + rpl::producer placeholder; + QString value; + int maxLength = 0; + bool required = false; +}; + +class Field final { +public: + Field(QWidget *parent, FieldConfig &&config); + + [[nodiscard]] RpWidget *widget() const; + [[nodiscard]] object_ptr ownedWidget() const; + + [[nodiscard]] QString value() const; + + void setFocus(); + void setFocusFast(); + void showError(); + +private: + const FieldType _type = FieldType::Text; + const base::unique_qptr _wrap; + InputField *_input = nullptr; + MaskedInputField *_masked = nullptr; + +}; + +} // namespace Payments::Ui diff --git a/Telegram/SourceFiles/payments/ui/payments_form_summary.cpp b/Telegram/SourceFiles/payments/ui/payments_form_summary.cpp index 0f63805bee..68c5965c62 100644 --- a/Telegram/SourceFiles/payments/ui/payments_form_summary.cpp +++ b/Telegram/SourceFiles/payments/ui/payments_form_summary.cpp @@ -58,7 +58,7 @@ 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); + return (amount < 0) ? (QString::fromUtf8("\xe2\x88\x92") + base) : base; } int64 FormSummary::computeTotalAmount() const { @@ -87,6 +87,9 @@ void FormSummary::setupControls() { _submit->addClickHandler([=] { _delegate->panelSubmit(); }); + if (!_invoice) { + _submit->hide(); + } using namespace rpl::mappers; @@ -317,10 +320,12 @@ not_null FormSummary::setupContent() { }, inner->lifetime()); setupCover(inner); - Settings::AddDivider(inner); - setupPrices(inner); - Settings::AddDivider(inner); - setupSections(inner); + if (_invoice) { + Settings::AddDivider(inner); + setupPrices(inner); + Settings::AddDivider(inner); + setupSections(inner); + } return inner; } diff --git a/Telegram/SourceFiles/payments/ui/payments_panel.cpp b/Telegram/SourceFiles/payments/ui/payments_panel.cpp index 1ae7054ec8..d39a7ab697 100644 --- a/Telegram/SourceFiles/payments/ui/payments_panel.cpp +++ b/Telegram/SourceFiles/payments/ui/payments_panel.cpp @@ -23,7 +23,6 @@ namespace Payments::Ui { Panel::Panel(not_null delegate) : _delegate(delegate) , _widget(std::make_unique()) { - _widget->setTitle(tr::lng_payments_checkout_title()); _widget->setInnerSize(st::passportPanelSize); _widget->setWindowFlag(Qt::WindowStaysOnTopHint, false); @@ -52,6 +51,7 @@ void Panel::showForm( const RequestedInformation ¤t, const PaymentMethodDetails &method, const ShippingOptions &options) { + _widget->setTitle(tr::lng_payments_checkout_title()); auto form = base::make_unique_q( _widget.get(), invoice, @@ -74,6 +74,7 @@ void Panel::showEditInformation( const Invoice &invoice, const RequestedInformation ¤t, InformationField field) { + _widget->setTitle(tr::lng_payments_shipping_address_title()); auto edit = base::make_unique_q( _widget.get(), invoice, @@ -83,7 +84,7 @@ void Panel::showEditInformation( _weakEditInformation = edit.get(); _widget->showInner(std::move(edit)); _widget->setBackAllowed(true); - _weakEditInformation->setFocus(field); + _weakEditInformation->setFocusFast(field); } void Panel::showInformationError( @@ -125,6 +126,7 @@ void Panel::chooseShippingOption(const ShippingOptions &options) { } void Panel::showEditPaymentMethod(const PaymentMethodDetails &method) { + _widget->setTitle(tr::lng_payments_card_title()); if (method.native.supported) { showEditCard(method.native, CardField::Number); } else if (!showWebview(method.url, true)) { @@ -221,7 +223,7 @@ void Panel::showEditCard( _weakEditCard = edit.get(); _widget->showInner(std::move(edit)); _widget->setBackAllowed(true); - _weakEditCard->setFocus(field); + _weakEditCard->setFocusFast(field); } void Panel::showCardError( @@ -230,11 +232,12 @@ void Panel::showCardError( if (_weakEditCard) { _weakEditCard->showError(field); } else { - showEditCard(native, field); - if (_weakEditCard - && field == CardField::AddressCountry) { - _weakEditCard->showError(field); - } + // We cancelled card edit already. + //showEditCard(native, field); + //if (_weakEditCard + // && field == CardField::AddressCountry) { + // _weakEditCard->showError(field); + //} } } diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index fd8ada4b0e..66b94f63fb 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -75,6 +75,8 @@ PRIVATE payments/ui/payments_edit_information.h payments/ui/payments_form_summary.cpp payments/ui/payments_form_summary.h + payments/ui/payments_field.cpp + payments/ui/payments_field.h payments/ui/payments_panel.cpp payments/ui/payments_panel.h payments/ui/payments_panel_data.h