mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-17 22:57:11 +02:00
Implement new bot web-app methods.
This commit is contained in:
parent
8255de1ba8
commit
d77c7a70ab
14 changed files with 478 additions and 236 deletions
|
@ -1611,6 +1611,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_action_suggested_video" = "{user} suggests you to use this profile video.";
|
||||
"lng_action_suggested_video_button" = "View Video";
|
||||
"lng_action_attach_menu_bot_allowed" = "You allowed this bot to message you when you added it in the attachment menu.";
|
||||
"lng_action_webapp_bot_allowed" = "You allowed this bot to message you in his web-app.";
|
||||
"lng_action_set_wallpaper_me" = "You set a new wallpaper for this chat";
|
||||
"lng_action_set_wallpaper" = "{user} set a new wallpaper for this chat";
|
||||
"lng_action_set_wallpaper_button" = "View Wallpaper";
|
||||
|
@ -1799,6 +1800,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_bot_share_location_unavailable" = "Sorry, location sharing is currently unavailable in Telegram Desktop.";
|
||||
"lng_bot_share_phone" = "Do you want to share your phone number with this bot?";
|
||||
"lng_bot_share_phone_confirm" = "Share";
|
||||
"lng_bot_allow_write_title" = "Allow messaging";
|
||||
"lng_bot_allow_write" = "Do you want to allow this bot writing you?";
|
||||
"lng_bot_allow_write_confirm" = "Allow";
|
||||
|
||||
"lng_attach_failed" = "Failed";
|
||||
"lng_attach_file" = "File";
|
||||
|
|
|
@ -3316,24 +3316,24 @@ void ApiWrap::forwardMessages(
|
|||
_session->data().sendHistoryChangeNotifications();
|
||||
}
|
||||
|
||||
void ApiWrap::shareContact(
|
||||
FullMsgId ApiWrap::shareContact(
|
||||
const QString &phone,
|
||||
const QString &firstName,
|
||||
const QString &lastName,
|
||||
const SendAction &action) {
|
||||
const auto userId = UserId(0);
|
||||
sendSharedContact(phone, firstName, lastName, userId, action);
|
||||
return sendSharedContact(phone, firstName, lastName, userId, action);
|
||||
}
|
||||
|
||||
void ApiWrap::shareContact(
|
||||
FullMsgId ApiWrap::shareContact(
|
||||
not_null<UserData*> user,
|
||||
const SendAction &action) {
|
||||
const auto userId = peerToUser(user->id);
|
||||
const auto phone = _session->data().findContactPhone(user);
|
||||
if (phone.isEmpty()) {
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
sendSharedContact(
|
||||
return sendSharedContact(
|
||||
phone,
|
||||
user->firstName,
|
||||
user->lastName,
|
||||
|
@ -3341,7 +3341,7 @@ void ApiWrap::shareContact(
|
|||
action);
|
||||
}
|
||||
|
||||
void ApiWrap::sendSharedContact(
|
||||
FullMsgId ApiWrap::sendSharedContact(
|
||||
const QString &phone,
|
||||
const QString &firstName,
|
||||
const QString &lastName,
|
||||
|
@ -3391,6 +3391,7 @@ void ApiWrap::sendSharedContact(
|
|||
MTP_string(), // vcard
|
||||
MTP_long(userId.bare)),
|
||||
HistoryMessageMarkupData());
|
||||
const auto result = item->fullId();
|
||||
|
||||
const auto media = MTP_inputMediaContact(
|
||||
MTP_string(phone),
|
||||
|
@ -3405,6 +3406,8 @@ void ApiWrap::sendSharedContact(
|
|||
(action.options.scheduled
|
||||
? Data::HistoryUpdate::Flag::ScheduledSent
|
||||
: Data::HistoryUpdate::Flag::MessageSent));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ApiWrap::sendVoiceMessage(
|
||||
|
|
|
@ -290,12 +290,14 @@ public:
|
|||
Data::ResolvedForwardDraft &&draft,
|
||||
const SendAction &action,
|
||||
FnMut<void()> &&successCallback = nullptr);
|
||||
void shareContact(
|
||||
FullMsgId shareContact(
|
||||
const QString &phone,
|
||||
const QString &firstName,
|
||||
const QString &lastName,
|
||||
const SendAction &action);
|
||||
void shareContact(not_null<UserData*> user, const SendAction &action);
|
||||
FullMsgId shareContact(
|
||||
not_null<UserData*> user,
|
||||
const SendAction &action);
|
||||
void applyAffectedMessages(
|
||||
not_null<PeerData*> peer,
|
||||
const MTPmessages_AffectedMessages &result);
|
||||
|
@ -484,7 +486,7 @@ private:
|
|||
SharedMediaType type,
|
||||
Api::SearchResult &&parsed);
|
||||
|
||||
void sendSharedContact(
|
||||
FullMsgId sendSharedContact(
|
||||
const QString &phone,
|
||||
const QString &firstName,
|
||||
const QString &lastName,
|
||||
|
|
|
@ -370,7 +370,6 @@ QString UiIntegration::phrasePanelCloseAnyway() {
|
|||
return tr::lng_bot_close_warning_sure(tr::now);
|
||||
}
|
||||
|
||||
#if 0 // disabled for now
|
||||
QString UiIntegration::phraseBotSharePhone() {
|
||||
return tr::lng_bot_share_phone(tr::now);
|
||||
}
|
||||
|
@ -382,7 +381,18 @@ QString UiIntegration::phraseBotSharePhoneTitle() {
|
|||
QString UiIntegration::phraseBotSharePhoneConfirm() {
|
||||
return tr::lng_bot_share_phone_confirm(tr::now);
|
||||
}
|
||||
#endif
|
||||
|
||||
QString UiIntegration::phraseBotAllowWrite() {
|
||||
return tr::lng_bot_allow_write(tr::now);
|
||||
}
|
||||
|
||||
QString UiIntegration::phraseBotAllowWriteTitle() {
|
||||
return tr::lng_bot_allow_write_title(tr::now);
|
||||
}
|
||||
|
||||
QString UiIntegration::phraseBotAllowWriteConfirm() {
|
||||
return tr::lng_bot_allow_write_confirm(tr::now);
|
||||
}
|
||||
|
||||
bool OpenGLLastCheckFailed() {
|
||||
return QFile::exists(OpenGLCheckFilePath());
|
||||
|
|
|
@ -84,11 +84,12 @@ public:
|
|||
QString phrasePanelCloseWarning() override;
|
||||
QString phrasePanelCloseUnsaved() override;
|
||||
QString phrasePanelCloseAnyway() override;
|
||||
#if 0 // disabled for now
|
||||
QString phraseBotSharePhone() override;
|
||||
QString phraseBotSharePhoneTitle() override;
|
||||
QString phraseBotSharePhoneConfirm() override;
|
||||
#endif
|
||||
QString phraseBotAllowWrite() override;
|
||||
QString phraseBotAllowWriteTitle() override;
|
||||
QString phraseBotAllowWriteConfirm() override;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -1173,6 +1173,7 @@ ServiceAction ParseServiceAction(
|
|||
content.domain = ParseString(*domain);
|
||||
}
|
||||
content.attachMenu = data.is_attach_menu();
|
||||
content.fromRequest = data.is_from_request();
|
||||
result.content = content;
|
||||
}, [&](const MTPDmessageActionSecureValuesSentMe &data) {
|
||||
// Should not be in user inbox.
|
||||
|
|
|
@ -439,6 +439,7 @@ struct ActionBotAllowed {
|
|||
Utf8String app;
|
||||
Utf8String domain;
|
||||
bool attachMenu = false;
|
||||
bool fromRequest = false;
|
||||
};
|
||||
|
||||
struct ActionSecureValuesSent {
|
||||
|
|
|
@ -1127,6 +1127,8 @@ auto HtmlWriter::Wrap::pushMessage(
|
|||
return data.attachMenu
|
||||
? "You allowed this bot to message you "
|
||||
"when you added it in the attachment menu."_q
|
||||
: data.fromRequest
|
||||
? "You allowed this bot to message you in his web-app."_q
|
||||
: data.app.isEmpty()
|
||||
? ("You allowed this bot to message you when you opened "
|
||||
+ SerializeString(data.app))
|
||||
|
|
|
@ -477,6 +477,8 @@ QByteArray SerializeMessage(
|
|||
}, [&](const ActionBotAllowed &data) {
|
||||
if (data.attachMenu) {
|
||||
pushAction("attach_menu_bot_allowed");
|
||||
} else if (data.fromRequest) {
|
||||
pushAction("web_app_bot_allowed");
|
||||
} else if (data.appId) {
|
||||
pushAction("allow_sending_messages");
|
||||
push("reason_app_id", data.appId);
|
||||
|
|
|
@ -3887,6 +3887,10 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
|
|||
result.text = {
|
||||
tr::lng_action_attach_menu_bot_allowed(tr::now)
|
||||
};
|
||||
} else if (action.is_from_request()) {
|
||||
result.text = {
|
||||
tr::lng_action_webapp_bot_allowed(tr::now)
|
||||
};
|
||||
} else if (const auto app = action.vapp()) {
|
||||
const auto bot = history()->peer->asUser();
|
||||
const auto botId = bot ? bot->id : PeerId();
|
||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "api/api_common.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "data/data_bot_app.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_document.h"
|
||||
|
@ -462,6 +463,226 @@ void AttachWebView::request(
|
|||
resolve();
|
||||
}
|
||||
|
||||
Webview::ThemeParams AttachWebView::botThemeParams() {
|
||||
return Window::Theme::WebViewParams();
|
||||
}
|
||||
|
||||
bool AttachWebView::botHandleLocalUri(QString uri) {
|
||||
const auto local = Core::TryConvertUrlToLocal(uri);
|
||||
if (uri == local || Core::InternalPassportLink(local)) {
|
||||
return local.startsWith(u"tg://"_q);
|
||||
} else if (!local.startsWith(u"tg://"_q, Qt::CaseInsensitive)) {
|
||||
return false;
|
||||
}
|
||||
botClose();
|
||||
crl::on_main([=, shownUrl = _lastShownUrl] {
|
||||
const auto variant = QVariant::fromValue(ClickHandlerContext{
|
||||
.attachBotWebviewUrl = shownUrl,
|
||||
});
|
||||
UrlClickHandler::Open(local, variant);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
void AttachWebView::botHandleInvoice(QString slug) {
|
||||
Expects(_panel != nullptr);
|
||||
|
||||
using Result = Payments::CheckoutResult;
|
||||
const auto weak = base::make_weak(_panel.get());
|
||||
const auto reactivate = [=](Result result) {
|
||||
if (const auto strong = weak.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.");
|
||||
}());
|
||||
}
|
||||
};
|
||||
_panel->hideForPayment();
|
||||
Payments::CheckoutProcess::Start(&_bot->session(), slug, reactivate);
|
||||
}
|
||||
|
||||
void AttachWebView::botHandleMenuButton(Ui::BotWebView::MenuButton button) {
|
||||
Expects(_bot != nullptr);
|
||||
Expects(_panel != nullptr);
|
||||
|
||||
using Button = Ui::BotWebView::MenuButton;
|
||||
const auto bot = _bot;
|
||||
switch (button) {
|
||||
case Button::OpenBot:
|
||||
botClose();
|
||||
if (bot->session().windows().empty()) {
|
||||
Core::App().domain().activate(&bot->session().account());
|
||||
}
|
||||
if (!bot->session().windows().empty()) {
|
||||
const auto window = bot->session().windows().front();
|
||||
window->showPeerHistory(bot);
|
||||
window->window().activate();
|
||||
}
|
||||
break;
|
||||
case Button::RemoveFromMenu:
|
||||
const auto attached = ranges::find(
|
||||
_attachBots,
|
||||
not_null{ _bot },
|
||||
&AttachWebViewBot::user);
|
||||
const auto name = (attached != end(_attachBots))
|
||||
? attached->name
|
||||
: _bot->name();
|
||||
const auto done = crl::guard(this, [=] {
|
||||
removeFromMenu(bot);
|
||||
botClose();
|
||||
if (const auto active = Core::App().activeWindow()) {
|
||||
active->activate();
|
||||
}
|
||||
});
|
||||
_panel->showBox(Ui::MakeConfirmBox({
|
||||
tr::lng_bot_remove_from_menu_sure(
|
||||
tr::now,
|
||||
lt_bot,
|
||||
Ui::Text::Bold(name),
|
||||
Ui::Text::WithEntities),
|
||||
done,
|
||||
}));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AttachWebView::botSendData(QByteArray data) {
|
||||
if (!_context
|
||||
|| _context->fromSwitch
|
||||
|| _context->fromBotApp
|
||||
|| _context->action.history->peer != _bot
|
||||
|| _lastShownQueryId) {
|
||||
return;
|
||||
}
|
||||
const auto randomId = base::RandomValue<uint64>();
|
||||
_session->api().request(MTPmessages_SendWebViewData(
|
||||
_bot->inputUser,
|
||||
MTP_long(randomId),
|
||||
MTP_string(_lastShownButtonText),
|
||||
MTP_bytes(data)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
_session->api().applyUpdates(result);
|
||||
}).send();
|
||||
crl::on_main(this, [=] { cancel(); });
|
||||
}
|
||||
|
||||
void AttachWebView::botSwitchInlineQuery(
|
||||
std::vector<QString> chatTypes,
|
||||
QString query) {
|
||||
const auto controller = _context
|
||||
? _context->controller.get()
|
||||
: nullptr;
|
||||
const auto types = PeerTypesFromNames(chatTypes);
|
||||
if (!_bot
|
||||
|| !_bot->isBot()
|
||||
|| _bot->botInfo->inlinePlaceholder.isEmpty()
|
||||
|| !controller) {
|
||||
return;
|
||||
} else if (!types) {
|
||||
if (_context->dialogsEntryState.key.owningHistory()) {
|
||||
controller->switchInlineQuery(
|
||||
_context->dialogsEntryState,
|
||||
_bot,
|
||||
query);
|
||||
}
|
||||
} else {
|
||||
const auto bot = _bot;
|
||||
const auto done = [=](not_null<Data::Thread*> thread) {
|
||||
controller->switchInlineQuery(thread, bot, query);
|
||||
};
|
||||
ShowChooseBox(
|
||||
controller,
|
||||
types,
|
||||
done,
|
||||
tr::lng_inline_switch_choose());
|
||||
}
|
||||
crl::on_main(this, [=] { cancel(); });
|
||||
}
|
||||
|
||||
void AttachWebView::botCheckWriteAccess(Fn<void(bool allowed)> callback) {
|
||||
_session->api().request(MTPbots_CanSendMessage(
|
||||
_bot->inputUser
|
||||
)).done([=](const MTPBool &result) {
|
||||
callback(mtpIsTrue(result));
|
||||
}).fail([=] {
|
||||
callback(false);
|
||||
}).send();
|
||||
}
|
||||
|
||||
void AttachWebView::botAllowWriteAccess(Fn<void(bool allowed)> callback) {
|
||||
_session->api().request(MTPbots_AllowSendMessage(
|
||||
_bot->inputUser
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
_session->api().applyUpdates(result);
|
||||
callback(true);
|
||||
}).fail([=] {
|
||||
callback(false);
|
||||
}).send();
|
||||
}
|
||||
|
||||
void AttachWebView::botSharePhone(Fn<void(bool shared)> callback) {
|
||||
const auto history = _bot->owner().history(_bot);
|
||||
auto action = Api::SendAction(history);
|
||||
action.clearDraft = false;
|
||||
const auto id = history->session().api().shareContact(
|
||||
_bot->session().user(),
|
||||
action);
|
||||
const auto owner = &_bot->owner();
|
||||
const auto lifetime = std::make_shared<rpl::lifetime>();
|
||||
const auto check = [=] {
|
||||
const auto item = id ? owner->message(id) : nullptr;
|
||||
if (!item || item->hasFailed()) {
|
||||
lifetime->destroy();
|
||||
callback(false);
|
||||
}
|
||||
};
|
||||
|
||||
_bot->session().changes().historyUpdates(
|
||||
history,
|
||||
Data::HistoryUpdate::Flag::ClientSideMessages
|
||||
) | rpl::start_with_next(check, *lifetime);
|
||||
|
||||
owner->itemRemoved(
|
||||
) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
|
||||
if (item->fullId() == id) {
|
||||
check();
|
||||
}
|
||||
}, *lifetime);
|
||||
|
||||
owner->itemIdChanged(
|
||||
) | rpl::start_with_next([=](const Data::Session::IdChange &change) {
|
||||
if (FullMsgId(change.newId.peer, change.oldId) == id) {
|
||||
lifetime->destroy();
|
||||
callback(true);
|
||||
}
|
||||
}, *lifetime);
|
||||
|
||||
check();
|
||||
}
|
||||
|
||||
void AttachWebView::botInvokeCustomMethod(
|
||||
Ui::BotWebView::CustomMethodRequest request) {
|
||||
const auto callback = request.callback;
|
||||
_bot->session().api().request(MTPbots_InvokeWebViewCustomMethod(
|
||||
_bot->inputUser,
|
||||
MTP_string(request.method),
|
||||
MTP_dataJSON(MTP_bytes(request.params))
|
||||
)).done([=](const MTPDataJSON &result) {
|
||||
callback(result.data().vdata().v);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
callback(base::make_unexpected(error.type()));
|
||||
}).send();
|
||||
}
|
||||
|
||||
void AttachWebView::botClose() {
|
||||
crl::on_main(this, [=] { cancel(); });
|
||||
}
|
||||
|
||||
AttachWebView::Context AttachWebView::LookupContext(
|
||||
not_null<Window::SessionController*> controller,
|
||||
const Api::SendAction &action) {
|
||||
|
@ -568,7 +789,7 @@ void AttachWebView::cancel() {
|
|||
ActiveWebViews().remove(this);
|
||||
_session->api().request(base::take(_requestId)).cancel();
|
||||
_session->api().request(base::take(_prolongId)).cancel();
|
||||
_panel = nullptr;
|
||||
base::take(_panel);
|
||||
_lastShownContext = base::take(_context);
|
||||
_bot = nullptr;
|
||||
_app = nullptr;
|
||||
|
@ -723,6 +944,8 @@ std::optional<Api::SendAction> AttachWebView::lookupLastAction(
|
|||
}
|
||||
|
||||
void AttachWebView::resolve() {
|
||||
Expects(!_panel);
|
||||
|
||||
resolveUsername(_botUsername, [=](not_null<PeerData*> bot) {
|
||||
if (!_context) {
|
||||
return;
|
||||
|
@ -1004,98 +1227,6 @@ void AttachWebView::show(
|
|||
bool allowClipboardRead) {
|
||||
Expects(_bot != nullptr && _context != nullptr);
|
||||
|
||||
const auto close = crl::guard(this, [=] {
|
||||
crl::on_main(this, [=] { cancel(); });
|
||||
});
|
||||
const auto sendData = crl::guard(this, [=](QByteArray data) {
|
||||
if (!_context
|
||||
|| _context->fromSwitch
|
||||
|| _context->fromBotApp
|
||||
|| _context->action.history->peer != _bot
|
||||
|| queryId) {
|
||||
return;
|
||||
}
|
||||
const auto randomId = base::RandomValue<uint64>();
|
||||
_session->api().request(MTPmessages_SendWebViewData(
|
||||
_bot->inputUser,
|
||||
MTP_long(randomId),
|
||||
MTP_string(buttonText),
|
||||
MTP_bytes(data)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
_session->api().applyUpdates(result);
|
||||
}).send();
|
||||
crl::on_main(this, [=] { cancel(); });
|
||||
});
|
||||
const auto switchInlineQuery = crl::guard(this, [=](
|
||||
std::vector<QString> typeNames,
|
||||
QString query) {
|
||||
const auto controller = _context
|
||||
? _context->controller.get()
|
||||
: nullptr;
|
||||
const auto types = PeerTypesFromNames(typeNames);
|
||||
if (!_bot
|
||||
|| !_bot->isBot()
|
||||
|| _bot->botInfo->inlinePlaceholder.isEmpty()
|
||||
|| !controller) {
|
||||
return;
|
||||
} else if (!types) {
|
||||
if (_context->dialogsEntryState.key.owningHistory()) {
|
||||
controller->switchInlineQuery(
|
||||
_context->dialogsEntryState,
|
||||
_bot,
|
||||
query);
|
||||
}
|
||||
} else {
|
||||
const auto bot = _bot;
|
||||
const auto done = [=](not_null<Data::Thread*> thread) {
|
||||
controller->switchInlineQuery(thread, bot, query);
|
||||
};
|
||||
ShowChooseBox(
|
||||
controller,
|
||||
types,
|
||||
done,
|
||||
tr::lng_inline_switch_choose());
|
||||
}
|
||||
crl::on_main(this, [=] { cancel(); });
|
||||
});
|
||||
const auto handleLocalUri = [close, url](QString uri) {
|
||||
const auto local = Core::TryConvertUrlToLocal(uri);
|
||||
if (uri == local || Core::InternalPassportLink(local)) {
|
||||
return local.startsWith(u"tg://"_q);
|
||||
} else if (!local.startsWith(u"tg://"_q, Qt::CaseInsensitive)) {
|
||||
return false;
|
||||
}
|
||||
close();
|
||||
crl::on_main([=] {
|
||||
const auto variant = QVariant::fromValue(ClickHandlerContext{
|
||||
.attachBotWebviewUrl = url,
|
||||
});
|
||||
UrlClickHandler::Open(local, variant);
|
||||
});
|
||||
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(_bot);
|
||||
ActiveWebViews().emplace(this);
|
||||
|
||||
|
@ -1104,9 +1235,6 @@ void AttachWebView::show(
|
|||
_attachBots,
|
||||
not_null{ _bot },
|
||||
&AttachWebViewBot::user);
|
||||
const auto name = (attached != end(_attachBots))
|
||||
? attached->name
|
||||
: _bot->name();
|
||||
const auto hasSettings = (attached != end(_attachBots))
|
||||
&& !attached->inactive
|
||||
&& attached->hasSettings;
|
||||
|
@ -1119,59 +1247,19 @@ void AttachWebView::show(
|
|||
| (hasRemoveFromMenu ? Button::RemoveFromMenu : Button::None);
|
||||
const auto bot = _bot;
|
||||
|
||||
const auto handleMenuButton = crl::guard(this, [=](Button button) {
|
||||
switch (button) {
|
||||
case Button::OpenBot:
|
||||
close();
|
||||
if (bot->session().windows().empty()) {
|
||||
Core::App().domain().activate(&bot->session().account());
|
||||
}
|
||||
if (!bot->session().windows().empty()) {
|
||||
const auto window = bot->session().windows().front();
|
||||
window->showPeerHistory(bot);
|
||||
window->window().activate();
|
||||
}
|
||||
break;
|
||||
case Button::RemoveFromMenu:
|
||||
if (const auto strong = panel->get()) {
|
||||
const auto done = crl::guard(this, [=] {
|
||||
removeFromMenu(bot);
|
||||
close();
|
||||
if (const auto active = Core::App().activeWindow()) {
|
||||
active->activate();
|
||||
}
|
||||
});
|
||||
strong->showBox(Ui::MakeConfirmBox({
|
||||
tr::lng_bot_remove_from_menu_sure(
|
||||
tr::now,
|
||||
lt_bot,
|
||||
Ui::Text::Bold(name),
|
||||
Ui::Text::WithEntities),
|
||||
done,
|
||||
}));
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
_lastShownUrl = url;
|
||||
_lastShownQueryId = queryId;
|
||||
_lastShownButtonText = buttonText;
|
||||
base::take(_panel);
|
||||
_panel = Ui::BotWebView::Show({
|
||||
.url = url,
|
||||
.userDataPath = _session->domain().local().webviewDataPath(),
|
||||
.title = std::move(title),
|
||||
.bottom = rpl::single('@' + _bot->username()),
|
||||
.handleLocalUri = handleLocalUri,
|
||||
.handleInvoice = handleInvoice,
|
||||
.sendData = sendData,
|
||||
.switchInlineQuery = switchInlineQuery,
|
||||
.close = close,
|
||||
.phone = _session->user()->phone(),
|
||||
.delegate = static_cast<Ui::BotWebView::Delegate*>(this),
|
||||
.menuButtons = buttons,
|
||||
.handleMenuButton = handleMenuButton,
|
||||
.themeParams = [] { return Window::Theme::WebViewParams(); },
|
||||
.allowClipboardRead = allowClipboardRead,
|
||||
});
|
||||
*panel = _panel.get();
|
||||
started(queryId);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,9 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "mtproto/sender.h"
|
||||
#include "base/weak_ptr.h"
|
||||
#include "base/flags.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "ui/chat/attach/attach_bot_webview.h"
|
||||
|
||||
namespace Api {
|
||||
struct SendAction;
|
||||
|
@ -64,7 +65,9 @@ struct AttachWebViewBot {
|
|||
bool requestWriteAccess = false;
|
||||
};
|
||||
|
||||
class AttachWebView final : public base::has_weak_ptr {
|
||||
class AttachWebView final
|
||||
: public base::has_weak_ptr
|
||||
, public Ui::BotWebView::Delegate {
|
||||
public:
|
||||
explicit AttachWebView(not_null<Main::Session*> session);
|
||||
~AttachWebView();
|
||||
|
@ -130,6 +133,21 @@ public:
|
|||
private:
|
||||
struct Context;
|
||||
|
||||
Webview::ThemeParams botThemeParams() override;
|
||||
bool botHandleLocalUri(QString uri) override;
|
||||
void botHandleInvoice(QString slug) override;
|
||||
void botHandleMenuButton(Ui::BotWebView::MenuButton button) override;
|
||||
void botSendData(QByteArray data) override;
|
||||
void botSwitchInlineQuery(
|
||||
std::vector<QString> chatTypes,
|
||||
QString query) override;
|
||||
void botCheckWriteAccess(Fn<void(bool allowed)> callback) override;
|
||||
void botAllowWriteAccess(Fn<void(bool allowed)> callback) override;
|
||||
void botSharePhone(Fn<void(bool shared)> callback) override;
|
||||
void botInvokeCustomMethod(
|
||||
Ui::BotWebView::CustomMethodRequest request) override;
|
||||
void botClose() override;
|
||||
|
||||
[[nodiscard]] static Context LookupContext(
|
||||
not_null<Window::SessionController*> controller,
|
||||
const Api::SendAction &action);
|
||||
|
@ -185,6 +203,8 @@ private:
|
|||
std::unique_ptr<Context> _context;
|
||||
std::unique_ptr<Context> _lastShownContext;
|
||||
QString _lastShownUrl;
|
||||
uint64 _lastShownQueryId = 0;
|
||||
QString _lastShownButtonText;
|
||||
UserData *_bot = nullptr;
|
||||
QString _botUsername;
|
||||
QString _botAppName;
|
||||
|
|
|
@ -315,25 +315,12 @@ Panel::Progress::Progress(QWidget *parent, Fn<QRect()> rect)
|
|||
Panel::Panel(
|
||||
const QString &userDataPath,
|
||||
rpl::producer<QString> title,
|
||||
Fn<bool(QString)> handleLocalUri,
|
||||
Fn<void(QString)> handleInvoice,
|
||||
Fn<void(QByteArray)> sendData,
|
||||
Fn<void(std::vector<QString>, QString)> switchInlineQuery,
|
||||
Fn<void()> close,
|
||||
QString phone,
|
||||
not_null<Delegate*> delegate,
|
||||
MenuButtons menuButtons,
|
||||
Fn<void(MenuButton)> handleMenuButton,
|
||||
Fn<Webview::ThemeParams()> themeParams,
|
||||
bool allowClipboardRead)
|
||||
: _userDataPath(userDataPath)
|
||||
, _handleLocalUri(std::move(handleLocalUri))
|
||||
, _handleInvoice(std::move(handleInvoice))
|
||||
, _sendData(std::move(sendData))
|
||||
, _switchInlineQuery(std::move(switchInlineQuery))
|
||||
, _close(std::move(close))
|
||||
, _phone(phone)
|
||||
, _delegate(delegate)
|
||||
, _menuButtons(menuButtons)
|
||||
, _handleMenuButton(std::move(handleMenuButton))
|
||||
, _widget(std::make_unique<SeparatePanel>())
|
||||
, _allowClipboardRead(allowClipboardRead) {
|
||||
_widget->setInnerSize(st::paymentsPanelSize);
|
||||
|
@ -344,14 +331,16 @@ Panel::Panel(
|
|||
if (_closeNeedConfirmation) {
|
||||
scheduleCloseWithConfirmation();
|
||||
} else {
|
||||
_close();
|
||||
_delegate->botClose();
|
||||
}
|
||||
}, _widget->lifetime());
|
||||
|
||||
_widget->closeEvents(
|
||||
) | rpl::filter([=] {
|
||||
return !_hiddenForPayment;
|
||||
}) | rpl::start_with_next(_close, _widget->lifetime());
|
||||
}) | rpl::start_with_next([=] {
|
||||
_delegate->botClose();
|
||||
}, _widget->lifetime());
|
||||
|
||||
_widget->backRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
|
@ -367,7 +356,7 @@ Panel::Panel(
|
|||
_themeUpdateScheduled = true;
|
||||
crl::on_main(_widget.get(), [=] {
|
||||
_themeUpdateScheduled = false;
|
||||
updateThemeParams(themeParams());
|
||||
updateThemeParams(_delegate->botThemeParams());
|
||||
});
|
||||
}, _widget->lifetime());
|
||||
|
||||
|
@ -540,7 +529,7 @@ bool Panel::showWebview(
|
|||
}
|
||||
if (_menuButtons & MenuButton::OpenBot) {
|
||||
callback(tr::lng_bot_open(tr::now), [=] {
|
||||
_handleMenuButton(MenuButton::OpenBot);
|
||||
_delegate->botHandleMenuButton(MenuButton::OpenBot);
|
||||
}, &st::menuIconLeave);
|
||||
}
|
||||
callback(tr::lng_bot_reload_page(tr::now), [=] {
|
||||
|
@ -548,7 +537,7 @@ bool Panel::showWebview(
|
|||
}, &st::menuIconRestore);
|
||||
if (_menuButtons & MenuButton::RemoveFromMenu) {
|
||||
const auto handler = [=] {
|
||||
_handleMenuButton(MenuButton::RemoveFromMenu);
|
||||
_delegate->botHandleMenuButton(MenuButton::RemoveFromMenu);
|
||||
};
|
||||
callback({
|
||||
.text = tr::lng_bot_remove_from_menu(tr::now),
|
||||
|
@ -624,7 +613,7 @@ bool Panel::createWebview() {
|
|||
const auto command = list.at(0).toString();
|
||||
const auto arguments = ParseMethodArgs(list.at(1).toString());
|
||||
if (command == "web_app_close") {
|
||||
_close();
|
||||
_delegate->botClose();
|
||||
} else if (command == "web_app_data_send") {
|
||||
sendDataMessage(arguments);
|
||||
} else if (command == "web_app_switch_inline_query") {
|
||||
|
@ -645,8 +634,12 @@ bool Panel::createWebview() {
|
|||
openInvoice(arguments);
|
||||
} else if (command == "web_app_open_popup") {
|
||||
openPopup(arguments);
|
||||
} else if (command == "web_app_request_write_access") {
|
||||
requestWriteAccess();
|
||||
} else if (command == "web_app_request_phone") {
|
||||
requestPhone();
|
||||
} else if (command == "web_app_invoke_custom_method") {
|
||||
invokeCustomMethod(arguments);
|
||||
} else if (command == "web_app_setup_closing_behavior") {
|
||||
setupClosingBehaviour(arguments);
|
||||
} else if (command == "web_app_read_text_from_clipboard") {
|
||||
|
@ -655,7 +648,7 @@ bool Panel::createWebview() {
|
|||
});
|
||||
|
||||
raw->setNavigationStartHandler([=](const QString &uri, bool newWindow) {
|
||||
if (_handleLocalUri(uri)) {
|
||||
if (_delegate->botHandleLocalUri(uri)) {
|
||||
return false;
|
||||
} else if (newWindow) {
|
||||
return true;
|
||||
|
@ -694,27 +687,27 @@ void Panel::setTitle(rpl::producer<QString> title) {
|
|||
|
||||
void Panel::sendDataMessage(const QJsonObject &args) {
|
||||
if (args.isEmpty()) {
|
||||
_close();
|
||||
_delegate->botClose();
|
||||
return;
|
||||
}
|
||||
const auto data = args["data"].toString();
|
||||
if (data.isEmpty()) {
|
||||
LOG(("BotWebView Error: Bad 'data' in sendDataMessage."));
|
||||
_close();
|
||||
_delegate->botClose();
|
||||
return;
|
||||
}
|
||||
_sendData(data.toUtf8());
|
||||
_delegate->botSendData(data.toUtf8());
|
||||
}
|
||||
|
||||
void Panel::switchInlineQueryMessage(const QJsonObject &args) {
|
||||
if (args.isEmpty()) {
|
||||
_close();
|
||||
_delegate->botClose();
|
||||
return;
|
||||
}
|
||||
const auto query = args["query"].toString();
|
||||
if (query.isEmpty()) {
|
||||
LOG(("BotWebView Error: Bad 'query' in switchInlineQueryMessage."));
|
||||
_close();
|
||||
_delegate->botClose();
|
||||
return;
|
||||
}
|
||||
const auto valid = base::flat_set<QString>{
|
||||
|
@ -735,26 +728,26 @@ void Panel::switchInlineQueryMessage(const QJsonObject &args) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
_switchInlineQuery(types, query);
|
||||
_delegate->botSwitchInlineQuery(types, query);
|
||||
}
|
||||
|
||||
void Panel::openTgLink(const QJsonObject &args) {
|
||||
if (args.isEmpty()) {
|
||||
_close();
|
||||
_delegate->botClose();
|
||||
return;
|
||||
}
|
||||
const auto path = args["path_full"].toString();
|
||||
if (path.isEmpty()) {
|
||||
LOG(("BotWebView Error: Bad 'path_full' in openTgLink."));
|
||||
_close();
|
||||
_delegate->botClose();
|
||||
return;
|
||||
}
|
||||
_handleLocalUri("https://t.me" + path);
|
||||
_delegate->botHandleLocalUri("https://t.me" + path);
|
||||
}
|
||||
|
||||
void Panel::openExternalLink(const QJsonObject &args) {
|
||||
if (args.isEmpty()) {
|
||||
_close();
|
||||
_delegate->botClose();
|
||||
return;
|
||||
}
|
||||
const auto url = args["url"].toString();
|
||||
|
@ -762,7 +755,7 @@ void Panel::openExternalLink(const QJsonObject &args) {
|
|||
if (url.isEmpty()
|
||||
|| (!lower.startsWith("http://") && !lower.startsWith("https://"))) {
|
||||
LOG(("BotWebView Error: Bad 'url' in openExternalLink."));
|
||||
_close();
|
||||
_delegate->botClose();
|
||||
return;
|
||||
} else if (!allowOpenLink()) {
|
||||
return;
|
||||
|
@ -772,21 +765,21 @@ void Panel::openExternalLink(const QJsonObject &args) {
|
|||
|
||||
void Panel::openInvoice(const QJsonObject &args) {
|
||||
if (args.isEmpty()) {
|
||||
_close();
|
||||
_delegate->botClose();
|
||||
return;
|
||||
}
|
||||
const auto slug = args["slug"].toString();
|
||||
if (slug.isEmpty()) {
|
||||
LOG(("BotWebView Error: Bad 'slug' in openInvoice."));
|
||||
_close();
|
||||
_delegate->botClose();
|
||||
return;
|
||||
}
|
||||
_handleInvoice(slug);
|
||||
_delegate->botHandleInvoice(slug);
|
||||
}
|
||||
|
||||
void Panel::openPopup(const QJsonObject &args) {
|
||||
if (args.isEmpty()) {
|
||||
_close();
|
||||
_delegate->botClose();
|
||||
return;
|
||||
}
|
||||
using Button = Webview::PopupArgs::Button;
|
||||
|
@ -805,7 +798,7 @@ void Panel::openPopup(const QJsonObject &args) {
|
|||
const auto i = types.find(fields["type"].toString());
|
||||
if (i == end(types)) {
|
||||
LOG(("BotWebView Error: Bad 'type' in openPopup buttons."));
|
||||
_close();
|
||||
_delegate->botClose();
|
||||
return;
|
||||
}
|
||||
buttons.push_back({
|
||||
|
@ -816,11 +809,11 @@ void Panel::openPopup(const QJsonObject &args) {
|
|||
}
|
||||
if (message.isEmpty()) {
|
||||
LOG(("BotWebView Error: Bad 'message' in openPopup."));
|
||||
_close();
|
||||
_delegate->botClose();
|
||||
return;
|
||||
} else if (buttons.empty()) {
|
||||
LOG(("BotWebView Error: Bad 'buttons' in openPopup."));
|
||||
_close();
|
||||
_delegate->botClose();
|
||||
return;
|
||||
}
|
||||
const auto widget = _webview->window.widget();
|
||||
|
@ -838,8 +831,65 @@ void Panel::openPopup(const QJsonObject &args) {
|
|||
}
|
||||
}
|
||||
|
||||
void Panel::requestWriteAccess() {
|
||||
if (_inBlockingRequest) {
|
||||
replyRequestWriteAccess(false);
|
||||
return;
|
||||
}
|
||||
_inBlockingRequest = true;
|
||||
const auto finish = [=](bool allowed) {
|
||||
_inBlockingRequest = false;
|
||||
replyRequestWriteAccess(allowed);
|
||||
};
|
||||
const auto weak = base::make_weak(this);
|
||||
_delegate->botCheckWriteAccess([=](bool allowed) {
|
||||
if (!weak) {
|
||||
return;
|
||||
} else if (allowed) {
|
||||
finish(true);
|
||||
return;
|
||||
}
|
||||
using Button = Webview::PopupArgs::Button;
|
||||
const auto widget = _webview->window.widget();
|
||||
const auto integration = &Ui::Integration::Instance();
|
||||
const auto result = Webview::ShowBlockingPopup({
|
||||
.parent = widget ? widget->window() : nullptr,
|
||||
.title = integration->phraseBotAllowWriteTitle(),
|
||||
.text = integration->phraseBotAllowWrite(),
|
||||
.buttons = {
|
||||
{
|
||||
.id = "allow",
|
||||
.text = integration->phraseBotAllowWriteConfirm(),
|
||||
},
|
||||
{ .id = "cancel", .type = Button::Type::Cancel },
|
||||
},
|
||||
});
|
||||
if (!weak) {
|
||||
return;
|
||||
} else if (result.id == "allow") {
|
||||
_delegate->botAllowWriteAccess(crl::guard(this, finish));
|
||||
} else {
|
||||
finish(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Panel::replyRequestWriteAccess(bool allowed) {
|
||||
postEvent("write_access_requested", QJsonObject{
|
||||
{ u"status"_q, allowed ? u"allowed"_q : u"cancelled"_q }
|
||||
});
|
||||
}
|
||||
|
||||
void Panel::requestPhone() {
|
||||
#if 0 // disabled for now
|
||||
if (_inBlockingRequest) {
|
||||
replyRequestPhone(false);
|
||||
return;
|
||||
}
|
||||
_inBlockingRequest = true;
|
||||
const auto finish = [=](bool shared) {
|
||||
_inBlockingRequest = false;
|
||||
replyRequestPhone(shared);
|
||||
};
|
||||
using Button = Webview::PopupArgs::Button;
|
||||
const auto widget = _webview->window.widget();
|
||||
const auto weak = base::make_weak(this);
|
||||
|
@ -853,15 +903,62 @@ void Panel::requestPhone() {
|
|||
.id = "share",
|
||||
.text = integration->phraseBotSharePhoneConfirm(),
|
||||
},
|
||||
{.id = "cancel", .type = Button::Type::Cancel },
|
||||
{ .id = "cancel", .type = Button::Type::Cancel },
|
||||
},
|
||||
});
|
||||
if (weak) {
|
||||
postEvent("phone_requested", (result.id == "share")
|
||||
? QJsonObject{ { u"phone_number"_q, _phone } }
|
||||
: EventData());
|
||||
if (!weak) {
|
||||
return;
|
||||
} else if (result.id == "share") {
|
||||
_delegate->botSharePhone(crl::guard(this, finish));
|
||||
} else {
|
||||
finish(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Panel::replyRequestPhone(bool shared) {
|
||||
postEvent("phone_requested", QJsonObject{
|
||||
{ u"status"_q, shared ? u"sent"_q : u"cancelled"_q }
|
||||
});
|
||||
}
|
||||
|
||||
void Panel::invokeCustomMethod(const QJsonObject &args) {
|
||||
const auto requestId = args["req_id"];
|
||||
if (requestId.isUndefined()) {
|
||||
return;
|
||||
}
|
||||
const auto finish = [=](QJsonObject response) {
|
||||
replyCustomMethod(requestId, std::move(response));
|
||||
};
|
||||
auto callback = crl::guard(this, [=](CustomMethodResult result) {
|
||||
if (result) {
|
||||
auto error = QJsonParseError();
|
||||
const auto parsed = QJsonDocument::fromJson(
|
||||
"{ \"result\": " + *result + '}',
|
||||
&error);
|
||||
if (error.error != QJsonParseError::NoError
|
||||
|| !parsed.isObject()
|
||||
|| parsed.object().size() != 1) {
|
||||
finish({ { u"error"_q, u"Could not parse response."_q } });
|
||||
} else {
|
||||
finish(parsed.object());
|
||||
}
|
||||
} else {
|
||||
finish({ { u"error"_q, result.error() } });
|
||||
}
|
||||
});
|
||||
const auto params = QJsonDocument(
|
||||
args["params"].toObject()
|
||||
).toJson(QJsonDocument::Compact);
|
||||
_delegate->botInvokeCustomMethod({
|
||||
.method = args["method"].toString(),
|
||||
.params = params,
|
||||
.callback = std::move(callback),
|
||||
});
|
||||
}
|
||||
|
||||
void Panel::replyCustomMethod(QJsonValue requestId, QJsonObject response) {
|
||||
response["req_id"] = requestId;
|
||||
postEvent(u"custom_method_invoked"_q, response);
|
||||
}
|
||||
|
||||
void Panel::requestClipboardText(const QJsonObject &args) {
|
||||
|
@ -929,7 +1026,7 @@ void Panel::closeWithConfirmation() {
|
|||
if (!weak) {
|
||||
return;
|
||||
} else if (result.id != "cancel") {
|
||||
_close();
|
||||
_delegate->botClose();
|
||||
} else {
|
||||
_closeWithConfirmationScheduled = false;
|
||||
}
|
||||
|
@ -941,7 +1038,7 @@ void Panel::setupClosingBehaviour(const QJsonObject &args) {
|
|||
|
||||
void Panel::processMainButtonMessage(const QJsonObject &args) {
|
||||
if (args.isEmpty()) {
|
||||
_close();
|
||||
_delegate->botClose();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1158,20 +1255,13 @@ rpl::lifetime &Panel::lifetime() {
|
|||
}
|
||||
|
||||
std::unique_ptr<Panel> Show(Args &&args) {
|
||||
const auto params = args.themeParams();
|
||||
auto result = std::make_unique<Panel>(
|
||||
args.userDataPath,
|
||||
std::move(args.title),
|
||||
std::move(args.handleLocalUri),
|
||||
std::move(args.handleInvoice),
|
||||
std::move(args.sendData),
|
||||
std::move(args.switchInlineQuery),
|
||||
std::move(args.close),
|
||||
args.phone,
|
||||
args.delegate,
|
||||
args.menuButtons,
|
||||
std::move(args.handleMenuButton),
|
||||
std::move(args.themeParams),
|
||||
args.allowClipboardRead);
|
||||
const auto params = args.delegate->botThemeParams();
|
||||
if (!result->showWebview(args.url, params, std::move(args.bottom))) {
|
||||
const auto available = Webview::Availability();
|
||||
if (available.error != Webview::Available::Error::None) {
|
||||
|
|
|
@ -7,10 +7,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/expected.h"
|
||||
#include "base/object_ptr.h"
|
||||
#include "base/weak_ptr.h"
|
||||
#include "base/flags.h"
|
||||
|
||||
class QJsonObject;
|
||||
class QJsonValue;
|
||||
|
||||
namespace Ui {
|
||||
class BoxContent;
|
||||
class RpWidget;
|
||||
|
@ -40,20 +44,37 @@ enum class MenuButton {
|
|||
inline constexpr bool is_flag_type(MenuButton) { return true; }
|
||||
using MenuButtons = base::flags<MenuButton>;
|
||||
|
||||
using CustomMethodResult = base::expected<QByteArray, QString>;
|
||||
struct CustomMethodRequest {
|
||||
QString method;
|
||||
QByteArray params;
|
||||
Fn<void(CustomMethodResult)> callback;
|
||||
};
|
||||
|
||||
class Delegate {
|
||||
public:
|
||||
virtual Webview::ThemeParams botThemeParams() = 0;
|
||||
virtual bool botHandleLocalUri(QString uri) = 0;
|
||||
virtual void botHandleInvoice(QString slug) = 0;
|
||||
virtual void botHandleMenuButton(MenuButton button) = 0;
|
||||
virtual void botSendData(QByteArray data) = 0;
|
||||
virtual void botSwitchInlineQuery(
|
||||
std::vector<QString> chatTypes,
|
||||
QString query) = 0;
|
||||
virtual void botCheckWriteAccess(Fn<void(bool allowed)> callback) = 0;
|
||||
virtual void botAllowWriteAccess(Fn<void(bool allowed)> callback) = 0;
|
||||
virtual void botSharePhone(Fn<void(bool shared)> callback) = 0;
|
||||
virtual void botInvokeCustomMethod(CustomMethodRequest request) = 0;
|
||||
virtual void botClose() = 0;
|
||||
};
|
||||
|
||||
class Panel final : public base::has_weak_ptr {
|
||||
public:
|
||||
Panel(
|
||||
const QString &userDataPath,
|
||||
rpl::producer<QString> title,
|
||||
Fn<bool(QString)> handleLocalUri,
|
||||
Fn<void(QString)> handleInvoice,
|
||||
Fn<void(QByteArray)> sendData,
|
||||
Fn<void(std::vector<QString>, QString)> switchInlineQuery,
|
||||
Fn<void()> close,
|
||||
QString phone,
|
||||
not_null<Delegate*> delegate,
|
||||
MenuButtons menuButtons,
|
||||
Fn<void(MenuButton)> handleMenuButton,
|
||||
Fn<Webview::ThemeParams()> themeParams,
|
||||
bool allowClipboardRead);
|
||||
~Panel();
|
||||
|
||||
|
@ -96,7 +117,12 @@ private:
|
|||
void openExternalLink(const QJsonObject &args);
|
||||
void openInvoice(const QJsonObject &args);
|
||||
void openPopup(const QJsonObject &args);
|
||||
void requestWriteAccess();
|
||||
void replyRequestWriteAccess(bool allowed);
|
||||
void requestPhone();
|
||||
void replyRequestPhone(bool shared);
|
||||
void invokeCustomMethod(const QJsonObject &args);
|
||||
void replyCustomMethod(QJsonValue requestId, QJsonObject response);
|
||||
void requestClipboardText(const QJsonObject &args);
|
||||
void setupClosingBehaviour(const QJsonObject &args);
|
||||
void createMainButton();
|
||||
|
@ -115,15 +141,9 @@ private:
|
|||
void setupProgressGeometry();
|
||||
|
||||
QString _userDataPath;
|
||||
Fn<bool(QString)> _handleLocalUri;
|
||||
Fn<void(QString)> _handleInvoice;
|
||||
Fn<void(QByteArray)> _sendData;
|
||||
Fn<void(std::vector<QString>, QString)> _switchInlineQuery;
|
||||
Fn<void()> _close;
|
||||
QString _phone;
|
||||
const not_null<Delegate*> _delegate;
|
||||
bool _closeNeedConfirmation = false;
|
||||
MenuButtons _menuButtons = {};
|
||||
Fn<void(MenuButton)> _handleMenuButton;
|
||||
std::unique_ptr<SeparatePanel> _widget;
|
||||
std::unique_ptr<WebviewWithLifetime> _webview;
|
||||
std::unique_ptr<RpWidget> _webviewBottom;
|
||||
|
@ -139,6 +159,7 @@ private:
|
|||
bool _hiddenForPayment = false;
|
||||
bool _closeWithConfirmationScheduled = false;
|
||||
bool _allowClipboardRead = false;
|
||||
bool _inBlockingRequest = false;
|
||||
|
||||
};
|
||||
|
||||
|
@ -147,15 +168,8 @@ struct Args {
|
|||
QString userDataPath;
|
||||
rpl::producer<QString> title;
|
||||
rpl::producer<QString> bottom;
|
||||
Fn<bool(QString)> handleLocalUri;
|
||||
Fn<void(QString)> handleInvoice;
|
||||
Fn<void(QByteArray)> sendData;
|
||||
Fn<void(std::vector<QString>, QString)> switchInlineQuery;
|
||||
Fn<void()> close;
|
||||
QString phone;
|
||||
not_null<Delegate*> delegate;
|
||||
MenuButtons menuButtons;
|
||||
Fn<void(MenuButton)> handleMenuButton;
|
||||
Fn<Webview::ThemeParams()> themeParams;
|
||||
bool allowClipboardRead = false;
|
||||
};
|
||||
[[nodiscard]] std::unique_ptr<Panel> Show(Args &&args);
|
||||
|
|
Loading…
Add table
Reference in a new issue