Move webview attach code to a separate module.

This commit is contained in:
John Preston 2022-03-29 13:00:10 +04:00
parent aed1904b4c
commit c6ded00461
9 changed files with 411 additions and 223 deletions

View file

@ -765,6 +765,8 @@ PRIVATE
info/profile/info_profile_widget.h
info/settings/info_settings_widget.cpp
info/settings/info_settings_widget.h
inline_bots/bot_attach_web_view.cpp
inline_bots/bot_attach_web_view.h
inline_bots/inline_bot_layout_internal.cpp
inline_bots/inline_bot_layout_internal.h
inline_bots/inline_bot_layout_item.cpp

View file

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_bot.h"
#include "info/info_memento.h"
#include "inline_bots/bot_attach_web_view.h"
#include "core/click_handler_types.h"
#include "core/application.h"
#include "media/clip/media_clip_reader.h"
@ -222,23 +223,19 @@ void activateBotCommand(
} break;
case ButtonType::WebView: {
if (const auto m = CheckMainWidget(&msg->history()->session())) {
if (const auto bot = msg->getMessageBot()) {
m->controller()->requestAttachWebview(
bot,
bot,
{ .text = button->text, .url = button->data });
}
if (const auto bot = msg->getMessageBot()) {
bot->session().attachWebView().request(
bot,
bot,
{ .text = button->text, .url = button->data });
}
} break;
case ButtonType::SimpleWebView: {
if (const auto m = CheckMainWidget(&msg->history()->session())) {
if (const auto bot = msg->getMessageBot()) {
m->controller()->requestAttachSimpleWebview(
bot,
button->data);
}
if (const auto bot = msg->getMessageBot()) {
bot->session().attachWebView().requestSimple(
bot,
button->data);
}
} break;
}

View file

@ -0,0 +1,281 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "inline_bots/bot_attach_web_view.h"
#include "data/data_user.h"
#include "data/data_session.h"
#include "main/main_session.h"
#include "main/main_domain.h"
#include "storage/storage_domain.h"
#include "ui/boxes/confirm_box.h"
#include "ui/toasts/common_toasts.h"
#include "ui/chat/attach/attach_bot_webview.h"
#include "window/themes/window_theme.h"
#include "window/window_controller.h"
#include "core/application.h"
#include "lang/lang_keys.h"
#include "base/random.h"
#include "apiwrap.h"
namespace InlineBots {
namespace {
[[nodiscard]] UserData *ParseAttachBot(
not_null<Main::Session*> session,
const MTPAttachMenuBot &bot) {
return bot.match([&](const MTPDattachMenuBot &data) {
const auto user = session->data().userLoaded(UserId(data.vbot_id()));
return (user && user->isBot() && user->botInfo->supportsAttachMenu)
? user
: nullptr;
});
}
} // namespace
AttachWebView::AttachWebView(not_null<Main::Session*> session)
: _session(session) {
}
AttachWebView::~AttachWebView() = default;
void AttachWebView::request(
not_null<PeerData*> peer,
const QString &botUsername) {
const auto username = _bot ? _bot->username : _botUsername;
if (_peer == peer && username.toLower() == botUsername.toLower()) {
if (_panel) {
_panel->requestActivate();
}
return;
}
cancel();
_peer = peer;
_botUsername = botUsername;
resolve();
}
void AttachWebView::request(
not_null<PeerData*> peer,
not_null<UserData*> bot,
const WebViewButton &button) {
if (_peer == peer && _bot == bot) {
if (_panel) {
_panel->requestActivate();
} else if (_requestId) {
return;
}
}
cancel();
_bot = bot;
_peer = peer;
request(button);
}
void AttachWebView::request(const WebViewButton &button) {
Expects(_peer != nullptr && _bot != nullptr);
using Flag = MTPmessages_RequestWebView::Flag;
const auto flags = Flag::f_theme_params
| (button.url.isEmpty() ? Flag(0) : Flag::f_url);
_requestId = _session->api().request(MTPmessages_RequestWebView(
MTP_flags(flags),
_peer->input,
_bot->inputUser,
MTP_bytes(button.url),
MTP_dataJSON(MTP_bytes(Window::Theme::WebViewParams())),
MTPint() // reply_to_msg_id
)).done([=](const MTPWebViewResult &result) {
_requestId = 0;
result.match([&](const MTPDwebViewResultUrl &data) {
show(data.vquery_id().v, qs(data.vurl()), button.text);
}, [&](const MTPDwebViewResultConfirmationRequired &data) {
_session->data().processUsers(data.vusers());
const auto &received = data.vbot();
if (const auto bot = ParseAttachBot(_session, received)) {
if (_bot != bot) {
cancel();
return;
}
requestAddToMenu([=] {
request(button);
});
} else {
cancel();
}
});
}).fail([=](const MTP::Error &error) {
_requestId = 0;
int a = error.code();
}).send();
}
void AttachWebView::cancel() {
_session->api().request(base::take(_requestId)).cancel();
_panel = nullptr;
_peer = _bot = nullptr;
_botUsername = QString();
}
void AttachWebView::resolve() {
if (!_bot) {
requestByUsername();
}
}
void AttachWebView::requestByUsername() {
resolveUsername(_botUsername, [=](not_null<PeerData*> bot) {
_bot = bot->asUser();
if (!_bot || !_bot->isBot() || !_bot->botInfo->supportsAttachMenu) {
Ui::ShowMultilineToast({
// #TODO webview lang
.text = { u"This bot isn't supported in the attach menu."_q }
});
return;
}
request();
});
}
void AttachWebView::resolveUsername(
const QString &username,
Fn<void(not_null<PeerData*>)> done) {
if (const auto peer = _peer->owner().peerByUsername(username)) {
done(peer);
return;
}
_session->api().request(base::take(_requestId)).cancel();
_requestId = _session->api().request(MTPcontacts_ResolveUsername(
MTP_string(username)
)).done([=](const MTPcontacts_ResolvedPeer &result) {
_requestId = 0;
result.match([&](const MTPDcontacts_resolvedPeer &data) {
_peer->owner().processUsers(data.vusers());
_peer->owner().processChats(data.vchats());
if (const auto peerId = peerFromMTP(data.vpeer())) {
done(_peer->owner().peer(peerId));
}
});
}).fail([=](const MTP::Error &error) {
_requestId = 0;
if (error.code() == 400) {
Ui::ShowMultilineToast({
.text = {
tr::lng_username_not_found(tr::now, lt_user, username) }
});
}
}).send();
}
void AttachWebView::requestSimple(
not_null<UserData*> bot,
const QByteArray &url) {
if (_peer != bot || _bot != bot) {
return;
}
cancel();
_bot = bot;
_peer = bot;
using Flag = MTPmessages_RequestSimpleWebView::Flag;
_requestId = _session->api().request(MTPmessages_RequestSimpleWebView(
MTP_flags(0),
bot->inputUser,
MTP_bytes(url),
MTPDataJSON()
)).done([=](const MTPSimpleWebViewResult &result) {
_requestId = 0;
result.match([&](const MTPDsimpleWebViewResultUrl &data) {
const auto queryId = uint64();
show(queryId, qs(data.vurl()));
});
}).fail([=](const MTP::Error &error) {
_requestId = 0;
int a = error.code();
}).send();
}
void AttachWebView::show(
uint64 queryId,
const QString &url,
const QString &buttonText) {
Expects(_bot != nullptr && _peer != nullptr);
const auto close = crl::guard(this, [=] {
cancel();
});
const auto sendData = crl::guard(this, [=](QByteArray data) {
if (_peer != _bot || !queryId) {
cancel();
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();
cancel();
});
_panel = Ui::BotWebView::Show({
.url = url,
.userDataPath = _session->domain().local().webviewDataPath(),
.sendData = sendData,
.close = close,
.themeParams = [] { return Window::Theme::WebViewParams(); },
});
_session->data().webViewResultSent(
) | rpl::filter([=](const Data::Session::WebViewResultSent &sent) {
return (sent.peerId == _peer->id)
&& (sent.botId == _bot->id)
&& (sent.queryId == queryId);
}) | rpl::start_with_next([=] {
cancel();
}, _panel->lifetime());
}
void AttachWebView::requestAddToMenu(Fn<void()> callback) {
Expects(_bot != nullptr);
const auto done = [=](Fn<void()> close) {
toggleInMenu( true, [=] {
callback();
close();
});
};
const auto active = Core::App().activeWindow();
if (!active) {
return;
}
_confirmAddBox = active->show(Ui::MakeConfirmBox({
u"Do you want to? "_q + _bot->name,
done,
}));
}
void AttachWebView::toggleInMenu(bool enabled, Fn<void()> callback) {
Expects(_bot != nullptr);
_requestId = _session->api().request(MTPmessages_ToggleBotInAttachMenu(
_bot->inputUser,
MTP_bool(enabled)
)).done([=](const MTPBool &result) {
_requestId = 0;
callback();
}).fail([=] {
cancel();
}).send();
}
} // namespace InlineBots

View file

@ -0,0 +1,74 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "mtproto/sender.h"
#include "base/weak_ptr.h"
namespace Ui {
class GenericBox;
} // namespace Ui
namespace Ui::BotWebView {
class Panel;
} // namespace Ui::BotWebView
namespace Main {
class Session;
} // namespace Main
namespace InlineBots {
class AttachWebView final : public base::has_weak_ptr {
public:
explicit AttachWebView(not_null<Main::Session*> session);
~AttachWebView();
struct WebViewButton {
QString text;
QByteArray url;
bool simple = false;
};
void request(not_null<PeerData*> peer, const QString &botUsername);
void request(
not_null<PeerData*> peer,
not_null<UserData*> bot,
const WebViewButton &button = WebViewButton());
void requestSimple(not_null<UserData*> bot, const QByteArray &url);
private:
void cancel();
void resolve();
void request(const WebViewButton &button = WebViewButton());
void requestByUsername();
void resolveUsername(
const QString &username,
Fn<void(not_null<PeerData*>)> done);
void toggleInMenu(bool enabled, Fn<void()> callback);
void show(
uint64 queryId,
const QString &url,
const QString &buttonText = QString());
void requestAddToMenu(Fn<void()> callback);
const not_null<Main::Session*> _session;
PeerData *_peer = nullptr;
UserData *_bot = nullptr;
QString _botUsername;
QPointer<Ui::GenericBox> _confirmAddBox;
mtpRequestId _requestId = 0;
std::unique_ptr<Ui::BotWebView::Panel> _panel;
};
} // namespace InlineBots

View file

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/mtproto_config.h"
#include "chat_helpers/stickers_emoji_pack.h"
#include "chat_helpers/stickers_dice_pack.h"
#include "inline_bots/bot_attach_web_view.h"
#include "storage/file_download.h"
#include "storage/download_manager_mtproto.h"
#include "storage/file_upload.h"
@ -90,6 +91,7 @@ Session::Session(
, _emojiStickersPack(std::make_unique<Stickers::EmojiPack>(this))
, _diceStickersPacks(std::make_unique<Stickers::DicePacks>(this))
, _sendAsPeers(std::make_unique<SendAsPeers>(this))
, _attachWebView(std::make_unique<InlineBots::AttachWebView>(this))
, _supportHelper(Support::Helper::Create(this))
, _saveSettingsTimer([=] { saveSettings(); }) {
Expects(_settings != nullptr);

View file

@ -53,6 +53,10 @@ class EmojiPack;
class DicePacks;
} // namespace Stickers;
namespace InlineBots {
class AttachWebView;
} // namespace InlineBots
namespace Main {
class Account;
@ -117,6 +121,9 @@ public:
[[nodiscard]] SendAsPeers &sendAsPeers() const {
return *_sendAsPeers;
}
[[nodiscard]] InlineBots::AttachWebView &attachWebView() const {
return *_attachWebView;
}
void saveSettings();
void saveSettingsDelayed(crl::time delay = kDefaultSaveDelay);
@ -194,6 +201,7 @@ private:
const std::unique_ptr<Stickers::EmojiPack> _emojiStickersPack;
const std::unique_ptr<Stickers::DicePacks> _diceStickersPacks;
const std::unique_ptr<SendAsPeers> _sendAsPeers;
const std::unique_ptr<InlineBots::AttachWebView> _attachWebView;
const std::unique_ptr<Support::Helper> _supportHelper;

View file

@ -313,12 +313,23 @@ bool Panel::createWebview() {
if (command == "webview_close") {
_close();
} else if (command == "webview_data_send") {
//const auto tmp = list.at(1).toObject()["data"].toString().toUtf8();
const auto send = [send = _sendData, message] {
send(message.toJson(QJsonDocument::Compact));
};
_close();
send();
auto error = QJsonParseError();
auto json = list.at(1).toString();
const auto dictionary = QJsonDocument::fromJson(
json.toUtf8(),
&error);
if (error.error != QJsonParseError::NoError) {
LOG(("BotWebView Error: Could not parse \"%1\".").arg(json));
_close();
return;
}
const auto data = dictionary.object()["data"].toString();
if (data.isEmpty()) {
LOG(("BotWebView Error: Bad data \"%1\".").arg(json));
_close();
return;
}
_sendData(data.toUtf8());
}
});

View file

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_filters_menu.h"
#include "info/info_memento.h"
#include "info/info_controller.h"
#include "inline_bots/bot_attach_web_view.h"
#include "history/history.h"
#include "history/history_item.h"
#include "history/view/history_view_replies_section.h"
@ -66,7 +67,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/boxes/confirm_box.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "main/main_domain.h"
#include "main/main_session.h"
#include "main/main_session_settings.h"
#include "apiwrap.h"
@ -74,7 +74,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_global_privacy.h"
#include "support/support_helper.h"
#include "storage/file_upload.h"
#include "storage/storage_domain.h"
#include "facades.h"
#include "window/themes/window_theme.h"
#include "styles/style_window.h"
@ -132,17 +131,6 @@ constexpr auto kNightBaseFile = ":/gui/night-custom-base.tdesktop-theme"_cs;
};
}
[[nodiscard]] UserData *ParseAttachBot(
not_null<Main::Session*> session,
const MTPAttachMenuBot &bot) {
return bot.match([&](const MTPDattachMenuBot &data) {
const auto user = session->data().userLoaded(UserId(data.vbot_id()));
return (user && user->isBot() && user->botInfo->supportsAttachMenu)
? user
: nullptr;
});
}
} // namespace
void ActivateWindow(not_null<SessionController*> controller) {
@ -205,8 +193,8 @@ void SessionNavigation::showPeerByLink(const PeerByLinkInfo &info) {
}
void SessionNavigation::resolvePhone(
const QString &phone,
Fn<void(not_null<PeerData*>)> done) {
const QString &phone,
Fn<void(not_null<PeerData*>)> done) {
if (const auto peer = _session->data().userByPhone(phone)) {
done(peer);
return;
@ -228,8 +216,8 @@ void SessionNavigation::resolvePhone(
}
void SessionNavigation::resolveUsername(
const QString &username,
Fn<void(not_null<PeerData*>)> done) {
const QString &username,
Fn<void(not_null<PeerData*>)> done) {
if (const auto peer = _session->data().peerByUsername(username)) {
done(peer);
return;
@ -249,8 +237,8 @@ void SessionNavigation::resolveUsername(
}
void SessionNavigation::resolveDone(
const MTPcontacts_ResolvedPeer &result,
Fn<void(not_null<PeerData*>)> done) {
const MTPcontacts_ResolvedPeer &result,
Fn<void(not_null<PeerData*>)> done) {
_resolveRequestId = 0;
Ui::hideLayer();
result.match([&](const MTPDcontacts_resolvedPeer &data) {
@ -263,8 +251,8 @@ void SessionNavigation::resolveDone(
}
void SessionNavigation::resolveChannelById(
ChannelId channelId,
Fn<void(not_null<ChannelData*>)> done) {
ChannelId channelId,
Fn<void(not_null<ChannelData*>)> done) {
if (const auto channel = _session->data().channelLoaded(channelId)) {
done(channel);
return;
@ -292,8 +280,8 @@ void SessionNavigation::resolveChannelById(
}
void SessionNavigation::showPeerByLinkResolved(
not_null<PeerData*> peer,
const PeerByLinkInfo &info) {
not_null<PeerData*> peer,
const PeerByLinkInfo &info) {
auto params = SectionShow{
SectionShow::Way::Forward
};
@ -310,7 +298,7 @@ void SessionNavigation::showPeerByLinkResolved(
const auto bad = [=] {
Ui::ShowMultilineToast({
.text = { tr::lng_group_invite_bad_link(tr::now) }
});
});
};
const auto hash = *info.voicechatHash;
_api.request(base::take(_resolveRequestId)).cancel();
@ -403,154 +391,11 @@ void SessionNavigation::showPeerByLinkResolved(
}
crl::on_main(this, [=] {
showPeerHistory(peer->id, params, msgId);
resolveAttachWebview(peer, attachBotUsername);
peer->session().attachWebView().request(peer, attachBotUsername);
});
}
}
void SessionNavigation::resolveAttachWebview(
not_null<PeerData*> peer,
const QString &botUsername) {
if (!peer->isUser() || botUsername.isEmpty()) {
return;
}
resolveUsername(botUsername, [=](not_null<PeerData*> bot) {
const auto user = bot->asUser();
if (!user || !user->isBot() || !user->botInfo->supportsAttachMenu) {
Ui::ShowMultilineToast({
// #TODO webview lang
.text = { u"This bot isn't supported in the attach menu."_q }
});
return;
}
requestAttachWebview(peer, user);
});
}
void SessionNavigation::requestAttachWebview(
not_null<PeerData*> peer,
not_null<UserData*> bot,
const WebViewButton &button) {
using Flag = MTPmessages_RequestWebView::Flag;
const auto flags = Flag::f_theme_params
| (button.url.isEmpty() ? Flag(0) : Flag::f_url);
_api.request(MTPmessages_RequestWebView(
MTP_flags(flags),
peer->input,
bot->inputUser,
MTP_bytes(button.url),
MTP_dataJSON(MTP_bytes(Theme::WebViewParams())),
MTPint() // reply_to_msg_id
)).done([=](const MTPWebViewResult &result) {
result.match([&](const MTPDwebViewResultUrl &data) {
showAttachWebview(
peer,
bot,
data.vquery_id().v,
qs(data.vurl()),
button.text);
}, [&](const MTPDwebViewResultConfirmationRequired &data) {
session().data().processUsers(data.vusers());
const auto &received = data.vbot();
if (const auto bot = ParseAttachBot(&session(), received)) {
requestAddToMenu(bot, [=] {
requestAttachWebview(peer, bot, button);
});
}
});
}).fail([=](const MTP::Error &error) {
int a = error.code();
}).send();
}
void SessionNavigation::requestAttachSimpleWebview(
not_null<UserData*> bot,
const QByteArray &url) {
using Flag = MTPmessages_RequestSimpleWebView::Flag;
_api.request(MTPmessages_RequestSimpleWebView(
MTP_flags(0),
bot->inputUser,
MTP_bytes(url),
MTPDataJSON()
)).done([=](const MTPSimpleWebViewResult &result) {
result.match([&](const MTPDsimpleWebViewResultUrl &data) {
const auto queryId = uint64();
showAttachWebview(bot, bot, queryId, qs(data.vurl()));
});
}).fail([=](const MTP::Error &error) {
int a = error.code();
}).send();
}
void SessionNavigation::showAttachWebview(
not_null<PeerData*> peer,
not_null<UserData*> bot,
uint64 queryId,
const QString &url,
const QString &buttonText) {
const auto close = crl::guard(this, [=] {
_botWebView = nullptr;
});
const auto sendData = crl::guard(this, [=](QByteArray data) {
if (peer != bot || !queryId) {
return;
}
const auto randomId = base::RandomValue<uint64>();
const auto api = &session().api();
api->request(MTPmessages_SendWebViewData(
bot->inputUser,
MTP_long(randomId),
MTP_string(buttonText),
MTP_bytes(data)
)).done([=](const MTPUpdates &result) {
api->applyUpdates(result);
}).send();
});
_botWebView = Ui::BotWebView::Show({
.url = url,
.userDataPath = session().domain().local().webviewDataPath(),
.sendData = sendData,
.close = close,
.themeParams = [] { return Window::Theme::WebViewParams(); },
});
session().data().webViewResultSent(
) | rpl::filter([=](const Data::Session::WebViewResultSent &sent) {
return (sent.peerId == peer->id)
&& (sent.botId == bot->id)
&& (sent.queryId == queryId);
}) | rpl::start_with_next([=] {
_botWebView = nullptr;
}, _botWebView->lifetime());
}
void SessionNavigation::requestAddToMenu(
not_null<UserData*> bot,
Fn<void()> callback) {
const auto done = [=](Fn<void()> close) {
toggleInMenu(bot, true, [=] {
callback();
close();
});
};
show(Ui::MakeConfirmBox({
u"Do you want to? "_q + bot->name,
done,
}));
}
void SessionNavigation::toggleInMenu(
not_null<UserData*> bot,
bool enabled,
Fn<void()> callback) {
_api.request(MTPmessages_ToggleBotInAttachMenu(
bot->inputUser,
MTP_bool(enabled)
)).done([=](const MTPBool &result) {
callback();
}).send();
}
void SessionNavigation::showRepliesForMessage(
not_null<History*> history,
MsgId rootId,

View file

@ -36,6 +36,10 @@ namespace Main {
class Session;
} // namespace Main
namespace InlineBots {
class AttachWebView;
} // namespace InlineBots
namespace Settings {
enum class Type;
} // namespace Settings
@ -61,10 +65,6 @@ struct ChatThemeBackgroundData;
class MessageSendingAnimationController;
} // namespace Ui
namespace Ui::BotWebView {
class Panel;
} // namespace Ui::BotWebView
namespace Data {
struct CloudTheme;
enum class CloudThemeType;
@ -201,22 +201,6 @@ public:
};
void showPeerByLink(const PeerByLinkInfo &info);
struct WebViewButton {
QString text;
QByteArray url;
bool simple = false;
};
void resolveAttachWebview(
not_null<PeerData*> peer,
const QString &botUsername);
void requestAttachWebview(
not_null<PeerData*> peer,
not_null<UserData*> bot,
const WebViewButton &button = WebViewButton());
void requestAttachSimpleWebview(
not_null<UserData*> bot,
const QByteArray &url);
void showRepliesForMessage(
not_null<History*> history,
MsgId rootId,
@ -264,7 +248,6 @@ public:
FullMsgId contextId,
const SectionShow &params = SectionShow());
private:
void resolvePhone(
const QString &phone,
@ -284,19 +267,6 @@ private:
not_null<PeerData*> peer,
const PeerByLinkInfo &info);
void requestAddToMenu(not_null<UserData*> bot, Fn<void()> callback);
void showAttachWebview(
not_null<PeerData*> peer,
not_null<UserData*> bot,
uint64 queryId,
const QString &url,
const QString &buttonText = QString());
void toggleInMenu(
not_null<UserData*> bot,
bool enabled,
Fn<void()> callback);
const not_null<Main::Session*> _session;
MTP::Sender _api;
@ -307,8 +277,6 @@ private:
MsgId _showingRepliesRootId = 0;
mtpRequestId _showingRepliesRequestId = 0;
std::unique_ptr<Ui::BotWebView::Panel> _botWebView;
};
class SessionController : public SessionNavigation {