2021-03-23 12:34:34 +00:00
|
|
|
/*
|
|
|
|
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_panel.h"
|
|
|
|
|
|
|
|
#include "payments/ui/payments_form_summary.h"
|
2021-03-23 16:06:59 +00:00
|
|
|
#include "payments/ui/payments_edit_information.h"
|
2021-03-25 15:27:30 +00:00
|
|
|
#include "payments/ui/payments_edit_card.h"
|
2021-03-23 12:34:34 +00:00
|
|
|
#include "payments/ui/payments_panel_delegate.h"
|
2021-03-30 14:49:41 +00:00
|
|
|
#include "payments/ui/payments_field.h"
|
2021-03-23 12:34:34 +00:00
|
|
|
#include "ui/widgets/separate_panel.h"
|
2021-03-30 17:18:39 +00:00
|
|
|
#include "ui/widgets/checkbox.h"
|
2021-04-01 09:27:39 +00:00
|
|
|
#include "ui/wrap/fade_wrap.h"
|
2021-03-23 16:06:59 +00:00
|
|
|
#include "ui/boxes/single_choice_box.h"
|
2021-03-30 17:18:39 +00:00
|
|
|
#include "ui/text/format_values.h"
|
2021-04-02 15:18:49 +00:00
|
|
|
#include "ui/text/text_utilities.h"
|
2021-04-12 15:24:36 +00:00
|
|
|
#include "ui/effects/radial_animation.h"
|
2021-03-23 12:34:34 +00:00
|
|
|
#include "lang/lang_keys.h"
|
2021-03-25 16:58:52 +00:00
|
|
|
#include "webview/webview_embed.h"
|
2021-04-02 15:18:49 +00:00
|
|
|
#include "webview/webview_interface.h"
|
2021-03-23 12:34:34 +00:00
|
|
|
#include "styles/style_payments.h"
|
2021-03-30 17:18:39 +00:00
|
|
|
#include "styles/style_layers.h"
|
2021-03-23 12:34:34 +00:00
|
|
|
|
|
|
|
namespace Payments::Ui {
|
2021-04-12 15:24:36 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
constexpr auto kProgressDuration = crl::time(200);
|
|
|
|
constexpr auto kProgressOpacity = 0.3;
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
struct Panel::Progress {
|
|
|
|
Progress(QWidget *parent, Fn<QRect()> rect);
|
|
|
|
|
|
|
|
RpWidget widget;
|
|
|
|
InfiniteRadialAnimation animation;
|
|
|
|
Animations::Simple shownAnimation;
|
|
|
|
bool shown = true;
|
|
|
|
rpl::lifetime geometryLifetime;
|
|
|
|
};
|
|
|
|
|
2021-06-29 08:07:16 +00:00
|
|
|
struct Panel::WebviewWithLifetime {
|
|
|
|
WebviewWithLifetime(
|
|
|
|
QWidget *parent = nullptr,
|
|
|
|
Webview::WindowConfig config = Webview::WindowConfig());
|
|
|
|
|
|
|
|
Webview::Window window;
|
2021-09-30 10:53:18 +00:00
|
|
|
QPointer<RpWidget> lastHidingBox;
|
2021-06-29 08:07:16 +00:00
|
|
|
rpl::lifetime lifetime;
|
|
|
|
};
|
|
|
|
|
|
|
|
Panel::WebviewWithLifetime::WebviewWithLifetime(
|
|
|
|
QWidget *parent,
|
|
|
|
Webview::WindowConfig config)
|
|
|
|
: window(parent, std::move(config)) {
|
|
|
|
}
|
|
|
|
|
2021-04-12 15:24:36 +00:00
|
|
|
Panel::Progress::Progress(QWidget *parent, Fn<QRect()> rect)
|
|
|
|
: widget(parent)
|
|
|
|
, animation(
|
|
|
|
[=] { if (!anim::Disabled()) widget.update(rect()); },
|
2021-04-13 12:38:38 +00:00
|
|
|
st::paymentsLoading) {
|
2021-04-12 15:24:36 +00:00
|
|
|
}
|
2021-03-23 12:34:34 +00:00
|
|
|
|
|
|
|
Panel::Panel(not_null<PanelDelegate*> delegate)
|
|
|
|
: _delegate(delegate)
|
|
|
|
, _widget(std::make_unique<SeparatePanel>()) {
|
2021-04-02 15:18:49 +00:00
|
|
|
_widget->setInnerSize(st::paymentsPanelSize);
|
2021-03-24 11:30:01 +00:00
|
|
|
_widget->setWindowFlag(Qt::WindowStaysOnTopHint, false);
|
2021-03-23 12:34:34 +00:00
|
|
|
|
|
|
|
_widget->closeRequests(
|
|
|
|
) | rpl::start_with_next([=] {
|
|
|
|
_delegate->panelRequestClose();
|
|
|
|
}, _widget->lifetime());
|
|
|
|
|
|
|
|
_widget->closeEvents(
|
|
|
|
) | rpl::start_with_next([=] {
|
|
|
|
_delegate->panelCloseSure();
|
|
|
|
}, _widget->lifetime());
|
|
|
|
}
|
|
|
|
|
2021-03-25 16:58:52 +00:00
|
|
|
Panel::~Panel() {
|
2021-06-29 08:07:16 +00:00
|
|
|
_webview = nullptr;
|
2021-04-12 15:24:36 +00:00
|
|
|
_progress = nullptr;
|
2021-03-25 16:58:52 +00:00
|
|
|
_widget = nullptr;
|
|
|
|
}
|
2021-03-23 12:34:34 +00:00
|
|
|
|
|
|
|
void Panel::requestActivate() {
|
|
|
|
_widget->showAndActivate();
|
|
|
|
}
|
|
|
|
|
2021-04-12 15:24:36 +00:00
|
|
|
void Panel::toggleProgress(bool shown) {
|
|
|
|
if (!_progress) {
|
|
|
|
if (!shown) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_progress = std::make_unique<Progress>(
|
|
|
|
_widget.get(),
|
|
|
|
[=] { return progressRect(); });
|
|
|
|
_progress->widget.paintRequest(
|
|
|
|
) | rpl::start_with_next([=](QRect clip) {
|
|
|
|
auto p = QPainter(&_progress->widget);
|
|
|
|
p.setOpacity(
|
|
|
|
_progress->shownAnimation.value(_progress->shown ? 1. : 0.));
|
2021-04-13 12:38:38 +00:00
|
|
|
auto thickness = st::paymentsLoading.thickness;
|
2021-04-12 15:24:36 +00:00
|
|
|
if (progressWithBackground()) {
|
|
|
|
auto color = st::windowBg->c;
|
|
|
|
color.setAlphaF(kProgressOpacity);
|
|
|
|
p.fillRect(clip, color);
|
|
|
|
}
|
|
|
|
const auto rect = progressRect().marginsRemoved(
|
|
|
|
{ thickness, thickness, thickness, thickness });
|
|
|
|
InfiniteRadialAnimation::Draw(
|
|
|
|
p,
|
|
|
|
_progress->animation.computeState(),
|
|
|
|
rect.topLeft(),
|
|
|
|
rect.size() - QSize(),
|
|
|
|
_progress->widget.width(),
|
2021-04-13 12:38:38 +00:00
|
|
|
st::paymentsLoading.color,
|
2021-04-12 15:24:36 +00:00
|
|
|
thickness);
|
|
|
|
}, _progress->widget.lifetime());
|
|
|
|
_progress->widget.show();
|
|
|
|
_progress->animation.start();
|
|
|
|
} else if (_progress->shown == shown) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const auto callback = [=] {
|
|
|
|
if (!_progress->shownAnimation.animating() && !_progress->shown) {
|
|
|
|
_progress = nullptr;
|
|
|
|
} else {
|
|
|
|
_progress->widget.update();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
_progress->shown = shown;
|
|
|
|
_progress->shownAnimation.start(
|
|
|
|
callback,
|
|
|
|
shown ? 0. : 1.,
|
|
|
|
shown ? 1. : 0.,
|
|
|
|
kProgressDuration);
|
|
|
|
if (shown) {
|
|
|
|
setupProgressGeometry();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Panel::progressWithBackground() const {
|
|
|
|
return (_progress->widget.width() == _widget->innerGeometry().width());
|
|
|
|
}
|
|
|
|
|
|
|
|
QRect Panel::progressRect() const {
|
|
|
|
const auto rect = _progress->widget.rect();
|
|
|
|
if (!progressWithBackground()) {
|
|
|
|
return rect;
|
|
|
|
}
|
|
|
|
const auto size = st::defaultBoxButton.height;
|
|
|
|
return QRect(
|
|
|
|
rect.x() + (rect.width() - size) / 2,
|
|
|
|
rect.y() + (rect.height() - size) / 2,
|
|
|
|
size,
|
|
|
|
size);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::setupProgressGeometry() {
|
|
|
|
if (!_progress || !_progress->shown) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_progress->geometryLifetime.destroy();
|
|
|
|
if (_webviewBottom) {
|
|
|
|
_webviewBottom->geometryValue(
|
|
|
|
) | rpl::start_with_next([=](QRect bottom) {
|
|
|
|
const auto height = bottom.height();
|
2021-04-13 12:38:38 +00:00
|
|
|
const auto size = st::paymentsLoading.size;
|
|
|
|
const auto skip = (height - size.height()) / 2;
|
2021-04-12 15:24:36 +00:00
|
|
|
const auto inner = _widget->innerGeometry();
|
|
|
|
const auto right = inner.x() + inner.width();
|
|
|
|
const auto top = inner.y() + inner.height() - height;
|
|
|
|
// This doesn't work, because first we get the correct bottom
|
|
|
|
// geometry and after that we get the previous event (which
|
|
|
|
// triggered the 'fire' of correct geometry before getting here).
|
|
|
|
//const auto right = bottom.x() + bottom.width();
|
|
|
|
//const auto top = bottom.y();
|
2021-04-13 12:38:38 +00:00
|
|
|
_progress->widget.setGeometry(QRect{
|
|
|
|
QPoint(right - skip - size.width(), top + skip),
|
|
|
|
size });
|
2021-04-12 15:24:36 +00:00
|
|
|
}, _progress->geometryLifetime);
|
|
|
|
} else if (_weakFormSummary) {
|
|
|
|
_weakFormSummary->sizeValue(
|
|
|
|
) | rpl::start_with_next([=](QSize form) {
|
|
|
|
const auto full = _widget->innerGeometry();
|
|
|
|
const auto size = st::defaultBoxButton.height;
|
|
|
|
const auto inner = _weakFormSummary->contentHeight();
|
|
|
|
const auto left = full.height() - inner;
|
|
|
|
if (left >= 2 * size) {
|
|
|
|
_progress->widget.setGeometry(
|
|
|
|
full.x() + (full.width() - size) / 2,
|
|
|
|
full.y() + inner + (left - size) / 2,
|
|
|
|
size,
|
|
|
|
size);
|
|
|
|
} else {
|
|
|
|
_progress->widget.setGeometry(full);
|
|
|
|
}
|
|
|
|
}, _progress->geometryLifetime);
|
|
|
|
} else if (_weakEditInformation) {
|
|
|
|
_weakEditInformation->geometryValue(
|
|
|
|
) | rpl::start_with_next([=] {
|
|
|
|
_progress->widget.setGeometry(_widget->innerGeometry());
|
|
|
|
}, _progress->geometryLifetime);
|
|
|
|
} else if (_weakEditCard) {
|
|
|
|
_weakEditCard->geometryValue(
|
|
|
|
) | rpl::start_with_next([=] {
|
|
|
|
_progress->widget.setGeometry(_widget->innerGeometry());
|
|
|
|
}, _progress->geometryLifetime);
|
|
|
|
}
|
|
|
|
_progress->widget.show();
|
|
|
|
_progress->widget.raise();
|
|
|
|
if (_progress->shown) {
|
|
|
|
_progress->widget.setFocus();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-23 16:06:59 +00:00
|
|
|
void Panel::showForm(
|
|
|
|
const Invoice &invoice,
|
|
|
|
const RequestedInformation ¤t,
|
2021-03-25 16:58:52 +00:00
|
|
|
const PaymentMethodDetails &method,
|
2021-03-23 16:06:59 +00:00
|
|
|
const ShippingOptions &options) {
|
2021-04-02 15:18:49 +00:00
|
|
|
if (invoice && !method.ready && !method.native.supported) {
|
|
|
|
const auto available = Webview::Availability();
|
|
|
|
if (available.error != Webview::Available::Error::None) {
|
|
|
|
showWebviewError(
|
|
|
|
tr::lng_payments_webview_no_use(tr::now),
|
|
|
|
available);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-01 15:22:01 +00:00
|
|
|
_testMode = invoice.isTest;
|
|
|
|
setTitle(invoice.receipt
|
2021-03-30 06:01:31 +00:00
|
|
|
? tr::lng_payments_receipt_title()
|
|
|
|
: tr::lng_payments_checkout_title());
|
2021-03-26 13:05:31 +00:00
|
|
|
auto form = base::make_unique_q<FormSummary>(
|
|
|
|
_widget.get(),
|
|
|
|
invoice,
|
|
|
|
current,
|
|
|
|
method,
|
|
|
|
options,
|
2021-04-02 11:46:48 +00:00
|
|
|
_delegate,
|
|
|
|
_formScrollTop.current());
|
2021-03-26 13:05:31 +00:00
|
|
|
_weakFormSummary = form.get();
|
|
|
|
_widget->showInner(std::move(form));
|
2021-03-24 11:30:01 +00:00
|
|
|
_widget->setBackAllowed(false);
|
2021-04-02 11:46:48 +00:00
|
|
|
_formScrollTop = _weakFormSummary->scrollTopValue();
|
2021-04-12 15:24:36 +00:00
|
|
|
setupProgressGeometry();
|
2021-03-23 16:06:59 +00:00
|
|
|
}
|
|
|
|
|
2021-03-26 13:05:31 +00:00
|
|
|
void Panel::updateFormThumbnail(const QImage &thumbnail) {
|
|
|
|
if (_weakFormSummary) {
|
|
|
|
_weakFormSummary->updateThumbnail(thumbnail);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-23 16:06:59 +00:00
|
|
|
void Panel::showEditInformation(
|
|
|
|
const Invoice &invoice,
|
|
|
|
const RequestedInformation ¤t,
|
2021-03-25 15:27:30 +00:00
|
|
|
InformationField field) {
|
2021-04-01 15:22:01 +00:00
|
|
|
setTitle(tr::lng_payments_shipping_address_title());
|
2021-03-24 11:30:01 +00:00
|
|
|
auto edit = base::make_unique_q<EditInformation>(
|
2021-03-23 16:06:59 +00:00
|
|
|
_widget.get(),
|
|
|
|
invoice,
|
|
|
|
current,
|
|
|
|
field,
|
2021-03-24 11:30:01 +00:00
|
|
|
_delegate);
|
2021-03-25 15:27:30 +00:00
|
|
|
_weakEditInformation = edit.get();
|
2021-03-24 11:30:01 +00:00
|
|
|
_widget->showInner(std::move(edit));
|
|
|
|
_widget->setBackAllowed(true);
|
2021-03-26 15:23:12 +00:00
|
|
|
_weakEditInformation->setFocusFast(field);
|
2021-04-12 15:24:36 +00:00
|
|
|
setupProgressGeometry();
|
2021-03-24 11:30:01 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 15:27:30 +00:00
|
|
|
void Panel::showInformationError(
|
2021-03-24 11:30:01 +00:00
|
|
|
const Invoice &invoice,
|
|
|
|
const RequestedInformation ¤t,
|
2021-03-25 15:27:30 +00:00
|
|
|
InformationField field) {
|
|
|
|
if (_weakEditInformation) {
|
|
|
|
_weakEditInformation->showError(field);
|
2021-03-24 11:30:01 +00:00
|
|
|
} else {
|
|
|
|
showEditInformation(invoice, current, field);
|
2021-03-25 15:27:30 +00:00
|
|
|
if (_weakEditInformation
|
|
|
|
&& field == InformationField::ShippingCountry) {
|
|
|
|
_weakEditInformation->showError(field);
|
2021-03-24 11:30:01 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-23 16:06:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::chooseShippingOption(const ShippingOptions &options) {
|
2021-04-02 12:10:23 +00:00
|
|
|
showBox(Box([=](not_null<GenericBox*> box) {
|
2021-03-23 16:06:59 +00:00
|
|
|
const auto i = ranges::find(
|
|
|
|
options.list,
|
|
|
|
options.selectedId,
|
|
|
|
&ShippingOption::id);
|
2021-03-30 17:18:39 +00:00
|
|
|
const auto index = (i != end(options.list))
|
2021-04-06 14:53:03 +00:00
|
|
|
? int(i - begin(options.list))
|
2021-03-30 17:18:39 +00:00
|
|
|
: -1;
|
2021-04-02 12:10:23 +00:00
|
|
|
const auto group = std::make_shared<RadiobuttonGroup>(index);
|
2021-03-30 17:18:39 +00:00
|
|
|
|
|
|
|
const auto layout = box->verticalLayout();
|
|
|
|
auto counter = 0;
|
|
|
|
for (const auto &option : options.list) {
|
|
|
|
const auto index = counter++;
|
|
|
|
const auto button = layout->add(
|
2021-04-02 12:10:23 +00:00
|
|
|
object_ptr<Radiobutton>(
|
2021-03-30 17:18:39 +00:00
|
|
|
layout,
|
|
|
|
group,
|
|
|
|
index,
|
|
|
|
QString(),
|
|
|
|
st::defaultBoxCheckbox,
|
|
|
|
st::defaultRadio),
|
|
|
|
st::paymentsShippingMargin);
|
|
|
|
const auto label = CreateChild<FlatLabel>(
|
|
|
|
layout.get(),
|
|
|
|
option.title,
|
|
|
|
st::paymentsShippingLabel);
|
|
|
|
const auto total = ranges::accumulate(
|
|
|
|
option.prices,
|
|
|
|
int64(0),
|
|
|
|
std::plus<>(),
|
|
|
|
&LabeledPrice::price);
|
|
|
|
const auto price = CreateChild<FlatLabel>(
|
|
|
|
layout.get(),
|
|
|
|
FillAmountAndCurrency(total, options.currency),
|
|
|
|
st::paymentsShippingPrice);
|
|
|
|
const auto area = CreateChild<AbstractButton>(layout.get());
|
|
|
|
area->setClickedCallback([=] { group->setValue(index); });
|
|
|
|
button->geometryValue(
|
|
|
|
) | rpl::start_with_next([=](QRect geometry) {
|
|
|
|
label->move(
|
|
|
|
geometry.topLeft() + st::paymentsShippingLabelPosition);
|
|
|
|
price->move(
|
|
|
|
geometry.topLeft() + st::paymentsShippingPricePosition);
|
|
|
|
const auto right = geometry.x()
|
|
|
|
+ st::paymentsShippingLabelPosition.x();
|
|
|
|
area->setGeometry(
|
|
|
|
right,
|
|
|
|
geometry.y(),
|
|
|
|
std::max(
|
|
|
|
label->x() + label->width() - right,
|
|
|
|
price->x() + price->width() - right),
|
|
|
|
price->y() + price->height() - geometry.y());
|
|
|
|
}, button->lifetime());
|
|
|
|
}
|
|
|
|
|
|
|
|
box->setTitle(tr::lng_payments_shipping_method());
|
|
|
|
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
|
|
|
group->setChangedCallback([=](int index) {
|
|
|
|
if (index >= 0) {
|
|
|
|
_delegate->panelChangeShippingOption(
|
|
|
|
options.list[index].id);
|
|
|
|
box->closeBox();
|
|
|
|
}
|
2021-03-23 16:06:59 +00:00
|
|
|
});
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2021-03-30 14:49:41 +00:00
|
|
|
void Panel::chooseTips(const Invoice &invoice) {
|
|
|
|
const auto max = invoice.tipsMax;
|
|
|
|
const auto now = invoice.tipsSelected;
|
2021-04-01 09:27:39 +00:00
|
|
|
const auto currency = invoice.currency;
|
2021-04-02 12:10:23 +00:00
|
|
|
showBox(Box([=](not_null<GenericBox*> box) {
|
2021-04-01 09:27:39 +00:00
|
|
|
box->setTitle(tr::lng_payments_tips_box_title());
|
2021-03-30 14:49:41 +00:00
|
|
|
const auto row = box->lifetime().make_state<Field>(
|
|
|
|
box,
|
|
|
|
FieldConfig{
|
2021-04-01 09:27:39 +00:00
|
|
|
.type = FieldType::Money,
|
2021-03-30 14:49:41 +00:00
|
|
|
.value = QString::number(now),
|
2021-04-02 16:47:46 +00:00
|
|
|
.currency = currency,
|
2021-03-30 14:49:41 +00:00
|
|
|
});
|
|
|
|
box->setFocusCallback([=] {
|
|
|
|
row->setFocusFast();
|
|
|
|
});
|
|
|
|
box->addRow(row->ownedWidget());
|
2021-04-01 09:27:39 +00:00
|
|
|
const auto errorWrap = box->addRow(
|
|
|
|
object_ptr<FadeWrap<FlatLabel>>(
|
|
|
|
box,
|
|
|
|
object_ptr<FlatLabel>(
|
|
|
|
box,
|
|
|
|
tr::lng_payments_tips_max(
|
|
|
|
lt_amount,
|
|
|
|
rpl::single(FillAmountAndCurrency(max, currency))),
|
|
|
|
st::paymentTipsErrorLabel)),
|
|
|
|
st::paymentTipsErrorPadding);
|
|
|
|
errorWrap->hide(anim::type::instant);
|
2021-04-02 11:46:48 +00:00
|
|
|
const auto submit = [=] {
|
2021-03-30 14:49:41 +00:00
|
|
|
const auto value = row->value().toLongLong();
|
2021-04-01 11:58:39 +00:00
|
|
|
if (value > max) {
|
2021-03-30 14:49:41 +00:00
|
|
|
row->showError();
|
2021-04-01 09:27:39 +00:00
|
|
|
errorWrap->show(anim::type::normal);
|
2021-03-30 14:49:41 +00:00
|
|
|
} else {
|
|
|
|
_delegate->panelChangeTips(value);
|
|
|
|
box->closeBox();
|
|
|
|
}
|
2021-04-02 11:46:48 +00:00
|
|
|
};
|
|
|
|
row->submitted(
|
|
|
|
) | rpl::start_with_next(submit, box->lifetime());
|
|
|
|
box->addButton(tr::lng_settings_save(), submit);
|
2021-03-30 14:49:41 +00:00
|
|
|
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2021-03-25 16:58:52 +00:00
|
|
|
void Panel::showEditPaymentMethod(const PaymentMethodDetails &method) {
|
2021-04-01 14:39:44 +00:00
|
|
|
auto bottomText = method.canSaveInformation
|
|
|
|
? rpl::producer<QString>()
|
|
|
|
: tr::lng_payments_processed_by(
|
|
|
|
lt_provider,
|
|
|
|
rpl::single(method.provider));
|
2021-04-01 15:22:01 +00:00
|
|
|
setTitle(tr::lng_payments_card_title());
|
2021-03-25 16:58:52 +00:00
|
|
|
if (method.native.supported) {
|
|
|
|
showEditCard(method.native, CardField::Number);
|
2021-04-01 14:39:44 +00:00
|
|
|
} else if (!showWebview(method.url, true, std::move(bottomText))) {
|
2021-04-02 15:18:49 +00:00
|
|
|
const auto available = Webview::Availability();
|
|
|
|
if (available.error != Webview::Available::Error::None) {
|
|
|
|
showWebviewError(
|
|
|
|
tr::lng_payments_webview_no_card(tr::now),
|
|
|
|
available);
|
|
|
|
} else {
|
|
|
|
showCriticalError({ "Error: Could not initialize WebView." });
|
|
|
|
}
|
|
|
|
_widget->setBackAllowed(true);
|
2021-03-31 17:15:49 +00:00
|
|
|
} else if (method.canSaveInformation) {
|
|
|
|
const auto &padding = st::paymentsPanelPadding;
|
|
|
|
_saveWebviewInformation = CreateChild<Checkbox>(
|
|
|
|
_webviewBottom.get(),
|
|
|
|
tr::lng_payments_save_information(tr::now),
|
|
|
|
false);
|
|
|
|
const auto height = padding.top()
|
|
|
|
+ _saveWebviewInformation->heightNoMargins()
|
|
|
|
+ padding.bottom();
|
|
|
|
_saveWebviewInformation->moveToLeft(padding.right(), padding.top());
|
|
|
|
_saveWebviewInformation->show();
|
|
|
|
_webviewBottom->resize(_webviewBottom->width(), height);
|
2021-03-25 16:58:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-13 10:05:31 +00:00
|
|
|
void Panel::showWebviewProgress() {
|
2021-04-13 11:30:37 +00:00
|
|
|
if (_webviewProgress && _progress && _progress->shown) {
|
2021-04-13 10:05:31 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
_webviewProgress = true;
|
|
|
|
toggleProgress(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::hideWebviewProgress() {
|
|
|
|
if (!_webviewProgress) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_webviewProgress = false;
|
|
|
|
toggleProgress(false);
|
|
|
|
}
|
|
|
|
|
2021-04-01 14:39:44 +00:00
|
|
|
bool Panel::showWebview(
|
|
|
|
const QString &url,
|
|
|
|
bool allowBack,
|
|
|
|
rpl::producer<QString> bottomText) {
|
2021-03-25 16:58:52 +00:00
|
|
|
if (!_webview && !createWebview()) {
|
|
|
|
return false;
|
|
|
|
}
|
2021-04-13 10:05:31 +00:00
|
|
|
showWebviewProgress();
|
2021-04-12 08:50:31 +00:00
|
|
|
_widget->destroyLayer();
|
2021-06-29 08:07:16 +00:00
|
|
|
_webview->window.navigate(url);
|
2021-03-25 16:58:52 +00:00
|
|
|
_widget->setBackAllowed(allowBack);
|
2021-04-01 14:39:44 +00:00
|
|
|
if (bottomText) {
|
|
|
|
const auto &padding = st::paymentsPanelPadding;
|
|
|
|
const auto label = CreateChild<FlatLabel>(
|
|
|
|
_webviewBottom.get(),
|
|
|
|
std::move(bottomText),
|
|
|
|
st::paymentsWebviewBottom);
|
|
|
|
const auto height = padding.top()
|
|
|
|
+ label->heightNoMargins()
|
|
|
|
+ padding.bottom();
|
|
|
|
rpl::combine(
|
|
|
|
_webviewBottom->widthValue(),
|
|
|
|
label->widthValue()
|
|
|
|
) | rpl::start_with_next([=](int outerWidth, int width) {
|
|
|
|
label->move((outerWidth - width) / 2, padding.top());
|
|
|
|
}, label->lifetime());
|
|
|
|
label->show();
|
|
|
|
_webviewBottom->resize(_webviewBottom->width(), height);
|
|
|
|
}
|
2021-03-25 16:58:52 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Panel::createWebview() {
|
|
|
|
auto container = base::make_unique_q<RpWidget>(_widget.get());
|
|
|
|
|
2021-03-31 17:15:49 +00:00
|
|
|
_webviewBottom = std::make_unique<RpWidget>(_widget.get());
|
|
|
|
const auto bottom = _webviewBottom.get();
|
|
|
|
bottom->show();
|
|
|
|
|
|
|
|
bottom->heightValue(
|
|
|
|
) | rpl::start_with_next([=, raw = container.get()](int height) {
|
|
|
|
const auto inner = _widget->innerGeometry();
|
|
|
|
bottom->move(inner.x(), inner.y() + inner.height() - height);
|
|
|
|
raw->resize(inner.width(), inner.height() - height);
|
|
|
|
bottom->resizeToWidth(inner.width());
|
|
|
|
}, bottom->lifetime());
|
2021-03-25 16:58:52 +00:00
|
|
|
container->show();
|
2021-03-25 15:27:30 +00:00
|
|
|
|
2021-06-29 08:07:16 +00:00
|
|
|
_webview = std::make_unique<WebviewWithLifetime>(
|
2021-03-25 16:58:52 +00:00
|
|
|
container.get(),
|
|
|
|
Webview::WindowConfig{
|
|
|
|
.userDataPath = _delegate->panelWebviewDataPath(),
|
|
|
|
});
|
2021-06-29 08:07:16 +00:00
|
|
|
const auto raw = &_webview->window;
|
2021-03-25 16:58:52 +00:00
|
|
|
QObject::connect(container.get(), &QObject::destroyed, [=] {
|
2021-06-29 08:07:16 +00:00
|
|
|
if (_webview && &_webview->window == raw) {
|
2021-03-25 16:58:52 +00:00
|
|
|
_webview = nullptr;
|
2021-04-13 11:30:37 +00:00
|
|
|
if (_webviewProgress) {
|
|
|
|
hideWebviewProgress();
|
2021-04-13 12:52:40 +00:00
|
|
|
if (_progress && !_progress->shown) {
|
2021-04-13 11:30:37 +00:00
|
|
|
_progress = nullptr;
|
|
|
|
}
|
|
|
|
}
|
2021-03-25 16:58:52 +00:00
|
|
|
}
|
2021-03-31 17:15:49 +00:00
|
|
|
if (_webviewBottom.get() == bottom) {
|
|
|
|
_webviewBottom = nullptr;
|
|
|
|
}
|
2021-03-25 16:58:52 +00:00
|
|
|
});
|
|
|
|
if (!raw->widget()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
container->geometryValue(
|
|
|
|
) | rpl::start_with_next([=](QRect geometry) {
|
|
|
|
raw->widget()->setGeometry(geometry);
|
2021-06-29 08:07:16 +00:00
|
|
|
}, _webview->lifetime);
|
2021-03-25 16:58:52 +00:00
|
|
|
|
|
|
|
raw->setMessageHandler([=](const QJsonDocument &message) {
|
2021-03-31 17:15:49 +00:00
|
|
|
const auto save = _saveWebviewInformation
|
|
|
|
&& _saveWebviewInformation->checked();
|
|
|
|
_delegate->panelWebviewMessage(message, save);
|
2021-03-25 16:58:52 +00:00
|
|
|
});
|
|
|
|
|
2021-04-12 15:24:36 +00:00
|
|
|
raw->setNavigationStartHandler([=](const QString &uri) {
|
|
|
|
if (!_delegate->panelWebviewNavigationAttempt(uri)) {
|
|
|
|
return false;
|
|
|
|
}
|
2021-04-13 10:05:31 +00:00
|
|
|
showWebviewProgress();
|
2021-04-12 15:24:36 +00:00
|
|
|
return true;
|
|
|
|
});
|
|
|
|
raw->setNavigationDoneHandler([=](bool success) {
|
2021-04-13 10:05:31 +00:00
|
|
|
hideWebviewProgress();
|
2021-03-25 16:58:52 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
raw->init(R"(
|
|
|
|
window.TelegramWebviewProxy = {
|
|
|
|
postEvent: function(eventType, eventData) {
|
|
|
|
if (window.external && window.external.invoke) {
|
|
|
|
window.external.invoke(JSON.stringify([eventType, eventData]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};)");
|
|
|
|
|
|
|
|
_widget->showInner(std::move(container));
|
2021-04-12 15:24:36 +00:00
|
|
|
|
|
|
|
setupProgressGeometry();
|
|
|
|
|
2021-03-25 16:58:52 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::choosePaymentMethod(const PaymentMethodDetails &method) {
|
|
|
|
if (!method.ready) {
|
|
|
|
showEditPaymentMethod(method);
|
2021-03-25 15:27:30 +00:00
|
|
|
return;
|
|
|
|
}
|
2021-04-02 12:10:23 +00:00
|
|
|
showBox(Box([=](not_null<GenericBox*> box) {
|
2021-03-25 15:27:30 +00:00
|
|
|
const auto save = [=](int option) {
|
|
|
|
if (option) {
|
2021-03-25 16:58:52 +00:00
|
|
|
showEditPaymentMethod(method);
|
2021-03-25 15:27:30 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
SingleChoiceBox(box, {
|
|
|
|
.title = tr::lng_payments_payment_method(),
|
2021-03-25 16:58:52 +00:00
|
|
|
.options = { method.title, tr::lng_payments_new_card(tr::now) },
|
2021-03-25 15:27:30 +00:00
|
|
|
.initialSelection = 0,
|
|
|
|
.callback = save,
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2021-03-31 17:15:49 +00:00
|
|
|
void Panel::askSetPassword() {
|
2021-04-02 12:10:23 +00:00
|
|
|
showBox(Box([=](not_null<GenericBox*> box) {
|
2021-03-31 17:15:49 +00:00
|
|
|
box->addRow(
|
2021-04-02 12:10:23 +00:00
|
|
|
object_ptr<FlatLabel>(
|
2021-03-31 17:15:49 +00:00
|
|
|
box.get(),
|
|
|
|
tr::lng_payments_need_password(),
|
|
|
|
st::boxLabel),
|
|
|
|
st::boxPadding);
|
|
|
|
box->addButton(tr::lng_continue(), [=] {
|
|
|
|
_delegate->panelSetPassword();
|
|
|
|
box->closeBox();
|
|
|
|
});
|
|
|
|
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2021-04-02 12:10:23 +00:00
|
|
|
void Panel::showCloseConfirm() {
|
|
|
|
showBox(Box([=](not_null<GenericBox*> box) {
|
|
|
|
box->addRow(
|
|
|
|
object_ptr<FlatLabel>(
|
|
|
|
box.get(),
|
|
|
|
tr::lng_payments_sure_close(),
|
|
|
|
st::boxLabel),
|
|
|
|
st::boxPadding);
|
|
|
|
box->addButton(tr::lng_close(), [=] {
|
|
|
|
_delegate->panelCloseSure();
|
|
|
|
});
|
|
|
|
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2021-04-02 12:59:54 +00:00
|
|
|
void Panel::showWarning(const QString &bot, const QString &provider) {
|
|
|
|
showBox(Box([=](not_null<GenericBox*> box) {
|
|
|
|
box->setTitle(tr::lng_payments_warning_title());
|
|
|
|
box->addRow(object_ptr<FlatLabel>(
|
|
|
|
box.get(),
|
|
|
|
tr::lng_payments_warning_body(
|
|
|
|
lt_bot1,
|
|
|
|
rpl::single(bot),
|
|
|
|
lt_provider,
|
|
|
|
rpl::single(provider),
|
|
|
|
lt_bot2,
|
|
|
|
rpl::single(bot),
|
|
|
|
lt_bot3,
|
|
|
|
rpl::single(bot)),
|
|
|
|
st::boxLabel));
|
|
|
|
box->addButton(tr::lng_continue(), [=] {
|
|
|
|
_delegate->panelTrustAndSubmit();
|
|
|
|
box->closeBox();
|
|
|
|
});
|
|
|
|
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2021-03-25 15:27:30 +00:00
|
|
|
void Panel::showEditCard(
|
2021-03-25 16:58:52 +00:00
|
|
|
const NativeMethodDetails &native,
|
2021-03-25 15:27:30 +00:00
|
|
|
CardField field) {
|
2021-03-25 16:58:52 +00:00
|
|
|
Expects(native.supported);
|
|
|
|
|
2021-03-25 15:27:30 +00:00
|
|
|
auto edit = base::make_unique_q<EditCard>(
|
|
|
|
_widget.get(),
|
|
|
|
native,
|
|
|
|
field,
|
|
|
|
_delegate);
|
|
|
|
_weakEditCard = edit.get();
|
|
|
|
_widget->showInner(std::move(edit));
|
|
|
|
_widget->setBackAllowed(true);
|
2021-03-26 15:23:12 +00:00
|
|
|
_weakEditCard->setFocusFast(field);
|
2021-04-12 15:24:36 +00:00
|
|
|
setupProgressGeometry();
|
2021-03-25 15:27:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::showCardError(
|
2021-03-25 16:58:52 +00:00
|
|
|
const NativeMethodDetails &native,
|
2021-03-25 15:27:30 +00:00
|
|
|
CardField field) {
|
|
|
|
if (_weakEditCard) {
|
|
|
|
_weakEditCard->showError(field);
|
|
|
|
} else {
|
2021-03-26 15:23:12 +00:00
|
|
|
// We cancelled card edit already.
|
|
|
|
//showEditCard(native, field);
|
|
|
|
//if (_weakEditCard
|
|
|
|
// && field == CardField::AddressCountry) {
|
|
|
|
// _weakEditCard->showError(field);
|
|
|
|
//}
|
2021-03-25 15:27:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-01 15:22:01 +00:00
|
|
|
void Panel::setTitle(rpl::producer<QString> title) {
|
|
|
|
using namespace rpl::mappers;
|
|
|
|
if (_testMode) {
|
|
|
|
_widget->setTitle(std::move(title) | rpl::map(_1 + " (Test)"));
|
|
|
|
} else {
|
|
|
|
_widget->setTitle(std::move(title));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-24 11:30:01 +00:00
|
|
|
rpl::producer<> Panel::backRequests() const {
|
|
|
|
return _widget->backRequests();
|
|
|
|
}
|
|
|
|
|
2021-04-02 12:10:23 +00:00
|
|
|
void Panel::showBox(object_ptr<BoxContent> box) {
|
2021-09-30 10:53:18 +00:00
|
|
|
if (const auto widget = _webview ? _webview->window.widget() : nullptr) {
|
|
|
|
const auto hideNow = !widget->isHidden();
|
|
|
|
if (hideNow || _webview->lastHidingBox) {
|
|
|
|
const auto raw = _webview->lastHidingBox = box.data();
|
|
|
|
box->boxClosing(
|
|
|
|
) | rpl::start_with_next([=] {
|
|
|
|
const auto widget = _webview
|
|
|
|
? _webview->window.widget()
|
|
|
|
: nullptr;
|
|
|
|
if (widget
|
|
|
|
&& widget->isHidden()
|
|
|
|
&& _webview->lastHidingBox == raw) {
|
|
|
|
widget->show();
|
|
|
|
}
|
|
|
|
}, _webview->lifetime);
|
|
|
|
if (hideNow) {
|
|
|
|
widget->hide();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-03-23 16:06:59 +00:00
|
|
|
_widget->showBox(
|
|
|
|
std::move(box),
|
2021-04-02 12:10:23 +00:00
|
|
|
LayerOption::KeepOther,
|
2021-03-23 16:06:59 +00:00
|
|
|
anim::type::normal);
|
|
|
|
}
|
|
|
|
|
2021-03-24 11:30:01 +00:00
|
|
|
void Panel::showToast(const TextWithEntities &text) {
|
2021-03-23 16:06:59 +00:00
|
|
|
_widget->showToast(text);
|
2021-03-23 12:34:34 +00:00
|
|
|
}
|
|
|
|
|
2021-04-02 15:18:49 +00:00
|
|
|
void Panel::showCriticalError(const TextWithEntities &text) {
|
2021-04-13 11:30:37 +00:00
|
|
|
_progress = nullptr;
|
|
|
|
_webviewProgress = false;
|
2021-04-02 15:18:49 +00:00
|
|
|
if (!_weakFormSummary || !_weakFormSummary->showCriticalError(text)) {
|
|
|
|
auto error = base::make_unique_q<PaddingWrap<FlatLabel>>(
|
|
|
|
_widget.get(),
|
|
|
|
object_ptr<FlatLabel>(
|
|
|
|
_widget.get(),
|
|
|
|
rpl::single(text),
|
|
|
|
st::paymentsCriticalError),
|
|
|
|
st::paymentsCriticalErrorPadding);
|
|
|
|
error->entity()->setClickHandlerFilter([=](
|
|
|
|
const ClickHandlerPtr &handler,
|
|
|
|
Qt::MouseButton) {
|
|
|
|
const auto entity = handler->getTextEntity();
|
|
|
|
if (entity.type != EntityType::CustomUrl) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
_delegate->panelOpenUrl(entity.data);
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
_widget->showInner(std::move(error));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Panel::showWebviewError(
|
|
|
|
const QString &text,
|
|
|
|
const Webview::Available &information) {
|
|
|
|
using Error = Webview::Available::Error;
|
|
|
|
Expects(information.error != Error::None);
|
|
|
|
|
|
|
|
auto rich = TextWithEntities{ text };
|
|
|
|
rich.append("\n\n");
|
|
|
|
switch (information.error) {
|
|
|
|
case Error::NoWebview2: {
|
|
|
|
const auto command = QString(QChar(TextCommand));
|
|
|
|
const auto text = tr::lng_payments_webview_install_edge(
|
|
|
|
tr::now,
|
|
|
|
lt_link,
|
|
|
|
command);
|
|
|
|
const auto parts = text.split(command);
|
|
|
|
rich.append(parts.value(0))
|
|
|
|
.append(Text::Link(
|
|
|
|
"Microsoft Edge WebView2 Runtime",
|
|
|
|
"https://go.microsoft.com/fwlink/p/?LinkId=2124703"))
|
|
|
|
.append(parts.value(1));
|
|
|
|
} break;
|
|
|
|
case Error::NoGtkOrWebkit2Gtk:
|
|
|
|
rich.append(tr::lng_payments_webview_install_webkit(tr::now));
|
|
|
|
break;
|
|
|
|
case Error::MutterWM:
|
|
|
|
rich.append(tr::lng_payments_webview_switch_mutter(tr::now));
|
|
|
|
break;
|
|
|
|
case Error::Wayland:
|
|
|
|
rich.append(tr::lng_payments_webview_switch_wayland(tr::now));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
rich.append(QString::fromStdString(information.details));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
showCriticalError(rich);
|
|
|
|
}
|
|
|
|
|
2021-03-24 11:30:01 +00:00
|
|
|
rpl::lifetime &Panel::lifetime() {
|
|
|
|
return _widget->lifetime();
|
|
|
|
}
|
|
|
|
|
2021-03-23 12:34:34 +00:00
|
|
|
} // namespace Payments::Ui
|