Support clipboard reading for attach menu bots.

This commit is contained in:
John Preston 2022-12-20 20:43:19 +04:00
parent 4c181b6d08
commit c647afec02
4 changed files with 100 additions and 33 deletions

View file

@ -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<Ui::DropdownMenu> 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<BotAction>(
raw,
raw->menu()->st(),
bot,
[=] { bots->request(nullptr, actionFactory(), bot.user, {}); });
callback);
action->forceShown(
) | rpl::start_with_next([=](bool shown) {
if (shown) {

View file

@ -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<void()> callback = nullptr);

View file

@ -32,6 +32,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonArray>
#include <QtGui/QGuiApplication>
#include <QtGui/QClipboard>
namespace Ui::BotWebView {
namespace {
@ -344,7 +346,8 @@ Panel::Panel(
QString phone,
MenuButtons menuButtons,
Fn<void(MenuButton)> handleMenuButton,
Fn<Webview::ThemeParams()> themeParams)
Fn<Webview::ThemeParams()> 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<SeparatePanel>()) {
, _widget(std::make_unique<SeparatePanel>())
, _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 &params) {
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<QString>(data)
? v::get<QString>(data).toUtf8()
: QJsonDocument(
v::get<QJsonObject>(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<Panel> 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) {

View file

@ -52,7 +52,8 @@ public:
QString phone,
MenuButtons menuButtons,
Fn<void(MenuButton)> handleMenuButton,
Fn<Webview::ThemeParams()> themeParams);
Fn<Webview::ThemeParams()> 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<QString, QJsonObject>;
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<RpWidget> _webviewBottom;
QPointer<QWidget> _webviewParent;
std::unique_ptr<Button> _mainButton;
crl::time _mainButtonLastClick = 0;
mutable crl::time _mainButtonLastClick = 0;
std::unique_ptr<Progress> _progress;
rpl::event_stream<> _themeUpdateForced;
rpl::lifetime _fgLifetime;
@ -128,6 +134,7 @@ private:
bool _themeUpdateScheduled = false;
bool _hiddenForPayment = false;
bool _closeWithConfirmationScheduled = false;
bool _allowClipboardRead = false;
};
@ -144,6 +151,7 @@ struct Args {
MenuButtons menuButtons;
Fn<void(MenuButton)> handleMenuButton;
Fn<Webview::ThemeParams()> themeParams;
bool allowClipboardRead = false;
};
[[nodiscard]] std::unique_ptr<Panel> Show(Args &&args);