1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "payments/ui/payments_panel.h"
9
10 #include "payments/ui/payments_form_summary.h"
11 #include "payments/ui/payments_edit_information.h"
12 #include "payments/ui/payments_edit_card.h"
13 #include "payments/ui/payments_panel_delegate.h"
14 #include "payments/ui/payments_field.h"
15 #include "ui/widgets/separate_panel.h"
16 #include "ui/widgets/checkbox.h"
17 #include "ui/wrap/fade_wrap.h"
18 #include "ui/boxes/single_choice_box.h"
19 #include "ui/text/format_values.h"
20 #include "ui/text/text_utilities.h"
21 #include "ui/effects/radial_animation.h"
22 #include "lang/lang_keys.h"
23 #include "webview/webview_embed.h"
24 #include "webview/webview_interface.h"
25 #include "styles/style_payments.h"
26 #include "styles/style_layers.h"
27
28 namespace Payments::Ui {
29 namespace {
30
31 constexpr auto kProgressDuration = crl::time(200);
32 constexpr auto kProgressOpacity = 0.3;
33
34 } // namespace
35
36 struct Panel::Progress {
37 Progress(QWidget *parent, Fn<QRect()> rect);
38
39 RpWidget widget;
40 InfiniteRadialAnimation animation;
41 Animations::Simple shownAnimation;
42 bool shown = true;
43 rpl::lifetime geometryLifetime;
44 };
45
46 struct Panel::WebviewWithLifetime {
47 WebviewWithLifetime(
48 QWidget *parent = nullptr,
49 Webview::WindowConfig config = Webview::WindowConfig());
50
51 Webview::Window window;
52 QPointer<RpWidget> lastHidingBox;
53 rpl::lifetime lifetime;
54 };
55
WebviewWithLifetime(QWidget * parent,Webview::WindowConfig config)56 Panel::WebviewWithLifetime::WebviewWithLifetime(
57 QWidget *parent,
58 Webview::WindowConfig config)
59 : window(parent, std::move(config)) {
60 }
61
Progress(QWidget * parent,Fn<QRect ()> rect)62 Panel::Progress::Progress(QWidget *parent, Fn<QRect()> rect)
63 : widget(parent)
64 , animation(
65 [=] { if (!anim::Disabled()) widget.update(rect()); },
66 st::paymentsLoading) {
67 }
68
Panel(not_null<PanelDelegate * > delegate)69 Panel::Panel(not_null<PanelDelegate*> delegate)
70 : _delegate(delegate)
71 , _widget(std::make_unique<SeparatePanel>()) {
72 _widget->setInnerSize(st::paymentsPanelSize);
73 _widget->setWindowFlag(Qt::WindowStaysOnTopHint, false);
74
75 _widget->closeRequests(
76 ) | rpl::start_with_next([=] {
77 _delegate->panelRequestClose();
78 }, _widget->lifetime());
79
80 _widget->closeEvents(
81 ) | rpl::start_with_next([=] {
82 _delegate->panelCloseSure();
83 }, _widget->lifetime());
84 }
85
~Panel()86 Panel::~Panel() {
87 _webview = nullptr;
88 _progress = nullptr;
89 _widget = nullptr;
90 }
91
requestActivate()92 void Panel::requestActivate() {
93 _widget->showAndActivate();
94 }
95
toggleProgress(bool shown)96 void Panel::toggleProgress(bool shown) {
97 if (!_progress) {
98 if (!shown) {
99 return;
100 }
101 _progress = std::make_unique<Progress>(
102 _widget.get(),
103 [=] { return progressRect(); });
104 _progress->widget.paintRequest(
105 ) | rpl::start_with_next([=](QRect clip) {
106 auto p = QPainter(&_progress->widget);
107 p.setOpacity(
108 _progress->shownAnimation.value(_progress->shown ? 1. : 0.));
109 auto thickness = st::paymentsLoading.thickness;
110 if (progressWithBackground()) {
111 auto color = st::windowBg->c;
112 color.setAlphaF(kProgressOpacity);
113 p.fillRect(clip, color);
114 }
115 const auto rect = progressRect().marginsRemoved(
116 { thickness, thickness, thickness, thickness });
117 InfiniteRadialAnimation::Draw(
118 p,
119 _progress->animation.computeState(),
120 rect.topLeft(),
121 rect.size() - QSize(),
122 _progress->widget.width(),
123 st::paymentsLoading.color,
124 thickness);
125 }, _progress->widget.lifetime());
126 _progress->widget.show();
127 _progress->animation.start();
128 } else if (_progress->shown == shown) {
129 return;
130 }
131 const auto callback = [=] {
132 if (!_progress->shownAnimation.animating() && !_progress->shown) {
133 _progress = nullptr;
134 } else {
135 _progress->widget.update();
136 }
137 };
138 _progress->shown = shown;
139 _progress->shownAnimation.start(
140 callback,
141 shown ? 0. : 1.,
142 shown ? 1. : 0.,
143 kProgressDuration);
144 if (shown) {
145 setupProgressGeometry();
146 }
147 }
148
progressWithBackground() const149 bool Panel::progressWithBackground() const {
150 return (_progress->widget.width() == _widget->innerGeometry().width());
151 }
152
progressRect() const153 QRect Panel::progressRect() const {
154 const auto rect = _progress->widget.rect();
155 if (!progressWithBackground()) {
156 return rect;
157 }
158 const auto size = st::defaultBoxButton.height;
159 return QRect(
160 rect.x() + (rect.width() - size) / 2,
161 rect.y() + (rect.height() - size) / 2,
162 size,
163 size);
164 }
165
setupProgressGeometry()166 void Panel::setupProgressGeometry() {
167 if (!_progress || !_progress->shown) {
168 return;
169 }
170 _progress->geometryLifetime.destroy();
171 if (_webviewBottom) {
172 _webviewBottom->geometryValue(
173 ) | rpl::start_with_next([=](QRect bottom) {
174 const auto height = bottom.height();
175 const auto size = st::paymentsLoading.size;
176 const auto skip = (height - size.height()) / 2;
177 const auto inner = _widget->innerGeometry();
178 const auto right = inner.x() + inner.width();
179 const auto top = inner.y() + inner.height() - height;
180 // This doesn't work, because first we get the correct bottom
181 // geometry and after that we get the previous event (which
182 // triggered the 'fire' of correct geometry before getting here).
183 //const auto right = bottom.x() + bottom.width();
184 //const auto top = bottom.y();
185 _progress->widget.setGeometry(QRect{
186 QPoint(right - skip - size.width(), top + skip),
187 size });
188 }, _progress->geometryLifetime);
189 } else if (_weakFormSummary) {
190 _weakFormSummary->sizeValue(
191 ) | rpl::start_with_next([=](QSize form) {
192 const auto full = _widget->innerGeometry();
193 const auto size = st::defaultBoxButton.height;
194 const auto inner = _weakFormSummary->contentHeight();
195 const auto left = full.height() - inner;
196 if (left >= 2 * size) {
197 _progress->widget.setGeometry(
198 full.x() + (full.width() - size) / 2,
199 full.y() + inner + (left - size) / 2,
200 size,
201 size);
202 } else {
203 _progress->widget.setGeometry(full);
204 }
205 }, _progress->geometryLifetime);
206 } else if (_weakEditInformation) {
207 _weakEditInformation->geometryValue(
208 ) | rpl::start_with_next([=] {
209 _progress->widget.setGeometry(_widget->innerGeometry());
210 }, _progress->geometryLifetime);
211 } else if (_weakEditCard) {
212 _weakEditCard->geometryValue(
213 ) | rpl::start_with_next([=] {
214 _progress->widget.setGeometry(_widget->innerGeometry());
215 }, _progress->geometryLifetime);
216 }
217 _progress->widget.show();
218 _progress->widget.raise();
219 if (_progress->shown) {
220 _progress->widget.setFocus();
221 }
222 }
223
showForm(const Invoice & invoice,const RequestedInformation & current,const PaymentMethodDetails & method,const ShippingOptions & options)224 void Panel::showForm(
225 const Invoice &invoice,
226 const RequestedInformation ¤t,
227 const PaymentMethodDetails &method,
228 const ShippingOptions &options) {
229 if (invoice && !method.ready && !method.native.supported) {
230 const auto available = Webview::Availability();
231 if (available.error != Webview::Available::Error::None) {
232 showWebviewError(
233 tr::lng_payments_webview_no_use(tr::now),
234 available);
235 return;
236 }
237 }
238
239 _testMode = invoice.isTest;
240 setTitle(invoice.receipt
241 ? tr::lng_payments_receipt_title()
242 : tr::lng_payments_checkout_title());
243 auto form = base::make_unique_q<FormSummary>(
244 _widget.get(),
245 invoice,
246 current,
247 method,
248 options,
249 _delegate,
250 _formScrollTop.current());
251 _weakFormSummary = form.get();
252 _widget->showInner(std::move(form));
253 _widget->setBackAllowed(false);
254 _formScrollTop = _weakFormSummary->scrollTopValue();
255 setupProgressGeometry();
256 }
257
updateFormThumbnail(const QImage & thumbnail)258 void Panel::updateFormThumbnail(const QImage &thumbnail) {
259 if (_weakFormSummary) {
260 _weakFormSummary->updateThumbnail(thumbnail);
261 }
262 }
263
showEditInformation(const Invoice & invoice,const RequestedInformation & current,InformationField field)264 void Panel::showEditInformation(
265 const Invoice &invoice,
266 const RequestedInformation ¤t,
267 InformationField field) {
268 setTitle(tr::lng_payments_shipping_address_title());
269 auto edit = base::make_unique_q<EditInformation>(
270 _widget.get(),
271 invoice,
272 current,
273 field,
274 _delegate);
275 _weakEditInformation = edit.get();
276 _widget->showInner(std::move(edit));
277 _widget->setBackAllowed(true);
278 _weakEditInformation->setFocusFast(field);
279 setupProgressGeometry();
280 }
281
showInformationError(const Invoice & invoice,const RequestedInformation & current,InformationField field)282 void Panel::showInformationError(
283 const Invoice &invoice,
284 const RequestedInformation ¤t,
285 InformationField field) {
286 if (_weakEditInformation) {
287 _weakEditInformation->showError(field);
288 } else {
289 showEditInformation(invoice, current, field);
290 if (_weakEditInformation
291 && field == InformationField::ShippingCountry) {
292 _weakEditInformation->showError(field);
293 }
294 }
295 }
296
chooseShippingOption(const ShippingOptions & options)297 void Panel::chooseShippingOption(const ShippingOptions &options) {
298 showBox(Box([=](not_null<GenericBox*> box) {
299 const auto i = ranges::find(
300 options.list,
301 options.selectedId,
302 &ShippingOption::id);
303 const auto index = (i != end(options.list))
304 ? int(i - begin(options.list))
305 : -1;
306 const auto group = std::make_shared<RadiobuttonGroup>(index);
307
308 const auto layout = box->verticalLayout();
309 auto counter = 0;
310 for (const auto &option : options.list) {
311 const auto index = counter++;
312 const auto button = layout->add(
313 object_ptr<Radiobutton>(
314 layout,
315 group,
316 index,
317 QString(),
318 st::defaultBoxCheckbox,
319 st::defaultRadio),
320 st::paymentsShippingMargin);
321 const auto label = CreateChild<FlatLabel>(
322 layout.get(),
323 option.title,
324 st::paymentsShippingLabel);
325 const auto total = ranges::accumulate(
326 option.prices,
327 int64(0),
328 std::plus<>(),
329 &LabeledPrice::price);
330 const auto price = CreateChild<FlatLabel>(
331 layout.get(),
332 FillAmountAndCurrency(total, options.currency),
333 st::paymentsShippingPrice);
334 const auto area = CreateChild<AbstractButton>(layout.get());
335 area->setClickedCallback([=] { group->setValue(index); });
336 button->geometryValue(
337 ) | rpl::start_with_next([=](QRect geometry) {
338 label->move(
339 geometry.topLeft() + st::paymentsShippingLabelPosition);
340 price->move(
341 geometry.topLeft() + st::paymentsShippingPricePosition);
342 const auto right = geometry.x()
343 + st::paymentsShippingLabelPosition.x();
344 area->setGeometry(
345 right,
346 geometry.y(),
347 std::max(
348 label->x() + label->width() - right,
349 price->x() + price->width() - right),
350 price->y() + price->height() - geometry.y());
351 }, button->lifetime());
352 }
353
354 box->setTitle(tr::lng_payments_shipping_method());
355 box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
356 group->setChangedCallback([=](int index) {
357 if (index >= 0) {
358 _delegate->panelChangeShippingOption(
359 options.list[index].id);
360 box->closeBox();
361 }
362 });
363 }));
364 }
365
chooseTips(const Invoice & invoice)366 void Panel::chooseTips(const Invoice &invoice) {
367 const auto max = invoice.tipsMax;
368 const auto now = invoice.tipsSelected;
369 const auto currency = invoice.currency;
370 showBox(Box([=](not_null<GenericBox*> box) {
371 box->setTitle(tr::lng_payments_tips_box_title());
372 const auto row = box->lifetime().make_state<Field>(
373 box,
374 FieldConfig{
375 .type = FieldType::Money,
376 .value = QString::number(now),
377 .currency = currency,
378 });
379 box->setFocusCallback([=] {
380 row->setFocusFast();
381 });
382 box->addRow(row->ownedWidget());
383 const auto errorWrap = box->addRow(
384 object_ptr<FadeWrap<FlatLabel>>(
385 box,
386 object_ptr<FlatLabel>(
387 box,
388 tr::lng_payments_tips_max(
389 lt_amount,
390 rpl::single(FillAmountAndCurrency(max, currency))),
391 st::paymentTipsErrorLabel)),
392 st::paymentTipsErrorPadding);
393 errorWrap->hide(anim::type::instant);
394 const auto submit = [=] {
395 const auto value = row->value().toLongLong();
396 if (value > max) {
397 row->showError();
398 errorWrap->show(anim::type::normal);
399 } else {
400 _delegate->panelChangeTips(value);
401 box->closeBox();
402 }
403 };
404 row->submitted(
405 ) | rpl::start_with_next(submit, box->lifetime());
406 box->addButton(tr::lng_settings_save(), submit);
407 box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
408 }));
409 }
410
showEditPaymentMethod(const PaymentMethodDetails & method)411 void Panel::showEditPaymentMethod(const PaymentMethodDetails &method) {
412 auto bottomText = method.canSaveInformation
413 ? rpl::producer<QString>()
414 : tr::lng_payments_processed_by(
415 lt_provider,
416 rpl::single(method.provider));
417 setTitle(tr::lng_payments_card_title());
418 if (method.native.supported) {
419 showEditCard(method.native, CardField::Number);
420 } else if (!showWebview(method.url, true, std::move(bottomText))) {
421 const auto available = Webview::Availability();
422 if (available.error != Webview::Available::Error::None) {
423 showWebviewError(
424 tr::lng_payments_webview_no_card(tr::now),
425 available);
426 } else {
427 showCriticalError({ "Error: Could not initialize WebView." });
428 }
429 _widget->setBackAllowed(true);
430 } else if (method.canSaveInformation) {
431 const auto &padding = st::paymentsPanelPadding;
432 _saveWebviewInformation = CreateChild<Checkbox>(
433 _webviewBottom.get(),
434 tr::lng_payments_save_information(tr::now),
435 false);
436 const auto height = padding.top()
437 + _saveWebviewInformation->heightNoMargins()
438 + padding.bottom();
439 _saveWebviewInformation->moveToLeft(padding.right(), padding.top());
440 _saveWebviewInformation->show();
441 _webviewBottom->resize(_webviewBottom->width(), height);
442 }
443 }
444
showWebviewProgress()445 void Panel::showWebviewProgress() {
446 if (_webviewProgress && _progress && _progress->shown) {
447 return;
448 }
449 _webviewProgress = true;
450 toggleProgress(true);
451 }
452
hideWebviewProgress()453 void Panel::hideWebviewProgress() {
454 if (!_webviewProgress) {
455 return;
456 }
457 _webviewProgress = false;
458 toggleProgress(false);
459 }
460
showWebview(const QString & url,bool allowBack,rpl::producer<QString> bottomText)461 bool Panel::showWebview(
462 const QString &url,
463 bool allowBack,
464 rpl::producer<QString> bottomText) {
465 if (!_webview && !createWebview()) {
466 return false;
467 }
468 showWebviewProgress();
469 _widget->destroyLayer();
470 _webview->window.navigate(url);
471 _widget->setBackAllowed(allowBack);
472 if (bottomText) {
473 const auto &padding = st::paymentsPanelPadding;
474 const auto label = CreateChild<FlatLabel>(
475 _webviewBottom.get(),
476 std::move(bottomText),
477 st::paymentsWebviewBottom);
478 const auto height = padding.top()
479 + label->heightNoMargins()
480 + padding.bottom();
481 rpl::combine(
482 _webviewBottom->widthValue(),
483 label->widthValue()
484 ) | rpl::start_with_next([=](int outerWidth, int width) {
485 label->move((outerWidth - width) / 2, padding.top());
486 }, label->lifetime());
487 label->show();
488 _webviewBottom->resize(_webviewBottom->width(), height);
489 }
490 return true;
491 }
492
createWebview()493 bool Panel::createWebview() {
494 auto container = base::make_unique_q<RpWidget>(_widget.get());
495
496 _webviewBottom = std::make_unique<RpWidget>(_widget.get());
497 const auto bottom = _webviewBottom.get();
498 bottom->show();
499
500 bottom->heightValue(
501 ) | rpl::start_with_next([=, raw = container.get()](int height) {
502 const auto inner = _widget->innerGeometry();
503 bottom->move(inner.x(), inner.y() + inner.height() - height);
504 raw->resize(inner.width(), inner.height() - height);
505 bottom->resizeToWidth(inner.width());
506 }, bottom->lifetime());
507 container->show();
508
509 _webview = std::make_unique<WebviewWithLifetime>(
510 container.get(),
511 Webview::WindowConfig{
512 .userDataPath = _delegate->panelWebviewDataPath(),
513 });
514 const auto raw = &_webview->window;
515 QObject::connect(container.get(), &QObject::destroyed, [=] {
516 if (_webview && &_webview->window == raw) {
517 _webview = nullptr;
518 if (_webviewProgress) {
519 hideWebviewProgress();
520 if (_progress && !_progress->shown) {
521 _progress = nullptr;
522 }
523 }
524 }
525 if (_webviewBottom.get() == bottom) {
526 _webviewBottom = nullptr;
527 }
528 });
529 if (!raw->widget()) {
530 return false;
531 }
532
533 container->geometryValue(
534 ) | rpl::start_with_next([=](QRect geometry) {
535 raw->widget()->setGeometry(geometry);
536 }, _webview->lifetime);
537
538 raw->setMessageHandler([=](const QJsonDocument &message) {
539 const auto save = _saveWebviewInformation
540 && _saveWebviewInformation->checked();
541 _delegate->panelWebviewMessage(message, save);
542 });
543
544 raw->setNavigationStartHandler([=](const QString &uri) {
545 if (!_delegate->panelWebviewNavigationAttempt(uri)) {
546 return false;
547 }
548 showWebviewProgress();
549 return true;
550 });
551 raw->setNavigationDoneHandler([=](bool success) {
552 hideWebviewProgress();
553 });
554
555 raw->init(R"(
556 window.TelegramWebviewProxy = {
557 postEvent: function(eventType, eventData) {
558 if (window.external && window.external.invoke) {
559 window.external.invoke(JSON.stringify([eventType, eventData]));
560 }
561 }
562 };)");
563
564 _widget->showInner(std::move(container));
565
566 setupProgressGeometry();
567
568 return true;
569 }
570
choosePaymentMethod(const PaymentMethodDetails & method)571 void Panel::choosePaymentMethod(const PaymentMethodDetails &method) {
572 if (!method.ready) {
573 showEditPaymentMethod(method);
574 return;
575 }
576 showBox(Box([=](not_null<GenericBox*> box) {
577 const auto save = [=](int option) {
578 if (option) {
579 showEditPaymentMethod(method);
580 }
581 };
582 SingleChoiceBox(box, {
583 .title = tr::lng_payments_payment_method(),
584 .options = { method.title, tr::lng_payments_new_card(tr::now) },
585 .initialSelection = 0,
586 .callback = save,
587 });
588 }));
589 }
590
askSetPassword()591 void Panel::askSetPassword() {
592 showBox(Box([=](not_null<GenericBox*> box) {
593 box->addRow(
594 object_ptr<FlatLabel>(
595 box.get(),
596 tr::lng_payments_need_password(),
597 st::boxLabel),
598 st::boxPadding);
599 box->addButton(tr::lng_continue(), [=] {
600 _delegate->panelSetPassword();
601 box->closeBox();
602 });
603 box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
604 }));
605 }
606
showCloseConfirm()607 void Panel::showCloseConfirm() {
608 showBox(Box([=](not_null<GenericBox*> box) {
609 box->addRow(
610 object_ptr<FlatLabel>(
611 box.get(),
612 tr::lng_payments_sure_close(),
613 st::boxLabel),
614 st::boxPadding);
615 box->addButton(tr::lng_close(), [=] {
616 _delegate->panelCloseSure();
617 });
618 box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
619 }));
620 }
621
showWarning(const QString & bot,const QString & provider)622 void Panel::showWarning(const QString &bot, const QString &provider) {
623 showBox(Box([=](not_null<GenericBox*> box) {
624 box->setTitle(tr::lng_payments_warning_title());
625 box->addRow(object_ptr<FlatLabel>(
626 box.get(),
627 tr::lng_payments_warning_body(
628 lt_bot1,
629 rpl::single(bot),
630 lt_provider,
631 rpl::single(provider),
632 lt_bot2,
633 rpl::single(bot),
634 lt_bot3,
635 rpl::single(bot)),
636 st::boxLabel));
637 box->addButton(tr::lng_continue(), [=] {
638 _delegate->panelTrustAndSubmit();
639 box->closeBox();
640 });
641 box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
642 }));
643 }
644
showEditCard(const NativeMethodDetails & native,CardField field)645 void Panel::showEditCard(
646 const NativeMethodDetails &native,
647 CardField field) {
648 Expects(native.supported);
649
650 auto edit = base::make_unique_q<EditCard>(
651 _widget.get(),
652 native,
653 field,
654 _delegate);
655 _weakEditCard = edit.get();
656 _widget->showInner(std::move(edit));
657 _widget->setBackAllowed(true);
658 _weakEditCard->setFocusFast(field);
659 setupProgressGeometry();
660 }
661
showCardError(const NativeMethodDetails & native,CardField field)662 void Panel::showCardError(
663 const NativeMethodDetails &native,
664 CardField field) {
665 if (_weakEditCard) {
666 _weakEditCard->showError(field);
667 } else {
668 // We cancelled card edit already.
669 //showEditCard(native, field);
670 //if (_weakEditCard
671 // && field == CardField::AddressCountry) {
672 // _weakEditCard->showError(field);
673 //}
674 }
675 }
676
setTitle(rpl::producer<QString> title)677 void Panel::setTitle(rpl::producer<QString> title) {
678 using namespace rpl::mappers;
679 if (_testMode) {
680 _widget->setTitle(std::move(title) | rpl::map(_1 + " (Test)"));
681 } else {
682 _widget->setTitle(std::move(title));
683 }
684 }
685
backRequests() const686 rpl::producer<> Panel::backRequests() const {
687 return _widget->backRequests();
688 }
689
showBox(object_ptr<BoxContent> box)690 void Panel::showBox(object_ptr<BoxContent> box) {
691 if (const auto widget = _webview ? _webview->window.widget() : nullptr) {
692 const auto hideNow = !widget->isHidden();
693 if (hideNow || _webview->lastHidingBox) {
694 const auto raw = _webview->lastHidingBox = box.data();
695 box->boxClosing(
696 ) | rpl::start_with_next([=] {
697 const auto widget = _webview
698 ? _webview->window.widget()
699 : nullptr;
700 if (widget
701 && widget->isHidden()
702 && _webview->lastHidingBox == raw) {
703 widget->show();
704 }
705 }, _webview->lifetime);
706 if (hideNow) {
707 widget->hide();
708 }
709 }
710 }
711 _widget->showBox(
712 std::move(box),
713 LayerOption::KeepOther,
714 anim::type::normal);
715 }
716
showToast(const TextWithEntities & text)717 void Panel::showToast(const TextWithEntities &text) {
718 _widget->showToast(text);
719 }
720
showCriticalError(const TextWithEntities & text)721 void Panel::showCriticalError(const TextWithEntities &text) {
722 _progress = nullptr;
723 _webviewProgress = false;
724 if (!_weakFormSummary || !_weakFormSummary->showCriticalError(text)) {
725 auto error = base::make_unique_q<PaddingWrap<FlatLabel>>(
726 _widget.get(),
727 object_ptr<FlatLabel>(
728 _widget.get(),
729 rpl::single(text),
730 st::paymentsCriticalError),
731 st::paymentsCriticalErrorPadding);
732 error->entity()->setClickHandlerFilter([=](
733 const ClickHandlerPtr &handler,
734 Qt::MouseButton) {
735 const auto entity = handler->getTextEntity();
736 if (entity.type != EntityType::CustomUrl) {
737 return true;
738 }
739 _delegate->panelOpenUrl(entity.data);
740 return false;
741 });
742 _widget->showInner(std::move(error));
743 }
744 }
745
showWebviewError(const QString & text,const Webview::Available & information)746 void Panel::showWebviewError(
747 const QString &text,
748 const Webview::Available &information) {
749 using Error = Webview::Available::Error;
750 Expects(information.error != Error::None);
751
752 auto rich = TextWithEntities{ text };
753 rich.append("\n\n");
754 switch (information.error) {
755 case Error::NoWebview2: {
756 const auto command = QString(QChar(TextCommand));
757 const auto text = tr::lng_payments_webview_install_edge(
758 tr::now,
759 lt_link,
760 command);
761 const auto parts = text.split(command);
762 rich.append(parts.value(0))
763 .append(Text::Link(
764 "Microsoft Edge WebView2 Runtime",
765 "https://go.microsoft.com/fwlink/p/?LinkId=2124703"))
766 .append(parts.value(1));
767 } break;
768 case Error::NoGtkOrWebkit2Gtk:
769 rich.append(tr::lng_payments_webview_install_webkit(tr::now));
770 break;
771 case Error::MutterWM:
772 rich.append(tr::lng_payments_webview_switch_mutter(tr::now));
773 break;
774 case Error::Wayland:
775 rich.append(tr::lng_payments_webview_switch_wayland(tr::now));
776 break;
777 default:
778 rich.append(QString::fromStdString(information.details));
779 break;
780 }
781 showCriticalError(rich);
782 }
783
lifetime()784 rpl::lifetime &Panel::lifetime() {
785 return _widget->lifetime();
786 }
787
788 } // namespace Payments::Ui
789