mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Support adding tips in payments.
This commit is contained in:
parent
00c915e58d
commit
8cac76931e
13 changed files with 355 additions and 33 deletions
|
@ -1889,6 +1889,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_payments_billing_zip_code" = "Zip Code";
|
"lng_payments_billing_zip_code" = "Zip Code";
|
||||||
"lng_payments_save_payment_about" = "You can save your payment information for future use.";
|
"lng_payments_save_payment_about" = "You can save your payment information for future use.";
|
||||||
"lng_payments_save_information" = "Save Information";
|
"lng_payments_save_information" = "Save Information";
|
||||||
|
"lng_payments_tips_label" = "Tips";
|
||||||
|
"lng_payments_tips_title" = "Tips";
|
||||||
|
"lng_payments_tips_enter" = "Enter tips amount";
|
||||||
|
|
||||||
"lng_call_status_incoming" = "is calling you...";
|
"lng_call_status_incoming" = "is calling you...";
|
||||||
"lng_call_status_connecting" = "connecting...";
|
"lng_call_status_connecting" = "connecting...";
|
||||||
|
|
|
@ -254,6 +254,8 @@ void CheckoutProcess::handleError(const Error &error) {
|
||||||
|| id == u"PAYMENT_CREDENTIALS_INVALID"_q
|
|| id == u"PAYMENT_CREDENTIALS_INVALID"_q
|
||||||
|| id == u"PAYMENT_CREDENTIALS_ID_INVALID"_q) {
|
|| id == u"PAYMENT_CREDENTIALS_ID_INVALID"_q) {
|
||||||
showToast({ "Error: " + id + ". Your card has not been billed." });
|
showToast({ "Error: " + id + ". Your card has not been billed." });
|
||||||
|
} else {
|
||||||
|
showToast({ "Error: " + id });
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: Unexpected("Error type in CheckoutProcess::handleError.");
|
default: Unexpected("Error type in CheckoutProcess::handleError.");
|
||||||
|
@ -284,6 +286,7 @@ void CheckoutProcess::panelCloseSure() {
|
||||||
void CheckoutProcess::panelSubmit() {
|
void CheckoutProcess::panelSubmit() {
|
||||||
if (_form->invoice().receipt.paid) {
|
if (_form->invoice().receipt.paid) {
|
||||||
panelCloseSure();
|
panelCloseSure();
|
||||||
|
return;
|
||||||
} else if (_submitState == SubmitState::Validation
|
} else if (_submitState == SubmitState::Validation
|
||||||
|| _submitState == SubmitState::Finishing) {
|
|| _submitState == SubmitState::Finishing) {
|
||||||
return;
|
return;
|
||||||
|
@ -437,6 +440,10 @@ void CheckoutProcess::chooseShippingOption() {
|
||||||
_panel->chooseShippingOption(_form->shippingOptions());
|
_panel->chooseShippingOption(_form->shippingOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckoutProcess::chooseTips() {
|
||||||
|
_panel->chooseTips(_form->invoice());
|
||||||
|
}
|
||||||
|
|
||||||
void CheckoutProcess::editPaymentMethod() {
|
void CheckoutProcess::editPaymentMethod() {
|
||||||
_panel->choosePaymentMethod(_form->paymentMethod().ui);
|
_panel->choosePaymentMethod(_form->paymentMethod().ui);
|
||||||
}
|
}
|
||||||
|
@ -453,6 +460,18 @@ void CheckoutProcess::panelChangeShippingOption(const QString &id) {
|
||||||
showForm();
|
showForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckoutProcess::panelChooseTips() {
|
||||||
|
if (_submitState != SubmitState::None) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chooseTips();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckoutProcess::panelChangeTips(int64 value) {
|
||||||
|
_form->setTips(value);
|
||||||
|
showForm();
|
||||||
|
}
|
||||||
|
|
||||||
void CheckoutProcess::panelValidateInformation(
|
void CheckoutProcess::panelValidateInformation(
|
||||||
Ui::RequestedInformation data) {
|
Ui::RequestedInformation data) {
|
||||||
_form->validateInformation(data);
|
_form->validateInformation(data);
|
||||||
|
|
|
@ -67,6 +67,7 @@ private:
|
||||||
void showInformationError(Ui::InformationField field);
|
void showInformationError(Ui::InformationField field);
|
||||||
void showCardError(Ui::CardField field);
|
void showCardError(Ui::CardField field);
|
||||||
void chooseShippingOption();
|
void chooseShippingOption();
|
||||||
|
void chooseTips();
|
||||||
void editPaymentMethod();
|
void editPaymentMethod();
|
||||||
|
|
||||||
void performInitialSilentValidation();
|
void performInitialSilentValidation();
|
||||||
|
@ -84,6 +85,8 @@ private:
|
||||||
void panelEditPhone() override;
|
void panelEditPhone() override;
|
||||||
void panelChooseShippingOption() override;
|
void panelChooseShippingOption() override;
|
||||||
void panelChangeShippingOption(const QString &id) override;
|
void panelChangeShippingOption(const QString &id) override;
|
||||||
|
void panelChooseTips() override;
|
||||||
|
void panelChangeTips(int64 value) override;
|
||||||
|
|
||||||
void panelValidateInformation(Ui::RequestedInformation data) override;
|
void panelValidateInformation(Ui::RequestedInformation data) override;
|
||||||
void panelValidateCard(Ui::UncheckedCardDetails data) override;
|
void panelValidateCard(Ui::UncheckedCardDetails data) override;
|
||||||
|
|
|
@ -45,6 +45,10 @@ namespace {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int64 ParsePriceAmount(uint64 value) {
|
||||||
|
return *reinterpret_cast<const int64*>(&value);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::vector<Ui::LabeledPrice> ParsePrices(
|
[[nodiscard]] std::vector<Ui::LabeledPrice> ParsePrices(
|
||||||
const MTPVector<MTPLabeledPrice> &data) {
|
const MTPVector<MTPLabeledPrice> &data) {
|
||||||
return ranges::views::all(
|
return ranges::views::all(
|
||||||
|
@ -53,7 +57,7 @@ namespace {
|
||||||
return price.match([&](const MTPDlabeledPrice &data) {
|
return price.match([&](const MTPDlabeledPrice &data) {
|
||||||
return Ui::LabeledPrice{
|
return Ui::LabeledPrice{
|
||||||
.label = qs(data.vlabel()),
|
.label = qs(data.vlabel()),
|
||||||
.price = *reinterpret_cast<const int64*>(&data.vamount().v),
|
.price = ParsePriceAmount(data.vamount().v),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}) | ranges::to_vector;
|
}) | ranges::to_vector;
|
||||||
|
@ -290,6 +294,10 @@ void Form::processInvoice(const MTPDinvoice &data) {
|
||||||
.cover = std::move(_invoice.cover),
|
.cover = std::move(_invoice.cover),
|
||||||
|
|
||||||
.prices = ParsePrices(data.vprices()),
|
.prices = ParsePrices(data.vprices()),
|
||||||
|
.tipsMin = ParsePriceAmount(data.vmin_tip_amount().value_or_empty()),
|
||||||
|
.tipsMax = ParsePriceAmount(data.vmax_tip_amount().value_or_empty()),
|
||||||
|
.tipsSelected = ParsePriceAmount(
|
||||||
|
data.vdefault_tip_amount().value_or_empty()),
|
||||||
.currency = qs(data.vcurrency()),
|
.currency = qs(data.vcurrency()),
|
||||||
|
|
||||||
.isNameRequested = data.is_name_requested(),
|
.isNameRequested = data.is_name_requested(),
|
||||||
|
@ -330,8 +338,7 @@ void Form::processDetails(const MTPDpayments_paymentForm &data) {
|
||||||
void Form::processDetails(const MTPDpayments_paymentReceipt &data) {
|
void Form::processDetails(const MTPDpayments_paymentReceipt &data) {
|
||||||
_invoice.receipt = Ui::Receipt{
|
_invoice.receipt = Ui::Receipt{
|
||||||
.date = data.vdate().v,
|
.date = data.vdate().v,
|
||||||
.totalAmount = *reinterpret_cast<const int64*>(
|
.totalAmount = ParsePriceAmount(data.vtotal_amount().v),
|
||||||
&data.vtotal_amount().v),
|
|
||||||
.currency = qs(data.vcurrency()),
|
.currency = qs(data.vcurrency()),
|
||||||
.paid = true,
|
.paid = true,
|
||||||
};
|
};
|
||||||
|
@ -440,7 +447,8 @@ void Form::submit() {
|
||||||
: Flag::f_requested_info_id)
|
: Flag::f_requested_info_id)
|
||||||
| (_shippingOptions.selectedId.isEmpty()
|
| (_shippingOptions.selectedId.isEmpty()
|
||||||
? Flag(0)
|
? Flag(0)
|
||||||
: Flag::f_shipping_option_id)),
|
: Flag::f_shipping_option_id)
|
||||||
|
| (_invoice.tipsSelected ? Flag::f_tip_amount : Flag(0))),
|
||||||
MTP_long(_details.formId),
|
MTP_long(_details.formId),
|
||||||
_peer->input,
|
_peer->input,
|
||||||
MTP_int(_msgId),
|
MTP_int(_msgId),
|
||||||
|
@ -449,7 +457,7 @@ void Form::submit() {
|
||||||
MTP_inputPaymentCredentials(
|
MTP_inputPaymentCredentials(
|
||||||
MTP_flags(0),
|
MTP_flags(0),
|
||||||
MTP_dataJSON(MTP_bytes(_paymentMethod.newCredentials.data))),
|
MTP_dataJSON(MTP_bytes(_paymentMethod.newCredentials.data))),
|
||||||
MTP_long(0) // #TODO payments tip_amount
|
MTP_long(_invoice.tipsSelected)
|
||||||
)).done([=](const MTPpayments_PaymentResult &result) {
|
)).done([=](const MTPpayments_PaymentResult &result) {
|
||||||
result.match([&](const MTPDpayments_paymentResult &data) {
|
result.match([&](const MTPDpayments_paymentResult &data) {
|
||||||
_updates.fire(PaymentFinished{ data.vupdates() });
|
_updates.fire(PaymentFinished{ data.vupdates() });
|
||||||
|
@ -668,6 +676,13 @@ void Form::setShippingOption(const QString &id) {
|
||||||
_shippingOptions.selectedId = id;
|
_shippingOptions.selectedId = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Form::setTips(int64 value) {
|
||||||
|
_invoice.tipsSelected = std::clamp(
|
||||||
|
value,
|
||||||
|
_invoice.tipsMin,
|
||||||
|
_invoice.tipsMax);
|
||||||
|
}
|
||||||
|
|
||||||
void Form::processShippingOptions(const QVector<MTPShippingOption> &data) {
|
void Form::processShippingOptions(const QVector<MTPShippingOption> &data) {
|
||||||
_shippingOptions = Ui::ShippingOptions{ ranges::views::all(
|
_shippingOptions = Ui::ShippingOptions{ ranges::views::all(
|
||||||
data
|
data
|
||||||
|
|
|
@ -173,6 +173,7 @@ public:
|
||||||
void validateCard(const Ui::UncheckedCardDetails &details);
|
void validateCard(const Ui::UncheckedCardDetails &details);
|
||||||
void setPaymentCredentials(const NewCredentials &credentials);
|
void setPaymentCredentials(const NewCredentials &credentials);
|
||||||
void setShippingOption(const QString &id);
|
void setShippingOption(const QString &id);
|
||||||
|
void setTips(int64 value);
|
||||||
void submit();
|
void submit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -46,6 +46,7 @@ namespace {
|
||||||
case FieldType::CardCVC:
|
case FieldType::CardCVC:
|
||||||
case FieldType::Country:
|
case FieldType::Country:
|
||||||
case FieldType::Phone:
|
case FieldType::Phone:
|
||||||
|
case FieldType::PriceAmount:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Unexpected("FieldType in Payments::Ui::UseMaskedField.");
|
Unexpected("FieldType in Payments::Ui::UseMaskedField.");
|
||||||
|
@ -67,6 +68,7 @@ namespace {
|
||||||
case FieldType::CardCVC:
|
case FieldType::CardCVC:
|
||||||
case FieldType::Country:
|
case FieldType::Country:
|
||||||
case FieldType::Phone:
|
case FieldType::Phone:
|
||||||
|
case FieldType::PriceAmount:
|
||||||
return base::make_unique_q<RpWidget>(parent);
|
return base::make_unique_q<RpWidget>(parent);
|
||||||
}
|
}
|
||||||
Unexpected("FieldType in Payments::Ui::CreateWrap.");
|
Unexpected("FieldType in Payments::Ui::CreateWrap.");
|
||||||
|
@ -94,6 +96,7 @@ namespace {
|
||||||
case FieldType::CardExpireDate:
|
case FieldType::CardExpireDate:
|
||||||
case FieldType::CardCVC:
|
case FieldType::CardCVC:
|
||||||
case FieldType::Country:
|
case FieldType::Country:
|
||||||
|
case FieldType::PriceAmount:
|
||||||
return CreateChild<MaskedInputField>(
|
return CreateChild<MaskedInputField>(
|
||||||
wrap.get(),
|
wrap.get(),
|
||||||
st::paymentsField,
|
st::paymentsField,
|
||||||
|
|
|
@ -29,6 +29,7 @@ enum class FieldType {
|
||||||
Country,
|
Country,
|
||||||
Phone,
|
Phone,
|
||||||
Email,
|
Email,
|
||||||
|
PriceAmount,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FieldValidateRequest {
|
struct FieldValidateRequest {
|
||||||
|
|
|
@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
#include "ui/wrap/fade_wrap.h"
|
#include "ui/wrap/fade_wrap.h"
|
||||||
#include "ui/text/format_values.h"
|
#include "ui/text/format_values.h"
|
||||||
|
#include "ui/text/text_utilities.h"
|
||||||
#include "data/data_countries.h"
|
#include "data/data_countries.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
|
@ -84,7 +85,7 @@ int64 FormSummary::computeTotalAmount() const {
|
||||||
std::plus<>(),
|
std::plus<>(),
|
||||||
&LabeledPrice::price)
|
&LabeledPrice::price)
|
||||||
: int64(0);
|
: int64(0);
|
||||||
return total + shipping;
|
return total + shipping + _invoice.tipsSelected;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormSummary::setupControls() {
|
void FormSummary::setupControls() {
|
||||||
|
@ -193,12 +194,15 @@ void FormSummary::setupCover(not_null<VerticalLayout*> layout) {
|
||||||
void FormSummary::setupPrices(not_null<VerticalLayout*> layout) {
|
void FormSummary::setupPrices(not_null<VerticalLayout*> layout) {
|
||||||
const auto addRow = [&](
|
const auto addRow = [&](
|
||||||
const QString &label,
|
const QString &label,
|
||||||
const QString &value,
|
const TextWithEntities &value,
|
||||||
bool full = false) {
|
bool full = false) {
|
||||||
const auto &st = full
|
const auto &st = full
|
||||||
? st::paymentsFullPriceAmount
|
? st::paymentsFullPriceAmount
|
||||||
: st::paymentsPriceAmount;
|
: st::paymentsPriceAmount;
|
||||||
const auto right = CreateChild<FlatLabel>(layout.get(), value, st);
|
const auto right = CreateChild<FlatLabel>(
|
||||||
|
layout.get(),
|
||||||
|
rpl::single(value),
|
||||||
|
st);
|
||||||
const auto &padding = st::paymentsPricePadding;
|
const auto &padding = st::paymentsPricePadding;
|
||||||
const auto left = layout->add(
|
const auto left = layout->add(
|
||||||
object_ptr<FlatLabel>(
|
object_ptr<FlatLabel>(
|
||||||
|
@ -220,13 +224,14 @@ void FormSummary::setupPrices(not_null<VerticalLayout*> layout) {
|
||||||
) | rpl::start_with_next([=](int top, int width) {
|
) | rpl::start_with_next([=](int top, int width) {
|
||||||
right->moveToRight(st::paymentsPricePadding.right(), top, width);
|
right->moveToRight(st::paymentsPricePadding.right(), top, width);
|
||||||
}, right->lifetime());
|
}, right->lifetime());
|
||||||
|
return right;
|
||||||
};
|
};
|
||||||
|
|
||||||
Settings::AddSkip(layout, st::paymentsPricesTopSkip);
|
Settings::AddSkip(layout, st::paymentsPricesTopSkip);
|
||||||
if (_invoice.receipt) {
|
if (_invoice.receipt) {
|
||||||
addRow(
|
addRow(
|
||||||
tr::lng_payments_date_label(tr::now),
|
tr::lng_payments_date_label(tr::now),
|
||||||
langDateTime(base::unixtime::parse(_invoice.receipt.date)),
|
{ langDateTime(base::unixtime::parse(_invoice.receipt.date)) },
|
||||||
true);
|
true);
|
||||||
Settings::AddSkip(layout, st::paymentsPricesBottomSkip);
|
Settings::AddSkip(layout, st::paymentsPricesBottomSkip);
|
||||||
Settings::AddDivider(layout);
|
Settings::AddDivider(layout);
|
||||||
|
@ -237,7 +242,7 @@ void FormSummary::setupPrices(not_null<VerticalLayout*> layout) {
|
||||||
const QString &label,
|
const QString &label,
|
||||||
int64 amount,
|
int64 amount,
|
||||||
bool full = false) {
|
bool full = false) {
|
||||||
addRow(label, formatAmount(amount), full);
|
addRow(label, { formatAmount(amount) }, full);
|
||||||
};
|
};
|
||||||
for (const auto &price : _invoice.prices) {
|
for (const auto &price : _invoice.prices) {
|
||||||
add(price.label, price.price);
|
add(price.label, price.price);
|
||||||
|
@ -251,7 +256,27 @@ void FormSummary::setupPrices(not_null<VerticalLayout*> layout) {
|
||||||
add(price.label, price.price);
|
add(price.label, price.price);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
add(tr::lng_payments_total_label(tr::now), computeTotalAmount(), true);
|
|
||||||
|
const auto computedTotal = computeTotalAmount();
|
||||||
|
const auto total = _invoice.receipt.paid
|
||||||
|
? _invoice.receipt.totalAmount
|
||||||
|
: computedTotal;
|
||||||
|
if (_invoice.receipt.paid) {
|
||||||
|
if (const auto tips = total - computedTotal) {
|
||||||
|
add(tr::lng_payments_tips_label(tr::now), tips);
|
||||||
|
}
|
||||||
|
} else if (_invoice.tipsMax > 0) {
|
||||||
|
const auto text = formatAmount(_invoice.tipsSelected);
|
||||||
|
const auto label = addRow(
|
||||||
|
tr::lng_payments_tips_label(tr::now),
|
||||||
|
Ui::Text::Link(text, "internal:edit_tips"));
|
||||||
|
label->setClickHandlerFilter([=](auto&&...) {
|
||||||
|
_delegate->panelChooseTips();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
add(tr::lng_payments_total_label(tr::now), total, true);
|
||||||
Settings::AddSkip(layout, st::paymentsPricesBottomSkip);
|
Settings::AddSkip(layout, st::paymentsPricesBottomSkip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "payments/ui/payments_edit_information.h"
|
#include "payments/ui/payments_edit_information.h"
|
||||||
#include "payments/ui/payments_edit_card.h"
|
#include "payments/ui/payments_edit_card.h"
|
||||||
#include "payments/ui/payments_panel_delegate.h"
|
#include "payments/ui/payments_panel_delegate.h"
|
||||||
|
#include "payments/ui/payments_field.h"
|
||||||
#include "ui/widgets/separate_panel.h"
|
#include "ui/widgets/separate_panel.h"
|
||||||
#include "ui/boxes/single_choice_box.h"
|
#include "ui/boxes/single_choice_box.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
@ -19,6 +20,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "styles/style_passport.h"
|
#include "styles/style_passport.h"
|
||||||
|
|
||||||
namespace Payments::Ui {
|
namespace Payments::Ui {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
[[nodiscard]] auto PriceAmountValidator(int64 min, int64 max) {
|
||||||
|
return [=](FieldValidateRequest request) {
|
||||||
|
return FieldValidateResult{
|
||||||
|
.value = request.nowValue,
|
||||||
|
.position = request.nowPosition,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
Panel::Panel(not_null<PanelDelegate*> delegate)
|
Panel::Panel(not_null<PanelDelegate*> delegate)
|
||||||
: _delegate(delegate)
|
: _delegate(delegate)
|
||||||
|
@ -127,6 +140,40 @@ void Panel::chooseShippingOption(const ShippingOptions &options) {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Panel::chooseTips(const Invoice &invoice) {
|
||||||
|
const auto min = invoice.tipsMin;
|
||||||
|
const auto max = invoice.tipsMax;
|
||||||
|
const auto now = invoice.tipsSelected;
|
||||||
|
showBox(Box([=](not_null<Ui::GenericBox*> box) {
|
||||||
|
box->setTitle(tr::lng_payments_tips_title());
|
||||||
|
|
||||||
|
const auto row = box->lifetime().make_state<Field>(
|
||||||
|
box,
|
||||||
|
FieldConfig{
|
||||||
|
.type = FieldType::PriceAmount,
|
||||||
|
.placeholder = tr::lng_payments_tips_enter(),
|
||||||
|
.value = QString::number(now),
|
||||||
|
.validator = PriceAmountValidator(min, max),
|
||||||
|
});
|
||||||
|
box->setFocusCallback([=] {
|
||||||
|
row->setFocusFast();
|
||||||
|
});
|
||||||
|
box->addRow(row->ownedWidget());
|
||||||
|
box->addRow(object_ptr<FlatLabel>(box, "Min: " + QString::number(min), st::defaultFlatLabel));
|
||||||
|
box->addRow(object_ptr<FlatLabel>(box, "Max: " + QString::number(max), st::defaultFlatLabel));
|
||||||
|
box->addButton(tr::lng_settings_save(), [=] {
|
||||||
|
const auto value = row->value().toLongLong();
|
||||||
|
if (value < min || value > max) {
|
||||||
|
row->showError();
|
||||||
|
} else {
|
||||||
|
_delegate->panelChangeTips(value);
|
||||||
|
box->closeBox();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
void Panel::showEditPaymentMethod(const PaymentMethodDetails &method) {
|
void Panel::showEditPaymentMethod(const PaymentMethodDetails &method) {
|
||||||
_widget->setTitle(tr::lng_payments_card_title());
|
_widget->setTitle(tr::lng_payments_card_title());
|
||||||
if (method.native.supported) {
|
if (method.native.supported) {
|
||||||
|
|
|
@ -63,6 +63,7 @@ public:
|
||||||
const NativeMethodDetails &native,
|
const NativeMethodDetails &native,
|
||||||
CardField field);
|
CardField field);
|
||||||
void chooseShippingOption(const ShippingOptions &options);
|
void chooseShippingOption(const ShippingOptions &options);
|
||||||
|
void chooseTips(const Invoice &invoice);
|
||||||
void choosePaymentMethod(const PaymentMethodDetails &method);
|
void choosePaymentMethod(const PaymentMethodDetails &method);
|
||||||
|
|
||||||
bool showWebview(const QString &url, bool allowBack);
|
bool showWebview(const QString &url, bool allowBack);
|
||||||
|
|
|
@ -39,6 +39,9 @@ struct Invoice {
|
||||||
Cover cover;
|
Cover cover;
|
||||||
|
|
||||||
std::vector<LabeledPrice> prices;
|
std::vector<LabeledPrice> prices;
|
||||||
|
int64 tipsMin = 0;
|
||||||
|
int64 tipsMax = 0;
|
||||||
|
int64 tipsSelected = 0;
|
||||||
QString currency;
|
QString currency;
|
||||||
Receipt receipt;
|
Receipt receipt;
|
||||||
|
|
||||||
|
@ -53,7 +56,7 @@ struct Invoice {
|
||||||
bool emailSentToProvider = false;
|
bool emailSentToProvider = false;
|
||||||
|
|
||||||
[[nodiscard]] bool valid() const {
|
[[nodiscard]] bool valid() const {
|
||||||
return !currency.isEmpty() && !prices.empty();
|
return !currency.isEmpty() && (!prices.empty() || tipsMax);
|
||||||
}
|
}
|
||||||
[[nodiscard]] explicit operator bool() const {
|
[[nodiscard]] explicit operator bool() const {
|
||||||
return valid();
|
return valid();
|
||||||
|
|
|
@ -38,6 +38,8 @@ public:
|
||||||
virtual void panelEditPhone() = 0;
|
virtual void panelEditPhone() = 0;
|
||||||
virtual void panelChooseShippingOption() = 0;
|
virtual void panelChooseShippingOption() = 0;
|
||||||
virtual void panelChangeShippingOption(const QString &id) = 0;
|
virtual void panelChangeShippingOption(const QString &id) = 0;
|
||||||
|
virtual void panelChooseTips() = 0;
|
||||||
|
virtual void panelChangeTips(int64 value) = 0;
|
||||||
|
|
||||||
virtual void panelValidateInformation(RequestedInformation data) = 0;
|
virtual void panelValidateInformation(RequestedInformation data) = 0;
|
||||||
virtual void panelValidateCard(Ui::UncheckedCardDetails data) = 0;
|
virtual void panelValidateCard(Ui::UncheckedCardDetails data) = 0;
|
||||||
|
|
|
@ -10,6 +10,40 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
|
||||||
#include <QtCore/QLocale>
|
#include <QtCore/QLocale>
|
||||||
|
#include <locale>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
[[nodiscard]] QString FormatWithSeparators(
|
||||||
|
double amount,
|
||||||
|
char decimal,
|
||||||
|
char thousands) {
|
||||||
|
Expects(decimal != 0);
|
||||||
|
|
||||||
|
// Thanks https://stackoverflow.com/a/5058949
|
||||||
|
struct FormattingHelper : std::numpunct<char> {
|
||||||
|
FormattingHelper(char decimal, char thousands)
|
||||||
|
: decimal(decimal)
|
||||||
|
, thousands(thousands) {
|
||||||
|
}
|
||||||
|
|
||||||
|
char do_decimal_point() const override { return decimal; }
|
||||||
|
char do_thousands_sep() const override { return thousands; }
|
||||||
|
|
||||||
|
char decimal = '.';
|
||||||
|
char thousands = ',';
|
||||||
|
};
|
||||||
|
|
||||||
|
auto stream = std::ostringstream();
|
||||||
|
auto helper = FormattingHelper(decimal, thousands ? thousands : '?');
|
||||||
|
stream.imbue(std::locale(stream.getloc(), &helper));
|
||||||
|
stream << std::fixed << amount;
|
||||||
|
auto result = QString::fromStdString(stream.str());
|
||||||
|
if (!thousands) {
|
||||||
|
result.replace('?', QString());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
|
||||||
|
@ -126,13 +160,168 @@ QString FormatPlayedText(qint64 played, qint64 duration) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FillAmountAndCurrency(int64 amount, const QString ¤cy) {
|
QString FillAmountAndCurrency(int64 amount, const QString ¤cy) {
|
||||||
static const auto ShortCurrencyNames = QMap<QString, QString>{
|
struct Rule {
|
||||||
{ u"USD"_q, QString::fromUtf8("\x24") },
|
//const char *name = "";
|
||||||
{ u"GBP"_q, QString::fromUtf8("\xC2\xA3") },
|
//const char *native = "";
|
||||||
{ u"EUR"_q, QString::fromUtf8("\xE2\x82\xAC") },
|
const char *international = "";
|
||||||
{ u"JPY"_q, QString::fromUtf8("\xC2\xA5") },
|
char thousands = ',';
|
||||||
|
char decimal = '.';
|
||||||
|
bool left = true;
|
||||||
|
bool space = false;
|
||||||
};
|
};
|
||||||
static const auto Denominators = QMap<QString, int>{
|
static const auto kRules = std::vector<std::pair<QString, Rule>>{
|
||||||
|
{ u"AED"_q, { "", ',', '.', true, true } },
|
||||||
|
{ u"AFN"_q, {} },
|
||||||
|
{ u"ALL"_q, { "", '.', ',', false } },
|
||||||
|
{ u"AMD"_q, { "", ',', '.', false, true } },
|
||||||
|
{ u"ARS"_q, { "", '.', ',', true, true } },
|
||||||
|
{ u"AUD"_q, { "AU$" } },
|
||||||
|
{ u"AZN"_q, { "", ' ', ',', false, true } },
|
||||||
|
{ u"BAM"_q, { "", '.', ',', false, true } },
|
||||||
|
{ u"BDT"_q, { "", ',', '.', true, true } },
|
||||||
|
{ u"BGN"_q, { "", ' ', ',', false, true } },
|
||||||
|
{ u"BND"_q, { "", '.', ',', } },
|
||||||
|
{ u"BOB"_q, { "", '.', ',', true, true } },
|
||||||
|
{ u"BRL"_q, { "R$", '.', ',', true, true } },
|
||||||
|
{ u"BHD"_q, { "", ',', '.', true, true } },
|
||||||
|
{ u"BYR"_q, { "", ' ', ',', false, true } },
|
||||||
|
{ u"CAD"_q, { "CA$" } },
|
||||||
|
{ u"CHF"_q, { "", '\'', '.', false, true } },
|
||||||
|
{ u"CLP"_q, { "", '.', ',', true, true } },
|
||||||
|
{ u"CNY"_q, { "\x43\x4E\xC2\xA5" } },
|
||||||
|
{ u"COP"_q, { "", '.', ',', true, true } },
|
||||||
|
{ u"CRC"_q, { "", '.', ',', } },
|
||||||
|
{ u"CZK"_q, { "", ' ', ',', false, true } },
|
||||||
|
{ u"DKK"_q, { "", '\0', ',', false, true } },
|
||||||
|
{ u"DOP"_q, {} },
|
||||||
|
{ u"DZD"_q, { "", ',', '.', true, true } },
|
||||||
|
{ u"EGP"_q, { "", ',', '.', true, true } },
|
||||||
|
{ u"EUR"_q, { "\xE2\x82\xAC", ' ', ',', false, true } },
|
||||||
|
{ u"GBP"_q, { "\xC2\xA3" } },
|
||||||
|
{ u"GEL"_q, { "", ' ', ',', false, true } },
|
||||||
|
{ u"GTQ"_q, {} },
|
||||||
|
{ u"HKD"_q, { "HK$" } },
|
||||||
|
{ u"HNL"_q, { "", ',', '.', true, true } },
|
||||||
|
{ u"HRK"_q, { "", '.', ',', false, true } },
|
||||||
|
{ u"HUF"_q, { "", ' ', ',', false, true } },
|
||||||
|
{ u"IDR"_q, { "", '.', ',', } },
|
||||||
|
{ u"ILS"_q, { "\xE2\x82\xAA", ',', '.', true, true } },
|
||||||
|
{ u"INR"_q, { "\xE2\x82\xB9" } },
|
||||||
|
{ u"ISK"_q, { "", '.', ',', false, true } },
|
||||||
|
{ u"JMD"_q, {} },
|
||||||
|
{ u"JPY"_q, { "\xC2\xA5" } },
|
||||||
|
{ u"KES"_q, {} },
|
||||||
|
{ u"KGS"_q, { "", ' ', '-', false, true } },
|
||||||
|
{ u"KRW"_q, { "\xE2\x82\xA9" } },
|
||||||
|
{ u"KZT"_q, { "", ' ', '-', } },
|
||||||
|
{ u"LBP"_q, { "", ',', '.', true, true } },
|
||||||
|
{ u"LKR"_q, { "", ',', '.', true, true } },
|
||||||
|
{ u"MAD"_q, { "", ',', '.', true, true } },
|
||||||
|
{ u"MDL"_q, { "", ',', '.', false, true } },
|
||||||
|
{ u"MNT"_q, { "", ' ', ',', } },
|
||||||
|
{ u"MUR"_q, {} },
|
||||||
|
{ u"MVR"_q, { "", ',', '.', false, true } },
|
||||||
|
{ u"MXN"_q, { "MX$" } },
|
||||||
|
{ u"MYR"_q, {} },
|
||||||
|
{ u"MZN"_q, {} },
|
||||||
|
{ u"NGN"_q, {} },
|
||||||
|
{ u"NIO"_q, { "", ',', '.', true, true } },
|
||||||
|
{ u"NOK"_q, { "", ' ', ',', true, true } },
|
||||||
|
{ u"NPR"_q, {} },
|
||||||
|
{ u"NZD"_q, { "NZ$" } },
|
||||||
|
{ u"PAB"_q, { "", ',', '.', true, true } },
|
||||||
|
{ u"PEN"_q, { "", ',', '.', true, true } },
|
||||||
|
{ u"PHP"_q, {} },
|
||||||
|
{ u"PKR"_q, {} },
|
||||||
|
{ u"PLN"_q, { "", ' ', ',', false, true } },
|
||||||
|
{ u"PYG"_q, { "", '.', ',', true, true } },
|
||||||
|
{ u"QAR"_q, { "", ',', '.', true, true } },
|
||||||
|
{ u"RON"_q, { "", '.', ',', false, true } },
|
||||||
|
{ u"RSD"_q, { "", '.', ',', false, true } },
|
||||||
|
{ u"RUB"_q, { "", ' ', ',', false, true } },
|
||||||
|
{ u"SAR"_q, { "", ',', '.', true, true } },
|
||||||
|
{ u"SEK"_q, { "", '.', ',', false, true } },
|
||||||
|
{ u"SGD"_q, {} },
|
||||||
|
{ u"THB"_q, { "\xE0\xB8\xBF" } },
|
||||||
|
{ u"TJS"_q, { "", ' ', ';', false, true } },
|
||||||
|
{ u"TRY"_q, { "", '.', ',', false, true } },
|
||||||
|
{ u"TTD"_q, {} },
|
||||||
|
{ u"TWD"_q, { "NT$" } },
|
||||||
|
{ u"TZS"_q, {} },
|
||||||
|
{ u"UAH"_q, { "", ' ', ',', false } },
|
||||||
|
{ u"UGX"_q, {} },
|
||||||
|
{ u"USD"_q, { "$" } },
|
||||||
|
{ u"UYU"_q, { "", '.', ',', true, true } },
|
||||||
|
{ u"UZS"_q, { "", ' ', ',', false, true } },
|
||||||
|
{ u"VND"_q, { "\xE2\x82\xAB", '.', ',', false, true } },
|
||||||
|
{ u"YER"_q, { "", ',', '.', true, true } },
|
||||||
|
{ u"ZAR"_q, { "", ',', '.', true, true } },
|
||||||
|
{ u"IRR"_q, { "", ',', '/', false, true } },
|
||||||
|
{ u"IQD"_q, { "", ',', '.', true, true } },
|
||||||
|
{ u"VEF"_q, { "", '.', ',', true, true } },
|
||||||
|
{ u"SYP"_q, { "", ',', '.', true, true } },
|
||||||
|
|
||||||
|
//{ u"VUV"_q, { "", ',', '.', false } },
|
||||||
|
//{ u"WST"_q, {} },
|
||||||
|
//{ u"XAF"_q, { "FCFA", ',', '.', false } },
|
||||||
|
//{ u"XCD"_q, {} },
|
||||||
|
//{ u"XOF"_q, { "CFA", ' ', ',', false } },
|
||||||
|
//{ u"XPF"_q, { "", ',', '.', false } },
|
||||||
|
//{ u"ZMW"_q, {} },
|
||||||
|
//{ u"ANG"_q, {} },
|
||||||
|
//{ u"RWF"_q, { "", ' ', ',', true, true } },
|
||||||
|
//{ u"PGK"_q, {} },
|
||||||
|
//{ u"TOP"_q, {} },
|
||||||
|
//{ u"SBD"_q, {} },
|
||||||
|
//{ u"SCR"_q, {} },
|
||||||
|
//{ u"SHP"_q, {} },
|
||||||
|
//{ u"SLL"_q, {} },
|
||||||
|
//{ u"SOS"_q, {} },
|
||||||
|
//{ u"SRD"_q, {} },
|
||||||
|
//{ u"STD"_q, {} },
|
||||||
|
//{ u"SVC"_q, {} },
|
||||||
|
//{ u"SZL"_q, {} },
|
||||||
|
//{ u"AOA"_q, {} },
|
||||||
|
//{ u"AWG"_q, {} },
|
||||||
|
//{ u"BBD"_q, {} },
|
||||||
|
//{ u"BIF"_q, { "", ',', '.', false } },
|
||||||
|
//{ u"BMD"_q, {} },
|
||||||
|
//{ u"BSD"_q, {} },
|
||||||
|
//{ u"BWP"_q, {} },
|
||||||
|
//{ u"BZD"_q, {} },
|
||||||
|
//{ u"CDF"_q, { "", ',', '.', false } },
|
||||||
|
//{ u"CVE"_q, {} },
|
||||||
|
//{ u"DJF"_q, { "", ',', '.', false } },
|
||||||
|
//{ u"ETB"_q, {} },
|
||||||
|
//{ u"FJD"_q, {} },
|
||||||
|
//{ u"FKP"_q, {} },
|
||||||
|
//{ u"GIP"_q, {} },
|
||||||
|
//{ u"GMD"_q, { "", ',', '.', false } },
|
||||||
|
//{ u"GNF"_q, { "", ',', '.', false } },
|
||||||
|
//{ u"GYD"_q, {} },
|
||||||
|
//{ u"HTG"_q, {} },
|
||||||
|
//{ u"KHR"_q, { "", ',', '.', false } },
|
||||||
|
//{ u"KMF"_q, { "", ',', '.', false } },
|
||||||
|
//{ u"KYD"_q, {} },
|
||||||
|
//{ u"LAK"_q, { "", ',', '.', false } },
|
||||||
|
//{ u"LRD"_q, {} },
|
||||||
|
//{ u"LSL"_q, { "", ',', '.', false } },
|
||||||
|
//{ u"MGA"_q, {} },
|
||||||
|
//{ u"MKD"_q, { "", '.', ',', false, true } },
|
||||||
|
//{ u"MOP"_q, {} },
|
||||||
|
//{ u"MWK"_q, {} },
|
||||||
|
//{ u"NAD"_q, {} },
|
||||||
|
};
|
||||||
|
static const auto kRulesMap = [] {
|
||||||
|
// flat_multi_map_pair_type lacks some required constructors :(
|
||||||
|
auto &&pairs = kRules | ranges::views::transform([](auto &&pair) {
|
||||||
|
return base::flat_multi_map_pair_type<QString, Rule>(
|
||||||
|
pair.first,
|
||||||
|
pair.second);
|
||||||
|
});
|
||||||
|
return base::flat_map<QString, Rule>(begin(pairs), end(pairs));
|
||||||
|
}();
|
||||||
|
static const auto kDenominators = base::flat_map<QString, int>{
|
||||||
{ u"CLF"_q, 10000 },
|
{ u"CLF"_q, 10000 },
|
||||||
{ u"BHD"_q, 1000 },
|
{ u"BHD"_q, 1000 },
|
||||||
{ u"IQD"_q, 1000 },
|
{ u"IQD"_q, 1000 },
|
||||||
|
@ -163,21 +352,31 @@ QString FillAmountAndCurrency(int64 amount, const QString ¤cy) {
|
||||||
{ u"XPF"_q, 1 },
|
{ u"XPF"_q, 1 },
|
||||||
{ u"MRO"_q, 10 },
|
{ u"MRO"_q, 10 },
|
||||||
};
|
};
|
||||||
const auto currencyText = ShortCurrencyNames.value(currency, currency);
|
const auto denominatorIt = kDenominators.find(currency);
|
||||||
const auto denominator = Denominators.value(currency, 100);
|
const auto denominator = (denominatorIt != end(kDenominators))
|
||||||
const auto currencyValue = amount / float64(denominator);
|
? denominatorIt->second
|
||||||
const auto digits = [&] {
|
: 100;
|
||||||
auto result = 0;
|
const auto value = amount / float64(denominator);
|
||||||
for (auto test = 1; test < denominator; test *= 10) {
|
const auto ruleIt = kRulesMap.find(currency);
|
||||||
++result;
|
if (ruleIt == end(kRulesMap)) {
|
||||||
}
|
return QLocale::system().toCurrencyString(value, currency);
|
||||||
return result;
|
}
|
||||||
}();
|
const auto &rule = ruleIt->second;
|
||||||
return QLocale::system().toCurrencyString(currencyValue, currencyText);
|
const auto name = (*rule.international)
|
||||||
//auto amountBucks = amount / 100;
|
? QString::fromUtf8(rule.international)
|
||||||
//auto amountCents = amount % 100;
|
: currency;
|
||||||
//auto amountText = u"%1,%2").arg(amountBucks).arg(amountCents, 2, 10, QChar('0'));
|
auto result = QString();
|
||||||
//return currencyText + amountText;
|
if (rule.left) {
|
||||||
|
result.append(name);
|
||||||
|
if (rule.space) result.append(' ');
|
||||||
|
}
|
||||||
|
result.append(
|
||||||
|
FormatWithSeparators(value, rule.decimal, rule.thousands));
|
||||||
|
if (!rule.left) {
|
||||||
|
if (rule.space) result.append(' ');
|
||||||
|
result.append(name);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ComposeNameString(
|
QString ComposeNameString(
|
||||||
|
|
Loading…
Add table
Reference in a new issue