mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-03-31 07:52:06 +00:00
Start proper payments processing.
This commit is contained in:
parent
25bbde2739
commit
4c707bff29
@ -815,6 +815,10 @@ PRIVATE
|
||||
passport/passport_panel_form.h
|
||||
passport/passport_panel_password.cpp
|
||||
passport/passport_panel_password.h
|
||||
payments/payments_checkout_process.cpp
|
||||
payments/payments_checkout_process.h
|
||||
payments/payments_form.cpp
|
||||
payments/payments_form.h
|
||||
platform/linux/linux_desktop_environment.cpp
|
||||
platform/linux/linux_desktop_environment.h
|
||||
platform/linux/linux_gdk_helper.cpp
|
||||
@ -1014,8 +1018,6 @@ PRIVATE
|
||||
ui/widgets/level_meter.h
|
||||
ui/widgets/multi_select.cpp
|
||||
ui/widgets/multi_select.h
|
||||
ui/widgets/separate_panel.cpp
|
||||
ui/widgets/separate_panel.h
|
||||
ui/countryinput.cpp
|
||||
ui/countryinput.h
|
||||
ui/empty_userpic.cpp
|
||||
|
@ -1860,6 +1860,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_payments_invoice_label_test" = "Test invoice";
|
||||
"lng_payments_receipt_button" = "Receipt";
|
||||
|
||||
"lng_payments_checkout_title" = "Checkout";
|
||||
"lng_payments_total_label" = "Total";
|
||||
"lng_payments_pay_amount" = "Pay {amount}";
|
||||
//"lng_payments_payment_method" = "Payment Method"; // #TODO payments native
|
||||
"lng_payments_shipping_address" = "Shipping Information";
|
||||
"lng_payments_shipping_method" = "Shipping Method";
|
||||
"lng_payments_info_name" = "Name";
|
||||
"lng_payments_info_email" = "Email";
|
||||
"lng_payments_info_phone" = "Phone";
|
||||
"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"; // #TODO payments native
|
||||
//"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_save_payment_about" = "You can save your payment information for future use.";
|
||||
"lng_payments_save_information" = "Save Information";
|
||||
|
||||
"lng_call_status_incoming" = "is calling you...";
|
||||
"lng_call_status_connecting" = "connecting...";
|
||||
"lng_call_status_exchanging" = "exchanging encryption keys...";
|
||||
|
@ -126,6 +126,10 @@ QString UiIntegration::timeFormat() {
|
||||
return cTimeFormat();
|
||||
}
|
||||
|
||||
QWidget *UiIntegration::modalWindowParent() {
|
||||
return Core::App().getModalParent();
|
||||
}
|
||||
|
||||
std::shared_ptr<ClickHandler> UiIntegration::createLinkHandler(
|
||||
const EntityLinkData &data,
|
||||
const std::any &context) {
|
||||
|
@ -46,6 +46,8 @@ public:
|
||||
void startFontsEnd() override;
|
||||
QString timeFormat() override;
|
||||
|
||||
QWidget *modalWindowParent() override;
|
||||
|
||||
std::shared_ptr<ClickHandler> createLinkHandler(
|
||||
const EntityLinkData &data,
|
||||
const std::any &context) override;
|
||||
|
@ -31,154 +31,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/view/media/history_view_media.h"
|
||||
#include "payments/payments_checkout_process.h"
|
||||
#include "data/data_session.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
#include "webview/webview_embed.h"
|
||||
#include "webview/webview_interface.h"
|
||||
#include "core/local_url_handlers.h"
|
||||
#include "ui/widgets/window.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonValue>
|
||||
|
||||
namespace Api {
|
||||
|
||||
void GetPaymentForm(not_null<const HistoryItem*> msg) {
|
||||
const auto msgId = msg->id;
|
||||
const auto session = &msg->history()->session();
|
||||
session->api().request(MTPpayments_GetPaymentForm(
|
||||
MTP_int(msgId)
|
||||
)).done([=](const MTPpayments_PaymentForm &result) {
|
||||
const auto window = new Ui::Window();
|
||||
window->setGeometry({
|
||||
style::ConvertScale(100),
|
||||
style::ConvertScale(100),
|
||||
style::ConvertScale(640),
|
||||
style::ConvertScale(480)
|
||||
});
|
||||
window->show();
|
||||
|
||||
window->events() | rpl::start_with_next([=](not_null<QEvent*> e) {
|
||||
if (e->type() == QEvent::Close) {
|
||||
window->deleteLater();
|
||||
}
|
||||
}, window->lifetime());
|
||||
|
||||
const auto body = window->body();
|
||||
body->paintRequest(
|
||||
) | rpl::start_with_next([=](QRect clip) {
|
||||
QPainter(body).fillRect(clip, st::windowBg);
|
||||
}, body->lifetime());
|
||||
|
||||
const auto webview = Ui::CreateChild<Webview::Window>(
|
||||
window,
|
||||
window);
|
||||
if (!webview->widget()) {
|
||||
delete window;
|
||||
Ui::show(Box<InformBox>(
|
||||
tr::lng_payments_not_supported(tr::now)));
|
||||
return;
|
||||
}
|
||||
|
||||
body->geometryValue(
|
||||
) | rpl::start_with_next([=](QRect geometry) {
|
||||
webview->widget()->setGeometry(geometry);
|
||||
}, body->lifetime());
|
||||
|
||||
webview->setMessageHandler([=](const QJsonDocument &message) {
|
||||
if (!message.isArray()) {
|
||||
LOG(("Payments Error: "
|
||||
"Not an array received in buy_callback arguments."));
|
||||
return;
|
||||
}
|
||||
const auto list = message.array();
|
||||
if (list.at(0).toString() != "payment_form_submit") {
|
||||
return;
|
||||
} else if (!list.at(1).isString()) {
|
||||
LOG(("Payments Error: "
|
||||
"Not a string received in buy_callback result."));
|
||||
return;
|
||||
}
|
||||
|
||||
auto error = QJsonParseError();
|
||||
const auto document = QJsonDocument::fromJson(
|
||||
list.at(1).toString().toUtf8(),
|
||||
&error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
LOG(("Payments Error: "
|
||||
"Failed to parse buy_callback arguments, error: %1."
|
||||
).arg(error.errorString()));
|
||||
return;
|
||||
} else if (!document.isObject()) {
|
||||
LOG(("Payments Error: "
|
||||
"Not an object decoded in buy_callback result."));
|
||||
return;
|
||||
}
|
||||
const auto root = document.object();
|
||||
const auto title = root.value("title").toString();
|
||||
const auto credentials = root.value("credentials");
|
||||
if (!credentials.isObject()) {
|
||||
LOG(("Payments Error: "
|
||||
"Not an object received in payment credentials."));
|
||||
return;
|
||||
}
|
||||
const auto serializedCredentials = QJsonDocument(
|
||||
credentials.toObject()
|
||||
).toJson(QJsonDocument::Compact);
|
||||
session->api().request(MTPpayments_SendPaymentForm(
|
||||
MTP_flags(0),
|
||||
MTP_int(msgId),
|
||||
MTPstring(), // requested_info_id
|
||||
MTPstring(), // shipping_option_id,
|
||||
MTP_inputPaymentCredentials(
|
||||
MTP_flags(0),
|
||||
MTP_dataJSON(MTP_bytes(serializedCredentials)))
|
||||
)).done([=](const MTPpayments_PaymentResult &result) {
|
||||
result.match([&](const MTPDpayments_paymentResult &data) {
|
||||
delete window;
|
||||
App::wnd()->activate();
|
||||
session->api().applyUpdates(data.vupdates());
|
||||
}, [&](const MTPDpayments_paymentVerificationNeeded &data) {
|
||||
webview->navigate(qs(data.vurl()));
|
||||
});
|
||||
}).fail([=](const RPCError &error) {
|
||||
delete window;
|
||||
App::wnd()->activate();
|
||||
Ui::Toast::Show("payments.sendPaymentForm: " + error.type());
|
||||
}).send();
|
||||
});
|
||||
|
||||
webview->setNavigationHandler([=](const QString &uri) {
|
||||
if (Core::TryConvertUrlToLocal(uri) == uri) {
|
||||
return true;
|
||||
}
|
||||
window->deleteLater();
|
||||
App::wnd()->activate();
|
||||
return false;
|
||||
});
|
||||
|
||||
webview->init(R"(
|
||||
window.TelegramWebviewProxy = {
|
||||
postEvent: function(eventType, eventData) {
|
||||
if (window.external && window.external.invoke) {
|
||||
window.external.invoke(JSON.stringify([eventType, eventData]));
|
||||
}
|
||||
}
|
||||
};)");
|
||||
|
||||
const auto &data = result.c_payments_paymentForm();
|
||||
webview->navigate(qs(data.vurl()));
|
||||
}).fail([=](const RPCError &error) {
|
||||
App::wnd()->activate();
|
||||
Ui::Toast::Show("payments.getPaymentForm: " + error.type());
|
||||
}).send();
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] MainWidget *CheckMainWidget(not_null<Main::Session*> session) {
|
||||
@ -266,12 +122,7 @@ void activateBotCommand(
|
||||
} break;
|
||||
|
||||
case ButtonType::Buy: {
|
||||
if (Webview::Supported()) {
|
||||
Api::GetPaymentForm(msg);
|
||||
} else {
|
||||
Ui::show(Box<InformBox>(
|
||||
tr::lng_payments_not_supported(tr::now)));
|
||||
}
|
||||
Payments::CheckoutProcess::Start(msg);
|
||||
} break;
|
||||
|
||||
case ButtonType::Url: {
|
||||
|
240
Telegram/SourceFiles/payments/payments_checkout_process.cpp
Normal file
240
Telegram/SourceFiles/payments/payments_checkout_process.cpp
Normal file
@ -0,0 +1,240 @@
|
||||
/*
|
||||
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/payments_checkout_process.h"
|
||||
|
||||
#include "payments/payments_form.h"
|
||||
#include "payments/ui/payments_panel.h"
|
||||
#include "payments/ui/payments_webview.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_account.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history.h"
|
||||
#include "core/local_url_handlers.h" // TryConvertUrlToLocal.
|
||||
#include "apiwrap.h"
|
||||
|
||||
// #TODO payments errors
|
||||
#include "mainwindow.h"
|
||||
#include "ui/toast/toast.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonValue>
|
||||
|
||||
namespace Payments {
|
||||
namespace {
|
||||
|
||||
struct SessionProcesses {
|
||||
base::flat_map<FullMsgId, std::unique_ptr<CheckoutProcess>> map;
|
||||
rpl::lifetime lifetime;
|
||||
};
|
||||
|
||||
base::flat_map<not_null<Main::Session*>, SessionProcesses> Processes;
|
||||
|
||||
[[nodiscard]] SessionProcesses &LookupSessionProcesses(
|
||||
not_null<const HistoryItem*> item) {
|
||||
const auto session = &item->history()->session();
|
||||
const auto i = Processes.find(session);
|
||||
if (i != end(Processes)) {
|
||||
return i->second;
|
||||
}
|
||||
const auto j = Processes.emplace(session).first;
|
||||
auto &result = j->second;
|
||||
session->account().sessionChanges(
|
||||
) | rpl::start_with_next([=] {
|
||||
Processes.erase(session);
|
||||
}, result.lifetime);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void CheckoutProcess::Start(not_null<const HistoryItem*> item) {
|
||||
auto &processes = LookupSessionProcesses(item);
|
||||
const auto session = &item->history()->session();
|
||||
const auto id = item->fullId();
|
||||
const auto i = processes.map.find(id);
|
||||
if (i != end(processes.map)) {
|
||||
i->second->requestActivate();
|
||||
return;
|
||||
}
|
||||
const auto j = processes.map.emplace(
|
||||
id,
|
||||
std::make_unique<CheckoutProcess>(session, id, PrivateTag{})).first;
|
||||
j->second->requestActivate();
|
||||
}
|
||||
|
||||
CheckoutProcess::CheckoutProcess(
|
||||
not_null<Main::Session*> session,
|
||||
FullMsgId itemId,
|
||||
PrivateTag)
|
||||
: _session(session)
|
||||
, _form(std::make_unique<Form>(session, itemId))
|
||||
, _panel(std::make_unique<Ui::Panel>(panelDelegate())) {
|
||||
_form->updates(
|
||||
) | rpl::start_with_next([=](const FormUpdate &update) {
|
||||
handleFormUpdate(update);
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
CheckoutProcess::~CheckoutProcess() {
|
||||
}
|
||||
|
||||
void CheckoutProcess::requestActivate() {
|
||||
_panel->requestActivate();
|
||||
}
|
||||
|
||||
not_null<Ui::PanelDelegate*> CheckoutProcess::panelDelegate() {
|
||||
return static_cast<PanelDelegate*>(this);
|
||||
}
|
||||
|
||||
void CheckoutProcess::handleFormUpdate(const FormUpdate &update) {
|
||||
v::match(update.data, [&](const FormReady &) {
|
||||
_panel->showForm(_form->invoice());
|
||||
}, [&](const FormError &error) {
|
||||
handleFormError(error);
|
||||
}, [&](const SendError &error) {
|
||||
handleSendError(error);
|
||||
}, [&](const VerificationNeeded &info) {
|
||||
if (_webviewWindow) {
|
||||
_webviewWindow->navigate(info.url);
|
||||
} else {
|
||||
_webviewWindow = std::make_unique<Ui::WebviewWindow>(
|
||||
info.url,
|
||||
panelDelegate());
|
||||
if (!_webviewWindow->shown()) {
|
||||
// #TODO payments errors
|
||||
}
|
||||
}
|
||||
}, [&](const PaymentFinished &result) {
|
||||
const auto weak = base::make_weak(this);
|
||||
_session->api().applyUpdates(result.updates);
|
||||
if (weak) {
|
||||
panelCloseSure();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CheckoutProcess::handleFormError(const FormError &error) {
|
||||
// #TODO payments errors
|
||||
const auto &type = error.type;
|
||||
if (type == u"PROVIDER_ACCOUNT_INVALID"_q) {
|
||||
|
||||
} else if (type == u"PROVIDER_ACCOUNT_TIMEOUT"_q) {
|
||||
|
||||
} else if (type == u"INVOICE_ALREADY_PAID"_q) {
|
||||
|
||||
}
|
||||
App::wnd()->activate();
|
||||
Ui::Toast::Show("payments.getPaymentForm: " + type);
|
||||
}
|
||||
|
||||
void CheckoutProcess::handleSendError(const SendError &error) {
|
||||
// #TODO payments errors
|
||||
const auto &type = error.type;
|
||||
if (type == u"REQUESTED_INFO_INVALID"_q) {
|
||||
|
||||
} else if (type == u"SHIPPING_OPTION_INVALID"_q) {
|
||||
|
||||
} else if (type == u"PAYMENT_FAILED"_q) {
|
||||
|
||||
} else if (type == u"PAYMENT_CREDENTIALS_INVALID"_q) {
|
||||
|
||||
} else if (type == u"PAYMENT_CREDENTIALS_ID_INVALID"_q) {
|
||||
|
||||
} else if (type == u"BOT_PRECHECKOUT_FAILED"_q) {
|
||||
|
||||
}
|
||||
App::wnd()->activate();
|
||||
Ui::Toast::Show("payments.sendPaymentForm: " + type);
|
||||
}
|
||||
|
||||
void CheckoutProcess::panelRequestClose() {
|
||||
panelCloseSure(); // #TODO payments confirmation
|
||||
}
|
||||
|
||||
void CheckoutProcess::panelCloseSure() {
|
||||
const auto i = Processes.find(_session);
|
||||
if (i == end(Processes)) {
|
||||
return;
|
||||
}
|
||||
const auto j = ranges::find(i->second.map, this, [](const auto &pair) {
|
||||
return pair.second.get();
|
||||
});
|
||||
if (j == end(i->second.map)) {
|
||||
return;
|
||||
}
|
||||
i->second.map.erase(j);
|
||||
if (i->second.map.empty()) {
|
||||
Processes.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckoutProcess::panelSubmit() {
|
||||
_webviewWindow = std::make_unique<Ui::WebviewWindow>(
|
||||
_form->details().url,
|
||||
panelDelegate());
|
||||
if (!_webviewWindow->shown()) {
|
||||
// #TODO payments errors
|
||||
}
|
||||
}
|
||||
|
||||
void CheckoutProcess::panelWebviewMessage(const QJsonDocument &message) {
|
||||
if (!message.isArray()) {
|
||||
LOG(("Payments Error: "
|
||||
"Not an array received in buy_callback arguments."));
|
||||
return;
|
||||
}
|
||||
const auto list = message.array();
|
||||
if (list.at(0).toString() != "payment_form_submit") {
|
||||
return;
|
||||
} else if (!list.at(1).isString()) {
|
||||
LOG(("Payments Error: "
|
||||
"Not a string received in buy_callback result."));
|
||||
return;
|
||||
}
|
||||
|
||||
auto error = QJsonParseError();
|
||||
const auto document = QJsonDocument::fromJson(
|
||||
list.at(1).toString().toUtf8(),
|
||||
&error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
LOG(("Payments Error: "
|
||||
"Failed to parse buy_callback arguments, error: %1."
|
||||
).arg(error.errorString()));
|
||||
return;
|
||||
} else if (!document.isObject()) {
|
||||
LOG(("Payments Error: "
|
||||
"Not an object decoded in buy_callback result."));
|
||||
return;
|
||||
}
|
||||
const auto root = document.object();
|
||||
const auto title = root.value("title").toString();
|
||||
const auto credentials = root.value("credentials");
|
||||
if (!credentials.isObject()) {
|
||||
LOG(("Payments Error: "
|
||||
"Not an object received in payment credentials."));
|
||||
return;
|
||||
}
|
||||
const auto serializedCredentials = QJsonDocument(
|
||||
credentials.toObject()
|
||||
).toJson(QJsonDocument::Compact);
|
||||
|
||||
_form->send(serializedCredentials);
|
||||
}
|
||||
|
||||
bool CheckoutProcess::panelWebviewNavigationAttempt(const QString &uri) {
|
||||
if (Core::TryConvertUrlToLocal(uri) == uri) {
|
||||
return true;
|
||||
}
|
||||
panelCloseSure();
|
||||
App::wnd()->activate();
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Payments
|
69
Telegram/SourceFiles/payments/payments_checkout_process.h
Normal file
69
Telegram/SourceFiles/payments/payments_checkout_process.h
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "payments/ui/payments_panel_delegate.h"
|
||||
#include "base/weak_ptr.h"
|
||||
|
||||
class HistoryItem;
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Payments::Ui {
|
||||
class Panel;
|
||||
class WebviewWindow;
|
||||
} // namespace Payments::Ui
|
||||
|
||||
namespace Payments {
|
||||
|
||||
class Form;
|
||||
struct FormUpdate;
|
||||
struct FormError;
|
||||
struct SendError;
|
||||
|
||||
class CheckoutProcess final
|
||||
: public base::has_weak_ptr
|
||||
, private Ui::PanelDelegate {
|
||||
struct PrivateTag {};
|
||||
|
||||
public:
|
||||
static void Start(not_null<const HistoryItem*> item);
|
||||
|
||||
CheckoutProcess(
|
||||
not_null<Main::Session*> session,
|
||||
FullMsgId itemId,
|
||||
PrivateTag);
|
||||
~CheckoutProcess();
|
||||
|
||||
void requestActivate();
|
||||
|
||||
private:
|
||||
[[nodiscard]] not_null<PanelDelegate*> panelDelegate();
|
||||
|
||||
void handleFormUpdate(const FormUpdate &update);
|
||||
void handleFormError(const FormError &error);
|
||||
void handleSendError(const SendError &error);
|
||||
|
||||
void panelRequestClose() override;
|
||||
void panelCloseSure() override;
|
||||
void panelSubmit() override;
|
||||
void panelWebviewMessage(const QJsonDocument &message) override;
|
||||
bool panelWebviewNavigationAttempt(const QString &uri) override;
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
const std::unique_ptr<Form> _form;
|
||||
const std::unique_ptr<Ui::Panel> _panel;
|
||||
std::unique_ptr<Ui::WebviewWindow> _webviewWindow;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Payments
|
154
Telegram/SourceFiles/payments/payments_form.cpp
Normal file
154
Telegram/SourceFiles/payments/payments_form.cpp
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
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/payments_form.h"
|
||||
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "apiwrap.h"
|
||||
|
||||
namespace Payments {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] Ui::Address ParseAddress(const MTPPostAddress &address) {
|
||||
return address.match([](const MTPDpostAddress &data) {
|
||||
return Ui::Address{
|
||||
.address1 = qs(data.vstreet_line1()),
|
||||
.address2 = qs(data.vstreet_line2()),
|
||||
.city = qs(data.vcity()),
|
||||
.state = qs(data.vstate()),
|
||||
.countryIso2 = qs(data.vcountry_iso2()),
|
||||
.postCode = qs(data.vpost_code()),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Form::Form(not_null<Main::Session*> session, FullMsgId itemId)
|
||||
: _session(session)
|
||||
, _msgId(itemId.msg) {
|
||||
requestForm();
|
||||
}
|
||||
|
||||
void Form::requestForm() {
|
||||
_session->api().request(MTPpayments_GetPaymentForm(
|
||||
MTP_int(_msgId)
|
||||
)).done([=](const MTPpayments_PaymentForm &result) {
|
||||
result.match([&](const auto &data) {
|
||||
processForm(data);
|
||||
});
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_updates.fire({ FormError{ error.type() } });
|
||||
}).send();
|
||||
}
|
||||
|
||||
void Form::processForm(const MTPDpayments_paymentForm &data) {
|
||||
_session->data().processUsers(data.vusers());
|
||||
|
||||
data.vinvoice().match([&](const auto &data) {
|
||||
processInvoice(data);
|
||||
});
|
||||
processDetails(data);
|
||||
if (const auto info = data.vsaved_info()) {
|
||||
info->match([&](const auto &data) {
|
||||
processSavedInformation(data);
|
||||
});
|
||||
}
|
||||
if (const auto credentials = data.vsaved_credentials()) {
|
||||
credentials->match([&](const auto &data) {
|
||||
processSavedCredentials(data);
|
||||
});
|
||||
}
|
||||
|
||||
_updates.fire({ FormReady{} });
|
||||
}
|
||||
|
||||
void Form::processInvoice(const MTPDinvoice &data) {
|
||||
auto &&prices = ranges::views::all(
|
||||
data.vprices().v
|
||||
) | ranges::views::transform([](const MTPLabeledPrice &price) {
|
||||
return price.match([&](const MTPDlabeledPrice &data) {
|
||||
return Ui::LabeledPrice{
|
||||
.label = qs(data.vlabel()),
|
||||
.price = data.vamount().v,
|
||||
};
|
||||
});
|
||||
});
|
||||
_invoice = Ui::Invoice{
|
||||
.prices = prices | ranges::to_vector,
|
||||
.currency = qs(data.vcurrency()),
|
||||
|
||||
.isNameRequested = data.is_name_requested(),
|
||||
.isPhoneRequested = data.is_phone_requested(),
|
||||
.isEmailRequested = data.is_email_requested(),
|
||||
.isShippingAddressRequested = data.is_shipping_address_requested(),
|
||||
.isFlexible = data.is_flexible(),
|
||||
.isTest = data.is_test(),
|
||||
|
||||
.phoneSentToProvider = data.is_phone_to_provider(),
|
||||
.emailSentToProvider = data.is_email_to_provider(),
|
||||
};
|
||||
}
|
||||
|
||||
void Form::processDetails(const MTPDpayments_paymentForm &data) {
|
||||
_session->data().processUsers(data.vusers());
|
||||
const auto nativeParams = data.vnative_params();
|
||||
auto nativeParamsJson = nativeParams
|
||||
? nativeParams->match(
|
||||
[&](const MTPDdataJSON &data) { return data.vdata().v; })
|
||||
: QByteArray();
|
||||
_details = FormDetails{
|
||||
.url = qs(data.vurl()),
|
||||
.nativeProvider = qs(data.vnative_provider().value_or_empty()),
|
||||
.nativeParamsJson = std::move(nativeParamsJson),
|
||||
.botId = data.vbot_id().v,
|
||||
.providerId = data.vprovider_id().v,
|
||||
.canSaveCredentials = data.is_can_save_credentials(),
|
||||
.passwordMissing = data.is_password_missing(),
|
||||
};
|
||||
}
|
||||
|
||||
void Form::processSavedInformation(const MTPDpaymentRequestedInfo &data) {
|
||||
const auto address = data.vshipping_address();
|
||||
_savedInformation = Ui::SavedInformation{
|
||||
.name = qs(data.vname().value_or_empty()),
|
||||
.phone = qs(data.vphone().value_or_empty()),
|
||||
.email = qs(data.vemail().value_or_empty()),
|
||||
.shippingAddress = address ? ParseAddress(*address) : Ui::Address(),
|
||||
};
|
||||
}
|
||||
|
||||
void Form::processSavedCredentials(
|
||||
const MTPDpaymentSavedCredentialsCard &data) {
|
||||
_savedCredentials = Ui::SavedCredentials{
|
||||
.id = qs(data.vid()),
|
||||
.title = qs(data.vtitle()),
|
||||
};
|
||||
}
|
||||
|
||||
void Form::send(const QByteArray &serializedCredentials) {
|
||||
_session->api().request(MTPpayments_SendPaymentForm(
|
||||
MTP_flags(0),
|
||||
MTP_int(_msgId),
|
||||
MTPstring(), // requested_info_id
|
||||
MTPstring(), // shipping_option_id,
|
||||
MTP_inputPaymentCredentials(
|
||||
MTP_flags(0),
|
||||
MTP_dataJSON(MTP_bytes(serializedCredentials)))
|
||||
)).done([=](const MTPpayments_PaymentResult &result) {
|
||||
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) {
|
||||
_updates.fire({ SendError{ error.type() } });
|
||||
}).send();
|
||||
}
|
||||
|
||||
} // namespace Payments
|
106
Telegram/SourceFiles/payments/payments_form.h
Normal file
106
Telegram/SourceFiles/payments/payments_form.h
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "payments/ui/payments_panel_data.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Payments {
|
||||
|
||||
struct FormDetails {
|
||||
QString url;
|
||||
QString nativeProvider;
|
||||
QByteArray nativeParamsJson;
|
||||
UserId botId = 0;
|
||||
UserId providerId = 0;
|
||||
bool canSaveCredentials = false;
|
||||
bool passwordMissing = false;
|
||||
|
||||
[[nodiscard]] bool valid() const {
|
||||
return !url.isEmpty();
|
||||
}
|
||||
[[nodiscard]] explicit operator bool() const {
|
||||
return valid();
|
||||
}
|
||||
};
|
||||
|
||||
struct FormReady {};
|
||||
|
||||
struct FormError {
|
||||
QString type;
|
||||
};
|
||||
|
||||
struct SendError {
|
||||
QString type;
|
||||
};
|
||||
|
||||
struct VerificationNeeded {
|
||||
QString url;
|
||||
};
|
||||
|
||||
struct PaymentFinished {
|
||||
MTPUpdates updates;
|
||||
};
|
||||
|
||||
struct FormUpdate {
|
||||
std::variant<
|
||||
FormReady,
|
||||
FormError,
|
||||
SendError,
|
||||
VerificationNeeded,
|
||||
PaymentFinished> data;
|
||||
};
|
||||
|
||||
class Form final {
|
||||
public:
|
||||
Form(not_null<Main::Session*> session, FullMsgId itemId);
|
||||
|
||||
[[nodiscard]] const Ui::Invoice &invoice() const {
|
||||
return _invoice;
|
||||
}
|
||||
[[nodiscard]] const FormDetails &details() const {
|
||||
return _details;
|
||||
}
|
||||
[[nodiscard]] const Ui::SavedInformation &savedInformation() const {
|
||||
return _savedInformation;
|
||||
}
|
||||
[[nodiscard]] const Ui::SavedCredentials &savedCredentials() const {
|
||||
return _savedCredentials;
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::producer<FormUpdate> updates() const {
|
||||
return _updates.events();
|
||||
}
|
||||
|
||||
void send(const QByteArray &serializedCredentials);
|
||||
|
||||
private:
|
||||
void requestForm();
|
||||
void processForm(const MTPDpayments_paymentForm &data);
|
||||
void processInvoice(const MTPDinvoice &data);
|
||||
void processDetails(const MTPDpayments_paymentForm &data);
|
||||
void processSavedInformation(const MTPDpaymentRequestedInfo &data);
|
||||
void processSavedCredentials(
|
||||
const MTPDpaymentSavedCredentialsCard &data);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
MsgId _msgId = 0;
|
||||
|
||||
Ui::Invoice _invoice;
|
||||
FormDetails _details;
|
||||
Ui::SavedInformation _savedInformation;
|
||||
Ui::SavedCredentials _savedCredentials;
|
||||
|
||||
rpl::event_stream<FormUpdate> _updates;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Payments
|
10
Telegram/SourceFiles/payments/ui/payments.style
Normal file
10
Telegram/SourceFiles/payments/ui/payments.style
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
using "ui/basic.style";
|
||||
|
||||
using "passport/passport.style";
|
87
Telegram/SourceFiles/payments/ui/payments_form_summary.cpp
Normal file
87
Telegram/SourceFiles/payments/ui/payments_form_summary.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
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_form_summary.h"
|
||||
|
||||
#include "payments/ui/payments_panel_delegate.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "styles/style_payments.h"
|
||||
#include "styles/style_passport.h"
|
||||
|
||||
namespace Payments::Ui {
|
||||
|
||||
using namespace ::Ui;
|
||||
|
||||
class PanelDelegate;
|
||||
|
||||
FormSummary::FormSummary(
|
||||
QWidget *parent,
|
||||
const Invoice &invoice,
|
||||
not_null<PanelDelegate*> delegate)
|
||||
: _delegate(delegate)
|
||||
, _scroll(this, st::passportPanelScroll)
|
||||
, _topShadow(this)
|
||||
, _bottomShadow(this)
|
||||
, _submit(
|
||||
this,
|
||||
tr::lng_payments_pay_amount(lt_amount, rpl::single(QString("much"))),
|
||||
st::passportPanelAuthorize) {
|
||||
setupControls();
|
||||
}
|
||||
|
||||
void FormSummary::setupControls() {
|
||||
const auto inner = setupContent();
|
||||
|
||||
_submit->addClickHandler([=] {
|
||||
_delegate->panelSubmit();
|
||||
});
|
||||
|
||||
using namespace rpl::mappers;
|
||||
|
||||
_topShadow->toggleOn(
|
||||
_scroll->scrollTopValue() | rpl::map(_1 > 0));
|
||||
_bottomShadow->toggleOn(rpl::combine(
|
||||
_scroll->scrollTopValue(),
|
||||
_scroll->heightValue(),
|
||||
inner->heightValue(),
|
||||
_1 + _2 < _3));
|
||||
}
|
||||
|
||||
not_null<Ui::RpWidget*> FormSummary::setupContent() {
|
||||
const auto inner = _scroll->setOwnedWidget(
|
||||
object_ptr<Ui::VerticalLayout>(this));
|
||||
|
||||
_scroll->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
inner->resizeToWidth(width);
|
||||
}, inner->lifetime());
|
||||
|
||||
return inner;
|
||||
}
|
||||
|
||||
void FormSummary::resizeEvent(QResizeEvent *e) {
|
||||
updateControlsGeometry();
|
||||
}
|
||||
|
||||
void FormSummary::updateControlsGeometry() {
|
||||
const auto submitTop = height() - _submit->height();
|
||||
_scroll->setGeometry(0, 0, width(), submitTop);
|
||||
_topShadow->resizeToWidth(width());
|
||||
_topShadow->moveToLeft(0, 0);
|
||||
_bottomShadow->resizeToWidth(width());
|
||||
_bottomShadow->moveToLeft(0, submitTop - st::lineWidth);
|
||||
_submit->setFullWidth(width());
|
||||
_submit->moveToLeft(0, submitTop);
|
||||
|
||||
_scroll->updateBars();
|
||||
}
|
||||
|
||||
} // namespace Payments::Ui
|
48
Telegram/SourceFiles/payments/ui/payments_form_summary.h
Normal file
48
Telegram/SourceFiles/payments/ui/payments_form_summary.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/rp_widget.h"
|
||||
#include "payments/ui/payments_panel_data.h"
|
||||
#include "base/object_ptr.h"
|
||||
|
||||
namespace Ui {
|
||||
class ScrollArea;
|
||||
class FadeShadow;
|
||||
class RoundButton;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Payments::Ui {
|
||||
|
||||
using namespace ::Ui;
|
||||
|
||||
class PanelDelegate;
|
||||
|
||||
class FormSummary final : public RpWidget {
|
||||
public:
|
||||
FormSummary(
|
||||
QWidget *parent,
|
||||
const Invoice &invoice,
|
||||
not_null<PanelDelegate*> delegate);
|
||||
|
||||
private:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
void setupControls();
|
||||
[[nodiscard]] not_null<Ui::RpWidget*> setupContent();
|
||||
void updateControlsGeometry();
|
||||
|
||||
const not_null<PanelDelegate*> _delegate;
|
||||
object_ptr<ScrollArea> _scroll;
|
||||
object_ptr<FadeShadow> _topShadow;
|
||||
object_ptr<FadeShadow> _bottomShadow;
|
||||
object_ptr<RoundButton> _submit;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Payments::Ui
|
47
Telegram/SourceFiles/payments/ui/payments_panel.cpp
Normal file
47
Telegram/SourceFiles/payments/ui/payments_panel.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
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"
|
||||
#include "payments/ui/payments_panel_delegate.h"
|
||||
#include "ui/widgets/separate_panel.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "styles/style_payments.h"
|
||||
#include "styles/style_passport.h"
|
||||
|
||||
namespace Payments::Ui {
|
||||
|
||||
Panel::Panel(not_null<PanelDelegate*> delegate)
|
||||
: _delegate(delegate)
|
||||
, _widget(std::make_unique<SeparatePanel>()) {
|
||||
_widget->setTitle(tr::lng_payments_checkout_title());
|
||||
_widget->setInnerSize(st::passportPanelSize);
|
||||
|
||||
_widget->closeRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
_delegate->panelRequestClose();
|
||||
}, _widget->lifetime());
|
||||
|
||||
_widget->closeEvents(
|
||||
) | rpl::start_with_next([=] {
|
||||
_delegate->panelCloseSure();
|
||||
}, _widget->lifetime());
|
||||
}
|
||||
|
||||
Panel::~Panel() = default;
|
||||
|
||||
void Panel::requestActivate() {
|
||||
_widget->showAndActivate();
|
||||
}
|
||||
|
||||
void Panel::showForm(const Invoice &invoice) {
|
||||
_widget->showInner(
|
||||
base::make_unique_q<FormSummary>(_widget.get(), invoice, _delegate));
|
||||
}
|
||||
|
||||
} // namespace Payments::Ui
|
36
Telegram/SourceFiles/payments/ui/payments_panel.h
Normal file
36
Telegram/SourceFiles/payments/ui/payments_panel.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Ui {
|
||||
class SeparatePanel;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Payments::Ui {
|
||||
|
||||
using namespace ::Ui;
|
||||
|
||||
class PanelDelegate;
|
||||
struct Invoice;
|
||||
|
||||
class Panel final {
|
||||
public:
|
||||
explicit Panel(not_null<PanelDelegate*> delegate);
|
||||
~Panel();
|
||||
|
||||
void requestActivate();
|
||||
|
||||
void showForm(const Invoice &invoice);
|
||||
|
||||
private:
|
||||
const not_null<PanelDelegate*> _delegate;
|
||||
std::unique_ptr<SeparatePanel> _widget;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Payments::Ui
|
86
Telegram/SourceFiles/payments/ui/payments_panel_data.h
Normal file
86
Telegram/SourceFiles/payments/ui/payments_panel_data.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Payments::Ui {
|
||||
|
||||
struct LabeledPrice {
|
||||
QString label;
|
||||
uint64 price = 0;
|
||||
};
|
||||
|
||||
struct Invoice {
|
||||
std::vector<LabeledPrice> prices;
|
||||
QString currency;
|
||||
|
||||
bool isNameRequested = false;
|
||||
bool isPhoneRequested = false;
|
||||
bool isEmailRequested = false;
|
||||
bool isShippingAddressRequested = false;
|
||||
bool isFlexible = false;
|
||||
bool isTest = false;
|
||||
|
||||
bool phoneSentToProvider = false;
|
||||
bool emailSentToProvider = false;
|
||||
|
||||
[[nodiscard]] bool valid() const {
|
||||
return !currency.isEmpty() && !prices.empty();
|
||||
}
|
||||
[[nodiscard]] explicit operator bool() const {
|
||||
return valid();
|
||||
}
|
||||
};
|
||||
|
||||
struct Address {
|
||||
QString address1;
|
||||
QString address2;
|
||||
QString city;
|
||||
QString state;
|
||||
QString countryIso2;
|
||||
QString postCode;
|
||||
|
||||
[[nodiscard]] bool valid() const {
|
||||
return !address1.isEmpty()
|
||||
&& !city.isEmpty()
|
||||
&& !countryIso2.isEmpty();
|
||||
}
|
||||
[[nodiscard]] explicit operator bool() const {
|
||||
return valid();
|
||||
}
|
||||
};
|
||||
|
||||
struct SavedInformation {
|
||||
QString name;
|
||||
QString phone;
|
||||
QString email;
|
||||
Address shippingAddress;
|
||||
|
||||
[[nodiscard]] bool empty() const {
|
||||
return name.isEmpty()
|
||||
&& phone.isEmpty()
|
||||
&& email.isEmpty()
|
||||
&& !shippingAddress;
|
||||
}
|
||||
[[nodiscard]] explicit operator bool() const {
|
||||
return !empty();
|
||||
}
|
||||
};
|
||||
|
||||
struct SavedCredentials {
|
||||
QString id;
|
||||
QString title;
|
||||
|
||||
[[nodiscard]] bool valid() const {
|
||||
return !id.isEmpty();
|
||||
}
|
||||
[[nodiscard]] explicit operator bool() const {
|
||||
return valid();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Payments::Ui
|
24
Telegram/SourceFiles/payments/ui/payments_panel_delegate.h
Normal file
24
Telegram/SourceFiles/payments/ui/payments_panel_delegate.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
class QJsonDocument;
|
||||
class QString;
|
||||
|
||||
namespace Payments::Ui {
|
||||
|
||||
class PanelDelegate {
|
||||
public:
|
||||
virtual void panelRequestClose() = 0;
|
||||
virtual void panelCloseSure() = 0;
|
||||
virtual void panelSubmit() = 0;
|
||||
virtual void panelWebviewMessage(const QJsonDocument &message) = 0;
|
||||
virtual bool panelWebviewNavigationAttempt(const QString &uri) = 0;
|
||||
};
|
||||
|
||||
} // namespace Payments::Ui
|
94
Telegram/SourceFiles/payments/ui/payments_webview.cpp
Normal file
94
Telegram/SourceFiles/payments/ui/payments_webview.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
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_webview.h"
|
||||
|
||||
#include "payments/ui/payments_panel_delegate.h"
|
||||
#include "webview/webview_embed.h"
|
||||
#include "webview/webview_interface.h"
|
||||
#include "ui/widgets/window.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "lang/lang_keys.h"
|
||||
|
||||
namespace Payments::Ui {
|
||||
|
||||
using namespace ::Ui;
|
||||
|
||||
class PanelDelegate;
|
||||
|
||||
WebviewWindow::WebviewWindow(
|
||||
const QString &url,
|
||||
not_null<PanelDelegate*> delegate) {
|
||||
if (!url.startsWith("https://", Qt::CaseInsensitive)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto window = &_window;
|
||||
|
||||
window->setGeometry({
|
||||
style::ConvertScale(100),
|
||||
style::ConvertScale(100),
|
||||
style::ConvertScale(640),
|
||||
style::ConvertScale(480)
|
||||
});
|
||||
window->show();
|
||||
|
||||
window->events() | rpl::start_with_next([=](not_null<QEvent*> e) {
|
||||
if (e->type() == QEvent::Close) {
|
||||
delegate->panelCloseSure();
|
||||
}
|
||||
}, window->lifetime());
|
||||
|
||||
const auto body = window->body();
|
||||
body->paintRequest(
|
||||
) | rpl::start_with_next([=](QRect clip) {
|
||||
QPainter(body).fillRect(clip, st::windowBg);
|
||||
}, body->lifetime());
|
||||
|
||||
_webview = Ui::CreateChild<Webview::Window>(
|
||||
window,
|
||||
window);
|
||||
if (!_webview->widget()) {
|
||||
return;
|
||||
}
|
||||
|
||||
body->geometryValue(
|
||||
) | rpl::start_with_next([=](QRect geometry) {
|
||||
_webview->widget()->setGeometry(geometry);
|
||||
}, body->lifetime());
|
||||
|
||||
_webview->setMessageHandler([=](const QJsonDocument &message) {
|
||||
delegate->panelWebviewMessage(message);
|
||||
});
|
||||
|
||||
_webview->setNavigationHandler([=](const QString &uri) {
|
||||
return delegate->panelWebviewNavigationAttempt(uri);
|
||||
});
|
||||
|
||||
_webview->init(R"(
|
||||
window.TelegramWebviewProxy = {
|
||||
postEvent: function(eventType, eventData) {
|
||||
if (window.external && window.external.invoke) {
|
||||
window.external.invoke(JSON.stringify([eventType, eventData]));
|
||||
}
|
||||
}
|
||||
};)");
|
||||
|
||||
navigate(url);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool WebviewWindow::shown() const {
|
||||
return _webview && _webview->widget();
|
||||
}
|
||||
|
||||
void WebviewWindow::navigate(const QString &url) {
|
||||
if (shown()) {
|
||||
_webview->navigate(url);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Payments::Ui
|
37
Telegram/SourceFiles/payments/ui/payments_webview.h
Normal file
37
Telegram/SourceFiles/payments/ui/payments_webview.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/widgets/window.h"
|
||||
|
||||
namespace Webview {
|
||||
class Window;
|
||||
} // namespace Webview
|
||||
|
||||
namespace Payments::Ui {
|
||||
|
||||
using namespace ::Ui;
|
||||
|
||||
class PanelDelegate;
|
||||
|
||||
class WebviewWindow final {
|
||||
public:
|
||||
WebviewWindow(
|
||||
const QString &url,
|
||||
not_null<PanelDelegate*> delegate);
|
||||
|
||||
[[nodiscard]] bool shown() const;
|
||||
void navigate(const QString &url);
|
||||
|
||||
private:
|
||||
Ui::Window _window;
|
||||
Webview::Window *_webview = nullptr;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Payments::Ui
|
@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "ui/widgets/separate_panel.h"
|
||||
|
||||
#include "window/main_window.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
@ -17,14 +16,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "ui/platform/ui_platform_utility.h"
|
||||
#include "ui/layers/layer_widget.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "core/application.h"
|
||||
#include "app.h"
|
||||
#include "styles/style_widgets.h"
|
||||
#include "styles/style_info.h"
|
||||
#include "styles/style_calls.h"
|
||||
#include "logs.h" // #TODO logs
|
||||
|
||||
#include <QtGui/QWindow>
|
||||
#include <QtGui/QScreen>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QDesktopWidget>
|
||||
|
||||
@ -35,7 +33,7 @@ SeparatePanel::SeparatePanel()
|
||||
, _back(this, object_ptr<Ui::IconButton>(this, st::separatePanelBack))
|
||||
, _body(this) {
|
||||
setMouseTracking(true);
|
||||
setWindowIcon(Window::CreateIcon());
|
||||
setWindowIcon(QGuiApplication::windowIcon());
|
||||
initControls();
|
||||
initLayout();
|
||||
}
|
||||
@ -155,13 +153,11 @@ void SeparatePanel::initLayout() {
|
||||
setAttribute(Qt::WA_TranslucentBackground, true);
|
||||
|
||||
createBorderImage();
|
||||
subscribe(Window::Theme::Background(), [=](
|
||||
const Window::Theme::BackgroundUpdate &update) {
|
||||
if (update.paletteChanged()) {
|
||||
createBorderImage();
|
||||
Ui::ForceFullRepaint(this);
|
||||
}
|
||||
});
|
||||
style::PaletteChanged(
|
||||
) | rpl::start_with_next([=] {
|
||||
createBorderImage();
|
||||
Ui::ForceFullRepaint(this);
|
||||
}, lifetime());
|
||||
|
||||
Ui::Platform::InitOnTopPanel(this);
|
||||
}
|
||||
@ -170,10 +166,10 @@ void SeparatePanel::createBorderImage() {
|
||||
const auto shadowPadding = st::callShadow.extend;
|
||||
const auto cacheSize = st::separatePanelBorderCacheSize;
|
||||
auto cache = QImage(
|
||||
cacheSize * cIntRetinaFactor(),
|
||||
cacheSize * cIntRetinaFactor(),
|
||||
cacheSize * style::DevicePixelRatio(),
|
||||
cacheSize * style::DevicePixelRatio(),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
cache.setDevicePixelRatio(cRetinaFactor());
|
||||
cache.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
cache.fill(Qt::transparent);
|
||||
{
|
||||
Painter p(&cache);
|
||||
@ -189,7 +185,7 @@ void SeparatePanel::createBorderImage() {
|
||||
st::callRadius,
|
||||
st::callRadius);
|
||||
}
|
||||
_borderParts = App::pixmapFromImageInPlace(std::move(cache));
|
||||
_borderParts = Ui::PixmapFromImage(std::move(cache));
|
||||
}
|
||||
|
||||
void SeparatePanel::toggleOpacityAnimation(bool visible) {
|
||||
@ -346,7 +342,12 @@ void SeparatePanel::setInnerSize(QSize size) {
|
||||
}
|
||||
|
||||
void SeparatePanel::initGeometry(QSize size) {
|
||||
const auto center = Core::App().getPointForCallPanelCenter();
|
||||
const auto active = QApplication::activeWindow();
|
||||
const auto center = !active
|
||||
? QGuiApplication::primaryScreen()->geometry().center()
|
||||
: (active->isVisible() && active->isActiveWindow())
|
||||
? active->geometry().center()
|
||||
: active->windowHandle()->screen()->geometry().center();
|
||||
_useTransparency = Ui::Platform::TranslucentWindowsSupported(center);
|
||||
_padding = _useTransparency
|
||||
? st::callShadow.extend
|
||||
@ -427,7 +428,7 @@ void SeparatePanel::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
|
||||
void SeparatePanel::paintShadowBorder(Painter &p) const {
|
||||
const auto factor = cIntRetinaFactor();
|
||||
const auto factor = style::DevicePixelRatio();
|
||||
const auto size = st::separatePanelBorderCacheSize;
|
||||
const auto part1 = size / 3;
|
||||
const auto part2 = size - part1;
|
||||
|
@ -22,7 +22,7 @@ class FadeWrapScaled;
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class SeparatePanel : public Ui::RpWidget, private base::Subscriber {
|
||||
class SeparatePanel final : public Ui::RpWidget {
|
||||
public:
|
||||
SeparatePanel();
|
||||
|
||||
|
2
Telegram/ThirdParty/tgcalls
vendored
2
Telegram/ThirdParty/tgcalls
vendored
@ -1 +1 @@
|
||||
Subproject commit eded7cc540123eaf26361958b9a61c65cb2f7cfc
|
||||
Subproject commit 65498491465fa64ffdf96a2b7cdeb67bfb697d5b
|
@ -24,6 +24,7 @@ set(style_files
|
||||
intro/intro.style
|
||||
media/player/media_player.style
|
||||
passport/passport.style
|
||||
payments/ui/payments.style
|
||||
profile/profile.style
|
||||
settings/settings.style
|
||||
media/view/media_view.style
|
||||
@ -60,6 +61,15 @@ PRIVATE
|
||||
media/clip/media_clip_reader.cpp
|
||||
media/clip/media_clip_reader.h
|
||||
|
||||
payments/ui/payments_form_summary.cpp
|
||||
payments/ui/payments_form_summary.h
|
||||
payments/ui/payments_panel.cpp
|
||||
payments/ui/payments_panel.h
|
||||
payments/ui/payments_panel_data.h
|
||||
payments/ui/payments_panel_delegate.h
|
||||
payments/ui/payments_webview.cpp
|
||||
payments/ui/payments_webview.h
|
||||
|
||||
platform/mac/file_bookmark_mac.h
|
||||
platform/mac/file_bookmark_mac.mm
|
||||
platform/platform_file_bookmark.h
|
||||
@ -114,6 +124,8 @@ PRIVATE
|
||||
ui/text/text_options.h
|
||||
ui/toasts/common_toasts.cpp
|
||||
ui/toasts/common_toasts.h
|
||||
ui/widgets/separate_panel.cpp
|
||||
ui/widgets/separate_panel.h
|
||||
ui/cached_round_corners.cpp
|
||||
ui/cached_round_corners.h
|
||||
ui/grouped_layout.cpp
|
||||
@ -131,6 +143,8 @@ target_link_libraries(td_ui
|
||||
PUBLIC
|
||||
tdesktop::td_lang
|
||||
desktop-app::lib_ui
|
||||
desktop-app::lib_ffmpeg
|
||||
desktop-app::lib_lottie
|
||||
PRIVATE
|
||||
desktop-app::lib_ffmpeg
|
||||
desktop-app::lib_webview
|
||||
)
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 8f0c0164cdce6bcbc7bcfe531963e2a552f6290d
|
||||
Subproject commit 7a5fd82692d2fb5df9b48c08c354f4400157a999
|
@ -1 +1 @@
|
||||
Subproject commit 404c83d77e5edb8a39f8e9f56a6340960fe5070e
|
||||
Subproject commit 45faed44e7f4d11fec79b7a70e4a35dc91ef3fdb
|
@ -1 +1 @@
|
||||
Subproject commit 99089134e34c19e4c6fdb25569ade0d6f081bdb1
|
||||
Subproject commit 8686905ee40eb8dbe171024e04e41a32069c8add
|
@ -1 +1 @@
|
||||
Subproject commit f95214cbe4b0a31ac989e0aceb8cc4f63c1322e6
|
||||
Subproject commit 5270a1dbbdbee643e187e175f798595b4bc49996
|
@ -1 +1 @@
|
||||
Subproject commit 7491d160231a18dec6aec1f3c1e1575382d10745
|
||||
Subproject commit 49887261a55665f6e195049bcc22b6495a44cc36
|
Loading…
Reference in New Issue
Block a user