Improve checkout main page design.

This commit is contained in:
John Preston 2021-03-26 17:05:31 +04:00
parent 56031a6402
commit fafea73ea7
28 changed files with 448 additions and 140 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 985 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 949 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 711 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1022 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1865,24 +1865,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_payments_pay_amount" = "Pay {amount}";
"lng_payments_payment_method" = "Payment Method";
"lng_payments_new_card" = "New Card...";
"lng_payments_payment_method_ph" = "Enter your card details";
"lng_payments_shipping_address" = "Shipping Information";
"lng_payments_shipping_address_ph" = "Enter your shipping information";
"lng_payments_shipping_address" = "Shipping Address";
"lng_payments_receiver_information" = "Receiver";
"lng_payments_address_street1" = "Address 1";
"lng_payments_address_street2" = "Address 2";
"lng_payments_address_city" = "City";
"lng_payments_address_state" = "State";
"lng_payments_address_country" = "Country";
"lng_payments_address_postcode" = "Postcode";
"lng_payments_shipping_method" = "Shipping Method";
"lng_payments_shipping_method_ph" = "Choose your shipping method";
"lng_payments_info_name" = "Name";
"lng_payments_info_name_ph" = "Enter your name";
"lng_payments_info_email" = "Email";
"lng_payments_info_email_ph" = "Enter your email";
"lng_payments_info_phone" = "Phone";
"lng_payments_info_phone_ph" = "Enter your phone number";
"lng_payments_shipping_address_title" = "Shipping Address";
"lng_payments_save_shipping_about" = "You can save your shipping information for future use.";
"lng_payments_payment_card" = "Payment Card";
"lng_payments_cardholder_title" = "Cardholder";
"lng_payments_cardholder_about" = "Cardholder Name";
"lng_payments_billing_address" = "Billing Address";
"lng_payments_zip_code" = "Zip Code";
"lng_payments_card_title" = "New Card";
"lng_payments_card_number" = "Card Number";
"lng_payments_card_holder" = "Cardholder name";
"lng_payments_billing_address" = "Billing Information";
"lng_payments_billing_country" = "Country";
"lng_payments_billing_zip_code" = "Zip Code";
"lng_payments_save_payment_about" = "You can save your payment information for future use.";
"lng_payments_save_information" = "Save Information";

View File

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_domain.h"
#include "history/history_item.h"
#include "history/history.h"
#include "data/data_user.h" // UserData::isBot.
#include "core/local_url_handlers.h" // TryConvertUrlToLocal.
#include "core/file_utilities.h" // File::OpenUrl.
#include "apiwrap.h"
@ -105,6 +106,8 @@ void CheckoutProcess::handleFormUpdate(const FormUpdate &update) {
if (!_initialSilentValidation) {
showForm();
}
}, [&](const ThumbnailUpdated &data) {
_panel->updateFormThumbnail(data.thumbnail);
}, [&](const ValidateFinished &) {
if (_initialSilentValidation) {
_initialSilentValidation = false;
@ -114,16 +117,16 @@ void CheckoutProcess::handleFormUpdate(const FormUpdate &update) {
_submitState = SubmitState::Validated;
panelSubmit();
}
}, [&](const PaymentMethodUpdate&) {
}, [&](const PaymentMethodUpdate &) {
showForm();
}, [&](const VerificationNeeded &info) {
if (!_panel->showWebview(info.url, false)) {
File::OpenUrl(info.url);
}, [&](const VerificationNeeded &data) {
if (!_panel->showWebview(data.url, false)) {
File::OpenUrl(data.url);
panelCloseSure();
}
}, [&](const PaymentFinished &result) {
}, [&](const PaymentFinished &data) {
const auto weak = base::make_weak(this);
_session->api().applyUpdates(result.updates);
_session->api().applyUpdates(data.updates);
if (weak) {
panelCloseSure();
}

View File

@ -9,10 +9,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "data/data_session.h"
#include "apiwrap.h"
#include "data/data_media_types.h"
#include "data/data_user.h"
#include "data/data_photo.h"
#include "data/data_photo_media.h"
#include "data/data_file_origin.h"
#include "history/history_item.h"
#include "stripe/stripe_api_client.h"
#include "stripe/stripe_error.h"
#include "stripe/stripe_token.h"
#include "ui/image/image.h"
#include "apiwrap.h"
#include "styles/style_payments.h" // paymentsThumbnailSize.
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
@ -82,15 +90,110 @@ namespace {
Form::Form(not_null<Main::Session*> session, FullMsgId itemId)
: _session(session)
, _api(&_session->mtp())
, _msgId(itemId.msg) {
, _msgId(itemId) {
fillInvoiceFromMessage();
requestForm();
}
Form::~Form() = default;
void Form::fillInvoiceFromMessage() {
if (const auto item = _session->data().message(_msgId)) {
if (const auto media = item->media()) {
if (const auto invoice = media->invoice()) {
_invoice.cover = Ui::Cover{
.title = invoice->title,
.description = invoice->description,
};
if (const auto photo = invoice->photo) {
loadThumbnail(photo);
}
}
}
}
}
void Form::loadThumbnail(not_null<PhotoData*> photo) {
Expects(!_thumbnailLoadProcess);
auto view = photo->createMediaView();
if (auto good = prepareGoodThumbnail(view); !good.isNull()) {
_invoice.cover.thumbnail = std::move(good);
return;
}
_thumbnailLoadProcess = std::make_unique<ThumbnailLoadProcess>();
if (auto blurred = prepareBlurredThumbnail(view); !blurred.isNull()) {
_invoice.cover.thumbnail = std::move(blurred);
_thumbnailLoadProcess->blurredSet = true;
} else {
_invoice.cover.thumbnail = prepareEmptyThumbnail();
}
_thumbnailLoadProcess->view = std::move(view);
photo->load(Data::PhotoSize::Thumbnail, _msgId);
_session->downloaderTaskFinished(
) | rpl::start_with_next([=] {
const auto &view = _thumbnailLoadProcess->view;
if (auto good = prepareGoodThumbnail(view); !good.isNull()) {
_invoice.cover.thumbnail = std::move(good);
_thumbnailLoadProcess = nullptr;
} else if (_thumbnailLoadProcess->blurredSet) {
return;
} else if (auto blurred = prepareBlurredThumbnail(view)
; !blurred.isNull()) {
_invoice.cover.thumbnail = std::move(blurred);
_thumbnailLoadProcess->blurredSet = true;
} else {
return;
}
_updates.fire(ThumbnailUpdated{ _invoice.cover.thumbnail });
}, _thumbnailLoadProcess->lifetime);
}
QImage Form::prepareGoodThumbnail(
const std::shared_ptr<Data::PhotoMedia> &view) const {
using Size = Data::PhotoSize;
if (const auto large = view->image(Size::Large)) {
return prepareThumbnail(large);
} else if (const auto thumbnail = view->image(Size::Thumbnail)) {
return prepareThumbnail(thumbnail);
}
return QImage();
}
QImage Form::prepareBlurredThumbnail(
const std::shared_ptr<Data::PhotoMedia> &view) const {
if (const auto small = view->image(Data::PhotoSize::Small)) {
return prepareThumbnail(small, true);
} else if (const auto blurred = view->thumbnailInline()) {
return prepareThumbnail(blurred, true);
}
return QImage();
}
QImage Form::prepareThumbnail(
not_null<const Image*> image,
bool blurred) const {
auto result = image->original().scaled(
st::paymentsThumbnailSize * cIntRetinaFactor(),
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
Images::prepareRound(result, ImageRoundRadius::Large);
result.setDevicePixelRatio(cRetinaFactor());
return result;
}
QImage Form::prepareEmptyThumbnail() const {
auto result = QImage(
st::paymentsThumbnailSize * cIntRetinaFactor(),
QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(cRetinaFactor());
result.fill(Qt::transparent);
return result;
}
void Form::requestForm() {
_api.request(MTPpayments_GetPaymentForm(
MTP_int(_msgId)
MTP_int(_msgId.msg)
)).done([=](const MTPpayments_PaymentForm &result) {
result.match([&](const auto &data) {
processForm(data);
@ -123,6 +226,8 @@ void Form::processForm(const MTPDpayments_paymentForm &data) {
void Form::processInvoice(const MTPDinvoice &data) {
_invoice = Ui::Invoice{
.cover = std::move(_invoice.cover),
.prices = ParsePrices(data.vprices()),
.currency = qs(data.vcurrency()),
@ -154,6 +259,11 @@ void Form::processDetails(const MTPDpayments_paymentForm &data) {
.canSaveCredentials = data.is_can_save_credentials(),
.passwordMissing = data.is_password_missing(),
};
if (_details.botId) {
if (const auto bot = _session->data().userLoaded(_details.botId)) {
_invoice.cover.seller = bot->name;
}
}
}
void Form::processSavedInformation(const MTPDpaymentRequestedInfo &data) {
@ -240,7 +350,7 @@ void Form::submit() {
| (_shippingOptions.selectedId.isEmpty()
? Flag(0)
: Flag::f_shipping_option_id)),
MTP_int(_msgId),
MTP_int(_msgId.msg),
MTP_string(_requestedInformationId),
MTP_string(_shippingOptions.selectedId),
MTP_inputPaymentCredentials(
@ -267,7 +377,7 @@ void Form::validateInformation(const Ui::RequestedInformation &information) {
_validatedInformation = information;
_validateRequestId = _api.request(MTPpayments_ValidateRequestedInfo(
MTP_flags(0), // #TODO payments save information
MTP_int(_msgId),
MTP_int(_msgId.msg),
Serialize(information)
)).done([=](const MTPpayments_ValidatedRequestedInfo &result) {
_validateRequestId = 0;

View File

@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/weak_ptr.h"
#include "mtproto/sender.h"
class Image;
namespace Stripe {
class APIClient;
} // namespace Stripe
@ -19,6 +21,10 @@ namespace Main {
class Session;
} // namespace Main
namespace Data {
class PhotoMedia;
} // namespace Data
namespace Payments {
struct FormDetails {
@ -38,6 +44,12 @@ struct FormDetails {
}
};
struct ThumbnailLoadProcess {
std::shared_ptr<Data::PhotoMedia> view;
bool blurredSet = false;
rpl::lifetime lifetime;
};
struct SavedCredentials {
QString id;
QString title;
@ -88,6 +100,9 @@ struct PaymentMethod {
};
struct FormReady {};
struct ThumbnailUpdated {
QImage thumbnail;
};
struct ValidateFinished {};
struct PaymentMethodUpdate {};
struct VerificationNeeded {
@ -109,6 +124,7 @@ struct Error {
struct FormUpdate : std::variant<
FormReady,
ThumbnailUpdated,
ValidateFinished,
PaymentMethodUpdate,
VerificationNeeded,
@ -149,6 +165,18 @@ public:
void submit();
private:
void fillInvoiceFromMessage();
void loadThumbnail(not_null<PhotoData*> photo);
[[nodiscard]] QImage prepareGoodThumbnail(
const std::shared_ptr<Data::PhotoMedia> &view) const;
[[nodiscard]] QImage prepareBlurredThumbnail(
const std::shared_ptr<Data::PhotoMedia> &view) const;
[[nodiscard]] QImage prepareThumbnail(
not_null<const Image*> image,
bool blurred = false) const;
[[nodiscard]] QImage prepareEmptyThumbnail() const;
void requestForm();
void processForm(const MTPDpayments_paymentForm &data);
void processInvoice(const MTPDinvoice &data);
@ -167,9 +195,10 @@ private:
const not_null<Main::Session*> _session;
MTP::Sender _api;
MsgId _msgId = 0;
FullMsgId _msgId;
Ui::Invoice _invoice;
std::unique_ptr<ThumbnailLoadProcess> _thumbnailLoadProcess;
FormDetails _details;
Ui::RequestedInformation _savedInformation;
PaymentMethod _paymentMethod;

View File

@ -7,12 +7,52 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
using "ui/basic.style";
using "passport/passport.style";
using "info/info.style";
paymentsFormPricePadding: margins(22px, 7px, 22px, 6px);
paymentsPanelSubmit: RoundButton(passportPasswordSubmit) {
paymentsPanelSubmit: RoundButton(defaultActiveButton) {
width: 0px;
height: 49px;
padding: margins(0px, -3px, 0px, 0px);
textTop: 16px;
}
paymentsCoverPadding: margins(26px, 0px, 26px, 13px);
paymentsDescription: FlatLabel(defaultFlatLabel) {
minWidth: 160px;
textFg: windowFg;
}
paymentsTitle: FlatLabel(paymentsDescription) {
style: semiboldTextStyle;
}
paymentsSeller: FlatLabel(paymentsDescription) {
textFg: windowSubTextFg;
}
paymentsPriceLabel: paymentsDescription;
paymentsPriceAmount: defaultFlatLabel;
paymentsFullPriceLabel: paymentsTitle;
paymentsFullPriceAmount: FlatLabel(defaultFlatLabel) {
style: semiboldTextStyle;
}
paymentsTitleTop: 0px;
paymentsDescriptionTop: 3px;
paymentsSellerTop: 4px;
paymentsThumbnailSize: size(80px, 80px);
paymentsThumbnailSkip: 18px;
paymentsPricesTopSkip: 12px;
paymentsPricesBottomSkip: 13px;
paymentsPricePadding: margins(28px, 6px, 28px, 5px);
paymentsSectionsTopSkip: 11px;
paymentsSectionButton: SettingsButton(infoProfileButton) {
padding: margins(68px, 11px, 14px, 9px);
}
paymentsIconPaymentMethod: icon {{ "payments/payment_card", menuIconFg }};
paymentsIconShippingAddress: icon {{ "payments/payment_address", menuIconFg }};
paymentsIconName: icon {{ "payments/payment_name", menuIconFg }};
paymentsIconEmail: icon {{ "payments/payment_email", menuIconFg }};
paymentsIconPhone: icon {{ "payments/payment_phone", menuIconFg }};
paymentsIconShippingMethod: icon {{ "payments/payment_shipping", menuIconFg }};

View File

@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "payments/ui/payments_form_summary.h"
#include "payments/ui/payments_panel_delegate.h"
#include "passport/ui/passport_form_row.h"
#include "settings/settings_common.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
@ -22,7 +22,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Payments::Ui {
using namespace ::Ui;
using namespace Passport::Ui;
class PanelDelegate;
@ -45,16 +44,24 @@ FormSummary::FormSummary(
this,
tr::lng_payments_pay_amount(
lt_amount,
rpl::single(computeTotalAmount())),
rpl::single(formatAmount(computeTotalAmount()))),
st::paymentsPanelSubmit) {
setupControls();
}
QString FormSummary::computeAmount(int64 amount) const {
return FillAmountAndCurrency(amount, _invoice.currency);
void FormSummary::updateThumbnail(const QImage &thumbnail) {
_invoice.cover.thumbnail = thumbnail;
_thumbnails.fire_copy(thumbnail);
}
QString FormSummary::computeTotalAmount() const {
QString FormSummary::formatAmount(int64 amount) const {
const auto base = FillAmountAndCurrency(
std::abs(amount),
_invoice.currency);
return (amount > 0) ? base : (QString::fromUtf8("\xe2\x88\x92") + base);
}
int64 FormSummary::computeTotalAmount() const {
const auto total = ranges::accumulate(
_invoice.prices,
int64(0),
@ -71,7 +78,7 @@ QString FormSummary::computeTotalAmount() const {
std::plus<>(),
&LabeledPrice::price)
: int64(0);
return computeAmount(total + shipping);
return total + shipping;
}
void FormSummary::setupControls() {
@ -92,22 +99,125 @@ void FormSummary::setupControls() {
_1 + _2 < _3));
}
not_null<Ui::RpWidget*> FormSummary::setupContent() {
const auto inner = _scroll->setOwnedWidget(
object_ptr<Ui::VerticalLayout>(this));
void FormSummary::setupCover(not_null<VerticalLayout*> layout) {
struct State {
QImage thumbnail;
FlatLabel *title = nullptr;
FlatLabel *description = nullptr;
FlatLabel *seller = nullptr;
};
_scroll->widthValue(
) | rpl::start_with_next([=](int width) {
inner->resizeToWidth(width);
}, inner->lifetime());
const auto cover = layout->add(object_ptr<RpWidget>(layout));
const auto state = cover->lifetime().make_state<State>();
state->title = CreateChild<FlatLabel>(
cover,
_invoice.cover.title,
st::paymentsTitle);
state->description = CreateChild<FlatLabel>(
cover,
_invoice.cover.description,
st::paymentsDescription);
state->seller = CreateChild<FlatLabel>(
cover,
_invoice.cover.seller,
st::paymentsSeller);
cover->paintRequest(
) | rpl::start_with_next([=](QRect clip) {
if (state->thumbnail.isNull()) {
return;
}
const auto &padding = st::paymentsCoverPadding;
const auto thumbnailSkip = st::paymentsThumbnailSize.width()
+ st::paymentsThumbnailSkip;
const auto left = padding.left();
const auto top = padding.top();
const auto rect = QRect(
QPoint(left, top),
state->thumbnail.size() / state->thumbnail.devicePixelRatio());
if (rect.intersects(clip)) {
QPainter(cover).drawImage(rect, state->thumbnail);
}
}, cover->lifetime());
rpl::combine(
cover->widthValue(),
_thumbnails.events_starting_with_copy(_invoice.cover.thumbnail)
) | rpl::start_with_next([=](int width, QImage &&thumbnail) {
const auto &padding = st::paymentsCoverPadding;
const auto thumbnailSkip = st::paymentsThumbnailSize.width()
+ st::paymentsThumbnailSkip;
const auto left = padding.left()
+ (thumbnail.isNull() ? 0 : thumbnailSkip);
const auto available = width
- padding.left()
- padding.right()
- (thumbnail.isNull() ? 0 : thumbnailSkip);
state->title->resizeToNaturalWidth(available);
state->title->moveToLeft(
left,
padding.top() + st::paymentsTitleTop);
state->description->resizeToNaturalWidth(available);
state->description->moveToLeft(
left,
(state->title->y()
+ state->title->height()
+ st::paymentsDescriptionTop));
state->seller->resizeToNaturalWidth(available);
state->seller->moveToLeft(
left,
(state->description->y()
+ state->description->height()
+ st::paymentsSellerTop));
const auto thumbnailHeight = padding.top()
+ (thumbnail.isNull()
? 0
: int(thumbnail.height() / thumbnail.devicePixelRatio()))
+ padding.bottom();
const auto height = state->seller->y()
+ state->seller->height()
+ padding.bottom();
cover->resize(width, std::max(thumbnailHeight, height));
state->thumbnail = std::move(thumbnail);
cover->update();
}, cover->lifetime());
}
void FormSummary::setupPrices(not_null<VerticalLayout*> layout) {
Settings::AddSkip(layout, st::paymentsPricesTopSkip);
const auto add = [&](
const QString &label,
int64 amount,
bool full = false) {
const auto &st = full
? st::paymentsFullPriceAmount
: st::paymentsPriceAmount;
const auto right = CreateChild<FlatLabel>(
layout.get(),
formatAmount(amount),
st);
const auto &padding = st::paymentsPricePadding;
const auto left = layout->add(
object_ptr<FlatLabel>(
layout,
label,
(full
? st::paymentsFullPriceLabel
: st::paymentsPriceLabel)),
style::margins(
padding.left(),
padding.top(),
(padding.right()
+ right->naturalWidth()
+ 2 * st.style.font->spacew),
padding.bottom()));
rpl::combine(
left->topValue(),
layout->widthValue()
) | rpl::start_with_next([=](int top, int width) {
right->moveToRight(st::paymentsPricePadding.right(), top, width);
}, right->lifetime());
};
for (const auto &price : _invoice.prices) {
inner->add(
object_ptr<Ui::FlatLabel>(
inner,
price.label + ": " + computeAmount(price.price),
st::passportFormPolicy),
st::paymentsFormPricePadding);
add(price.label, price.price);
}
const auto selected = ranges::find(
_options.list,
@ -115,44 +225,35 @@ not_null<Ui::RpWidget*> FormSummary::setupContent() {
&ShippingOption::id);
if (selected != end(_options.list)) {
for (const auto &price : selected->prices) {
inner->add(
object_ptr<Ui::FlatLabel>(
inner,
price.label + ": " + computeAmount(price.price),
st::passportFormPolicy),
st::paymentsFormPricePadding);
add(price.label, price.price);
}
}
inner->add(
object_ptr<Ui::FlatLabel>(
inner,
"Total: " + computeTotalAmount(),
st::passportFormHeader),
st::passportFormHeaderPadding);
add(tr::lng_payments_total_label(tr::now), computeTotalAmount(), true);
Settings::AddSkip(layout, st::paymentsPricesBottomSkip);
}
inner->add(
object_ptr<Ui::BoxContentDivider>(
inner,
st::passportFormDividerHeight),
{ 0, 0, 0, st::passportFormHeaderPadding.top() });
void FormSummary::setupSections(not_null<VerticalLayout*> layout) {
Settings::AddSkip(layout, st::paymentsSectionsTopSkip);
const auto method = inner->add(object_ptr<FormRow>(inner));
method->addClickHandler([=] {
_delegate->panelEditPaymentMethod();
});
method->updateContent(
tr::lng_payments_payment_method(tr::now),
(_method.ready
? _method.title
: tr::lng_payments_payment_method_ph(tr::now)),
_method.ready,
false,
anim::type::instant);
const auto add = [&](
rpl::producer<QString> title,
const QString &label,
const style::icon *icon,
Fn<void()> handler) {
Settings::AddButtonWithLabel(
layout,
std::move(title),
rpl::single(label),
st::paymentsSectionButton,
icon
)->addClickHandler(std::move(handler));
};
add(
tr::lng_payments_payment_method(),
_method.title,
&st::paymentsIconPaymentMethod,
[=] { _delegate->panelEditPaymentMethod(); });
if (_invoice.isShippingAddressRequested) {
const auto info = inner->add(object_ptr<FormRow>(inner));
info->addClickHandler([=] {
_delegate->panelEditShippingInformation();
});
auto list = QStringList();
const auto push = [&](const QString &value) {
if (!value.isEmpty()) {
@ -165,65 +266,61 @@ not_null<Ui::RpWidget*> FormSummary::setupContent() {
push(_information.shippingAddress.state);
push(_information.shippingAddress.countryIso2);
push(_information.shippingAddress.postcode);
info->updateContent(
tr::lng_payments_shipping_address(tr::now),
(list.isEmpty()
? tr::lng_payments_shipping_address_ph(tr::now)
: list.join(", ")),
!list.isEmpty(),
false,
anim::type::instant);
add(
tr::lng_payments_shipping_address(),
list.join(", "),
&st::paymentsIconShippingAddress,
[=] { _delegate->panelEditShippingInformation(); });
}
if (!_options.list.empty()) {
const auto options = inner->add(object_ptr<FormRow>(inner));
options->addClickHandler([=] {
_delegate->panelChooseShippingOption();
});
options->updateContent(
tr::lng_payments_shipping_method(tr::now),
(selected != end(_options.list)
? selected->title
: tr::lng_payments_shipping_method_ph(tr::now)),
(selected != end(_options.list)),
false,
anim::type::instant);
const auto selected = ranges::find(
_options.list,
_options.selectedId,
&ShippingOption::id);
add(
tr::lng_payments_shipping_method(),
(selected != end(_options.list)) ? selected->title : QString(),
&st::paymentsIconShippingMethod,
[=] { _delegate->panelChooseShippingOption(); });
}
if (_invoice.isNameRequested) {
const auto name = inner->add(object_ptr<FormRow>(inner));
name->addClickHandler([=] { _delegate->panelEditName(); });
name->updateContent(
tr::lng_payments_info_name(tr::now),
(_information.name.isEmpty()
? tr::lng_payments_info_name_ph(tr::now)
: _information.name),
!_information.name.isEmpty(),
false,
anim::type::instant);
add(
tr::lng_payments_info_name(),
_information.name,
&st::paymentsIconName,
[=] { _delegate->panelEditName(); });
}
if (_invoice.isEmailRequested) {
const auto email = inner->add(object_ptr<FormRow>(inner));
email->addClickHandler([=] { _delegate->panelEditEmail(); });
email->updateContent(
tr::lng_payments_info_email(tr::now),
(_information.email.isEmpty()
? tr::lng_payments_info_email_ph(tr::now)
: _information.email),
!_information.email.isEmpty(),
false,
anim::type::instant);
add(
tr::lng_payments_info_email(),
_information.email,
&st::paymentsIconEmail,
[=] { _delegate->panelEditEmail(); });
}
if (_invoice.isPhoneRequested) {
const auto phone = inner->add(object_ptr<FormRow>(inner));
phone->addClickHandler([=] { _delegate->panelEditPhone(); });
phone->updateContent(
tr::lng_payments_info_phone(tr::now),
(_information.phone.isEmpty()
? tr::lng_payments_info_phone_ph(tr::now)
: _information.phone),
!_information.phone.isEmpty(),
false,
anim::type::instant);
add(
tr::lng_payments_info_phone(),
_information.phone,
&st::paymentsIconPhone,
[=] { _delegate->panelEditPhone(); });
}
Settings::AddSkip(layout, st::paymentsSectionsTopSkip);
}
not_null<RpWidget*> FormSummary::setupContent() {
const auto inner = _scroll->setOwnedWidget(
object_ptr<VerticalLayout>(this));
_scroll->widthValue(
) | rpl::start_with_next([=](int width) {
inner->resizeToWidth(width);
}, inner->lifetime());
setupCover(inner);
Settings::AddDivider(inner);
setupPrices(inner);
Settings::AddDivider(inner);
setupSections(inner);
return inner;
}

View File

@ -15,6 +15,7 @@ namespace Ui {
class ScrollArea;
class FadeShadow;
class RoundButton;
class VerticalLayout;
} // namespace Ui
namespace Payments::Ui {
@ -33,15 +34,20 @@ public:
const ShippingOptions &options,
not_null<PanelDelegate*> delegate);
void updateThumbnail(const QImage &thumbnail);
private:
void resizeEvent(QResizeEvent *e) override;
void setupControls();
[[nodiscard]] not_null<Ui::RpWidget*> setupContent();
void setupCover(not_null<VerticalLayout*> layout);
void setupPrices(not_null<VerticalLayout*> layout);
void setupSections(not_null<VerticalLayout*> layout);
void updateControlsGeometry();
[[nodiscard]] QString computeAmount(int64 amount) const;
[[nodiscard]] QString computeTotalAmount() const;
[[nodiscard]] QString formatAmount(int64 amount) const;
[[nodiscard]] int64 computeTotalAmount() const;
const not_null<PanelDelegate*> _delegate;
Invoice _invoice;
@ -52,6 +58,7 @@ private:
object_ptr<FadeShadow> _topShadow;
object_ptr<FadeShadow> _bottomShadow;
object_ptr<RoundButton> _submit;
rpl::event_stream<QImage> _thumbnails;
};

View File

@ -52,17 +52,24 @@ void Panel::showForm(
const RequestedInformation &current,
const PaymentMethodDetails &method,
const ShippingOptions &options) {
_widget->showInner(
base::make_unique_q<FormSummary>(
_widget.get(),
invoice,
current,
method,
options,
_delegate));
auto form = base::make_unique_q<FormSummary>(
_widget.get(),
invoice,
current,
method,
options,
_delegate);
_weakFormSummary = form.get();
_widget->showInner(std::move(form));
_widget->setBackAllowed(false);
}
void Panel::updateFormThumbnail(const QImage &thumbnail) {
if (_weakFormSummary) {
_weakFormSummary->updateThumbnail(thumbnail);
}
}
void Panel::showEditInformation(
const Invoice &invoice,
const RequestedInformation &current,

View File

@ -28,6 +28,7 @@ struct RequestedInformation;
struct ShippingOptions;
enum class InformationField;
enum class CardField;
class FormSummary;
class EditInformation;
class EditCard;
struct PaymentMethodDetails;
@ -45,6 +46,7 @@ public:
const RequestedInformation &current,
const PaymentMethodDetails &method,
const ShippingOptions &options);
void updateFormThumbnail(const QImage &thumbnail);
void showEditInformation(
const Invoice &invoice,
const RequestedInformation &current,
@ -78,6 +80,7 @@ private:
const not_null<PanelDelegate*> _delegate;
std::unique_ptr<SeparatePanel> _widget;
std::unique_ptr<Webview::Window> _webview;
QPointer<FormSummary> _weakFormSummary;
QPointer<EditInformation> _weakEditInformation;
QPointer<EditCard> _weakEditCard;

View File

@ -14,7 +14,16 @@ struct LabeledPrice {
int64 price = 0;
};
struct Cover {
QString title;
QString description;
QString seller;
QImage thumbnail;
};
struct Invoice {
Cover cover;
std::vector<LabeledPrice> prices;
QString currency;