Improve checkout information / card page design.

This commit is contained in:
John Preston 2021-03-26 19:23:12 +04:00
parent fafea73ea7
commit 47fdef1e38
13 changed files with 468 additions and 319 deletions

View File

@ -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";

View File

@ -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());
}

View File

@ -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();

View File

@ -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);

View File

@ -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<RpWidget*> EditCard::setupContent() {
const auto showBox = [=](object_ptr<BoxContent> 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<Field>(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<FixedHeightWidget>(
inner,
_number->widget()->height()),
st::paymentsFieldPadding);
_expire = std::make_unique<Field>(container, FieldConfig{
.type = FieldType::CardExpireDate,
.placeholder = rpl::single(u"MM / YY"_q),
.required = true,
});
_cvc = std::make_unique<Field>(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<Ui::FlatLabel>(
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(),
};
}

View File

@ -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<PanelDelegate*> 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<Ui::RpWidget*> 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<FadeShadow> _bottomShadow;
object_ptr<RoundButton> _done;
Row *_number = nullptr;
Row *_cvc = nullptr;
Row *_expire = nullptr;
Row *_name = nullptr;
Row *_country = nullptr;
Row *_zip = nullptr;
std::unique_ptr<Field> _number;
std::unique_ptr<Field> _cvc;
std::unique_ptr<Field> _expire;
std::unique_ptr<Field> _name;
std::unique_ptr<Field> _country;
std::unique_ptr<Field> _zip;
CardField _focusField = CardField::Number;

View File

@ -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<RpWidget*> EditInformation::setupContent() {
const auto showBox = [=](object_ptr<BoxContent> box) {
_delegate->panelShowBox(std::move(box));
};
using Type = Passport::Ui::PanelDetailsType;
auto maxLabelWidth = 0;
const auto add = [&](FieldConfig &&config) {
auto result = std::make_unique<Field>(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<RpWidget*> 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(),
},
};
}

View File

@ -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 &current,
InformationField field,
not_null<PanelDelegate*> 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<Ui::RpWidget*> 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<FadeShadow> _bottomShadow;
object_ptr<RoundButton> _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<Field> _street1;
std::unique_ptr<Field> _street2;
std::unique_ptr<Field> _city;
std::unique_ptr<Field> _state;
std::unique_ptr<Field> _country;
std::unique_ptr<Field> _postcode;
std::unique_ptr<Field> _name;
std::unique_ptr<Field> _email;
std::unique_ptr<Field> _phone;
InformationField _focusField = InformationField::ShippingStreet;

View File

@ -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<RpWidget> CreateWrap(
QWidget *parent,
FieldConfig &config) {
switch (config.type) {
case FieldType::Text:
case FieldType::Email:
return base::make_unique_q<InputField>(
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<RpWidget>(parent);
}
Unexpected("FieldType in Payments::Ui::CreateWrap.");
}
[[nodiscard]] InputField *LookupInputField(
not_null<RpWidget*> wrap,
FieldConfig &config) {
return UseMaskedField(config.type)
? nullptr
: static_cast<InputField*>(wrap.get());
}
[[nodiscard]] MaskedInputField *LookupMaskedField(
not_null<RpWidget*> 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<MaskedInputField>(
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<RpWidget> Field::ownedWidget() const {
return object_ptr<RpWidget>::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

View File

@ -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<QString> 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<RpWidget> ownedWidget() const;
[[nodiscard]] QString value() const;
void setFocus();
void setFocusFast();
void showError();
private:
const FieldType _type = FieldType::Text;
const base::unique_qptr<RpWidget> _wrap;
InputField *_input = nullptr;
MaskedInputField *_masked = nullptr;
};
} // namespace Payments::Ui

View File

@ -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<RpWidget*> 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;
}

View File

@ -23,7 +23,6 @@ namespace Payments::Ui {
Panel::Panel(not_null<PanelDelegate*> delegate)
: _delegate(delegate)
, _widget(std::make_unique<SeparatePanel>()) {
_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 &current,
const PaymentMethodDetails &method,
const ShippingOptions &options) {
_widget->setTitle(tr::lng_payments_checkout_title());
auto form = base::make_unique_q<FormSummary>(
_widget.get(),
invoice,
@ -74,6 +74,7 @@ void Panel::showEditInformation(
const Invoice &invoice,
const RequestedInformation &current,
InformationField field) {
_widget->setTitle(tr::lng_payments_shipping_address_title());
auto edit = base::make_unique_q<EditInformation>(
_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);
//}
}
}

View File

@ -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