mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-02-24 17:26:58 +00:00
Implement separate instances for web apps.
This commit is contained in:
parent
9461095c88
commit
fd982b90db
@ -488,20 +488,23 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
|
||||
|
||||
case ButtonType::WebView: {
|
||||
if (const auto bot = item->getMessageBot()) {
|
||||
bot->session().attachWebView().request(
|
||||
controller,
|
||||
Api::SendAction(bot->owner().history(bot)),
|
||||
bot,
|
||||
{ .text = button->text, .url = button->data });
|
||||
bot->session().attachWebView().open({
|
||||
.bot = bot,
|
||||
.context = { .controller = controller },
|
||||
.button = { .text = button->text, .url = button->data },
|
||||
.source = InlineBots::WebViewSourceButton{ .simple = false },
|
||||
});
|
||||
}
|
||||
} break;
|
||||
|
||||
case ButtonType::SimpleWebView: {
|
||||
if (const auto bot = item->getMessageBot()) {
|
||||
bot->session().attachWebView().requestSimple(
|
||||
controller,
|
||||
bot,
|
||||
{ .text = button->text, .url = button->data });
|
||||
bot->session().attachWebView().open({
|
||||
.bot = bot,
|
||||
.context = { .controller = controller },
|
||||
.button = {.text = button->text, .url = button->data },
|
||||
.source = InlineBots::WebViewSourceButton{ .simple = true },
|
||||
});
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
@ -231,7 +231,11 @@ Application::~Application() {
|
||||
// For example Domain::removeRedundantAccounts() is called from
|
||||
// Domain::finish() and there is a violation on Ensures(started()).
|
||||
Payments::CheckoutProcess::ClearAll();
|
||||
InlineBots::AttachWebView::ClearAll();
|
||||
for (const auto &[index, account] : _domain->accounts()) {
|
||||
if (account->sessionExists()) {
|
||||
account->session().attachWebView().closeAll();
|
||||
}
|
||||
}
|
||||
_iv->closeAll();
|
||||
|
||||
_domain->finish();
|
||||
|
@ -191,11 +191,13 @@ void BotGameUrlClickHandler::onClick(ClickContext context) const {
|
||||
const auto title = game->title;
|
||||
const auto itemId = my.itemId;
|
||||
const auto openGame = [=] {
|
||||
bot->session().attachWebView().showGame({
|
||||
bot->session().attachWebView().open({
|
||||
.bot = bot,
|
||||
.context = itemId,
|
||||
.url = url,
|
||||
.title = title,
|
||||
.button = {.url = url.toUtf8() },
|
||||
.source = InlineBots::WebViewSourceGame{
|
||||
.messageId = itemId,
|
||||
.title = title,
|
||||
},
|
||||
});
|
||||
};
|
||||
if (_bot->isVerified()
|
||||
|
@ -21,6 +21,10 @@ namespace Ui {
|
||||
class Show;
|
||||
} // namespace Ui
|
||||
|
||||
namespace InlineBots {
|
||||
struct WebViewContext;
|
||||
} // namespace InlineBots
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
@ -38,10 +42,10 @@ class SessionController;
|
||||
class PeerData;
|
||||
struct ClickHandlerContext {
|
||||
FullMsgId itemId;
|
||||
QString attachBotWebviewUrl;
|
||||
// Is filled from sections.
|
||||
Fn<HistoryView::ElementDelegate*()> elementDelegate;
|
||||
base::weak_ptr<Window::SessionController> sessionWindow;
|
||||
std::shared_ptr<InlineBots::WebViewContext> botWebviewContext;
|
||||
std::shared_ptr<Ui::Show> show;
|
||||
bool mayShowConfirmation = false;
|
||||
bool skipBotAutoLogin = false;
|
||||
|
@ -573,8 +573,11 @@ bool ResolveUsernameOrPhone(
|
||||
: (appname.isEmpty() && params.contains(u"startapp"_q))
|
||||
? params.value(u"startapp"_q)
|
||||
: std::optional<QString>()),
|
||||
.attachBotMenuOpen = (appname.isEmpty()
|
||||
.attachBotMainOpen = (appname.isEmpty()
|
||||
&& params.contains(u"startapp"_q)),
|
||||
.attachBotMainCompact = (appname.isEmpty()
|
||||
&& params.contains(u"startapp"_q)
|
||||
&& (params.value(u"mode"_q) == u"compact"_q)),
|
||||
.attachBotChooseTypes = InlineBots::ParseChooseTypes(
|
||||
params.value(u"choose"_q)),
|
||||
.voicechatHash = (params.contains(u"livestream"_q)
|
||||
@ -585,7 +588,7 @@ bool ResolveUsernameOrPhone(
|
||||
? std::make_optional(params.value(u"voicechat"_q))
|
||||
: std::nullopt),
|
||||
.clickFromMessageId = myContext.itemId,
|
||||
.clickFromAttachBotWebviewUrl = myContext.attachBotWebviewUrl,
|
||||
.clickFromBotWebviewContext = myContext.botWebviewContext,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
@ -626,7 +629,7 @@ bool ResolvePrivatePost(
|
||||
}
|
||||
: Window::RepliesByLinkInfo{ v::null },
|
||||
.clickFromMessageId = my.itemId,
|
||||
.clickFromAttachBotWebviewUrl = my.attachBotWebviewUrl,
|
||||
.clickFromBotWebviewContext = my.botWebviewContext,
|
||||
});
|
||||
controller->window().activate();
|
||||
return true;
|
||||
@ -1178,7 +1181,7 @@ bool ResolveChatLink(
|
||||
controller->showPeerByLink(Window::PeerByLinkInfo{
|
||||
.chatLinkSlug = match->captured(1),
|
||||
.clickFromMessageId = myContext.itemId,
|
||||
.clickFromAttachBotWebviewUrl = myContext.attachBotWebviewUrl,
|
||||
.clickFromBotWebviewContext = myContext.botWebviewContext,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
@ -4940,7 +4940,14 @@ bool HistoryWidget::updateCmdStartShown() {
|
||||
const auto user = _peer ? _peer->asUser() : nullptr;
|
||||
const auto bot = (user && user->isBot()) ? user : nullptr;
|
||||
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()) {
|
||||
_fieldAutocomplete->hideAnimated();
|
||||
} 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/timer.h"
|
||||
#include "base/weak_ptr.h"
|
||||
#include "dialogs/dialogs_key.h"
|
||||
#include "api/api_common.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "ui/chat/attach/attach_bot_webview.h"
|
||||
#include "ui/rp_widget.h"
|
||||
|
||||
namespace Api {
|
||||
struct SendAction;
|
||||
} // namespace Api
|
||||
namespace Data {
|
||||
class Thread;
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
class Show;
|
||||
class GenericBox;
|
||||
class DropdownMenu;
|
||||
} // namespace Ui
|
||||
@ -47,6 +50,8 @@ enum class CheckoutResult;
|
||||
|
||||
namespace InlineBots {
|
||||
|
||||
class WebViewInstance;
|
||||
|
||||
enum class PeerType : uint8 {
|
||||
SameBot = 0x01,
|
||||
Bot = 0x02,
|
||||
@ -86,95 +91,176 @@ struct AddToMenuOpenApp {
|
||||
not_null<BotAppData*> app;
|
||||
QString startCommand;
|
||||
};
|
||||
using AddToMenuOpen = std::variant<
|
||||
struct AddToMenuOpen : std::variant<
|
||||
AddToMenuOpenAttach,
|
||||
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 Ui::BotWebView::Delegate {
|
||||
public:
|
||||
explicit AttachWebView(not_null<Main::Session*> session);
|
||||
~AttachWebView();
|
||||
explicit WebViewInstance(WebViewDescriptor &&descriptor);
|
||||
~WebViewInstance();
|
||||
|
||||
struct WebViewButton {
|
||||
QString text;
|
||||
QString startCommand;
|
||||
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);
|
||||
[[nodiscard]] Main::Session &session() const;
|
||||
[[nodiscard]] not_null<UserData*> bot() const;
|
||||
[[nodiscard]] WebViewSource source() const;
|
||||
|
||||
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 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);
|
||||
void activate();
|
||||
void close();
|
||||
|
||||
[[nodiscard]] std::shared_ptr<Main::SessionShow> uiShow();
|
||||
|
||||
static void ClearAll();
|
||||
|
||||
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;
|
||||
bool botHandleLocalUri(QString uri, bool keepOpen) override;
|
||||
@ -194,35 +280,81 @@ private:
|
||||
void botShareGameScore() override;
|
||||
void botClose() override;
|
||||
|
||||
[[nodiscard]] static Context LookupContext(
|
||||
not_null<Window::SessionController*> controller,
|
||||
const Api::SendAction &action);
|
||||
[[nodiscard]] static bool IsSame(
|
||||
const std::unique_ptr<Context> &a,
|
||||
const Context &b);
|
||||
const std::shared_ptr<Ui::Show> _parentShow;
|
||||
const not_null<Main::Session*> _session;
|
||||
const not_null<UserData*> _bot;
|
||||
const WebViewContext _context;
|
||||
const WebViewButton _button;
|
||||
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,
|
||||
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);
|
||||
void requestWithOptionalConfirm(
|
||||
|
||||
enum class AddToMenuResult {
|
||||
AlreadyInMenu,
|
||||
Added,
|
||||
Unsupported,
|
||||
Cancelled,
|
||||
};
|
||||
void requestAddToMenu(
|
||||
not_null<UserData*> bot,
|
||||
const WebViewButton &button,
|
||||
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);
|
||||
Fn<void(AddToMenuResult, PeerTypes supported)> done);
|
||||
void acceptMainMenuDisclaimer(
|
||||
not_null<Window::SessionController*> controller,
|
||||
const WebViewButton &button);
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
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 {
|
||||
Removed,
|
||||
@ -232,67 +364,35 @@ private:
|
||||
void toggleInMenu(
|
||||
not_null<UserData*> bot,
|
||||
ToggledState state,
|
||||
Fn<void()> callback = nullptr);
|
||||
|
||||
void show(
|
||||
uint64 queryId,
|
||||
const QString &url,
|
||||
const QString &buttonText = QString(),
|
||||
bool allowClipboardRead = false,
|
||||
const BotAppData *app = nullptr,
|
||||
bool fromMainMenu = false);
|
||||
Fn<void(bool success)> callback = nullptr);
|
||||
void confirmAddToMenu(
|
||||
AttachWebViewBot bot,
|
||||
Fn<void()> 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);
|
||||
Fn<void(bool added)> callback = nullptr);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
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 _botAppName;
|
||||
QString _startCommand;
|
||||
BotAppData *_app = nullptr;
|
||||
QPointer<Ui::GenericBox> _confirmAddBox;
|
||||
bool _appConfirmationRequired = false;
|
||||
bool _appRequestWriteAccess = false;
|
||||
|
||||
mtpRequestId _requestId = 0;
|
||||
mtpRequestId _prolongId = 0;
|
||||
|
||||
uint64 _botsHash = 0;
|
||||
mtpRequestId _botsRequestId = 0;
|
||||
std::vector<Fn<void()>> _botsRequestCallbacks;
|
||||
|
||||
std::unique_ptr<Context> _addToMenuContext;
|
||||
UserData *_addToMenuBot = nullptr;
|
||||
mtpRequestId _addToMenuId = 0;
|
||||
AddToMenuOpen _addToMenuOpen;
|
||||
base::weak_ptr<Window::SessionController> _addToMenuChooseController;
|
||||
struct AddToMenuProcess {
|
||||
mtpRequestId requestId = 0;
|
||||
std::vector<Fn<void(AddToMenuResult, PeerTypes supported)>> done;
|
||||
};
|
||||
base::flat_map<not_null<UserData*>, AddToMenuProcess> _addToMenu;
|
||||
|
||||
std::vector<AttachWebViewBot> _attachBots;
|
||||
rpl::event_stream<> _attachBotsUpdates;
|
||||
base::flat_set<not_null<UserData*>> _disclaimerAccepted;
|
||||
|
||||
FullMsgId _gameContext;
|
||||
|
||||
std::unique_ptr<Ui::BotWebView::Panel> _panel;
|
||||
bool _catchingCancelInShowCall = false;
|
||||
std::vector<std::unique_ptr<WebViewInstance>> _instances;
|
||||
|
||||
};
|
||||
|
||||
|
@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/effects/path_shift_gradient.h"
|
||||
#include "ui/painter.h"
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
#include "history/history.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
|
||||
@ -677,10 +678,13 @@ void Inner::switchPm() {
|
||||
if (!_inlineBot || !_inlineBot->isBot()) {
|
||||
return;
|
||||
} else if (!_switchPmUrl.isEmpty()) {
|
||||
_inlineBot->session().attachWebView().requestSimple(
|
||||
_controller,
|
||||
_inlineBot,
|
||||
{ .url = _switchPmUrl, .fromSwitch = true });
|
||||
const auto bot = _inlineBot;
|
||||
_inlineBot->session().attachWebView().open({
|
||||
.bot = bot,
|
||||
.context = { .controller = _controller },
|
||||
.button = { .url = _switchPmUrl },
|
||||
.source = InlineBots::WebViewSourceSwitch(),
|
||||
});
|
||||
} else {
|
||||
_inlineBot->botInfo->startToken = _switchPmStartToken;
|
||||
_inlineBot->botInfo->inlineReturnTo
|
||||
|
@ -193,7 +193,7 @@ void MainWindow::setupPasscodeLock() {
|
||||
setInnerFocus();
|
||||
}
|
||||
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(
|
||||
std::move(box),
|
||||
LayerOption::KeepOther,
|
||||
|
@ -367,12 +367,15 @@ void SetupMenuBots(
|
||||
(height - icon->height()) / 2);
|
||||
}, button->lifetime());
|
||||
const auto weak = Ui::MakeWeak(container);
|
||||
const auto show = controller->uiShow();
|
||||
button->setAcceptBoth(true);
|
||||
button->clicks(
|
||||
) | rpl::start_with_next([=](Qt::MouseButton which) {
|
||||
if (which == Qt::LeftButton) {
|
||||
bots->requestSimple(controller, user, {
|
||||
.fromMainMenu = true,
|
||||
bots->open({
|
||||
.bot = user,
|
||||
.context = { .controller = controller },
|
||||
.source = InlineBots::WebViewSourceMainMenu(),
|
||||
});
|
||||
if (weak) {
|
||||
controller->window().hideSettingsAndLayer();
|
||||
@ -384,7 +387,7 @@ void SetupMenuBots(
|
||||
st::popupMenuWithIcons);
|
||||
(*menu)->addAction(
|
||||
tr::lng_bot_remove_from_menu(tr::now),
|
||||
[=] { bots->removeFromMenu(user); },
|
||||
[=] { bots->removeFromMenu(show, user); },
|
||||
&st::menuIconDelete);
|
||||
(*menu)->popup(QCursor::pos());
|
||||
}
|
||||
|
@ -614,17 +614,23 @@ void SessionNavigation::showPeerByLinkResolved(
|
||||
const auto contextPeer = item
|
||||
? item->history()->peer
|
||||
: bot;
|
||||
const auto action = bot->session().attachWebView().lookupLastAction(
|
||||
info.clickFromAttachBotWebviewUrl
|
||||
).value_or(Api::SendAction(bot->owner().history(contextPeer)));
|
||||
const auto action = info.clickFromBotWebviewContext
|
||||
? info.clickFromBotWebviewContext->action
|
||||
: Api::SendAction(bot->owner().history(contextPeer));
|
||||
crl::on_main(this, [=] {
|
||||
bot->session().attachWebView().requestApp(
|
||||
parentController(),
|
||||
action,
|
||||
bot,
|
||||
info.botAppName,
|
||||
info.startToken,
|
||||
info.botAppForceConfirmation);
|
||||
bot->session().attachWebView().open({
|
||||
.bot = bot,
|
||||
.context = {
|
||||
.controller = parentController(),
|
||||
.action = action,
|
||||
.maySkipConfirmation = !info.botAppForceConfirmation,
|
||||
},
|
||||
.button = { .startCommand = info.startToken },
|
||||
.source = InlineBots::WebViewSourceLinkApp{
|
||||
.appname = info.botAppName,
|
||||
.token = info.startToken,
|
||||
},
|
||||
});
|
||||
});
|
||||
} else if (bot && resolveType == ResolveType::ShareGame) {
|
||||
Window::ShowShareGameBox(parentController(), bot, info.startToken);
|
||||
@ -672,20 +678,25 @@ void SessionNavigation::showPeerByLinkResolved(
|
||||
crl::on_main(this, [=] {
|
||||
const auto history = peer->owner().history(peer);
|
||||
showPeerHistory(history, params, msgId);
|
||||
peer->session().attachWebView().request(
|
||||
|
||||
peer->session().attachWebView().openByUsername(
|
||||
parentController(),
|
||||
Api::SendAction(history),
|
||||
attachBotUsername,
|
||||
info.attachBotToggleCommand.value_or(QString()));
|
||||
});
|
||||
} else if (bot && info.attachBotMenuOpen) {
|
||||
} else if (bot && info.attachBotMainOpen) {
|
||||
const auto startCommand = info.attachBotToggleCommand.value_or(
|
||||
QString());
|
||||
bot->session().attachWebView().requestAddToMenu(
|
||||
bot,
|
||||
InlineBots::AddToMenuOpenMenu{ startCommand },
|
||||
parentController(),
|
||||
std::optional<Api::SendAction>());
|
||||
bot->session().attachWebView().open({
|
||||
.bot = bot,
|
||||
.context = { .controller = parentController() },
|
||||
.button = { .startCommand = startCommand },
|
||||
.source = InlineBots::WebViewSourceLinkBotProfile{
|
||||
.token = startCommand,
|
||||
.compact = info.attachBotMainCompact,
|
||||
},
|
||||
});
|
||||
} else if (bot && info.attachBotToggleCommand) {
|
||||
const auto itemId = info.clickFromMessageId;
|
||||
const auto item = _session->data().message(itemId);
|
||||
@ -695,17 +706,21 @@ void SessionNavigation::showPeerByLinkResolved(
|
||||
const auto contextUser = contextPeer
|
||||
? contextPeer->asUser()
|
||||
: nullptr;
|
||||
bot->session().attachWebView().requestAddToMenu(
|
||||
bot,
|
||||
InlineBots::AddToMenuOpenAttach{
|
||||
.startCommand = *info.attachBotToggleCommand,
|
||||
.chooseTypes = info.attachBotChooseTypes,
|
||||
bot->session().attachWebView().open({
|
||||
.bot = bot,
|
||||
.context = {
|
||||
.controller = parentController(),
|
||||
.action = (contextUser
|
||||
? Api::SendAction(
|
||||
contextUser->owner().history(contextUser))
|
||||
: std::optional<Api::SendAction>()),
|
||||
},
|
||||
parentController(),
|
||||
(contextUser
|
||||
? Api::SendAction(
|
||||
contextUser->owner().history(contextUser))
|
||||
: std::optional<Api::SendAction>()));
|
||||
.button = { .startCommand = *info.attachBotToggleCommand },
|
||||
.source = InlineBots::WebViewSourceLinkAttachMenu{
|
||||
.choose = info.attachBotChooseTypes,
|
||||
.token = *info.attachBotToggleCommand,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const auto draft = info.text;
|
||||
crl::on_main(this, [=] {
|
||||
|
@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace InlineBots {
|
||||
struct WebViewContext;
|
||||
} // namespace InlineBots
|
||||
|
||||
namespace Window {
|
||||
|
||||
enum class ResolveType {
|
||||
@ -45,11 +49,12 @@ struct PeerByLinkInfo {
|
||||
bool botAppForceConfirmation = false;
|
||||
QString attachBotUsername;
|
||||
std::optional<QString> attachBotToggleCommand;
|
||||
bool attachBotMenuOpen = false;
|
||||
bool attachBotMainOpen = false;
|
||||
bool attachBotMainCompact = false;
|
||||
InlineBots::PeerTypes attachBotChooseTypes;
|
||||
std::optional<QString> voicechatHash;
|
||||
FullMsgId clickFromMessageId;
|
||||
QString clickFromAttachBotWebviewUrl;
|
||||
std::shared_ptr<InlineBots::WebViewContext> clickFromBotWebviewContext;
|
||||
};
|
||||
|
||||
} // namespace Window
|
||||
|
Loading…
Reference in New Issue
Block a user