Add nice country choosing in payments.
This commit is contained in:
parent
9a722ea8d4
commit
bef5320163
|
@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_photo.h"
|
||||
#include "data/data_photo_media.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_countries.h"
|
||||
#include "history/history_item.h"
|
||||
#include "stripe/stripe_api_client.h"
|
||||
#include "stripe/stripe_error.h"
|
||||
|
@ -269,6 +270,7 @@ void Form::processDetails(const MTPDpayments_paymentForm &data) {
|
|||
void Form::processSavedInformation(const MTPDpaymentRequestedInfo &data) {
|
||||
const auto address = data.vshipping_address();
|
||||
_savedInformation = Ui::RequestedInformation{
|
||||
.defaultCountry = defaultCountry(),
|
||||
.name = qs(data.vname().value_or_empty()),
|
||||
.phone = qs(data.vphone().value_or_empty()),
|
||||
.email = qs(data.vemail().value_or_empty()),
|
||||
|
@ -291,6 +293,11 @@ void Form::refreshPaymentMethodDetails() {
|
|||
const auto &entered = _paymentMethod.newCredentials;
|
||||
_paymentMethod.ui.title = entered ? entered.title : saved.title;
|
||||
_paymentMethod.ui.ready = entered || saved;
|
||||
_paymentMethod.ui.native.defaultCountry = defaultCountry();
|
||||
}
|
||||
|
||||
QString Form::defaultCountry() const {
|
||||
return Data::CountryISO2ByPhone(_session->user()->phone());
|
||||
}
|
||||
|
||||
void Form::fillPaymentMethodInformation() {
|
||||
|
@ -375,6 +382,10 @@ void Form::validateInformation(const Ui::RequestedInformation &information) {
|
|||
_api.request(base::take(_validateRequestId)).cancel();
|
||||
}
|
||||
_validatedInformation = information;
|
||||
if (const auto error = localInformationError(information)) {
|
||||
_updates.fire_copy(error);
|
||||
return;
|
||||
}
|
||||
_validateRequestId = _api.request(MTPpayments_ValidateRequestedInfo(
|
||||
MTP_flags(0), // #TODO payments save information
|
||||
MTP_int(_msgId.msg),
|
||||
|
@ -404,6 +415,30 @@ void Form::validateInformation(const Ui::RequestedInformation &information) {
|
|||
}).send();
|
||||
}
|
||||
|
||||
Error Form::localInformationError(
|
||||
const Ui::RequestedInformation &information) const {
|
||||
const auto error = [](const QString &id) {
|
||||
return Error{ Error::Type::Validate, id };
|
||||
};
|
||||
if (_invoice.isShippingAddressRequested
|
||||
&& !information.shippingAddress) {
|
||||
return information.shippingAddress.address1.isEmpty()
|
||||
? error(u"ADDRESS_STREET_LINE1_INVALID"_q)
|
||||
: information.shippingAddress.city.isEmpty()
|
||||
? error(u"ADDRESS_CITY_INVALID"_q)
|
||||
: information.shippingAddress.countryIso2.isEmpty()
|
||||
? error(u"ADDRESS_COUNTRY_INVALID"_q)
|
||||
: (Unexpected("Shipping Address error."), Error());
|
||||
} else if (_invoice.isNameRequested && information.name.isEmpty()) {
|
||||
return error(u"REQ_INFO_NAME_INVALID"_q);
|
||||
} else if (_invoice.isEmailRequested && information.email.isEmpty()) {
|
||||
return error(u"REQ_INFO_EMAIL_INVALID"_q);
|
||||
} else if (_invoice.isPhoneRequested && information.phone.isEmpty()) {
|
||||
return error(u"REQ_INFO_PHONE_INVALID"_q);
|
||||
}
|
||||
return Error();
|
||||
}
|
||||
|
||||
void Form::validateCard(const Ui::UncheckedCardDetails &details) {
|
||||
Expects(!v::is_null(_paymentMethod.native.data));
|
||||
|
||||
|
|
|
@ -113,13 +113,21 @@ struct PaymentFinished {
|
|||
};
|
||||
struct Error {
|
||||
enum class Type {
|
||||
None,
|
||||
Form,
|
||||
Validate,
|
||||
Stripe,
|
||||
Send,
|
||||
};
|
||||
Type type = Type::Form;
|
||||
Type type = Type::None;
|
||||
QString id;
|
||||
|
||||
[[nodiscard]] bool empty() const {
|
||||
return (type == Type::None);
|
||||
}
|
||||
[[nodiscard]] explicit operator bool() const {
|
||||
return !empty();
|
||||
}
|
||||
};
|
||||
|
||||
struct FormUpdate : std::variant<
|
||||
|
@ -188,11 +196,15 @@ private:
|
|||
void fillPaymentMethodInformation();
|
||||
void fillStripeNativeMethod();
|
||||
void refreshPaymentMethodDetails();
|
||||
[[nodiscard]] QString defaultCountry() const;
|
||||
|
||||
void validateCard(
|
||||
const StripePaymentMethod &method,
|
||||
const Ui::UncheckedCardDetails &details);
|
||||
|
||||
[[nodiscard]] Error localInformationError(
|
||||
const Ui::RequestedInformation &information) const;
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
MTP::Sender _api;
|
||||
FullMsgId _msgId;
|
||||
|
|
|
@ -156,6 +156,8 @@ not_null<RpWidget*> EditCard::setupContent() {
|
|||
_country = add({
|
||||
.type = FieldType::Country,
|
||||
.placeholder = tr::lng_payments_billing_country(),
|
||||
.showBox = showBox,
|
||||
.defaultCountry = _native.defaultCountry,
|
||||
.required = true,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -133,6 +133,8 @@ not_null<RpWidget*> EditInformation::setupContent() {
|
|||
.type = FieldType::Country,
|
||||
.placeholder = tr::lng_payments_address_country(),
|
||||
.value = _information.shippingAddress.countryIso2,
|
||||
.showBox = showBox,
|
||||
.defaultCountry = _information.defaultCountry,
|
||||
.required = true,
|
||||
});
|
||||
_postcode = add({
|
||||
|
|
|
@ -8,11 +8,31 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "payments/ui/payments_field.h"
|
||||
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/boxes/country_select_box.h"
|
||||
#include "data/data_countries.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "styles/style_payments.h"
|
||||
|
||||
namespace Payments::Ui {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] QString Parse(const FieldConfig &config) {
|
||||
if (config.type == FieldType::Country) {
|
||||
return Data::CountryNameByISO2(config.value);
|
||||
}
|
||||
return config.value;
|
||||
}
|
||||
|
||||
[[nodiscard]] QString Format(
|
||||
const FieldConfig &config,
|
||||
const QString &parsed,
|
||||
const QString &countryIso2) {
|
||||
if (config.type == FieldType::Country) {
|
||||
return countryIso2;
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool UseMaskedField(FieldType type) {
|
||||
switch (type) {
|
||||
case FieldType::Text:
|
||||
|
@ -38,7 +58,7 @@ namespace {
|
|||
parent,
|
||||
st::paymentsField,
|
||||
std::move(config.placeholder),
|
||||
config.value);
|
||||
Parse(config));
|
||||
case FieldType::CardNumber:
|
||||
case FieldType::CardExpireDate:
|
||||
case FieldType::CardCVC:
|
||||
|
@ -76,7 +96,7 @@ namespace {
|
|||
wrap.get(),
|
||||
st::paymentsField,
|
||||
std::move(config.placeholder),
|
||||
config.value);
|
||||
Parse(config));
|
||||
}
|
||||
Unexpected("FieldType in Payments::Ui::LookupMaskedField.");
|
||||
}
|
||||
|
@ -84,20 +104,16 @@ namespace {
|
|||
} // namespace
|
||||
|
||||
Field::Field(QWidget *parent, FieldConfig &&config)
|
||||
: _type(config.type)
|
||||
: _config(config)
|
||||
, _wrap(CreateWrap(parent, config))
|
||||
, _input(LookupInputField(_wrap.get(), config))
|
||||
, _masked(LookupMaskedField(_wrap.get(), config)) {
|
||||
, _masked(LookupMaskedField(_wrap.get(), config))
|
||||
, _countryIso2(config.value) {
|
||||
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());
|
||||
setupMaskedGeometry();
|
||||
}
|
||||
if (_config.type == FieldType::Country) {
|
||||
setupCountry();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,12 +125,60 @@ object_ptr<RpWidget> Field::ownedWidget() const {
|
|||
return object_ptr<RpWidget>::fromRaw(_wrap.get());
|
||||
}
|
||||
|
||||
[[nodiscard]] QString Field::value() const {
|
||||
return _input ? _input->getLastText() : _masked->getLastText();
|
||||
QString Field::value() const {
|
||||
return Format(
|
||||
_config,
|
||||
_input ? _input->getLastText() : _masked->getLastText(),
|
||||
_countryIso2);
|
||||
}
|
||||
|
||||
void Field::setupMaskedGeometry() {
|
||||
Expects(_masked != nullptr);
|
||||
|
||||
_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());
|
||||
}
|
||||
|
||||
void Field::setupCountry() {
|
||||
Expects(_config.type == FieldType::Country);
|
||||
Expects(_masked != nullptr);
|
||||
|
||||
QObject::connect(_masked, &MaskedInputField::focused, [=] {
|
||||
setFocus();
|
||||
|
||||
const auto name = Data::CountryNameByISO2(_countryIso2);
|
||||
const auto country = !name.isEmpty()
|
||||
? _countryIso2
|
||||
: !_config.defaultCountry.isEmpty()
|
||||
? _config.defaultCountry
|
||||
: Platform::SystemCountry();
|
||||
auto box = Box<CountrySelectBox>(
|
||||
country,
|
||||
CountrySelectBox::Type::Countries);
|
||||
const auto raw = box.data();
|
||||
raw->countryChosen(
|
||||
) | rpl::start_with_next([=](QString iso2) {
|
||||
_countryIso2 = iso2;
|
||||
_masked->setText(Data::CountryNameByISO2(iso2));
|
||||
_masked->hideError();
|
||||
setFocus();
|
||||
raw->closeBox();
|
||||
}, _masked->lifetime());
|
||||
_config.showBox(std::move(box));
|
||||
});
|
||||
}
|
||||
|
||||
void Field::setFocus() {
|
||||
if (_input) {
|
||||
if (_config.type == FieldType::Country) {
|
||||
_wrap->setFocus();
|
||||
} else if (_input) {
|
||||
_input->setFocus();
|
||||
} else {
|
||||
_masked->setFocus();
|
||||
|
@ -122,7 +186,9 @@ void Field::setFocus() {
|
|||
}
|
||||
|
||||
void Field::setFocusFast() {
|
||||
if (_input) {
|
||||
if (_config.type == FieldType::Country) {
|
||||
setFocus();
|
||||
} else if (_input) {
|
||||
_input->setFocusFast();
|
||||
} else {
|
||||
_masked->setFocusFast();
|
||||
|
@ -130,7 +196,10 @@ void Field::setFocusFast() {
|
|||
}
|
||||
|
||||
void Field::showError() {
|
||||
if (_input) {
|
||||
if (_config.type == FieldType::Country) {
|
||||
setFocus();
|
||||
_masked->showErrorNoFocus();
|
||||
} else if (_input) {
|
||||
_input->showError();
|
||||
} else {
|
||||
_masked->showError();
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace Ui {
|
|||
class RpWidget;
|
||||
class InputField;
|
||||
class MaskedInputField;
|
||||
class BoxContent;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Payments::Ui {
|
||||
|
@ -34,6 +35,8 @@ struct FieldConfig {
|
|||
FieldType type = FieldType::Text;
|
||||
rpl::producer<QString> placeholder;
|
||||
QString value;
|
||||
Fn<void(object_ptr<BoxContent>)> showBox;
|
||||
QString defaultCountry;
|
||||
int maxLength = 0;
|
||||
bool required = false;
|
||||
};
|
||||
|
@ -52,10 +55,14 @@ public:
|
|||
void showError();
|
||||
|
||||
private:
|
||||
const FieldType _type = FieldType::Text;
|
||||
void setupMaskedGeometry();
|
||||
void setupCountry();
|
||||
|
||||
const FieldConfig _config;
|
||||
const base::unique_qptr<RpWidget> _wrap;
|
||||
InputField *_input = nullptr;
|
||||
MaskedInputField *_masked = nullptr;
|
||||
QString _countryIso2;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -87,6 +87,8 @@ struct Address {
|
|||
};
|
||||
|
||||
struct RequestedInformation {
|
||||
QString defaultCountry;
|
||||
|
||||
QString name;
|
||||
QString phone;
|
||||
QString email;
|
||||
|
@ -125,6 +127,8 @@ enum class InformationField {
|
|||
};
|
||||
|
||||
struct NativeMethodDetails {
|
||||
QString defaultCountry;
|
||||
|
||||
bool supported = false;
|
||||
bool needCountry = false;
|
||||
bool needZip = false;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 52ac632bc4b0c7c9e80d012e6aa6ba154949e0ef
|
||||
Subproject commit f288c5649c1d517ab0510d9275b735a4398b6db4
|
Loading…
Reference in New Issue