diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp index 746569730..7601ef562 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp @@ -490,7 +490,11 @@ void AttachWebView::request(const WebViewButton &button) { )).done([=](const MTPWebViewResult &result) { _requestId = 0; result.match([&](const MTPDwebViewResultUrl &data) { - show(data.vquery_id().v, qs(data.vurl()), button.text); + show( + data.vquery_id().v, + qs(data.vurl()), + button.text, + button.fromMenu); }); }).fail([=](const MTP::Error &error) { _requestId = 0; @@ -792,7 +796,8 @@ void AttachWebView::ClearAll() { void AttachWebView::show( uint64 queryId, const QString &url, - const QString &buttonText) { + const QString &buttonText, + bool fromMenu) { Expects(_bot != nullptr && _action.has_value()); const auto close = crl::guard(this, [=] { @@ -916,6 +921,7 @@ void AttachWebView::show( .menuButtons = buttons, .handleMenuButton = handleMenuButton, .themeParams = [] { return Window::Theme::WebViewParams(); }, + .allowClipboardRead = fromMenu, }); *panel = _panel.get(); started(queryId); @@ -1023,11 +1029,18 @@ std::unique_ptr MakeAttachBotsMenu( if (!PeerMatchesTypes(peer, bot.user, bot.types)) { continue; } + const auto callback = [=] { + bots->request( + nullptr, + actionFactory(), + bot.user, + { .fromMenu = true }); + }; auto action = base::make_unique_q( raw, raw->menu()->st(), bot, - [=] { bots->request(nullptr, actionFactory(), bot.user, {}); }); + callback); action->forceShown( ) | rpl::start_with_next([=](bool shown) { if (shown) { diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h index d360d07ef..2fb9545e5 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h @@ -69,6 +69,7 @@ public: QString text; QString startCommand; QByteArray url; + bool fromMenu = false; }; void request( const Api::SendAction &action, @@ -127,7 +128,8 @@ private: void show( uint64 queryId, const QString &url, - const QString &buttonText = QString()); + const QString &buttonText = QString(), + bool fromMenu = false); void confirmAddToMenu( AttachWebViewBot bot, Fn callback = nullptr); diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp index e3cdee2b5..2d8b689ee 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp @@ -32,6 +32,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include #include +#include +#include namespace Ui::BotWebView { namespace { @@ -344,7 +346,8 @@ Panel::Panel( QString phone, MenuButtons menuButtons, Fn handleMenuButton, - Fn themeParams) + Fn themeParams, + bool allowClipboardRead) : _userDataPath(userDataPath) , _handleLocalUri(std::move(handleLocalUri)) , _handleInvoice(std::move(handleInvoice)) @@ -353,7 +356,8 @@ Panel::Panel( , _phone(phone) , _menuButtons(menuButtons) , _handleMenuButton(std::move(handleMenuButton)) -, _widget(std::make_unique()) { +, _widget(std::make_unique()) +, _allowClipboardRead(allowClipboardRead) { _widget->setInnerSize(st::paymentsPanelSize); _widget->setWindowFlag(Qt::WindowStaysOnTopHint, false); @@ -663,6 +667,8 @@ bool Panel::createWebview() { requestPhone(); } else if (command == "web_app_setup_closing_behavior") { setupClosingBehaviour(arguments); + } else if (command == "web_app_read_text_from_clipboard") { + requestClipboardText(arguments); } }); @@ -737,16 +743,10 @@ void Panel::openExternalLink(const QJsonObject &args) { LOG(("BotWebView Error: Bad 'url' in openExternalLink.")); _close(); return; + } else if (!allowOpenLink()) { + return; } - const auto now = crl::now(); - if (_mainButtonLastClick - && _mainButtonLastClick + kProcessClickTimeout >= now) { - _mainButtonLastClick = 0; - File::OpenUrl(url); - } else { - const auto string = EncodeForJs(url); - _webview->window.eval(("window.open(\"" + string + "\");").toUtf8()); - } + File::OpenUrl(url); } void Panel::openInvoice(const QJsonObject &args) { @@ -816,9 +816,9 @@ void Panel::openPopup(const QJsonObject &args) { QJsonArray{ { QJsonValue(*result.id) } } ).toJson(QJsonDocument::Compact) + "[0]"; }; - postEvent( - "popup_closed", - result.id ? ("\"button_id\": " + safe()) : QString()); + postEvent("popup_closed", result.id + ? QJsonObject{ { u"button_id"_q, *result.id } } + : EventData()); } } @@ -841,15 +841,49 @@ void Panel::requestPhone() { }, }); if (weak) { - postEvent( - "phone_requested", - (result.id == "share" - ? "\"phone_number\": \"" + _phone + "\"" - : QString())); + postEvent("phone_requested", (result.id == "share") + ? QJsonObject{ { u"phone_number"_q, _phone } } + : EventData()); } #endif } +void Panel::requestClipboardText(const QJsonObject &args) { + const auto requestId = args["req_id"]; + if (requestId.isUndefined()) { + return; + } + auto result = QJsonObject(); + result["req_id"] = requestId; + if (allowClipboardQuery()) { + result["data"] = QGuiApplication::clipboard()->text(); + } + postEvent(u"clipboard_text_received"_q, result); +} + +bool Panel::allowOpenLink() const { + const auto now = crl::now(); + if (_mainButtonLastClick + && _mainButtonLastClick + kProcessClickTimeout >= now) { + _mainButtonLastClick = 0; + return true; + } + return true; +} + +bool Panel::allowClipboardQuery() const { + if (!_allowClipboardRead) { + return false; + } + const auto now = crl::now(); + if (_mainButtonLastClick + && _mainButtonLastClick + kProcessClickTimeout >= now) { + _mainButtonLastClick = 0; + return true; + } + return true; +} + void Panel::scheduleCloseWithConfirmation() { if (!_closeWithConfirmationScheduled) { _closeWithConfirmationScheduled = true; @@ -1031,16 +1065,17 @@ void Panel::updateThemeParams(const Webview::ThemeParams ¶ms) { params.scrollBgOver, params.scrollBarBg, params.scrollBarBgOver); - 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 + "\""); + postEvent("invoice_closed", QJsonObject{ + { u"slug"_q, slug }, + { u"status"_q, status }, + }); _widget->showAndActivate(); _hiddenForPayment = false; } @@ -1050,13 +1085,21 @@ void Panel::hideForPayment() { _widget->hideGetDuration(); } -void Panel::postEvent(const QString &event, const QString &data) { +void Panel::postEvent(const QString &event) { + postEvent(event, {}); +} + +void Panel::postEvent(const QString &event, EventData data) { + auto written = v::is(data) + ? v::get(data).toUtf8() + : QJsonDocument( + v::get(data)).toJson(QJsonDocument::Compact); _webview->window.eval(R"( if (window.TelegramGameProxy) { window.TelegramGameProxy.receiveEvent( ")" + event.toUtf8() - + '"' + (data.isEmpty() ? QByteArray() : ", {" + data.toUtf8() + '}') + + '"' + (written.isEmpty() ? QByteArray() : ", " + written) + R"(); } )"); @@ -1110,7 +1153,8 @@ std::unique_ptr Show(Args &&args) { args.phone, args.menuButtons, std::move(args.handleMenuButton), - std::move(args.themeParams)); + std::move(args.themeParams), + args.allowClipboardRead); if (!result->showWebview(args.url, params, std::move(args.bottom))) { const auto available = Webview::Availability(); if (available.error != Webview::Available::Error::None) { diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h index c9880da98..0acd1e71e 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h @@ -52,7 +52,8 @@ public: QString phone, MenuButtons menuButtons, Fn handleMenuButton, - Fn themeParams); + Fn themeParams, + bool allowClipboardRead); ~Panel(); void requestActivate(); @@ -94,13 +95,18 @@ private: void openInvoice(const QJsonObject &args); void openPopup(const QJsonObject &args); void requestPhone(); + void requestClipboardText(const QJsonObject &args); void setupClosingBehaviour(const QJsonObject &args); void createMainButton(); void scheduleCloseWithConfirmation(); void closeWithConfirmation(); - void postEvent(const QString &event, const QString &data = {}); + using EventData = std::variant; + void postEvent(const QString &event); + void postEvent(const QString &event, EventData data); + [[nodiscard]] bool allowOpenLink() const; + [[nodiscard]] bool allowClipboardQuery() const; [[nodiscard]] bool progressWithBackground() const; [[nodiscard]] QRect progressRect() const; void setupProgressGeometry(); @@ -119,7 +125,7 @@ private: std::unique_ptr _webviewBottom; QPointer _webviewParent; std::unique_ptr