mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-03-25 04:38:23 +00:00
Support custom popups in attach bots.
This commit is contained in:
parent
e954871cb9
commit
c1e7e63677
@ -1970,6 +1970,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_bot_menu_not_supported" = "This bot isn't supported in the attach menu.";
|
||||
"lng_bot_menu_already_added" = "This bot is already added in your attach menu.";
|
||||
"lng_bot_menu_button" = "Menu";
|
||||
"lng_bot_close_warning_title" = "Warning";
|
||||
"lng_bot_close_warning" = "Changes that you made may not be saved.";
|
||||
"lng_bot_close_warning_sure" = "Close anyway";
|
||||
|
||||
"lng_typing" = "typing";
|
||||
"lng_user_typing" = "{user} is typing";
|
||||
|
@ -333,10 +333,26 @@ QString UiIntegration::phraseButtonOk() {
|
||||
return tr::lng_box_ok(tr::now);
|
||||
}
|
||||
|
||||
QString UiIntegration::phraseButtonClose() {
|
||||
return tr::lng_close(tr::now);
|
||||
}
|
||||
|
||||
QString UiIntegration::phraseButtonCancel() {
|
||||
return tr::lng_cancel(tr::now);
|
||||
}
|
||||
|
||||
QString UiIntegration::phrasePanelCloseWarning() {
|
||||
return tr::lng_bot_close_warning_title(tr::now);
|
||||
}
|
||||
|
||||
QString UiIntegration::phrasePanelCloseUnsaved() {
|
||||
return tr::lng_bot_close_warning(tr::now);
|
||||
}
|
||||
|
||||
QString UiIntegration::phrasePanelCloseAnyway() {
|
||||
return tr::lng_bot_close_warning_sure(tr::now);
|
||||
}
|
||||
|
||||
bool OpenGLLastCheckFailed() {
|
||||
return QFile::exists(OpenGLCheckFilePath());
|
||||
}
|
||||
|
@ -75,7 +75,11 @@ public:
|
||||
QString phraseFormattingMonospace() override;
|
||||
QString phraseFormattingSpoiler() override;
|
||||
QString phraseButtonOk() override;
|
||||
QString phraseButtonClose() override;
|
||||
QString phraseButtonCancel() override;
|
||||
QString phrasePanelCloseWarning() override;
|
||||
QString phrasePanelCloseUnsaved() override;
|
||||
QString phrasePanelCloseAnyway() override;
|
||||
|
||||
};
|
||||
|
||||
|
@ -17,10 +17,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/menu/menu_add_action_callback.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "ui/integration.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "webview/webview_embed.h"
|
||||
#include "webview/webview_dialog.h"
|
||||
#include "webview/webview_interface.h"
|
||||
#include "base/debug_log.h"
|
||||
#include "base/invoke_queued.h"
|
||||
#include "styles/style_payments.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
@ -40,11 +43,14 @@ constexpr auto kLightnessThreshold = 128;
|
||||
constexpr auto kLightnessDelta = 32;
|
||||
|
||||
[[nodiscard]] QJsonObject ParseMethodArgs(const QString &json) {
|
||||
if (json.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
auto error = QJsonParseError();
|
||||
const auto dictionary = QJsonDocument::fromJson(json.toUtf8(), &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
LOG(("BotWebView Error: Could not parse \"%1\".").arg(json));
|
||||
return QJsonObject();
|
||||
return {};
|
||||
}
|
||||
return dictionary.object();
|
||||
}
|
||||
@ -349,7 +355,13 @@ Panel::Panel(
|
||||
_widget->setWindowFlag(Qt::WindowStaysOnTopHint, false);
|
||||
|
||||
_widget->closeRequests(
|
||||
) | rpl::start_with_next(_close, _widget->lifetime());
|
||||
) | rpl::start_with_next([=] {
|
||||
if (_closeNeedConfirmation) {
|
||||
scheduleCloseWithConfirmation();
|
||||
} else {
|
||||
_close();
|
||||
}
|
||||
}, _widget->lifetime());
|
||||
|
||||
_widget->closeEvents(
|
||||
) | rpl::filter([=] {
|
||||
@ -625,22 +637,27 @@ bool Panel::createWebview() {
|
||||
}
|
||||
const auto list = message.array();
|
||||
const auto command = list.at(0).toString();
|
||||
const auto arguments = ParseMethodArgs(list.at(1).toString());
|
||||
if (command == "web_app_close") {
|
||||
_close();
|
||||
} else if (command == "web_app_data_send") {
|
||||
sendDataMessage(list.at(1));
|
||||
sendDataMessage(arguments);
|
||||
} else if (command == "web_app_setup_main_button") {
|
||||
processMainButtonMessage(list.at(1));
|
||||
processMainButtonMessage(arguments);
|
||||
} else if (command == "web_app_setup_back_button") {
|
||||
processBackButtonMessage(list.at(1));
|
||||
processBackButtonMessage(arguments);
|
||||
} else if (command == "web_app_request_theme") {
|
||||
_themeUpdateForced.fire({});
|
||||
} else if (command == "web_app_open_tg_link") {
|
||||
openTgLink(list.at(1).toString());
|
||||
openTgLink(arguments);
|
||||
} else if (command == "web_app_open_link") {
|
||||
openExternalLink(list.at(1).toString());
|
||||
openExternalLink(arguments);
|
||||
} else if (command == "web_app_open_invoice") {
|
||||
openInvoice(list.at(1).toString());
|
||||
openInvoice(arguments);
|
||||
} else if (command == "web_app_open_popup") {
|
||||
openPopup(arguments);
|
||||
} else if (command == "web_app_setup_closing_behavior") {
|
||||
setupClosingBehaviour(arguments);
|
||||
}
|
||||
});
|
||||
|
||||
@ -675,41 +692,35 @@ void Panel::setTitle(rpl::producer<QString> title) {
|
||||
_widget->setTitle(std::move(title));
|
||||
}
|
||||
|
||||
void Panel::sendDataMessage(const QJsonValue &value) {
|
||||
const auto json = value.toString();
|
||||
const auto args = ParseMethodArgs(json);
|
||||
void Panel::sendDataMessage(const QJsonObject &args) {
|
||||
if (args.isEmpty()) {
|
||||
_close();
|
||||
return;
|
||||
}
|
||||
const auto data = args["data"].toString();
|
||||
if (data.isEmpty()) {
|
||||
LOG(("BotWebView Error: Bad data \"%1\".").arg(json));
|
||||
LOG(("BotWebView Error: Bad 'data' in sendDataMessage."));
|
||||
_close();
|
||||
return;
|
||||
}
|
||||
_sendData(data.toUtf8());
|
||||
}
|
||||
|
||||
void Panel::openTgLink(const QJsonValue &value) {
|
||||
const auto json = value.toString();
|
||||
const auto args = ParseMethodArgs(json);
|
||||
void Panel::openTgLink(const QJsonObject &args) {
|
||||
if (args.isEmpty()) {
|
||||
_close();
|
||||
return;
|
||||
}
|
||||
const auto path = args["path_full"].toString();
|
||||
if (path.isEmpty()) {
|
||||
LOG(("BotWebView Error: Bad tg link \"%1\".").arg(json));
|
||||
LOG(("BotWebView Error: Bad 'path_full' in openTgLink."));
|
||||
_close();
|
||||
return;
|
||||
}
|
||||
_handleLocalUri("https://t.me" + path);
|
||||
}
|
||||
|
||||
void Panel::openExternalLink(const QJsonValue &value) {
|
||||
const auto json = value.toString();
|
||||
const auto args = ParseMethodArgs(json);
|
||||
void Panel::openExternalLink(const QJsonObject &args) {
|
||||
if (args.isEmpty()) {
|
||||
_close();
|
||||
return;
|
||||
@ -718,7 +729,7 @@ void Panel::openExternalLink(const QJsonValue &value) {
|
||||
const auto lower = url.toLower();
|
||||
if (url.isEmpty()
|
||||
|| (!lower.startsWith("http://") && !lower.startsWith("https://"))) {
|
||||
LOG(("BotWebView Error: Bad external link \"%1\".").arg(json));
|
||||
LOG(("BotWebView Error: Bad 'url' in openExternalLink."));
|
||||
_close();
|
||||
return;
|
||||
}
|
||||
@ -733,25 +744,119 @@ void Panel::openExternalLink(const QJsonValue &value) {
|
||||
}
|
||||
}
|
||||
|
||||
void Panel::openInvoice(const QJsonValue &value) {
|
||||
const auto json = value.toString();
|
||||
const auto args = ParseMethodArgs(json);
|
||||
void Panel::openInvoice(const QJsonObject &args) {
|
||||
if (args.isEmpty()) {
|
||||
_close();
|
||||
return;
|
||||
}
|
||||
const auto slug = args["slug"].toString();
|
||||
if (slug.isEmpty()) {
|
||||
LOG(("BotWebView Error: Bad invoice \"%1\".").arg(json));
|
||||
LOG(("BotWebView Error: Bad 'slug' in openInvoice."));
|
||||
_close();
|
||||
return;
|
||||
}
|
||||
_handleInvoice(slug);
|
||||
}
|
||||
|
||||
void Panel::processMainButtonMessage(const QJsonValue &value) {
|
||||
const auto json = value.toString();
|
||||
const auto args = ParseMethodArgs(json);
|
||||
void Panel::openPopup(const QJsonObject &args) {
|
||||
if (args.isEmpty()) {
|
||||
_close();
|
||||
return;
|
||||
}
|
||||
using Button = Webview::PopupArgs::Button;
|
||||
using Type = Button::Type;
|
||||
const auto message = args["message"].toString();
|
||||
const auto types = base::flat_map<QString, Button::Type>{
|
||||
{ "default", Type::Default },
|
||||
{ "ok", Type::Ok },
|
||||
{ "close", Type::Close },
|
||||
{ "cancel", Type::Cancel },
|
||||
{ "destructive", Type::Destructive },
|
||||
};
|
||||
auto buttons = std::vector<Webview::PopupArgs::Button>();
|
||||
for (const auto &button : args["buttons"].toArray()) {
|
||||
const auto fields = button.toObject();
|
||||
const auto i = types.find(fields["type"].toString());
|
||||
if (i == end(types)) {
|
||||
LOG(("BotWebView Error: Bad 'type' in openPopup buttons."));
|
||||
_close();
|
||||
return;
|
||||
}
|
||||
buttons.push_back({
|
||||
.id = fields["id"].toString(),
|
||||
.text = fields["text"].toString(),
|
||||
.type = i->second,
|
||||
});
|
||||
}
|
||||
if (message.isEmpty()) {
|
||||
LOG(("BotWebView Error: Bad 'message' in openPopup."));
|
||||
_close();
|
||||
return;
|
||||
} else if (buttons.empty()) {
|
||||
LOG(("BotWebView Error: Bad 'buttons' in openPopup."));
|
||||
_close();
|
||||
return;
|
||||
}
|
||||
const auto widget = _webview->window.widget();
|
||||
const auto weak = base::make_weak(this);
|
||||
const auto result = Webview::ShowBlockingPopup({
|
||||
.parent = widget ? widget->window() : nullptr,
|
||||
.title = args["title"].toString(),
|
||||
.text = message,
|
||||
.buttons = std::move(buttons),
|
||||
});
|
||||
if (weak) {
|
||||
const auto safe = [&] {
|
||||
return QJsonDocument(
|
||||
QJsonArray{ { QJsonValue(*result.id) } }
|
||||
).toJson(QJsonDocument::Compact) + "[0]";
|
||||
};
|
||||
postEvent(
|
||||
"popup_closed",
|
||||
result.id ? ("\"button_id\": " + safe()) : QString());
|
||||
}
|
||||
}
|
||||
|
||||
void Panel::scheduleCloseWithConfirmation() {
|
||||
if (!_closeWithConfirmationScheduled) {
|
||||
_closeWithConfirmationScheduled = true;
|
||||
InvokeQueued(_widget.get(), [=] { closeWithConfirmation(); });
|
||||
}
|
||||
}
|
||||
|
||||
void Panel::closeWithConfirmation() {
|
||||
using Button = Webview::PopupArgs::Button;
|
||||
const auto widget = _webview->window.widget();
|
||||
const auto weak = base::make_weak(this);
|
||||
const auto integration = &Ui::Integration::Instance();
|
||||
const auto result = Webview::ShowBlockingPopup({
|
||||
.parent = widget ? widget->window() : nullptr,
|
||||
.title = integration->phrasePanelCloseWarning(),
|
||||
.text = integration->phrasePanelCloseUnsaved(),
|
||||
.buttons = {
|
||||
{
|
||||
.id = "close",
|
||||
.text = integration->phrasePanelCloseAnyway(),
|
||||
.type = Button::Type::Destructive,
|
||||
},
|
||||
{ .id = "cancel", .type = Button::Type::Cancel },
|
||||
},
|
||||
.ignoreFloodCheck = true,
|
||||
});
|
||||
if (!weak) {
|
||||
return;
|
||||
} else if (result.id != "cancel") {
|
||||
_close();
|
||||
} else {
|
||||
_closeWithConfirmationScheduled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Panel::setupClosingBehaviour(const QJsonObject &args) {
|
||||
_closeNeedConfirmation = args["need_confirmation"].toBool();
|
||||
}
|
||||
|
||||
void Panel::processMainButtonMessage(const QJsonObject &args) {
|
||||
if (args.isEmpty()) {
|
||||
_close();
|
||||
return;
|
||||
@ -795,9 +900,7 @@ void Panel::processMainButtonMessage(const QJsonValue &value) {
|
||||
});
|
||||
}
|
||||
|
||||
void Panel::processBackButtonMessage(const QJsonValue &value) {
|
||||
const auto json = value.toString();
|
||||
const auto args = ParseMethodArgs(json);
|
||||
void Panel::processBackButtonMessage(const QJsonObject &args) {
|
||||
_widget->setBackAllowed(args["is_visible"].toBool());
|
||||
}
|
||||
|
||||
|
@ -85,13 +85,17 @@ private:
|
||||
void showWebviewProgress();
|
||||
void hideWebviewProgress();
|
||||
void setTitle(rpl::producer<QString> title);
|
||||
void sendDataMessage(const QJsonValue &value);
|
||||
void processMainButtonMessage(const QJsonValue &value);
|
||||
void processBackButtonMessage(const QJsonValue &value);
|
||||
void openTgLink(const QJsonValue &value);
|
||||
void openExternalLink(const QJsonValue &value);
|
||||
void openInvoice(const QJsonValue &value);
|
||||
void sendDataMessage(const QJsonObject &args);
|
||||
void processMainButtonMessage(const QJsonObject &args);
|
||||
void processBackButtonMessage(const QJsonObject &args);
|
||||
void openTgLink(const QJsonObject &args);
|
||||
void openExternalLink(const QJsonObject &args);
|
||||
void openInvoice(const QJsonObject &args);
|
||||
void openPopup(const QJsonObject &args);
|
||||
void setupClosingBehaviour(const QJsonObject &args);
|
||||
void createMainButton();
|
||||
void scheduleCloseWithConfirmation();
|
||||
void closeWithConfirmation();
|
||||
|
||||
void postEvent(const QString &event, const QString &data = {});
|
||||
|
||||
@ -104,6 +108,7 @@ private:
|
||||
Fn<void(QString)> _handleInvoice;
|
||||
Fn<void(QByteArray)> _sendData;
|
||||
Fn<void()> _close;
|
||||
bool _closeNeedConfirmation = false;
|
||||
MenuButtons _menuButtons = {};
|
||||
Fn<void(MenuButton)> _handleMenuButton;
|
||||
std::unique_ptr<SeparatePanel> _widget;
|
||||
@ -119,6 +124,7 @@ private:
|
||||
bool _webviewProgress = false;
|
||||
bool _themeUpdateScheduled = false;
|
||||
bool _hiddenForPayment = false;
|
||||
bool _closeWithConfirmationScheduled = false;
|
||||
|
||||
};
|
||||
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit d7318d849ef89873a1b4f78f23e471555061921b
|
||||
Subproject commit a7074136b4d8ba17b623cf673afc054f3aa3145b
|
Loading…
Reference in New Issue
Block a user