mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Implement separate instances for web apps.
This commit is contained in:
parent
9461095c88
commit
fd982b90db
14 changed files with 1202 additions and 1084 deletions
|
@ -488,20 +488,23 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
|
||||||
|
|
||||||
case ButtonType::WebView: {
|
case ButtonType::WebView: {
|
||||||
if (const auto bot = item->getMessageBot()) {
|
if (const auto bot = item->getMessageBot()) {
|
||||||
bot->session().attachWebView().request(
|
bot->session().attachWebView().open({
|
||||||
controller,
|
.bot = bot,
|
||||||
Api::SendAction(bot->owner().history(bot)),
|
.context = { .controller = controller },
|
||||||
bot,
|
.button = { .text = button->text, .url = button->data },
|
||||||
{ .text = button->text, .url = button->data });
|
.source = InlineBots::WebViewSourceButton{ .simple = false },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case ButtonType::SimpleWebView: {
|
case ButtonType::SimpleWebView: {
|
||||||
if (const auto bot = item->getMessageBot()) {
|
if (const auto bot = item->getMessageBot()) {
|
||||||
bot->session().attachWebView().requestSimple(
|
bot->session().attachWebView().open({
|
||||||
controller,
|
.bot = bot,
|
||||||
bot,
|
.context = { .controller = controller },
|
||||||
{ .text = button->text, .url = button->data });
|
.button = {.text = button->text, .url = button->data },
|
||||||
|
.source = InlineBots::WebViewSourceButton{ .simple = true },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,7 +231,11 @@ Application::~Application() {
|
||||||
// For example Domain::removeRedundantAccounts() is called from
|
// For example Domain::removeRedundantAccounts() is called from
|
||||||
// Domain::finish() and there is a violation on Ensures(started()).
|
// Domain::finish() and there is a violation on Ensures(started()).
|
||||||
Payments::CheckoutProcess::ClearAll();
|
Payments::CheckoutProcess::ClearAll();
|
||||||
InlineBots::AttachWebView::ClearAll();
|
for (const auto &[index, account] : _domain->accounts()) {
|
||||||
|
if (account->sessionExists()) {
|
||||||
|
account->session().attachWebView().closeAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
_iv->closeAll();
|
_iv->closeAll();
|
||||||
|
|
||||||
_domain->finish();
|
_domain->finish();
|
||||||
|
|
|
@ -191,11 +191,13 @@ void BotGameUrlClickHandler::onClick(ClickContext context) const {
|
||||||
const auto title = game->title;
|
const auto title = game->title;
|
||||||
const auto itemId = my.itemId;
|
const auto itemId = my.itemId;
|
||||||
const auto openGame = [=] {
|
const auto openGame = [=] {
|
||||||
bot->session().attachWebView().showGame({
|
bot->session().attachWebView().open({
|
||||||
.bot = bot,
|
.bot = bot,
|
||||||
.context = itemId,
|
.button = {.url = url.toUtf8() },
|
||||||
.url = url,
|
.source = InlineBots::WebViewSourceGame{
|
||||||
.title = title,
|
.messageId = itemId,
|
||||||
|
.title = title,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
if (_bot->isVerified()
|
if (_bot->isVerified()
|
||||||
|
|
|
@ -21,6 +21,10 @@ namespace Ui {
|
||||||
class Show;
|
class Show;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace InlineBots {
|
||||||
|
struct WebViewContext;
|
||||||
|
} // namespace InlineBots
|
||||||
|
|
||||||
namespace Main {
|
namespace Main {
|
||||||
class Session;
|
class Session;
|
||||||
} // namespace Main
|
} // namespace Main
|
||||||
|
@ -38,10 +42,10 @@ class SessionController;
|
||||||
class PeerData;
|
class PeerData;
|
||||||
struct ClickHandlerContext {
|
struct ClickHandlerContext {
|
||||||
FullMsgId itemId;
|
FullMsgId itemId;
|
||||||
QString attachBotWebviewUrl;
|
|
||||||
// Is filled from sections.
|
// Is filled from sections.
|
||||||
Fn<HistoryView::ElementDelegate*()> elementDelegate;
|
Fn<HistoryView::ElementDelegate*()> elementDelegate;
|
||||||
base::weak_ptr<Window::SessionController> sessionWindow;
|
base::weak_ptr<Window::SessionController> sessionWindow;
|
||||||
|
std::shared_ptr<InlineBots::WebViewContext> botWebviewContext;
|
||||||
std::shared_ptr<Ui::Show> show;
|
std::shared_ptr<Ui::Show> show;
|
||||||
bool mayShowConfirmation = false;
|
bool mayShowConfirmation = false;
|
||||||
bool skipBotAutoLogin = false;
|
bool skipBotAutoLogin = false;
|
||||||
|
|
|
@ -573,8 +573,11 @@ bool ResolveUsernameOrPhone(
|
||||||
: (appname.isEmpty() && params.contains(u"startapp"_q))
|
: (appname.isEmpty() && params.contains(u"startapp"_q))
|
||||||
? params.value(u"startapp"_q)
|
? params.value(u"startapp"_q)
|
||||||
: std::optional<QString>()),
|
: std::optional<QString>()),
|
||||||
.attachBotMenuOpen = (appname.isEmpty()
|
.attachBotMainOpen = (appname.isEmpty()
|
||||||
&& params.contains(u"startapp"_q)),
|
&& params.contains(u"startapp"_q)),
|
||||||
|
.attachBotMainCompact = (appname.isEmpty()
|
||||||
|
&& params.contains(u"startapp"_q)
|
||||||
|
&& (params.value(u"mode"_q) == u"compact"_q)),
|
||||||
.attachBotChooseTypes = InlineBots::ParseChooseTypes(
|
.attachBotChooseTypes = InlineBots::ParseChooseTypes(
|
||||||
params.value(u"choose"_q)),
|
params.value(u"choose"_q)),
|
||||||
.voicechatHash = (params.contains(u"livestream"_q)
|
.voicechatHash = (params.contains(u"livestream"_q)
|
||||||
|
@ -585,7 +588,7 @@ bool ResolveUsernameOrPhone(
|
||||||
? std::make_optional(params.value(u"voicechat"_q))
|
? std::make_optional(params.value(u"voicechat"_q))
|
||||||
: std::nullopt),
|
: std::nullopt),
|
||||||
.clickFromMessageId = myContext.itemId,
|
.clickFromMessageId = myContext.itemId,
|
||||||
.clickFromAttachBotWebviewUrl = myContext.attachBotWebviewUrl,
|
.clickFromBotWebviewContext = myContext.botWebviewContext,
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -626,7 +629,7 @@ bool ResolvePrivatePost(
|
||||||
}
|
}
|
||||||
: Window::RepliesByLinkInfo{ v::null },
|
: Window::RepliesByLinkInfo{ v::null },
|
||||||
.clickFromMessageId = my.itemId,
|
.clickFromMessageId = my.itemId,
|
||||||
.clickFromAttachBotWebviewUrl = my.attachBotWebviewUrl,
|
.clickFromBotWebviewContext = my.botWebviewContext,
|
||||||
});
|
});
|
||||||
controller->window().activate();
|
controller->window().activate();
|
||||||
return true;
|
return true;
|
||||||
|
@ -1178,7 +1181,7 @@ bool ResolveChatLink(
|
||||||
controller->showPeerByLink(Window::PeerByLinkInfo{
|
controller->showPeerByLink(Window::PeerByLinkInfo{
|
||||||
.chatLinkSlug = match->captured(1),
|
.chatLinkSlug = match->captured(1),
|
||||||
.clickFromMessageId = myContext.itemId,
|
.clickFromMessageId = myContext.itemId,
|
||||||
.clickFromAttachBotWebviewUrl = myContext.attachBotWebviewUrl,
|
.clickFromBotWebviewContext = myContext.botWebviewContext,
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4940,7 +4940,14 @@ bool HistoryWidget::updateCmdStartShown() {
|
||||||
const auto user = _peer ? _peer->asUser() : nullptr;
|
const auto user = _peer ? _peer->asUser() : nullptr;
|
||||||
const auto bot = (user && user->isBot()) ? user : nullptr;
|
const auto bot = (user && user->isBot()) ? user : nullptr;
|
||||||
if (bot && !bot->botInfo->botMenuButtonUrl.isEmpty()) {
|
if (bot && !bot->botInfo->botMenuButtonUrl.isEmpty()) {
|
||||||
session().attachWebView().requestMenu(controller(), bot);
|
session().attachWebView().open({
|
||||||
|
.bot = bot,
|
||||||
|
.context = { .controller = controller() },
|
||||||
|
.button = {
|
||||||
|
.url = bot->botInfo->botMenuButtonUrl.toUtf8(),
|
||||||
|
},
|
||||||
|
.source = InlineBots::WebViewSourceBotMenu(),
|
||||||
|
});
|
||||||
} else if (!_fieldAutocomplete->isHidden()) {
|
} else if (!_fieldAutocomplete->isHidden()) {
|
||||||
_fieldAutocomplete->hideAnimated();
|
_fieldAutocomplete->hideAnimated();
|
||||||
} else {
|
} else {
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -10,15 +10,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/flags.h"
|
#include "base/flags.h"
|
||||||
#include "base/timer.h"
|
#include "base/timer.h"
|
||||||
#include "base/weak_ptr.h"
|
#include "base/weak_ptr.h"
|
||||||
|
#include "dialogs/dialogs_key.h"
|
||||||
|
#include "api/api_common.h"
|
||||||
#include "mtproto/sender.h"
|
#include "mtproto/sender.h"
|
||||||
#include "ui/chat/attach/attach_bot_webview.h"
|
#include "ui/chat/attach/attach_bot_webview.h"
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/rp_widget.h"
|
||||||
|
|
||||||
namespace Api {
|
namespace Data {
|
||||||
struct SendAction;
|
class Thread;
|
||||||
} // namespace Api
|
} // namespace Data
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
class Show;
|
||||||
class GenericBox;
|
class GenericBox;
|
||||||
class DropdownMenu;
|
class DropdownMenu;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
@ -47,6 +50,8 @@ enum class CheckoutResult;
|
||||||
|
|
||||||
namespace InlineBots {
|
namespace InlineBots {
|
||||||
|
|
||||||
|
class WebViewInstance;
|
||||||
|
|
||||||
enum class PeerType : uint8 {
|
enum class PeerType : uint8 {
|
||||||
SameBot = 0x01,
|
SameBot = 0x01,
|
||||||
Bot = 0x02,
|
Bot = 0x02,
|
||||||
|
@ -86,95 +91,176 @@ struct AddToMenuOpenApp {
|
||||||
not_null<BotAppData*> app;
|
not_null<BotAppData*> app;
|
||||||
QString startCommand;
|
QString startCommand;
|
||||||
};
|
};
|
||||||
using AddToMenuOpen = std::variant<
|
struct AddToMenuOpen : std::variant<
|
||||||
AddToMenuOpenAttach,
|
AddToMenuOpenAttach,
|
||||||
AddToMenuOpenMenu,
|
AddToMenuOpenMenu,
|
||||||
AddToMenuOpenApp>;
|
AddToMenuOpenApp> {
|
||||||
|
using variant::variant;
|
||||||
|
};
|
||||||
|
|
||||||
class AttachWebView final
|
struct WebViewSourceButton {
|
||||||
|
bool simple = false;
|
||||||
|
|
||||||
|
friend inline bool operator==(
|
||||||
|
WebViewSourceButton,
|
||||||
|
WebViewSourceButton) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WebViewSourceSwitch {
|
||||||
|
friend inline bool operator==(
|
||||||
|
const WebViewSourceSwitch &,
|
||||||
|
const WebViewSourceSwitch &) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WebViewSourceLinkApp { // t.me/botusername/appname
|
||||||
|
base::weak_ptr<WebViewInstance> from;
|
||||||
|
QString appname;
|
||||||
|
QString token;
|
||||||
|
|
||||||
|
friend inline bool operator==(
|
||||||
|
const WebViewSourceLinkApp &,
|
||||||
|
const WebViewSourceLinkApp &) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WebViewSourceLinkAttachMenu { // ?startattach
|
||||||
|
base::weak_ptr<WebViewInstance> from;
|
||||||
|
base::weak_ptr<Data::Thread> thread;
|
||||||
|
PeerTypes choose;
|
||||||
|
QString token;
|
||||||
|
|
||||||
|
friend inline bool operator==(
|
||||||
|
const WebViewSourceLinkAttachMenu &,
|
||||||
|
const WebViewSourceLinkAttachMenu &) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WebViewSourceLinkBotProfile { // t.me/botusername?startapp
|
||||||
|
base::weak_ptr<WebViewInstance> from;
|
||||||
|
QString token;
|
||||||
|
bool compact = false;
|
||||||
|
|
||||||
|
friend inline bool operator==(
|
||||||
|
const WebViewSourceLinkBotProfile &,
|
||||||
|
const WebViewSourceLinkBotProfile &) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WebViewSourceMainMenu {
|
||||||
|
friend inline bool operator==(
|
||||||
|
WebViewSourceMainMenu,
|
||||||
|
WebViewSourceMainMenu) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WebViewSourceAttachMenu {
|
||||||
|
base::weak_ptr<Data::Thread> thread;
|
||||||
|
|
||||||
|
friend inline bool operator==(
|
||||||
|
const WebViewSourceAttachMenu &,
|
||||||
|
const WebViewSourceAttachMenu &) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WebViewSourceBotMenu {
|
||||||
|
friend inline bool operator==(
|
||||||
|
WebViewSourceBotMenu,
|
||||||
|
WebViewSourceBotMenu) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WebViewSourceGame {
|
||||||
|
FullMsgId messageId;
|
||||||
|
QString title;
|
||||||
|
|
||||||
|
friend inline bool operator==(
|
||||||
|
WebViewSourceGame,
|
||||||
|
WebViewSourceGame) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WebViewSourceBotProfile {
|
||||||
|
friend inline bool operator==(
|
||||||
|
WebViewSourceBotProfile,
|
||||||
|
WebViewSourceBotProfile) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WebViewSource : std::variant<
|
||||||
|
WebViewSourceButton,
|
||||||
|
WebViewSourceSwitch,
|
||||||
|
WebViewSourceLinkApp,
|
||||||
|
WebViewSourceLinkAttachMenu,
|
||||||
|
WebViewSourceLinkBotProfile,
|
||||||
|
WebViewSourceMainMenu,
|
||||||
|
WebViewSourceAttachMenu,
|
||||||
|
WebViewSourceBotMenu,
|
||||||
|
WebViewSourceGame,
|
||||||
|
WebViewSourceBotProfile> {
|
||||||
|
using variant::variant;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WebViewButton {
|
||||||
|
QString text;
|
||||||
|
QString startCommand;
|
||||||
|
QByteArray url;
|
||||||
|
bool fromAttachMenu = false;
|
||||||
|
bool fromMainMenu = false;
|
||||||
|
bool fromSwitch = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WebViewContext {
|
||||||
|
base::weak_ptr<Window::SessionController> controller;
|
||||||
|
Dialogs::EntryState dialogsEntryState;
|
||||||
|
std::optional<Api::SendAction> action;
|
||||||
|
bool maySkipConfirmation = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WebViewDescriptor {
|
||||||
|
not_null<UserData*> bot;
|
||||||
|
std::shared_ptr<Ui::Show> parentShow;
|
||||||
|
WebViewContext context;
|
||||||
|
WebViewButton button;
|
||||||
|
WebViewSource source;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WebViewInstance final
|
||||||
: public base::has_weak_ptr
|
: public base::has_weak_ptr
|
||||||
, public Ui::BotWebView::Delegate {
|
, public Ui::BotWebView::Delegate {
|
||||||
public:
|
public:
|
||||||
explicit AttachWebView(not_null<Main::Session*> session);
|
explicit WebViewInstance(WebViewDescriptor &&descriptor);
|
||||||
~AttachWebView();
|
~WebViewInstance();
|
||||||
|
|
||||||
struct WebViewButton {
|
[[nodiscard]] Main::Session &session() const;
|
||||||
QString text;
|
[[nodiscard]] not_null<UserData*> bot() const;
|
||||||
QString startCommand;
|
[[nodiscard]] WebViewSource source() const;
|
||||||
QByteArray url;
|
|
||||||
bool fromAttachMenu = false;
|
|
||||||
bool fromMainMenu = false;
|
|
||||||
bool fromSwitch = false;
|
|
||||||
};
|
|
||||||
void request(
|
|
||||||
not_null<Window::SessionController*> controller,
|
|
||||||
const Api::SendAction &action,
|
|
||||||
const QString &botUsername,
|
|
||||||
const QString &startCommand);
|
|
||||||
void request(
|
|
||||||
not_null<Window::SessionController*> controller,
|
|
||||||
const Api::SendAction &action,
|
|
||||||
not_null<UserData*> bot,
|
|
||||||
const WebViewButton &button);
|
|
||||||
void requestSimple(
|
|
||||||
not_null<Window::SessionController*> controller,
|
|
||||||
not_null<UserData*> bot,
|
|
||||||
const WebViewButton &button);
|
|
||||||
void requestMenu(
|
|
||||||
not_null<Window::SessionController*> controller,
|
|
||||||
not_null<UserData*> bot);
|
|
||||||
void requestApp(
|
|
||||||
not_null<Window::SessionController*> controller,
|
|
||||||
const Api::SendAction &action,
|
|
||||||
not_null<UserData*> bot,
|
|
||||||
const QString &appName,
|
|
||||||
const QString &startParam,
|
|
||||||
bool forceConfirmation);
|
|
||||||
|
|
||||||
void cancel();
|
void activate();
|
||||||
|
void close();
|
||||||
void requestBots(Fn<void()> callback = nullptr);
|
|
||||||
[[nodiscard]] const std::vector<AttachWebViewBot> &attachBots() const {
|
|
||||||
return _attachBots;
|
|
||||||
}
|
|
||||||
[[nodiscard]] rpl::producer<> attachBotsUpdates() const {
|
|
||||||
return _attachBotsUpdates.events();
|
|
||||||
}
|
|
||||||
void notifyBotIconLoaded() {
|
|
||||||
_attachBotsUpdates.fire({});
|
|
||||||
}
|
|
||||||
[[nodiscard]] bool disclaimerAccepted(
|
|
||||||
const AttachWebViewBot &bot) const;
|
|
||||||
[[nodiscard]] bool showMainMenuNewBadge(
|
|
||||||
const AttachWebViewBot &bot) const;
|
|
||||||
|
|
||||||
void requestAddToMenu(
|
|
||||||
not_null<UserData*> bot,
|
|
||||||
AddToMenuOpen open);
|
|
||||||
void requestAddToMenu(
|
|
||||||
not_null<UserData*> bot,
|
|
||||||
AddToMenuOpen open,
|
|
||||||
Window::SessionController *controller,
|
|
||||||
std::optional<Api::SendAction> action);
|
|
||||||
void removeFromMenu(not_null<UserData*> bot);
|
|
||||||
|
|
||||||
[[nodiscard]] std::optional<Api::SendAction> lookupLastAction(
|
|
||||||
const QString &url) const;
|
|
||||||
|
|
||||||
struct ShowGameParams {
|
|
||||||
not_null<UserData*> bot;
|
|
||||||
FullMsgId context;
|
|
||||||
QString url;
|
|
||||||
QString title;
|
|
||||||
};
|
|
||||||
void showGame(ShowGameParams &¶ms);
|
|
||||||
|
|
||||||
[[nodiscard]] std::shared_ptr<Main::SessionShow> uiShow();
|
[[nodiscard]] std::shared_ptr<Main::SessionShow> uiShow();
|
||||||
|
|
||||||
static void ClearAll();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Context;
|
void resolve();
|
||||||
|
|
||||||
|
bool openAppFromBotMenuLink();
|
||||||
|
|
||||||
|
void requestButton();
|
||||||
|
void requestSimple();
|
||||||
|
void requestApp(bool allowWrite);
|
||||||
|
void requestWithMainMenuDisclaimer();
|
||||||
|
void requestWithMenuAdd();
|
||||||
|
void maybeChooseAndRequestButton(PeerTypes supported);
|
||||||
|
|
||||||
|
void resolveApp(
|
||||||
|
const QString &appname,
|
||||||
|
const QString &startparam,
|
||||||
|
bool forceConfirmation);
|
||||||
|
void confirmOpen(Fn<void()> done);
|
||||||
|
void confirmAppOpen(bool writeAccess, Fn<void(bool allowWrite)> done);
|
||||||
|
|
||||||
|
void show(const QString &url, uint64 queryId = 0);
|
||||||
|
void showGame();
|
||||||
|
void started(uint64 queryId);
|
||||||
|
|
||||||
|
[[nodiscard]] Window::SessionController *windowForThread(
|
||||||
|
not_null<Data::Thread*> thread);
|
||||||
|
|
||||||
|
auto nonPanelPaymentFormFactory(
|
||||||
|
Fn<void(Payments::CheckoutResult)> reactivate)
|
||||||
|
-> Fn<void(Payments::NonPanelPaymentForm)>;
|
||||||
|
|
||||||
Webview::ThemeParams botThemeParams() override;
|
Webview::ThemeParams botThemeParams() override;
|
||||||
bool botHandleLocalUri(QString uri, bool keepOpen) override;
|
bool botHandleLocalUri(QString uri, bool keepOpen) override;
|
||||||
|
@ -194,35 +280,81 @@ private:
|
||||||
void botShareGameScore() override;
|
void botShareGameScore() override;
|
||||||
void botClose() override;
|
void botClose() override;
|
||||||
|
|
||||||
[[nodiscard]] static Context LookupContext(
|
const std::shared_ptr<Ui::Show> _parentShow;
|
||||||
not_null<Window::SessionController*> controller,
|
const not_null<Main::Session*> _session;
|
||||||
const Api::SendAction &action);
|
const not_null<UserData*> _bot;
|
||||||
[[nodiscard]] static bool IsSame(
|
const WebViewContext _context;
|
||||||
const std::unique_ptr<Context> &a,
|
const WebViewButton _button;
|
||||||
const Context &b);
|
const WebViewSource _source;
|
||||||
|
|
||||||
bool openAppFromMenuLink(
|
BotAppData *_app = nullptr;
|
||||||
|
QString _appStartParam;
|
||||||
|
bool _dataSent = false;
|
||||||
|
|
||||||
|
mtpRequestId _requestId = 0;
|
||||||
|
mtpRequestId _prolongId = 0;
|
||||||
|
|
||||||
|
QString _panelUrl;
|
||||||
|
std::unique_ptr<Ui::BotWebView::Panel> _panel;
|
||||||
|
|
||||||
|
static base::weak_ptr<WebViewInstance> PendingActivation;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class AttachWebView final : public base::has_weak_ptr {
|
||||||
|
public:
|
||||||
|
explicit AttachWebView(not_null<Main::Session*> session);
|
||||||
|
~AttachWebView();
|
||||||
|
|
||||||
|
void open(WebViewDescriptor &&descriptor);
|
||||||
|
void openByUsername(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
|
const Api::SendAction &action,
|
||||||
|
const QString &botUsername,
|
||||||
|
const QString &startCommand);
|
||||||
|
|
||||||
|
void cancel();
|
||||||
|
|
||||||
|
void requestBots(Fn<void()> callback = nullptr);
|
||||||
|
[[nodiscard]] const std::vector<AttachWebViewBot> &attachBots() const {
|
||||||
|
return _attachBots;
|
||||||
|
}
|
||||||
|
[[nodiscard]] rpl::producer<> attachBotsUpdates() const {
|
||||||
|
return _attachBotsUpdates.events();
|
||||||
|
}
|
||||||
|
void notifyBotIconLoaded() {
|
||||||
|
_attachBotsUpdates.fire({});
|
||||||
|
}
|
||||||
|
[[nodiscard]] bool disclaimerAccepted(
|
||||||
|
const AttachWebViewBot &bot) const;
|
||||||
|
[[nodiscard]] bool showMainMenuNewBadge(
|
||||||
|
const AttachWebViewBot &bot) const;
|
||||||
|
|
||||||
|
void removeFromMenu(
|
||||||
|
std::shared_ptr<Ui::Show> show,
|
||||||
not_null<UserData*> bot);
|
not_null<UserData*> bot);
|
||||||
void requestWithOptionalConfirm(
|
|
||||||
|
enum class AddToMenuResult {
|
||||||
|
AlreadyInMenu,
|
||||||
|
Added,
|
||||||
|
Unsupported,
|
||||||
|
Cancelled,
|
||||||
|
};
|
||||||
|
void requestAddToMenu(
|
||||||
not_null<UserData*> bot,
|
not_null<UserData*> bot,
|
||||||
const WebViewButton &button,
|
Fn<void(AddToMenuResult, PeerTypes supported)> done);
|
||||||
const Context &context,
|
|
||||||
Window::SessionController *controllerForConfirm = nullptr);
|
|
||||||
|
|
||||||
void resolve();
|
|
||||||
void request(const WebViewButton &button);
|
|
||||||
void requestSimple(const WebViewButton &button);
|
|
||||||
void resolveUsername(
|
|
||||||
const QString &username,
|
|
||||||
Fn<void(not_null<PeerData*>)> done);
|
|
||||||
|
|
||||||
void confirmOpen(
|
|
||||||
not_null<Window::SessionController*> controller,
|
|
||||||
Fn<void()> done);
|
|
||||||
void acceptMainMenuDisclaimer(
|
void acceptMainMenuDisclaimer(
|
||||||
not_null<Window::SessionController*> controller,
|
std::shared_ptr<Ui::Show> show,
|
||||||
const WebViewButton &button);
|
not_null<UserData*> bot,
|
||||||
|
Fn<void(AddToMenuResult, PeerTypes supported)> done);
|
||||||
|
|
||||||
|
void close(not_null<WebViewInstance*> instance);
|
||||||
|
void closeAll();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void resolveUsername(
|
||||||
|
std::shared_ptr<Ui::Show> show,
|
||||||
|
Fn<void(not_null<PeerData*>)> done);
|
||||||
|
|
||||||
enum class ToggledState {
|
enum class ToggledState {
|
||||||
Removed,
|
Removed,
|
||||||
|
@ -232,67 +364,35 @@ private:
|
||||||
void toggleInMenu(
|
void toggleInMenu(
|
||||||
not_null<UserData*> bot,
|
not_null<UserData*> bot,
|
||||||
ToggledState state,
|
ToggledState state,
|
||||||
Fn<void()> callback = nullptr);
|
Fn<void(bool success)> callback = nullptr);
|
||||||
|
|
||||||
void show(
|
|
||||||
uint64 queryId,
|
|
||||||
const QString &url,
|
|
||||||
const QString &buttonText = QString(),
|
|
||||||
bool allowClipboardRead = false,
|
|
||||||
const BotAppData *app = nullptr,
|
|
||||||
bool fromMainMenu = false);
|
|
||||||
void confirmAddToMenu(
|
void confirmAddToMenu(
|
||||||
AttachWebViewBot bot,
|
AttachWebViewBot bot,
|
||||||
Fn<void()> callback = nullptr);
|
Fn<void(bool added)> callback = nullptr);
|
||||||
void confirmAppOpen(bool requestWriteAccess);
|
|
||||||
void requestAppView(bool allowWrite);
|
|
||||||
void started(uint64 queryId);
|
|
||||||
|
|
||||||
void showToast(
|
|
||||||
const QString &text,
|
|
||||||
Window::SessionController *controller = nullptr);
|
|
||||||
Fn<void(Payments::NonPanelPaymentForm)> nonPanelPaymentFormFactory(
|
|
||||||
Fn<void(Payments::CheckoutResult)> reactivate);
|
|
||||||
|
|
||||||
const not_null<Main::Session*> _session;
|
const not_null<Main::Session*> _session;
|
||||||
|
|
||||||
base::Timer _refreshTimer;
|
base::Timer _refreshTimer;
|
||||||
|
|
||||||
std::unique_ptr<Context> _context;
|
|
||||||
std::unique_ptr<Context> _lastShownContext;
|
|
||||||
QString _lastShownUrl;
|
|
||||||
uint64 _lastShownQueryId = 0;
|
|
||||||
QString _lastShownButtonText;
|
|
||||||
UserData *_bot = nullptr;
|
|
||||||
QString _botUsername;
|
QString _botUsername;
|
||||||
QString _botAppName;
|
|
||||||
QString _startCommand;
|
QString _startCommand;
|
||||||
BotAppData *_app = nullptr;
|
|
||||||
QPointer<Ui::GenericBox> _confirmAddBox;
|
|
||||||
bool _appConfirmationRequired = false;
|
|
||||||
bool _appRequestWriteAccess = false;
|
|
||||||
|
|
||||||
mtpRequestId _requestId = 0;
|
mtpRequestId _requestId = 0;
|
||||||
mtpRequestId _prolongId = 0;
|
|
||||||
|
|
||||||
uint64 _botsHash = 0;
|
uint64 _botsHash = 0;
|
||||||
mtpRequestId _botsRequestId = 0;
|
mtpRequestId _botsRequestId = 0;
|
||||||
std::vector<Fn<void()>> _botsRequestCallbacks;
|
std::vector<Fn<void()>> _botsRequestCallbacks;
|
||||||
|
|
||||||
std::unique_ptr<Context> _addToMenuContext;
|
struct AddToMenuProcess {
|
||||||
UserData *_addToMenuBot = nullptr;
|
mtpRequestId requestId = 0;
|
||||||
mtpRequestId _addToMenuId = 0;
|
std::vector<Fn<void(AddToMenuResult, PeerTypes supported)>> done;
|
||||||
AddToMenuOpen _addToMenuOpen;
|
};
|
||||||
base::weak_ptr<Window::SessionController> _addToMenuChooseController;
|
base::flat_map<not_null<UserData*>, AddToMenuProcess> _addToMenu;
|
||||||
|
|
||||||
std::vector<AttachWebViewBot> _attachBots;
|
std::vector<AttachWebViewBot> _attachBots;
|
||||||
rpl::event_stream<> _attachBotsUpdates;
|
rpl::event_stream<> _attachBotsUpdates;
|
||||||
base::flat_set<not_null<UserData*>> _disclaimerAccepted;
|
base::flat_set<not_null<UserData*>> _disclaimerAccepted;
|
||||||
|
|
||||||
FullMsgId _gameContext;
|
std::vector<std::unique_ptr<WebViewInstance>> _instances;
|
||||||
|
|
||||||
std::unique_ptr<Ui::BotWebView::Panel> _panel;
|
|
||||||
bool _catchingCancelInShowCall = false;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/effects/path_shift_gradient.h"
|
#include "ui/effects/path_shift_gradient.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "history/view/history_view_cursor_state.h"
|
#include "history/view/history_view_cursor_state.h"
|
||||||
|
#include "history/history.h"
|
||||||
#include "styles/style_chat_helpers.h"
|
#include "styles/style_chat_helpers.h"
|
||||||
#include "styles/style_menu_icons.h"
|
#include "styles/style_menu_icons.h"
|
||||||
|
|
||||||
|
@ -677,10 +678,13 @@ void Inner::switchPm() {
|
||||||
if (!_inlineBot || !_inlineBot->isBot()) {
|
if (!_inlineBot || !_inlineBot->isBot()) {
|
||||||
return;
|
return;
|
||||||
} else if (!_switchPmUrl.isEmpty()) {
|
} else if (!_switchPmUrl.isEmpty()) {
|
||||||
_inlineBot->session().attachWebView().requestSimple(
|
const auto bot = _inlineBot;
|
||||||
_controller,
|
_inlineBot->session().attachWebView().open({
|
||||||
_inlineBot,
|
.bot = bot,
|
||||||
{ .url = _switchPmUrl, .fromSwitch = true });
|
.context = { .controller = _controller },
|
||||||
|
.button = { .url = _switchPmUrl },
|
||||||
|
.source = InlineBots::WebViewSourceSwitch(),
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
_inlineBot->botInfo->startToken = _switchPmStartToken;
|
_inlineBot->botInfo->startToken = _switchPmStartToken;
|
||||||
_inlineBot->botInfo->inlineReturnTo
|
_inlineBot->botInfo->inlineReturnTo
|
||||||
|
|
|
@ -193,7 +193,7 @@ void MainWindow::setupPasscodeLock() {
|
||||||
setInnerFocus();
|
setInnerFocus();
|
||||||
}
|
}
|
||||||
if (const auto sessionController = controller().sessionController()) {
|
if (const auto sessionController = controller().sessionController()) {
|
||||||
sessionController->session().attachWebView().cancel();
|
sessionController->session().attachWebView().closeAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1282,6 +1282,27 @@ void Panel::showBox(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const auto raw = box.data();
|
||||||
|
|
||||||
|
InvokeQueued(raw, [=] {
|
||||||
|
if (raw->window()->isActiveWindow()) {
|
||||||
|
// In case focus is somewhat in a native child window,
|
||||||
|
// like a webview, Qt glitches here with input fields showing
|
||||||
|
// focused state, but not receiving any keyboard input:
|
||||||
|
//
|
||||||
|
// window()->windowHandle()->isActive() == false.
|
||||||
|
//
|
||||||
|
// Steps were: SeparatePanel with a WebView2 child,
|
||||||
|
// some interaction with mouse inside the WebView2,
|
||||||
|
// so that WebView2 gets focus and active window state,
|
||||||
|
// then we call setSearchAllowed() and after animation
|
||||||
|
// is finished try typing -> nothing happens.
|
||||||
|
//
|
||||||
|
// With this workaround it works fine.
|
||||||
|
_widget->activateWindow();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
_widget->showBox(
|
_widget->showBox(
|
||||||
std::move(box),
|
std::move(box),
|
||||||
LayerOption::KeepOther,
|
LayerOption::KeepOther,
|
||||||
|
|
|
@ -367,12 +367,15 @@ void SetupMenuBots(
|
||||||
(height - icon->height()) / 2);
|
(height - icon->height()) / 2);
|
||||||
}, button->lifetime());
|
}, button->lifetime());
|
||||||
const auto weak = Ui::MakeWeak(container);
|
const auto weak = Ui::MakeWeak(container);
|
||||||
|
const auto show = controller->uiShow();
|
||||||
button->setAcceptBoth(true);
|
button->setAcceptBoth(true);
|
||||||
button->clicks(
|
button->clicks(
|
||||||
) | rpl::start_with_next([=](Qt::MouseButton which) {
|
) | rpl::start_with_next([=](Qt::MouseButton which) {
|
||||||
if (which == Qt::LeftButton) {
|
if (which == Qt::LeftButton) {
|
||||||
bots->requestSimple(controller, user, {
|
bots->open({
|
||||||
.fromMainMenu = true,
|
.bot = user,
|
||||||
|
.context = { .controller = controller },
|
||||||
|
.source = InlineBots::WebViewSourceMainMenu(),
|
||||||
});
|
});
|
||||||
if (weak) {
|
if (weak) {
|
||||||
controller->window().hideSettingsAndLayer();
|
controller->window().hideSettingsAndLayer();
|
||||||
|
@ -384,7 +387,7 @@ void SetupMenuBots(
|
||||||
st::popupMenuWithIcons);
|
st::popupMenuWithIcons);
|
||||||
(*menu)->addAction(
|
(*menu)->addAction(
|
||||||
tr::lng_bot_remove_from_menu(tr::now),
|
tr::lng_bot_remove_from_menu(tr::now),
|
||||||
[=] { bots->removeFromMenu(user); },
|
[=] { bots->removeFromMenu(show, user); },
|
||||||
&st::menuIconDelete);
|
&st::menuIconDelete);
|
||||||
(*menu)->popup(QCursor::pos());
|
(*menu)->popup(QCursor::pos());
|
||||||
}
|
}
|
||||||
|
|
|
@ -614,17 +614,23 @@ void SessionNavigation::showPeerByLinkResolved(
|
||||||
const auto contextPeer = item
|
const auto contextPeer = item
|
||||||
? item->history()->peer
|
? item->history()->peer
|
||||||
: bot;
|
: bot;
|
||||||
const auto action = bot->session().attachWebView().lookupLastAction(
|
const auto action = info.clickFromBotWebviewContext
|
||||||
info.clickFromAttachBotWebviewUrl
|
? info.clickFromBotWebviewContext->action
|
||||||
).value_or(Api::SendAction(bot->owner().history(contextPeer)));
|
: Api::SendAction(bot->owner().history(contextPeer));
|
||||||
crl::on_main(this, [=] {
|
crl::on_main(this, [=] {
|
||||||
bot->session().attachWebView().requestApp(
|
bot->session().attachWebView().open({
|
||||||
parentController(),
|
.bot = bot,
|
||||||
action,
|
.context = {
|
||||||
bot,
|
.controller = parentController(),
|
||||||
info.botAppName,
|
.action = action,
|
||||||
info.startToken,
|
.maySkipConfirmation = !info.botAppForceConfirmation,
|
||||||
info.botAppForceConfirmation);
|
},
|
||||||
|
.button = { .startCommand = info.startToken },
|
||||||
|
.source = InlineBots::WebViewSourceLinkApp{
|
||||||
|
.appname = info.botAppName,
|
||||||
|
.token = info.startToken,
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
} else if (bot && resolveType == ResolveType::ShareGame) {
|
} else if (bot && resolveType == ResolveType::ShareGame) {
|
||||||
Window::ShowShareGameBox(parentController(), bot, info.startToken);
|
Window::ShowShareGameBox(parentController(), bot, info.startToken);
|
||||||
|
@ -672,20 +678,25 @@ void SessionNavigation::showPeerByLinkResolved(
|
||||||
crl::on_main(this, [=] {
|
crl::on_main(this, [=] {
|
||||||
const auto history = peer->owner().history(peer);
|
const auto history = peer->owner().history(peer);
|
||||||
showPeerHistory(history, params, msgId);
|
showPeerHistory(history, params, msgId);
|
||||||
peer->session().attachWebView().request(
|
|
||||||
|
peer->session().attachWebView().openByUsername(
|
||||||
parentController(),
|
parentController(),
|
||||||
Api::SendAction(history),
|
Api::SendAction(history),
|
||||||
attachBotUsername,
|
attachBotUsername,
|
||||||
info.attachBotToggleCommand.value_or(QString()));
|
info.attachBotToggleCommand.value_or(QString()));
|
||||||
});
|
});
|
||||||
} else if (bot && info.attachBotMenuOpen) {
|
} else if (bot && info.attachBotMainOpen) {
|
||||||
const auto startCommand = info.attachBotToggleCommand.value_or(
|
const auto startCommand = info.attachBotToggleCommand.value_or(
|
||||||
QString());
|
QString());
|
||||||
bot->session().attachWebView().requestAddToMenu(
|
bot->session().attachWebView().open({
|
||||||
bot,
|
.bot = bot,
|
||||||
InlineBots::AddToMenuOpenMenu{ startCommand },
|
.context = { .controller = parentController() },
|
||||||
parentController(),
|
.button = { .startCommand = startCommand },
|
||||||
std::optional<Api::SendAction>());
|
.source = InlineBots::WebViewSourceLinkBotProfile{
|
||||||
|
.token = startCommand,
|
||||||
|
.compact = info.attachBotMainCompact,
|
||||||
|
},
|
||||||
|
});
|
||||||
} else if (bot && info.attachBotToggleCommand) {
|
} else if (bot && info.attachBotToggleCommand) {
|
||||||
const auto itemId = info.clickFromMessageId;
|
const auto itemId = info.clickFromMessageId;
|
||||||
const auto item = _session->data().message(itemId);
|
const auto item = _session->data().message(itemId);
|
||||||
|
@ -695,17 +706,21 @@ void SessionNavigation::showPeerByLinkResolved(
|
||||||
const auto contextUser = contextPeer
|
const auto contextUser = contextPeer
|
||||||
? contextPeer->asUser()
|
? contextPeer->asUser()
|
||||||
: nullptr;
|
: nullptr;
|
||||||
bot->session().attachWebView().requestAddToMenu(
|
bot->session().attachWebView().open({
|
||||||
bot,
|
.bot = bot,
|
||||||
InlineBots::AddToMenuOpenAttach{
|
.context = {
|
||||||
.startCommand = *info.attachBotToggleCommand,
|
.controller = parentController(),
|
||||||
.chooseTypes = info.attachBotChooseTypes,
|
.action = (contextUser
|
||||||
|
? Api::SendAction(
|
||||||
|
contextUser->owner().history(contextUser))
|
||||||
|
: std::optional<Api::SendAction>()),
|
||||||
},
|
},
|
||||||
parentController(),
|
.button = { .startCommand = *info.attachBotToggleCommand },
|
||||||
(contextUser
|
.source = InlineBots::WebViewSourceLinkAttachMenu{
|
||||||
? Api::SendAction(
|
.choose = info.attachBotChooseTypes,
|
||||||
contextUser->owner().history(contextUser))
|
.token = *info.attachBotToggleCommand,
|
||||||
: std::optional<Api::SendAction>()));
|
},
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
const auto draft = info.text;
|
const auto draft = info.text;
|
||||||
crl::on_main(this, [=] {
|
crl::on_main(this, [=] {
|
||||||
|
|
|
@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
namespace InlineBots {
|
||||||
|
struct WebViewContext;
|
||||||
|
} // namespace InlineBots
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
|
|
||||||
enum class ResolveType {
|
enum class ResolveType {
|
||||||
|
@ -45,11 +49,12 @@ struct PeerByLinkInfo {
|
||||||
bool botAppForceConfirmation = false;
|
bool botAppForceConfirmation = false;
|
||||||
QString attachBotUsername;
|
QString attachBotUsername;
|
||||||
std::optional<QString> attachBotToggleCommand;
|
std::optional<QString> attachBotToggleCommand;
|
||||||
bool attachBotMenuOpen = false;
|
bool attachBotMainOpen = false;
|
||||||
|
bool attachBotMainCompact = false;
|
||||||
InlineBots::PeerTypes attachBotChooseTypes;
|
InlineBots::PeerTypes attachBotChooseTypes;
|
||||||
std::optional<QString> voicechatHash;
|
std::optional<QString> voicechatHash;
|
||||||
FullMsgId clickFromMessageId;
|
FullMsgId clickFromMessageId;
|
||||||
QString clickFromAttachBotWebviewUrl;
|
std::shared_ptr<InlineBots::WebViewContext> clickFromBotWebviewContext;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Window
|
} // namespace Window
|
||||||
|
|
Loading…
Add table
Reference in a new issue