Support blocking progress in payment panel.

This commit is contained in:
John Preston 2021-04-12 19:24:36 +04:00
parent 52b5c4cbe0
commit 0ead0879d7
8 changed files with 224 additions and 4 deletions

View File

@ -155,6 +155,7 @@ CheckoutProcess::CheckoutProcess(
panelCancelEdit();
}, _panel->lifetime());
showForm();
_panel->toggleProgress(true);
if (mode == Mode::Payment) {
_session->api().passwordState(
@ -180,7 +181,9 @@ not_null<Ui::PanelDelegate*> CheckoutProcess::panelDelegate() {
}
void CheckoutProcess::handleFormUpdate(const FormUpdate &update) {
v::match(update, [&](const FormReady &) {
v::match(update, [&](const ToggleProgress &data) {
_panel->toggleProgress(data.shown);
}, [&](const FormReady &) {
performInitialSilentValidation();
if (!_initialSilentValidation) {
showForm();

View File

@ -170,6 +170,14 @@ void Form::fillInvoiceFromMessage() {
}
}
void Form::showProgress() {
_updates.fire(ToggleProgress{ true });
}
void Form::hideProgress() {
_updates.fire(ToggleProgress{ false });
}
void Form::loadThumbnail(not_null<PhotoData*> photo) {
Expects(!_thumbnailLoadProcess);
@ -251,29 +259,35 @@ QImage Form::prepareEmptyThumbnail() const {
}
void Form::requestForm() {
showProgress();
_api.request(MTPpayments_GetPaymentForm(
MTP_flags(MTPpayments_GetPaymentForm::Flag::f_theme_params),
_peer->input,
MTP_int(_msgId),
MTP_dataJSON(MTP_bytes(ThemeParams()))
)).done([=](const MTPpayments_PaymentForm &result) {
hideProgress();
result.match([&](const auto &data) {
processForm(data);
});
}).fail([=](const MTP::Error &error) {
hideProgress();
_updates.fire(Error{ Error::Type::Form, error.type() });
}).send();
}
void Form::requestReceipt() {
showProgress();
_api.request(MTPpayments_GetPaymentReceipt(
_peer->input,
MTP_int(_msgId)
)).done([=](const MTPpayments_PaymentReceipt &result) {
hideProgress();
result.match([&](const auto &data) {
processReceipt(data);
});
}).fail([=](const MTP::Error &error) {
hideProgress();
_updates.fire(Error{ Error::Type::Form, error.type() });
}).send();
}
@ -551,6 +565,7 @@ void Form::submit() {
}
using Flag = MTPpayments_SendPaymentForm::Flag;
showProgress();
_api.request(MTPpayments_SendPaymentForm(
MTP_flags((_requestedInformationId.isEmpty()
? Flag(0)
@ -576,12 +591,14 @@ void Form::submit() {
MTP_bytes(password))),
MTP_long(_invoice.tipsSelected)
)).done([=](const MTPpayments_PaymentResult &result) {
hideProgress();
result.match([&](const MTPDpayments_paymentResult &data) {
_updates.fire(PaymentFinished{ data.vupdates() });
}, [&](const MTPDpayments_paymentVerificationNeeded &data) {
_updates.fire(VerificationNeeded{ qs(data.vurl()) });
});
}).fail([=](const MTP::Error &error) {
hideProgress();
_updates.fire(Error{ Error::Type::Send, error.type() });
}).send();
}
@ -612,6 +629,7 @@ void Form::validateInformation(const Ui::RequestedInformation &information) {
if (_validatedInformation == information) {
return;
}
hideProgress();
_api.request(base::take(_validateRequestId)).cancel();
}
_validatedInformation = information;
@ -625,6 +643,7 @@ void Form::validateInformation(const Ui::RequestedInformation &information) {
Assert(!_invoice.isEmailRequested || !information.email.isEmpty());
Assert(!_invoice.isPhoneRequested || !information.phone.isEmpty());
showProgress();
using Flag = MTPpayments_ValidateRequestedInfo::Flag;
_validateRequestId = _api.request(MTPpayments_ValidateRequestedInfo(
MTP_flags(information.save ? Flag::f_save : Flag(0)),
@ -632,6 +651,7 @@ void Form::validateInformation(const Ui::RequestedInformation &information) {
MTP_int(_msgId),
Serialize(information)
)).done([=](const MTPpayments_ValidatedRequestedInfo &result) {
hideProgress();
_validateRequestId = 0;
const auto oldSelectedId = _shippingOptions.selectedId;
result.match([&](const MTPDpayments_validatedRequestedInfo &data) {
@ -654,6 +674,7 @@ void Form::validateInformation(const Ui::RequestedInformation &information) {
}
_updates.fire(ValidateFinished{});
}).fail([=](const MTP::Error &error) {
hideProgress();
_validateRequestId = 0;
_updates.fire(Error{ Error::Type::Validate, error.type() });
}).send();
@ -798,9 +819,11 @@ void Form::validateCard(
.addressZip = details.addressZip,
.addressCountry = details.addressCountry,
};
showProgress();
_stripe->createTokenWithCard(std::move(card), crl::guard(this, [=](
Stripe::Token token,
Stripe::Error error) {
hideProgress();
_stripe = nullptr;
if (error) {
@ -846,9 +869,11 @@ void Form::validateCard(
.addressZip = details.addressZip,
.addressCountry = details.addressCountry,
};
showProgress();
_smartglocal->createTokenWithCard(std::move(card), crl::guard(this, [=](
SmartGlocal::Token token,
SmartGlocal::Error error) {
hideProgress();
_smartglocal = nullptr;
if (error) {

View File

@ -116,6 +116,9 @@ struct PaymentMethod {
Ui::PaymentMethodDetails ui;
};
struct ToggleProgress {
bool shown = true;
};
struct FormReady {};
struct ThumbnailUpdated {
QImage thumbnail;
@ -157,6 +160,7 @@ struct Error {
};
struct FormUpdate : std::variant<
ToggleProgress,
FormReady,
ThumbnailUpdated,
ValidateFinished,
@ -209,6 +213,8 @@ public:
private:
void fillInvoiceFromMessage();
void showProgress();
void hideProgress();
void loadThumbnail(not_null<PhotoData*> photo);
[[nodiscard]] QImage prepareGoodThumbnail(

View File

@ -129,6 +129,10 @@ bool FormSummary::showCriticalError(const TextWithEntities &text) {
return true;
}
int FormSummary::contentHeight() const {
return _invoice ? _scroll->height() : _layout->height();
}
void FormSummary::updateThumbnail(const QImage &thumbnail) {
_invoice.cover.thumbnail = thumbnail;
_thumbnails.fire_copy(thumbnail);

View File

@ -40,6 +40,7 @@ public:
[[nodiscard]] rpl::producer<int> scrollTopValue() const;
bool showCriticalError(const TextWithEntities &text);
[[nodiscard]] int contentHeight() const;
private:
void resizeEvent(QResizeEvent *e) override;

View File

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/boxes/single_choice_box.h"
#include "ui/text/format_values.h"
#include "ui/text/text_utilities.h"
#include "ui/effects/radial_animation.h"
#include "lang/lang_keys.h"
#include "webview/webview_embed.h"
#include "webview/webview_interface.h"
@ -25,6 +26,29 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_layers.h"
namespace Payments::Ui {
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;
};
Panel::Progress::Progress(QWidget *parent, Fn<QRect()> rect)
: widget(parent)
, animation(
[=] { if (!anim::Disabled()) widget.update(rect()); },
st::boxLoadingAnimation) {
}
Panel::Panel(not_null<PanelDelegate*> delegate)
: _delegate(delegate)
@ -45,6 +69,7 @@ Panel::Panel(not_null<PanelDelegate*> delegate)
Panel::~Panel() {
// Destroy _widget before _webview.
_progress = nullptr;
_widget = nullptr;
}
@ -52,6 +77,139 @@ void Panel::requestActivate() {
_widget->showAndActivate();
}
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.));
auto thickness = _webviewBottom
? st::boxLoadingAnimation.thickness
: (st::boxLoadingAnimation.thickness * 2);
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(),
st::boxLoadingAnimation.color,
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();
const auto size = height
- st::layerBox.buttonPadding.top()
- st::layerBox.buttonPadding.bottom();
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();
_progress->widget.setGeometry(
right - size - st::layerBox.buttonPadding.right(),
top + st::layerBox.buttonPadding.top(),
size,
size);
}, _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();
}
}
void Panel::showForm(
const Invoice &invoice,
const RequestedInformation &current,
@ -83,6 +241,7 @@ void Panel::showForm(
_widget->showInner(std::move(form));
_widget->setBackAllowed(false);
_formScrollTop = _weakFormSummary->scrollTopValue();
setupProgressGeometry();
}
void Panel::updateFormThumbnail(const QImage &thumbnail) {
@ -106,6 +265,7 @@ void Panel::showEditInformation(
_widget->showInner(std::move(edit));
_widget->setBackAllowed(true);
_weakEditInformation->setFocusFast(field);
setupProgressGeometry();
}
void Panel::showInformationError(
@ -278,6 +438,7 @@ bool Panel::showWebview(
if (!_webview && !createWebview()) {
return false;
}
toggleProgress(true);
_widget->destroyLayer();
_webview->navigate(url);
_widget->setBackAllowed(allowBack);
@ -347,8 +508,15 @@ bool Panel::createWebview() {
_delegate->panelWebviewMessage(message, save);
});
raw->setNavigationHandler([=](const QString &uri) {
return _delegate->panelWebviewNavigationAttempt(uri);
raw->setNavigationStartHandler([=](const QString &uri) {
if (!_delegate->panelWebviewNavigationAttempt(uri)) {
return false;
}
toggleProgress(true);
return true;
});
raw->setNavigationDoneHandler([=](bool success) {
toggleProgress(false);
});
raw->init(R"(
@ -361,6 +529,9 @@ postEvent: function(eventType, eventData) {
};)");
_widget->showInner(std::move(container));
setupProgressGeometry();
return true;
}
@ -452,6 +623,7 @@ void Panel::showEditCard(
_widget->showInner(std::move(edit));
_widget->setBackAllowed(true);
_weakEditCard->setFocusFast(field);
setupProgressGeometry();
}
void Panel::showCardError(
@ -494,6 +666,7 @@ void Panel::showToast(const TextWithEntities &text) {
}
void Panel::showCriticalError(const TextWithEntities &text) {
toggleProgress(false);
if (!_weakFormSummary || !_weakFormSummary->showCriticalError(text)) {
auto error = base::make_unique_q<PaddingWrap<FlatLabel>>(
_widget.get(),

View File

@ -43,6 +43,7 @@ public:
~Panel();
void requestActivate();
void toggleProgress(bool shown);
void showForm(
const Invoice &invoice,
@ -86,16 +87,23 @@ public:
[[nodiscard]] rpl::lifetime &lifetime();
private:
struct Progress;
bool createWebview();
void showWebviewError(
const QString &text,
const Webview::Available &information);
void setTitle(rpl::producer<QString> title);
[[nodiscard]] bool progressWithBackground() const;
[[nodiscard]] QRect progressRect() const;
void setupProgressGeometry();
const not_null<PanelDelegate*> _delegate;
std::unique_ptr<SeparatePanel> _widget;
std::unique_ptr<Webview::Window> _webview;
std::unique_ptr<RpWidget> _webviewBottom;
std::unique_ptr<Progress> _progress;
QPointer<Checkbox> _saveWebviewInformation;
QPointer<FormSummary> _weakFormSummary;
rpl::variable<int> _formScrollTop;

@ -1 +1 @@
Subproject commit c1548226d49db23f68bbf35f34cc820171aed65c
Subproject commit fa6828443c71932de74ec2a0ffa5f3e8d3bc894c