Support web_app_open_tg_link / web_app_open_invoice.

This commit is contained in:
John Preston 2022-05-11 18:55:04 +04:00
parent 26c99cea7c
commit 87662de2a6
11 changed files with 230 additions and 62 deletions

View file

@ -733,7 +733,7 @@ bool ResolveInvoice(
Payments::CheckoutProcess::Start( Payments::CheckoutProcess::Start(
&controller->session(), &controller->session(),
slug, slug,
crl::guard(window, [=] { window->activate(); })); crl::guard(window, [=](auto) { window->activate(); }));
return true; return true;
} }

View file

@ -120,7 +120,7 @@ void activateBotCommand(
Payments::CheckoutProcess::Start( Payments::CheckoutProcess::Start(
msg, msg,
Payments::Mode::Payment, Payments::Mode::Payment,
crl::guard(App::wnd(), [] { App::wnd()->activate(); })); crl::guard(App::wnd(), [](auto) { App::wnd()->activate(); }));
} break; } break;
case ButtonType::Url: { case ButtonType::Url: {

View file

@ -1032,24 +1032,31 @@ void History::applyServiceChanges(
} }
}, [&](const MTPDmessageActionPaymentSent &data) { }, [&](const MTPDmessageActionPaymentSent &data) {
if (const auto payment = item->Get<HistoryServicePayment>()) { if (const auto payment = item->Get<HistoryServicePayment>()) {
auto paid = std::optional<Payments::PaidInvoice>();
if (const auto message = payment->msg) { if (const auto message = payment->msg) {
if (const auto media = message->media()) { if (const auto media = message->media()) {
if (const auto invoice = media->invoice()) { if (const auto invoice = media->invoice()) {
using Payments::CheckoutProcess; paid = Payments::CheckoutProcess::InvoicePaid(
if (CheckoutProcess::TakePaymentStarted(message)) { message);
// Toast on a current active window.
Ui::ShowMultilineToast({
.text = tr::lng_payments_success(
tr::now,
lt_amount,
Ui::Text::Bold(payment->amount),
lt_title,
Ui::Text::Bold(invoice->title),
Ui::Text::WithEntities),
});
}
} }
} }
} else if (!payment->slug.isEmpty()) {
using Payments::CheckoutProcess;
paid = Payments::CheckoutProcess::InvoicePaid(
&session(),
payment->slug);
}
if (paid) {
// Toast on a current active window.
Ui::ShowMultilineToast({
.text = tr::lng_payments_success(
tr::now,
lt_amount,
Ui::Text::Bold(payment->amount),
lt_title,
Ui::Text::Bold(paid->title),
Ui::Text::WithEntities),
});
} }
} }
}, [&](const MTPDmessageActionSetChatTheme &data) { }, [&](const MTPDmessageActionSetChatTheme &data) {

View file

@ -1372,7 +1372,7 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) {
CheckoutProcess::Start( CheckoutProcess::Start(
item, item,
Mode::Receipt, Mode::Receipt,
crl::guard(weak, [=] { weak->window().activate(); })); crl::guard(weak, [=](auto) { weak->window().activate(); }));
} }
}); });
} else if (type == mtpc_messageActionGroupCall } else if (type == mtpc_messageActionGroupCall

View file

@ -34,6 +34,7 @@ struct HistoryServiceGameScore
struct HistoryServicePayment struct HistoryServicePayment
: public RuntimeComponent<HistoryServicePayment, HistoryItem> : public RuntimeComponent<HistoryServicePayment, HistoryItem>
, public HistoryServiceDependentData { , public HistoryServiceDependentData {
QString slug;
QString amount; QString amount;
ClickHandlerPtr invoiceLink; ClickHandlerPtr invoiceLink;
}; };

View file

@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/basic_click_handlers.h" #include "ui/basic_click_handlers.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "payments/payments_checkout_process.h"
#include "storage/storage_account.h" #include "storage/storage_account.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "base/random.h" #include "base/random.h"
@ -711,6 +712,28 @@ void AttachWebView::show(
close(); close();
return true; return true;
}; };
const auto panel = std::make_shared<
base::weak_ptr<Ui::BotWebView::Panel>>(nullptr);
const auto handleInvoice = [=, session = _session](QString slug) {
using Result = Payments::CheckoutResult;
const auto reactivate = [=](Result result) {
if (const auto strong = panel->get()) {
strong->invoiceClosed(slug, [&] {
switch (result) {
case Result::Paid: return "paid";
case Result::Failed: return "failed";
case Result::Pending: return "pending";
case Result::Cancelled: return "cancelled";
}
Unexpected("Payments::CheckoutResult value.");
}());
}
};
if (const auto strong = panel->get()) {
strong->hideForPayment();
}
Payments::CheckoutProcess::Start(session, slug, reactivate);
};
auto title = Info::Profile::NameValue( auto title = Info::Profile::NameValue(
_bot _bot
) | rpl::map([](const TextWithEntities &value) { ) | rpl::map([](const TextWithEntities &value) {
@ -723,10 +746,12 @@ void AttachWebView::show(
.title = std::move(title), .title = std::move(title),
.bottom = rpl::single('@' + _bot->username), .bottom = rpl::single('@' + _bot->username),
.handleLocalUri = handleLocalUri, .handleLocalUri = handleLocalUri,
.handleInvoice = handleInvoice,
.sendData = sendData, .sendData = sendData,
.close = close, .close = close,
.themeParams = [] { return Window::Theme::WebViewParams(); }, .themeParams = [] { return Window::Theme::WebViewParams(); },
}); });
*panel = _panel.get();
started(queryId); started(queryId);
} }

View file

@ -37,8 +37,8 @@ namespace {
struct SessionProcesses { struct SessionProcesses {
base::flat_map<FullMsgId, std::unique_ptr<CheckoutProcess>> byItem; base::flat_map<FullMsgId, std::unique_ptr<CheckoutProcess>> byItem;
base::flat_map<QString, std::unique_ptr<CheckoutProcess>> bySlug; base::flat_map<QString, std::unique_ptr<CheckoutProcess>> bySlug;
base::flat_set<FullMsgId> paymentStartedByItem; base::flat_map<FullMsgId, PaidInvoice> paymentStartedByItem;
base::flat_set<QString> paymentStartedBySlug; base::flat_map<QString, PaidInvoice> paymentStartedBySlug;
rpl::lifetime lifetime; rpl::lifetime lifetime;
}; };
@ -64,7 +64,7 @@ base::flat_map<not_null<Main::Session*>, SessionProcesses> Processes;
void CheckoutProcess::Start( void CheckoutProcess::Start(
not_null<const HistoryItem*> item, not_null<const HistoryItem*> item,
Mode mode, Mode mode,
Fn<void()> reactivate) { Fn<void(CheckoutResult)> reactivate) {
auto &processes = LookupSessionProcesses(&item->history()->session()); auto &processes = LookupSessionProcesses(&item->history()->session());
const auto media = item->media(); const auto media = item->media();
const auto invoice = media ? media->invoice() : nullptr; const auto invoice = media ? media->invoice() : nullptr;
@ -99,7 +99,7 @@ void CheckoutProcess::Start(
void CheckoutProcess::Start( void CheckoutProcess::Start(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const QString &slug, const QString &slug,
Fn<void()> reactivate) { Fn<void(CheckoutResult)> reactivate) {
auto &processes = LookupSessionProcesses(session); auto &processes = LookupSessionProcesses(session);
const auto i = processes.bySlug.find(slug); const auto i = processes.bySlug.find(slug);
if (i != end(processes.bySlug)) { if (i != end(processes.bySlug)) {
@ -117,24 +117,57 @@ void CheckoutProcess::Start(
j->second->requestActivate(); j->second->requestActivate();
} }
bool CheckoutProcess::TakePaymentStarted( std::optional<PaidInvoice> CheckoutProcess::InvoicePaid(
not_null<const HistoryItem*> item) { not_null<const HistoryItem*> item) {
const auto session = &item->history()->session(); const auto session = &item->history()->session();
const auto itemId = item->fullId(); const auto itemId = item->fullId();
const auto i = Processes.find(session); const auto i = Processes.find(session);
if (i == end(Processes) if (i == end(Processes)) {
|| !i->second.paymentStartedByItem.contains(itemId)) { return std::nullopt;
return false;
} }
i->second.paymentStartedByItem.erase(itemId); const auto k = i->second.paymentStartedByItem.find(itemId);
if (k == end(i->second.paymentStartedByItem)) {
return std::nullopt;
}
const auto result = k->second;
i->second.paymentStartedByItem.erase(k);
const auto j = i->second.byItem.find(itemId); const auto j = i->second.byItem.find(itemId);
if (j != end(i->second.byItem)) { if (j != end(i->second.byItem)) {
j->second->closeAndReactivate(); j->second->closeAndReactivate(CheckoutResult::Paid);
} else if (i->second.paymentStartedByItem.empty() } else if (i->second.paymentStartedByItem.empty()
&& i->second.byItem.empty()) { && i->second.byItem.empty()
&& i->second.paymentStartedBySlug.empty()
&& i->second.bySlug.empty()) {
Processes.erase(i); Processes.erase(i);
} }
return true; return result;
}
std::optional<PaidInvoice> CheckoutProcess::InvoicePaid(
not_null<Main::Session*> session,
const QString &slug) {
const auto i = Processes.find(session);
if (i == end(Processes)) {
return std::nullopt;
}
const auto k = i->second.paymentStartedBySlug.find(slug);
if (k == end(i->second.paymentStartedBySlug)) {
return std::nullopt;
}
const auto result = k->second;
i->second.paymentStartedBySlug.erase(k);
const auto j = i->second.bySlug.find(slug);
if (j != end(i->second.bySlug)) {
j->second->closeAndReactivate(CheckoutResult::Paid);
} else if (i->second.paymentStartedByItem.empty()
&& i->second.byItem.empty()
&& i->second.paymentStartedBySlug.empty()
&& i->second.bySlug.empty()) {
Processes.erase(i);
}
return result;
} }
void CheckoutProcess::ClearAll() { void CheckoutProcess::ClearAll() {
@ -142,18 +175,19 @@ void CheckoutProcess::ClearAll() {
} }
void CheckoutProcess::RegisterPaymentStart( void CheckoutProcess::RegisterPaymentStart(
not_null<CheckoutProcess*> process) { not_null<CheckoutProcess*> process,
PaidInvoice info) {
const auto i = Processes.find(process->_session); const auto i = Processes.find(process->_session);
Assert(i != end(Processes)); Assert(i != end(Processes));
for (const auto &[itemId, itemProcess] : i->second.byItem) { for (const auto &[itemId, itemProcess] : i->second.byItem) {
if (itemProcess.get() == process) { if (itemProcess.get() == process) {
i->second.paymentStartedByItem.emplace(itemId); i->second.paymentStartedByItem.emplace(itemId, info);
return; return;
} }
} }
for (const auto &[slug, itemProcess] : i->second.bySlug) { for (const auto &[slug, itemProcess] : i->second.bySlug) {
if (itemProcess.get() == process) { if (itemProcess.get() == process) {
i->second.paymentStartedBySlug.emplace(slug); i->second.paymentStartedBySlug.emplace(slug, info);
return; return;
} }
} }
@ -162,26 +196,33 @@ void CheckoutProcess::RegisterPaymentStart(
void CheckoutProcess::UnregisterPaymentStart( void CheckoutProcess::UnregisterPaymentStart(
not_null<CheckoutProcess*> process) { not_null<CheckoutProcess*> process) {
const auto i = Processes.find(process->_session); const auto i = Processes.find(process->_session);
if (i != end(Processes)) { if (i == end(Processes)) {
for (const auto &[itemId, itemProcess] : i->second.byItem) { return;
if (itemProcess.get() == process) { }
i->second.paymentStartedByItem.emplace(itemId); for (const auto &[itemId, itemProcess] : i->second.byItem) {
return; if (itemProcess.get() == process) {
} i->second.paymentStartedByItem.remove(itemId);
break;
} }
for (const auto &[slug, itemProcess] : i->second.bySlug) { }
if (itemProcess.get() == process) { for (const auto &[slug, itemProcess] : i->second.bySlug) {
i->second.paymentStartedBySlug.emplace(slug); if (itemProcess.get() == process) {
return; i->second.paymentStartedBySlug.remove(slug);
} break;
} }
} }
if (i->second.paymentStartedByItem.empty()
&& i->second.byItem.empty()
&& i->second.paymentStartedBySlug.empty()
&& i->second.bySlug.empty()) {
Processes.erase(i);
}
} }
CheckoutProcess::CheckoutProcess( CheckoutProcess::CheckoutProcess(
InvoiceId id, InvoiceId id,
Mode mode, Mode mode,
Fn<void()> reactivate, Fn<void(CheckoutResult)> reactivate,
PrivateTag) PrivateTag)
: _session(SessionFromId(id)) : _session(SessionFromId(id))
, _form(std::make_unique<Form>(id, (mode == Mode::Receipt))) , _form(std::make_unique<Form>(id, (mode == Mode::Receipt)))
@ -221,7 +262,8 @@ CheckoutProcess::CheckoutProcess(
CheckoutProcess::~CheckoutProcess() { CheckoutProcess::~CheckoutProcess() {
} }
void CheckoutProcess::setReactivateCallback(Fn<void()> reactivate) { void CheckoutProcess::setReactivateCallback(
Fn<void(CheckoutResult)> reactivate) {
_reactivate = std::move(reactivate); _reactivate = std::move(reactivate);
} }
@ -276,6 +318,8 @@ void CheckoutProcess::handleFormUpdate(const FormUpdate &update) {
auto bottomText = tr::lng_payments_processed_by( auto bottomText = tr::lng_payments_processed_by(
lt_provider, lt_provider,
rpl::single(_form->invoice().provider)); rpl::single(_form->invoice().provider));
_sendFormFailed = false;
_sendFormPending = true;
if (!_panel->showWebview(data.url, false, std::move(bottomText))) { if (!_panel->showWebview(data.url, false, std::move(bottomText))) {
File::OpenUrl(data.url); File::OpenUrl(data.url);
close(); close();
@ -284,7 +328,7 @@ void CheckoutProcess::handleFormUpdate(const FormUpdate &update) {
const auto weak = base::make_weak(this); const auto weak = base::make_weak(this);
_session->api().applyUpdates(data.updates); _session->api().applyUpdates(data.updates);
if (weak) { if (weak) {
closeAndReactivate(); closeAndReactivate(CheckoutResult::Paid);
} }
}, [&](const Error &error) { }, [&](const Error &error) {
handleError(error); handleError(error);
@ -386,6 +430,7 @@ void CheckoutProcess::handleError(const Error &error) {
} }
break; break;
case Error::Type::Send: case Error::Type::Send:
_sendFormFailed = true;
if (const auto box = _enterPasswordBox.data()) { if (const auto box = _enterPasswordBox.data()) {
box->closeBox(); box->closeBox();
} }
@ -424,14 +469,18 @@ void CheckoutProcess::panelRequestClose() {
} }
void CheckoutProcess::panelCloseSure() { void CheckoutProcess::panelCloseSure() {
closeAndReactivate(); closeAndReactivate(_sendFormFailed
? CheckoutResult::Failed
: _sendFormPending
? CheckoutResult::Pending
: CheckoutResult::Cancelled);
} }
void CheckoutProcess::closeAndReactivate() { void CheckoutProcess::closeAndReactivate(CheckoutResult result) {
const auto reactivate = std::move(_reactivate); const auto reactivate = std::move(_reactivate);
close(); close();
if (reactivate) { if (reactivate) {
reactivate(); reactivate(result);
} }
} }
@ -463,7 +512,7 @@ void CheckoutProcess::close() {
void CheckoutProcess::panelSubmit() { void CheckoutProcess::panelSubmit() {
if (_form->invoice().receipt.paid) { if (_form->invoice().receipt.paid) {
closeAndReactivate(); closeAndReactivate(CheckoutResult::Paid);
return; return;
} else if (_submitState == SubmitState::Validating } else if (_submitState == SubmitState::Validating
|| _submitState == SubmitState::Finishing) { || _submitState == SubmitState::Finishing) {
@ -485,7 +534,7 @@ void CheckoutProcess::panelSubmit() {
} else if (!method.newCredentials && !method.savedCredentials) { } else if (!method.newCredentials && !method.savedCredentials) {
editPaymentMethod(); editPaymentMethod();
} else { } else {
RegisterPaymentStart(this); RegisterPaymentStart(this, { _form->invoice().cover.title });
_submitState = SubmitState::Finishing; _submitState = SubmitState::Finishing;
_form->submit(); _form->submit();
} }
@ -550,7 +599,8 @@ bool CheckoutProcess::panelWebviewNavigationAttempt(const QString &uri) {
if (Core::TryConvertUrlToLocal(uri) == uri) { if (Core::TryConvertUrlToLocal(uri) == uri) {
return true; return true;
} }
crl::on_main(this, [=] { closeAndReactivate(); }); // #TODO payments
crl::on_main(this, [=] { closeAndReactivate(CheckoutResult::Paid); });
return false; return false;
} }

View file

@ -43,6 +43,17 @@ enum class Mode {
Receipt, Receipt,
}; };
enum class CheckoutResult {
Paid,
Pending,
Cancelled,
Failed,
};
struct PaidInvoice {
QString title;
};
class CheckoutProcess final class CheckoutProcess final
: public base::has_weak_ptr : public base::has_weak_ptr
, private Ui::PanelDelegate { , private Ui::PanelDelegate {
@ -52,19 +63,22 @@ public:
static void Start( static void Start(
not_null<const HistoryItem*> item, not_null<const HistoryItem*> item,
Mode mode, Mode mode,
Fn<void()> reactivate); Fn<void(CheckoutResult)> reactivate);
static void Start( static void Start(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const QString &slug, const QString &slug,
Fn<void()> reactivate); Fn<void(CheckoutResult)> reactivate);
[[nodiscard]] static bool TakePaymentStarted( [[nodiscard]] static std::optional<PaidInvoice> InvoicePaid(
not_null<const HistoryItem*> item); not_null<const HistoryItem*> item);
[[nodiscard]] static std::optional<PaidInvoice> InvoicePaid(
not_null<Main::Session*> session,
const QString &slug);
static void ClearAll(); static void ClearAll();
CheckoutProcess( CheckoutProcess(
InvoiceId id, InvoiceId id,
Mode mode, Mode mode,
Fn<void()> reactivate, Fn<void(CheckoutResult)> reactivate,
PrivateTag); PrivateTag);
~CheckoutProcess(); ~CheckoutProcess();
@ -77,12 +91,14 @@ private:
}; };
[[nodiscard]] not_null<PanelDelegate*> panelDelegate(); [[nodiscard]] not_null<PanelDelegate*> panelDelegate();
static void RegisterPaymentStart(not_null<CheckoutProcess*> process); static void RegisterPaymentStart(
not_null<CheckoutProcess*> process,
PaidInvoice info);
static void UnregisterPaymentStart(not_null<CheckoutProcess*> process); static void UnregisterPaymentStart(not_null<CheckoutProcess*> process);
void setReactivateCallback(Fn<void()> reactivate); void setReactivateCallback(Fn<void(CheckoutResult)> reactivate);
void requestActivate(); void requestActivate();
void closeAndReactivate(); void closeAndReactivate(CheckoutResult result);
void close(); void close();
void handleFormUpdate(const FormUpdate &update); void handleFormUpdate(const FormUpdate &update);
@ -137,9 +153,11 @@ private:
const std::unique_ptr<Form> _form; const std::unique_ptr<Form> _form;
const std::unique_ptr<Ui::Panel> _panel; const std::unique_ptr<Ui::Panel> _panel;
QPointer<PasscodeBox> _enterPasswordBox; QPointer<PasscodeBox> _enterPasswordBox;
Fn<void()> _reactivate; Fn<void(CheckoutResult)> _reactivate;
SubmitState _submitState = SubmitState::None; SubmitState _submitState = SubmitState::None;
bool _initialSilentValidation = false; bool _initialSilentValidation = false;
bool _sendFormPending = false;
bool _sendFormFailed = false;
bool _themeUpdateScheduled = false; bool _themeUpdateScheduled = false;
rpl::lifetime _gettingPasswordState; rpl::lifetime _gettingPasswordState;

View file

@ -673,7 +673,7 @@ void DownloadMtprotoTask::cdnPartLoaded(const MTPupload_CdnFile &result, mtpRequ
auto ivec = bytes::make_span(state.ivec); auto ivec = bytes::make_span(state.ivec);
std::copy(iv.begin(), iv.end(), ivec.begin()); std::copy(iv.begin(), iv.end(), ivec.begin());
auto counterOffset = static_cast<uint32>(requestData.offset) >> 4; auto counterOffset = static_cast<uint32>(requestData.offset >> 4);
state.ivec[15] = static_cast<uchar>(counterOffset & 0xFF); state.ivec[15] = static_cast<uchar>(counterOffset & 0xFF);
state.ivec[14] = static_cast<uchar>((counterOffset >> 8) & 0xFF); state.ivec[14] = static_cast<uchar>((counterOffset >> 8) & 0xFF);
state.ivec[13] = static_cast<uchar>((counterOffset >> 16) & 0xFF); state.ivec[13] = static_cast<uchar>((counterOffset >> 16) & 0xFF);

View file

@ -305,11 +305,13 @@ Panel::Panel(
const QString &userDataPath, const QString &userDataPath,
rpl::producer<QString> title, rpl::producer<QString> title,
Fn<bool(QString)> handleLocalUri, Fn<bool(QString)> handleLocalUri,
Fn<void(QString)> handleInvoice,
Fn<void(QByteArray)> sendData, Fn<void(QByteArray)> sendData,
Fn<void()> close, Fn<void()> close,
Fn<Webview::ThemeParams()> themeParams) Fn<Webview::ThemeParams()> themeParams)
: _userDataPath(userDataPath) : _userDataPath(userDataPath)
, _handleLocalUri(std::move(handleLocalUri)) , _handleLocalUri(std::move(handleLocalUri))
, _handleInvoice(std::move(handleInvoice))
, _sendData(std::move(sendData)) , _sendData(std::move(sendData))
, _close(std::move(close)) , _close(std::move(close))
, _widget(std::make_unique<SeparatePanel>()) { , _widget(std::make_unique<SeparatePanel>()) {
@ -320,7 +322,9 @@ Panel::Panel(
) | rpl::start_with_next(_close, _widget->lifetime()); ) | rpl::start_with_next(_close, _widget->lifetime());
_widget->closeEvents( _widget->closeEvents(
) | rpl::start_with_next(_close, _widget->lifetime()); ) | rpl::filter([=] {
return !_hiddenForPayment;
}) | rpl::start_with_next(_close, _widget->lifetime());
rpl::combine( rpl::combine(
style::PaletteChanged(), style::PaletteChanged(),
@ -568,6 +572,10 @@ bool Panel::createWebview() {
processMainButtonMessage(list.at(1)); processMainButtonMessage(list.at(1));
} else if (command == "web_app_request_theme") { } else if (command == "web_app_request_theme") {
_themeUpdateForced.fire({}); _themeUpdateForced.fire({});
} else if (command == "web_app_open_tg_link") {
openTgLink(list.at(1).toString());
} else if (command == "web_app_open_invoice") {
openInvoice(list.at(1).toString());
} }
}); });
@ -618,6 +626,38 @@ void Panel::sendDataMessage(const QJsonValue &value) {
_sendData(data.toUtf8()); _sendData(data.toUtf8());
} }
void Panel::openTgLink(const QJsonValue &value) {
const auto json = value.toString();
const auto args = ParseMethodArgs(json);
if (args.isEmpty()) {
_close();
return;
}
const auto path = args["path_full"].toString();
if (path.isEmpty()) {
LOG(("BotWebView Error: Bad tg link \"%1\".").arg(json));
_close();
return;
}
_handleLocalUri("https://t.me" + path);
}
void Panel::openInvoice(const QJsonValue &value) {
const auto json = value.toString();
const auto args = ParseMethodArgs(json);
if (args.isEmpty()) {
_close();
return;
}
const auto slug = args["slug"].toString();
if (slug.isEmpty()) {
LOG(("BotWebView Error: Bad invoice \"%1\".").arg(json));
_close();
return;
}
_handleInvoice(slug);
}
void Panel::processMainButtonMessage(const QJsonValue &value) { void Panel::processMainButtonMessage(const QJsonValue &value) {
const auto json = value.toString(); const auto json = value.toString();
const auto args = ParseMethodArgs(json); const auto args = ParseMethodArgs(json);
@ -760,6 +800,22 @@ void Panel::updateThemeParams(const Webview::ThemeParams &params) {
postEvent("theme_changed", "\"theme_params\": " + params.json); postEvent("theme_changed", "\"theme_params\": " + params.json);
} }
void Panel::invoiceClosed(const QString &slug, const QString &status) {
if (!_webview || !_webview->window.widget()) {
return;
}
postEvent(
"invoice_closed",
"\"slug\": \"" + slug + "\", \"status\": \"" + status + "\"");
_widget->showAndActivate();
_hiddenForPayment = false;
}
void Panel::hideForPayment() {
_hiddenForPayment = true;
_widget->hideGetDuration();
}
void Panel::postEvent(const QString &event, const QString &data) { void Panel::postEvent(const QString &event, const QString &data) {
_webview->window.eval(R"( _webview->window.eval(R"(
if (window.TelegramGameProxy) { if (window.TelegramGameProxy) {
@ -820,6 +876,7 @@ std::unique_ptr<Panel> Show(Args &&args) {
args.userDataPath, args.userDataPath,
std::move(args.title), std::move(args.title),
std::move(args.handleLocalUri), std::move(args.handleLocalUri),
std::move(args.handleInvoice),
std::move(args.sendData), std::move(args.sendData),
std::move(args.close), std::move(args.close),
std::move(args.themeParams)); std::move(args.themeParams));

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "base/object_ptr.h" #include "base/object_ptr.h"
#include "base/weak_ptr.h"
namespace Ui { namespace Ui {
class BoxContent; class BoxContent;
@ -29,12 +30,13 @@ struct MainButtonArgs {
QString text; QString text;
}; };
class Panel final { class Panel final : public base::has_weak_ptr {
public: public:
Panel( Panel(
const QString &userDataPath, const QString &userDataPath,
rpl::producer<QString> title, rpl::producer<QString> title,
Fn<bool(QString)> handleLocalUri, Fn<bool(QString)> handleLocalUri,
Fn<void(QString)> handleInvoice,
Fn<void(QByteArray)> sendData, Fn<void(QByteArray)> sendData,
Fn<void()> close, Fn<void()> close,
Fn<Webview::ThemeParams()> themeParams); Fn<Webview::ThemeParams()> themeParams);
@ -57,6 +59,9 @@ public:
void updateThemeParams(const Webview::ThemeParams &params); void updateThemeParams(const Webview::ThemeParams &params);
void hideForPayment();
void invoiceClosed(const QString &slug, const QString &status);
[[nodiscard]] rpl::lifetime &lifetime(); [[nodiscard]] rpl::lifetime &lifetime();
private: private:
@ -70,6 +75,8 @@ private:
void setTitle(rpl::producer<QString> title); void setTitle(rpl::producer<QString> title);
void sendDataMessage(const QJsonValue &value); void sendDataMessage(const QJsonValue &value);
void processMainButtonMessage(const QJsonValue &value); void processMainButtonMessage(const QJsonValue &value);
void openTgLink(const QJsonValue &value);
void openInvoice(const QJsonValue &value);
void createMainButton(); void createMainButton();
void postEvent(const QString &event, const QString &data = {}); void postEvent(const QString &event, const QString &data = {});
@ -80,6 +87,7 @@ private:
QString _userDataPath; QString _userDataPath;
Fn<bool(QString)> _handleLocalUri; Fn<bool(QString)> _handleLocalUri;
Fn<void(QString)> _handleInvoice;
Fn<void(QByteArray)> _sendData; Fn<void(QByteArray)> _sendData;
Fn<void()> _close; Fn<void()> _close;
std::unique_ptr<SeparatePanel> _widget; std::unique_ptr<SeparatePanel> _widget;
@ -93,6 +101,7 @@ private:
rpl::lifetime _bgLifetime; rpl::lifetime _bgLifetime;
bool _webviewProgress = false; bool _webviewProgress = false;
bool _themeUpdateScheduled = false; bool _themeUpdateScheduled = false;
bool _hiddenForPayment = false;
}; };
@ -102,6 +111,7 @@ struct Args {
rpl::producer<QString> title; rpl::producer<QString> title;
rpl::producer<QString> bottom; rpl::producer<QString> bottom;
Fn<bool(QString)> handleLocalUri; Fn<bool(QString)> handleLocalUri;
Fn<void(QString)> handleInvoice;
Fn<void(QByteArray)> sendData; Fn<void(QByteArray)> sendData;
Fn<void()> close; Fn<void()> close;
Fn<Webview::ThemeParams()> themeParams; Fn<Webview::ThemeParams()> themeParams;