Implement switch_webview and "web_app_switch_inline_query".

This commit is contained in:
John Preston 2023-03-01 17:27:13 +04:00 committed by 23rd
parent 34c1bd950e
commit 4fe568cb82
17 changed files with 415 additions and 152 deletions

View file

@ -289,10 +289,11 @@ bool SwitchInlineBotButtonReceived(
} }
void ActivateBotCommand(ClickHandlerContext context, int row, int column) { void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
const auto controller = context.sessionWindow.get(); const auto strong = context.sessionWindow.get();
if (!controller) { if (!strong) {
return; return;
} }
const auto controller = not_null{ strong };
const auto item = controller->session().data().message(context.itemId); const auto item = controller->session().data().message(context.itemId);
if (!item) { if (!item) {
return; return;

View file

@ -110,6 +110,9 @@ struct EntryState {
FilterId filterId = 0; FilterId filterId = 0;
MsgId rootId = 0; MsgId rootId = 0;
MsgId currentReplyToId = 0; MsgId currentReplyToId = 0;
friend inline constexpr auto operator<=>(EntryState, EntryState) noexcept
= default;
}; };
} // namespace Dialogs } // namespace Dialogs

View file

@ -924,8 +924,8 @@ void HistoryWidget::refreshJoinChannelText() {
void HistoryWidget::refreshTopBarActiveChat() { void HistoryWidget::refreshTopBarActiveChat() {
const auto state = computeDialogsEntryState(); const auto state = computeDialogsEntryState();
_topBar->setActiveChat(state, _history->sendActionPainter()); _topBar->setActiveChat(state, _history->sendActionPainter());
if (_inlineResults) { if (state.key) {
_inlineResults->setCurrentDialogsEntryState(state); controller()->setCurrentDialogsEntryState(state);
} }
} }
@ -1477,8 +1477,6 @@ void HistoryWidget::applyInlineBotQuery(UserData *bot, const QString &query) {
sendInlineResult(result); sendInlineResult(result);
} }
}); });
_inlineResults->setCurrentDialogsEntryState(
computeDialogsEntryState());
_inlineResults->setSendMenuType([=] { return sendMenuType(); }); _inlineResults->setSendMenuType([=] { return sendMenuType(); });
_inlineResults->requesting( _inlineResults->requesting(
) | rpl::start_with_next([=](bool requesting) { ) | rpl::start_with_next([=](bool requesting) {
@ -1761,14 +1759,21 @@ void HistoryWidget::setInnerFocus() {
} }
} }
bool HistoryWidget::notify_switchInlineBotButtonReceived(const QString &query, UserData *samePeerBot, MsgId samePeerReplyTo) { bool HistoryWidget::notify_switchInlineBotButtonReceived(
const QString &query,
UserData *samePeerBot,
MsgId samePeerReplyTo) {
if (samePeerBot) { if (samePeerBot) {
if (_history) { if (_history) {
const auto textWithTags = TextWithTags{ const auto textWithTags = TextWithTags{
'@' + samePeerBot->username() + ' ' + query, '@' + samePeerBot->username() + ' ' + query,
TextWithTags::Tags(), TextWithTags::Tags(),
}; };
MessageCursor cursor = { int(textWithTags.text.size()), int(textWithTags.text.size()), QFIXED_MAX }; MessageCursor cursor = {
int(textWithTags.text.size()),
int(textWithTags.text.size()),
QFIXED_MAX,
};
_history->setLocalDraft(std::make_unique<Data::Draft>( _history->setLocalDraft(std::make_unique<Data::Draft>(
textWithTags, textWithTags,
0, // replyTo 0, // replyTo
@ -1782,39 +1787,11 @@ bool HistoryWidget::notify_switchInlineBotButtonReceived(const QString &query, U
const auto to = bot->isBot() const auto to = bot->isBot()
? bot->botInfo->inlineReturnTo ? bot->botInfo->inlineReturnTo
: Dialogs::EntryState(); : Dialogs::EntryState();
const auto history = to.key.owningHistory(); if (!to.key.owningHistory()) {
if (!history) {
return false; return false;
} }
bot->botInfo->inlineReturnTo = Dialogs::EntryState(); bot->botInfo->inlineReturnTo = Dialogs::EntryState();
using Section = Dialogs::EntryState::Section; controller()->switchInlineQuery(to, bot, query);
const auto textWithTags = TextWithTags{
'@' + bot->username() + ' ' + query,
TextWithTags::Tags(),
};
MessageCursor cursor = { int(textWithTags.text.size()), int(textWithTags.text.size()), QFIXED_MAX };
auto draft = std::make_unique<Data::Draft>(
textWithTags,
to.currentReplyToId,
to.rootId,
cursor,
Data::PreviewState::Allowed);
if (to.section == Section::Scheduled) {
history->setDraft(Data::DraftKey::Scheduled(), std::move(draft));
controller()->showSection(
std::make_shared<HistoryView::ScheduledMemento>(history));
} else {
history->setLocalDraft(std::move(draft));
if (to.section == Section::Replies) {
controller()->showRepliesForMessage(history, to.rootId);
} else if (history == _history) {
applyDraft();
} else {
controller()->showPeerHistory(history->peer);
}
}
return true; return true;
} }
return false; return false;
@ -2417,6 +2394,7 @@ void HistoryWidget::refreshAttachBotsMenu() {
} }
_attachBotsMenu = InlineBots::MakeAttachBotsMenu( _attachBotsMenu = InlineBots::MakeAttachBotsMenu(
this, this,
controller(),
_history->peer, _history->peer,
[=] { return prepareSendAction({}); }, [=] { return prepareSendAction({}); },
[=](bool compress) { chooseAttach(compress); }); [=](bool compress) { chooseAttach(compress); });

View file

@ -1022,9 +1022,6 @@ void ComposeControls::setCurrentDialogsEntryState(Dialogs::EntryState state) {
_currentDialogsEntryState = state; _currentDialogsEntryState = state;
updateForwarding(); updateForwarding();
registerDraftSource(); registerDraftSource();
if (_inlineResults) {
_inlineResults->setCurrentDialogsEntryState(state);
}
} }
PeerData *ComposeControls::sendAsPeer() const { PeerData *ComposeControls::sendAsPeer() const {
@ -2405,6 +2402,7 @@ void ComposeControls::updateAttachBotsMenu() {
} }
_attachBotsMenu = InlineBots::MakeAttachBotsMenu( _attachBotsMenu = InlineBots::MakeAttachBotsMenu(
_parent, _parent,
_window,
_history->peer, _history->peer,
_sendActionFactory, _sendActionFactory,
[=](bool compress) { _attachRequests.fire_copy(compress); }); [=](bool compress) { _attachRequests.fire_copy(compress); });
@ -2860,8 +2858,6 @@ void ComposeControls::applyInlineBotQuery(
_inlineResults = std::make_unique<InlineBots::Layout::Widget>( _inlineResults = std::make_unique<InlineBots::Layout::Widget>(
_parent, _parent,
_window); _window);
_inlineResults->setCurrentDialogsEntryState(
_currentDialogsEntryState);
_inlineResults->setResultSelectedCallback([=]( _inlineResults->setResultSelectedCallback([=](
InlineBots::ResultSelected result) { InlineBots::ResultSelected result) {
if (result.open) { if (result.open) {

View file

@ -1461,6 +1461,7 @@ void RepliesWidget::refreshTopBarActiveChat() {
}; };
_topBar->setActiveChat(state, _sendAction.get()); _topBar->setActiveChat(state, _sendAction.get());
_composeControls->setCurrentDialogsEntryState(state); _composeControls->setCurrentDialogsEntryState(state);
controller()->setCurrentDialogsEntryState(state);
} }
MsgId RepliesWidget::replyToId() const { MsgId RepliesWidget::replyToId() const {
@ -1961,6 +1962,10 @@ bool RepliesWidget::showInternal(
if (!logMemento->getHighlightId()) { if (!logMemento->getHighlightId()) {
showAtPosition(Data::UnreadMessagePosition); showAtPosition(Data::UnreadMessagePosition);
} }
if (params.reapplyLocalDraft) {
_composeControls->applyDraft(
ComposeControls::FieldHistoryAction::NewEntry);
}
return true; return true;
} }
} }

View file

@ -134,6 +134,7 @@ ScheduledWidget::ScheduledWidget(
}; };
_topBar->setActiveChat(state, nullptr); _topBar->setActiveChat(state, nullptr);
_composeControls->setCurrentDialogsEntryState(state); _composeControls->setCurrentDialogsEntryState(state);
controller->setCurrentDialogsEntryState(state);
_topBar->move(0, 0); _topBar->move(0, 0);
_topBar->resizeToWidth(width()); _topBar->resizeToWidth(width());
@ -924,6 +925,10 @@ bool ScheduledWidget::showInternal(
if (auto logMemento = dynamic_cast<ScheduledMemento*>(memento.get())) { if (auto logMemento = dynamic_cast<ScheduledMemento*>(memento.get())) {
if (logMemento->getHistory() == history()) { if (logMemento->getHistory() == history()) {
restoreState(logMemento); restoreState(logMemento);
if (params.reapplyLocalDraft) {
_composeControls->applyDraft(
ComposeControls::FieldHistoryAction::NewEntry);
}
return true; return true;
} }
} }

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "inline_bots/bot_attach_web_view.h" #include "inline_bots/bot_attach_web_view.h"
#include "api/api_common.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_document.h" #include "data/data_document.h"
@ -42,6 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/random.h" #include "base/random.h"
#include "base/timer_rpl.h" #include "base/timer_rpl.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "mainwidget.h"
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
#include "styles/style_menu_icons.h" #include "styles/style_menu_icons.h"
@ -57,18 +59,6 @@ struct ParsedBot {
bool inactive = false; bool inactive = false;
}; };
[[nodiscard]] bool IsSame(
const std::optional<Api::SendAction> &a,
const Api::SendAction &b) {
// Check fields that are sent to API in bot attach webview requests.
return a.has_value()
&& (a->history == b.history)
&& (a->replyTo == b.replyTo)
&& (a->topicRootId == b.topicRootId)
&& (a->options.sendAs == b.options.sendAs)
&& (a->options.silent == b.options.silent);
}
[[nodiscard]] DocumentData *ResolveIcon( [[nodiscard]] DocumentData *ResolveIcon(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const MTPDattachMenuBot &data) { const MTPDattachMenuBot &data) {
@ -132,10 +122,29 @@ struct ParsedBot {
return result; return result;
} }
[[nodiscard]] PeerTypes PeerTypesFromNames(
const std::vector<QString> &names) {
auto result = PeerTypes();
for (const auto &name : names) {
//, bots, groups, channels
result |= (name == u"users"_q)
? PeerType::User
: name == u"bots"_q
? PeerType::Bot
: name == u"groups"_q
? PeerType::Group
: name == u"channels"_q
? PeerType::Broadcast
: PeerType(0);
}
return result;
}
void ShowChooseBox( void ShowChooseBox(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
PeerTypes types, PeerTypes types,
Fn<void(not_null<Data::Thread*>)> callback) { Fn<void(not_null<Data::Thread*>)> callback,
rpl::producer<QString> titleOverride = nullptr) {
const auto weak = std::make_shared<QPointer<Ui::BoxContent>>(); const auto weak = std::make_shared<QPointer<Ui::BoxContent>>();
auto done = [=](not_null<Data::Thread*> thread) mutable { auto done = [=](not_null<Data::Thread*> thread) mutable {
if (const auto strong = *weak) { if (const auto strong = *weak) {
@ -159,7 +168,10 @@ void ShowChooseBox(
return (types & PeerType::Group); return (types & PeerType::Group);
} }
}; };
auto initBox = [](not_null<PeerListBox*> box) { auto initBox = [=](not_null<PeerListBox*> box) {
if (titleOverride) {
box->setTitle(std::move(titleOverride));
}
box->addButton(tr::lng_cancel(), [box] { box->addButton(tr::lng_cancel(), [box] {
box->closeBox(); box->closeBox();
}); });
@ -407,6 +419,13 @@ PeerTypes ParseChooseTypes(QStringView choose) {
return result; return result;
} }
struct AttachWebView::Context {
base::weak_ptr<Window::SessionController> controller;
Dialogs::EntryState dialogsEntryState;
Api::SendAction action;
bool fromSwitch = false;
};
AttachWebView::AttachWebView(not_null<Main::Session*> session) AttachWebView::AttachWebView(not_null<Main::Session*> session)
: _session(session) { : _session(session) {
} }
@ -416,6 +435,7 @@ AttachWebView::~AttachWebView() {
} }
void AttachWebView::request( void AttachWebView::request(
not_null<Window::SessionController*> controller,
const Api::SendAction &action, const Api::SendAction &action,
const QString &botUsername, const QString &botUsername,
const QString &startCommand) { const QString &startCommand) {
@ -423,7 +443,8 @@ void AttachWebView::request(
return; return;
} }
const auto username = _bot ? _bot->username() : _botUsername; const auto username = _bot ? _bot->username() : _botUsername;
if (IsSame(_action, action) const auto context = LookupContext(controller, action);
if (IsSame(_context, context)
&& username.toLower() == botUsername.toLower() && username.toLower() == botUsername.toLower()
&& _startCommand == startCommand) { && _startCommand == startCommand) {
if (_panel) { if (_panel) {
@ -433,18 +454,55 @@ void AttachWebView::request(
} }
cancel(); cancel();
_action = action; _context = std::make_unique<Context>(context);
_botUsername = botUsername; _botUsername = botUsername;
_startCommand = startCommand; _startCommand = startCommand;
resolve(); resolve();
} }
AttachWebView::Context AttachWebView::LookupContext(
not_null<Window::SessionController*> controller,
const Api::SendAction &action) {
return {
.controller = controller,
.dialogsEntryState = controller->currentDialogsEntryState(),
.action = action,
};
}
bool AttachWebView::IsSame(
const std::unique_ptr<Context> &a,
const Context &b) {
// Check fields that are sent to API in bot attach webview requests.
return a
&& (a->controller == b.controller)
&& (a->dialogsEntryState == b.dialogsEntryState)
&& (a->fromSwitch == b.fromSwitch)
&& (a->action.history == b.action.history)
&& (a->action.replyTo == b.action.replyTo)
&& (a->action.topicRootId == b.action.topicRootId)
&& (a->action.options.sendAs == b.action.options.sendAs)
&& (a->action.options.silent == b.action.options.silent);
}
void AttachWebView::request( void AttachWebView::request(
Window::SessionController *controller, not_null<Window::SessionController*> controller,
const Api::SendAction &action, const Api::SendAction &action,
not_null<UserData*> bot, not_null<UserData*> bot,
const WebViewButton &button) { const WebViewButton &button) {
if (IsSame(_action, action) && _bot == bot) { requestWithOptionalConfirm(
bot,
button,
LookupContext(controller, action),
button.fromMenu ? nullptr : controller.get());
}
void AttachWebView::requestWithOptionalConfirm(
not_null<UserData*> bot,
const WebViewButton &button,
const Context &context,
Window::SessionController *controllerForConfirm) {
if (IsSame(_context, context) && _bot == bot) {
if (_panel) { if (_panel) {
_panel->requestActivate(); _panel->requestActivate();
} else if (_requestId) { } else if (_requestId) {
@ -454,9 +512,9 @@ void AttachWebView::request(
cancel(); cancel();
_bot = bot; _bot = bot;
_action = action; _context = std::make_unique<Context>(context);
if (controller) { if (controllerForConfirm) {
confirmOpen(controller, [=] { confirmOpen(controllerForConfirm, [=] {
request(button); request(button);
}); });
} else { } else {
@ -465,30 +523,31 @@ void AttachWebView::request(
} }
void AttachWebView::request(const WebViewButton &button) { void AttachWebView::request(const WebViewButton &button) {
Expects(_action.has_value() && _bot != nullptr); Expects(_context != nullptr && _bot != nullptr);
_startCommand = button.startCommand; _startCommand = button.startCommand;
const auto &action = _context->action;
using Flag = MTPmessages_RequestWebView::Flag; using Flag = MTPmessages_RequestWebView::Flag;
const auto flags = Flag::f_theme_params const auto flags = Flag::f_theme_params
| (button.url.isEmpty() ? Flag(0) : Flag::f_url) | (button.url.isEmpty() ? Flag(0) : Flag::f_url)
| (_startCommand.isEmpty() ? Flag(0) : Flag::f_start_param) | (_startCommand.isEmpty() ? Flag(0) : Flag::f_start_param)
| (_action->replyTo ? Flag::f_reply_to_msg_id : Flag(0)) | (action.replyTo ? Flag::f_reply_to_msg_id : Flag(0))
| (_action->topicRootId ? Flag::f_top_msg_id : Flag(0)) | (action.topicRootId ? Flag::f_top_msg_id : Flag(0))
| (_action->options.sendAs ? Flag::f_send_as : Flag(0)) | (action.options.sendAs ? Flag::f_send_as : Flag(0))
| (_action->options.silent ? Flag::f_silent : Flag(0)); | (action.options.silent ? Flag::f_silent : Flag(0));
_requestId = _session->api().request(MTPmessages_RequestWebView( _requestId = _session->api().request(MTPmessages_RequestWebView(
MTP_flags(flags), MTP_flags(flags),
_action->history->peer->input, action.history->peer->input,
_bot->inputUser, _bot->inputUser,
MTP_bytes(button.url), MTP_bytes(button.url),
MTP_string(_startCommand), MTP_string(_startCommand),
MTP_dataJSON(MTP_bytes(Window::Theme::WebViewParams().json)), MTP_dataJSON(MTP_bytes(Window::Theme::WebViewParams().json)),
MTP_string("tdesktop"), MTP_string("tdesktop"),
MTP_int(_action->replyTo.bare), MTP_int(action.replyTo.bare),
MTP_int(_action->topicRootId.bare), MTP_int(action.topicRootId.bare),
(_action->options.sendAs (action.options.sendAs
? _action->options.sendAs->input ? action.options.sendAs->input
: MTP_inputPeerEmpty()) : MTP_inputPeerEmpty())
)).done([=](const MTPWebViewResult &result) { )).done([=](const MTPWebViewResult &result) {
_requestId = 0; _requestId = 0;
@ -512,7 +571,7 @@ void AttachWebView::cancel() {
_session->api().request(base::take(_requestId)).cancel(); _session->api().request(base::take(_requestId)).cancel();
_session->api().request(base::take(_prolongId)).cancel(); _session->api().request(base::take(_prolongId)).cancel();
_panel = nullptr; _panel = nullptr;
_action = std::nullopt; _context = nullptr;
_bot = nullptr; _bot = nullptr;
_botUsername = QString(); _botUsername = QString();
_startCommand = QString(); _startCommand = QString();
@ -551,21 +610,35 @@ void AttachWebView::requestBots() {
} }
void AttachWebView::requestAddToMenu( void AttachWebView::requestAddToMenu(
const std::optional<Api::SendAction> &action, not_null<UserData*> bot,
const QString &startCommand) {
requestAddToMenu(bot, startCommand, nullptr, std::nullopt, PeerTypes());
}
void AttachWebView::requestAddToMenu(
not_null<UserData*> bot, not_null<UserData*> bot,
const QString &startCommand, const QString &startCommand,
Window::SessionController *controller, Window::SessionController *controller,
std::optional<Api::SendAction> action,
PeerTypes chooseTypes) { PeerTypes chooseTypes) {
Expects(controller != nullptr || _context != nullptr);
if (!bot->isBot() || !bot->botInfo->supportsAttachMenu) { if (!bot->isBot() || !bot->botInfo->supportsAttachMenu) {
Ui::ShowMultilineToast({ Ui::ShowMultilineToast({
.text = { tr::lng_bot_menu_not_supported(tr::now) }, .text = { tr::lng_bot_menu_not_supported(tr::now) },
}); });
return; return;
} }
const auto wasController = (controller != nullptr);
_addToMenuChooseController = base::make_weak(controller); _addToMenuChooseController = base::make_weak(controller);
_addToMenuStartCommand = startCommand; _addToMenuStartCommand = startCommand;
_addToMenuChooseTypes = chooseTypes; _addToMenuChooseTypes = chooseTypes;
_addToMenuAction = action; if (!controller) {
_addToMenuContext = base::take(_context);
} else if (action) {
_addToMenuContext = std::make_unique<Context>(
LookupContext(controller, *action));
}
if (_addToMenuId) { if (_addToMenuId) {
if (_addToMenuBot == bot) { if (_addToMenuBot == bot) {
return; return;
@ -578,32 +651,35 @@ void AttachWebView::requestAddToMenu(
)).done([=](const MTPAttachMenuBotsBot &result) { )).done([=](const MTPAttachMenuBotsBot &result) {
_addToMenuId = 0; _addToMenuId = 0;
const auto bot = base::take(_addToMenuBot); const auto bot = base::take(_addToMenuBot);
const auto contextAction = base::take(_addToMenuAction); const auto context = std::shared_ptr(base::take(_addToMenuContext));
const auto chooseTypes = base::take(_addToMenuChooseTypes); const auto chooseTypes = base::take(_addToMenuChooseTypes);
const auto startCommand = base::take(_addToMenuStartCommand); const auto startCommand = base::take(_addToMenuStartCommand);
const auto chooseController = base::take(_addToMenuChooseController); const auto chooseController = base::take(_addToMenuChooseController);
const auto open = [=](PeerTypes types) { const auto open = [=](PeerTypes types) {
if (const auto useTypes = chooseTypes & types) { const auto strong = chooseController.get();
if (const auto strong = chooseController.get()) { if (!strong) {
const auto done = [=](not_null<Data::Thread*> thread) { if (wasController) {
strong->showThread(thread); // Just ignore the click if controller was destroyed.
request( return true;
nullptr,
Api::SendAction(thread),
bot,
{ .startCommand = startCommand });
};
ShowChooseBox(strong, useTypes, done);
} }
} else if (const auto useTypes = chooseTypes & types) {
const auto done = [=](not_null<Data::Thread*> thread) {
strong->showThread(thread);
requestWithOptionalConfirm(
bot,
{ .startCommand = startCommand },
LookupContext(strong, Api::SendAction(thread)));
};
ShowChooseBox(strong, useTypes, done);
return true; return true;
} else if (!contextAction) { }
if (!context) {
return false; return false;
} }
request( requestWithOptionalConfirm(
nullptr,
*contextAction,
bot, bot,
{ .startCommand = startCommand }); { .startCommand = startCommand },
*context);
return true; return true;
}; };
result.match([&](const MTPDattachMenuBotsBot &data) { result.match([&](const MTPDattachMenuBotsBot &data) {
@ -630,7 +706,7 @@ void AttachWebView::requestAddToMenu(
}).fail([=] { }).fail([=] {
_addToMenuId = 0; _addToMenuId = 0;
_addToMenuBot = nullptr; _addToMenuBot = nullptr;
_addToMenuAction = std::nullopt; _addToMenuContext = nullptr;
_addToMenuStartCommand = QString(); _addToMenuStartCommand = QString();
Ui::ShowMultilineToast({ Ui::ShowMultilineToast({
.text = { tr::lng_bot_menu_not_supported(tr::now) }, .text = { tr::lng_bot_menu_not_supported(tr::now) },
@ -648,6 +724,9 @@ void AttachWebView::removeFromMenu(not_null<UserData*> bot) {
void AttachWebView::resolve() { void AttachWebView::resolve() {
resolveUsername(_botUsername, [=](not_null<PeerData*> bot) { resolveUsername(_botUsername, [=](not_null<PeerData*> bot) {
if (!_context) {
return;
}
_bot = bot->asUser(); _bot = bot->asUser();
if (!_bot) { if (!_bot) {
Ui::ShowMultilineToast({ Ui::ShowMultilineToast({
@ -655,7 +734,7 @@ void AttachWebView::resolve() {
}); });
return; return;
} }
requestAddToMenu(_action, _bot, _startCommand); requestAddToMenu(_bot, _startCommand);
}); });
} }
@ -696,7 +775,10 @@ void AttachWebView::requestSimple(
const WebViewButton &button) { const WebViewButton &button) {
cancel(); cancel();
_bot = bot; _bot = bot;
_action = Api::SendAction(bot->owner().history(bot)); _context = std::make_unique<Context>(LookupContext(
controller,
Api::SendAction(bot->owner().history(bot))));
_context->fromSwitch = button.fromSwitch;
confirmOpen(controller, [=] { confirmOpen(controller, [=] {
requestSimple(button); requestSimple(button);
}); });
@ -705,7 +787,8 @@ void AttachWebView::requestSimple(
void AttachWebView::requestSimple(const WebViewButton &button) { void AttachWebView::requestSimple(const WebViewButton &button) {
using Flag = MTPmessages_RequestSimpleWebView::Flag; using Flag = MTPmessages_RequestSimpleWebView::Flag;
_requestId = _session->api().request(MTPmessages_RequestSimpleWebView( _requestId = _session->api().request(MTPmessages_RequestSimpleWebView(
MTP_flags(Flag::f_theme_params), MTP_flags(Flag::f_theme_params
| (button.fromSwitch ? Flag::f_from_switch_webview : Flag())),
_bot->inputUser, _bot->inputUser,
MTP_bytes(button.url), MTP_bytes(button.url),
MTP_dataJSON(MTP_bytes(Window::Theme::WebViewParams().json)), MTP_dataJSON(MTP_bytes(Window::Theme::WebViewParams().json)),
@ -726,29 +809,32 @@ void AttachWebView::requestMenu(
not_null<UserData*> bot) { not_null<UserData*> bot) {
cancel(); cancel();
_bot = bot; _bot = bot;
_action = Api::SendAction(bot->owner().history(bot)); _context = std::make_unique<Context>(LookupContext(
controller,
Api::SendAction(bot->owner().history(bot))));
const auto url = bot->botInfo->botMenuButtonUrl; const auto url = bot->botInfo->botMenuButtonUrl;
const auto text = bot->botInfo->botMenuButtonText; const auto text = bot->botInfo->botMenuButtonText;
confirmOpen(controller, [=] { confirmOpen(controller, [=] {
const auto &action = _context->action;
using Flag = MTPmessages_RequestWebView::Flag; using Flag = MTPmessages_RequestWebView::Flag;
_requestId = _session->api().request(MTPmessages_RequestWebView( _requestId = _session->api().request(MTPmessages_RequestWebView(
MTP_flags(Flag::f_theme_params MTP_flags(Flag::f_theme_params
| Flag::f_url | Flag::f_url
| Flag::f_from_bot_menu | Flag::f_from_bot_menu
| (_action->replyTo? Flag::f_reply_to_msg_id : Flag(0)) | (action.replyTo? Flag::f_reply_to_msg_id : Flag(0))
| (_action->topicRootId ? Flag::f_top_msg_id : Flag(0)) | (action.topicRootId ? Flag::f_top_msg_id : Flag(0))
| (_action->options.sendAs ? Flag::f_send_as : Flag(0)) | (action.options.sendAs ? Flag::f_send_as : Flag(0))
| (_action->options.silent ? Flag::f_silent : Flag(0))), | (action.options.silent ? Flag::f_silent : Flag(0))),
_action->history->peer->input, action.history->peer->input,
_bot->inputUser, _bot->inputUser,
MTP_string(url), MTP_string(url),
MTPstring(), // start_param MTPstring(), // start_param
MTP_dataJSON(MTP_bytes(Window::Theme::WebViewParams().json)), MTP_dataJSON(MTP_bytes(Window::Theme::WebViewParams().json)),
MTP_string("tdesktop"), MTP_string("tdesktop"),
MTP_int(_action->replyTo.bare), MTP_int(action.replyTo.bare),
MTP_int(_action->topicRootId.bare), MTP_int(action.topicRootId.bare),
(_action->options.sendAs (action.options.sendAs
? _action->options.sendAs->input ? action.options.sendAs->input
: MTP_inputPeerEmpty()) : MTP_inputPeerEmpty())
)).done([=](const MTPWebViewResult &result) { )).done([=](const MTPWebViewResult &result) {
_requestId = 0; _requestId = 0;
@ -801,13 +887,16 @@ void AttachWebView::show(
const QString &url, const QString &url,
const QString &buttonText, const QString &buttonText,
bool allowClipboardRead) { bool allowClipboardRead) {
Expects(_bot != nullptr && _action.has_value()); Expects(_bot != nullptr && _context != nullptr);
const auto close = crl::guard(this, [=] { const auto close = crl::guard(this, [=] {
crl::on_main(this, [=] { cancel(); }); crl::on_main(this, [=] { cancel(); });
}); });
const auto sendData = crl::guard(this, [=](QByteArray data) { const auto sendData = crl::guard(this, [=](QByteArray data) {
if (!_action || _action->history->peer != _bot || queryId) { if (!_context
|| _context->fromSwitch
|| _context->action.history->peer != _bot
|| queryId) {
return; return;
} }
const auto randomId = base::RandomValue<uint64>(); const auto randomId = base::RandomValue<uint64>();
@ -821,6 +910,43 @@ void AttachWebView::show(
}).send(); }).send();
crl::on_main(this, [=] { cancel(); }); 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 botAndQuery = '@'
+ _bot->username()
+ ' '
+ query;
const auto done = [=](not_null<Data::Thread*> thread) {
return controller->content()->inlineSwitchChosen(
thread,
botAndQuery);
};
ShowChooseBox(
controller,
types,
done,
tr::lng_inline_switch_choose());
}
crl::on_main(this, [=] { cancel(); });
});
const auto handleLocalUri = [close](QString uri) { const auto handleLocalUri = [close](QString uri) {
const auto local = Core::TryConvertUrlToLocal(uri); const auto local = Core::TryConvertUrlToLocal(uri);
if (uri == local || Core::InternalPassportLink(local)) { if (uri == local || Core::InternalPassportLink(local)) {
@ -868,7 +994,8 @@ void AttachWebView::show(
const auto hasSettings = (attached != end(_attachBots)) const auto hasSettings = (attached != end(_attachBots))
&& !attached->inactive && !attached->inactive
&& attached->hasSettings; && attached->hasSettings;
const auto hasOpenBot = !_action || (_bot != _action->history->peer); const auto hasOpenBot = !_context
|| (_bot != _context->action.history->peer);
const auto hasRemoveFromMenu = (attached != end(_attachBots)) const auto hasRemoveFromMenu = (attached != end(_attachBots))
&& !attached->inactive; && !attached->inactive;
const auto buttons = (hasSettings ? Button::Settings : Button::None) const auto buttons = (hasSettings ? Button::Settings : Button::None)
@ -919,6 +1046,7 @@ void AttachWebView::show(
.handleLocalUri = handleLocalUri, .handleLocalUri = handleLocalUri,
.handleInvoice = handleInvoice, .handleInvoice = handleInvoice,
.sendData = sendData, .sendData = sendData,
.switchInlineQuery = switchInlineQuery,
.close = close, .close = close,
.phone = _session->user()->phone(), .phone = _session->user()->phone(),
.menuButtons = buttons, .menuButtons = buttons,
@ -931,7 +1059,11 @@ void AttachWebView::show(
} }
void AttachWebView::started(uint64 queryId) { void AttachWebView::started(uint64 queryId) {
Expects(_action.has_value() && _bot != nullptr); Expects(_bot != nullptr && _context != nullptr);
if (_context->fromSwitch) {
return;
}
_session->data().webViewResultSent( _session->data().webViewResultSent(
) | rpl::filter([=](const Data::Session::WebViewResultSent &sent) { ) | rpl::filter([=](const Data::Session::WebViewResultSent &sent) {
@ -940,6 +1072,7 @@ void AttachWebView::started(uint64 queryId) {
cancel(); cancel();
}, _panel->lifetime()); }, _panel->lifetime());
const auto action = _context->action;
base::timer_each( base::timer_each(
kProlongTimeout kProlongTimeout
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
@ -947,17 +1080,17 @@ void AttachWebView::started(uint64 queryId) {
_session->api().request(base::take(_prolongId)).cancel(); _session->api().request(base::take(_prolongId)).cancel();
_prolongId = _session->api().request(MTPmessages_ProlongWebView( _prolongId = _session->api().request(MTPmessages_ProlongWebView(
MTP_flags(Flag(0) MTP_flags(Flag(0)
| (_action->replyTo ? Flag::f_reply_to_msg_id : Flag(0)) | (action.replyTo ? Flag::f_reply_to_msg_id : Flag(0))
| (_action->topicRootId ? Flag::f_top_msg_id : Flag(0)) | (action.topicRootId ? Flag::f_top_msg_id : Flag(0))
| (_action->options.sendAs ? Flag::f_send_as : Flag(0)) | (action.options.sendAs ? Flag::f_send_as : Flag(0))
| (_action->options.silent ? Flag::f_silent : Flag(0))), | (action.options.silent ? Flag::f_silent : Flag(0))),
_action->history->peer->input, action.history->peer->input,
_bot->inputUser, _bot->inputUser,
MTP_long(queryId), MTP_long(queryId),
MTP_int(_action->replyTo.bare), MTP_int(action.replyTo.bare),
MTP_int(_action->topicRootId.bare), MTP_int(action.topicRootId.bare),
(_action->options.sendAs (action.options.sendAs
? _action->options.sendAs->input ? action.options.sendAs->input
: MTP_inputPeerEmpty()) : MTP_inputPeerEmpty())
)).done([=] { )).done([=] {
_prolongId = 0; _prolongId = 0;
@ -1041,6 +1174,7 @@ void AttachWebView::toggleInMenu(
std::unique_ptr<Ui::DropdownMenu> MakeAttachBotsMenu( std::unique_ptr<Ui::DropdownMenu> MakeAttachBotsMenu(
not_null<QWidget*> parent, not_null<QWidget*> parent,
not_null<Window::SessionController*> controller,
not_null<PeerData*> peer, not_null<PeerData*> peer,
Fn<Api::SendAction()> actionFactory, Fn<Api::SendAction()> actionFactory,
Fn<void(bool)> attach) { Fn<void(bool)> attach) {
@ -1076,7 +1210,7 @@ std::unique_ptr<Ui::DropdownMenu> MakeAttachBotsMenu(
} }
const auto callback = [=] { const auto callback = [=] {
bots->request( bots->request(
nullptr, controller,
actionFactory(), actionFactory(),
bot.user, bot.user,
{ .fromMenu = true }); { .fromMenu = true });

View file

@ -7,11 +7,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
#include "api/api_common.h"
#include "mtproto/sender.h" #include "mtproto/sender.h"
#include "base/weak_ptr.h" #include "base/weak_ptr.h"
#include "base/flags.h" #include "base/flags.h"
namespace Api {
struct SendAction;
} // namespace Api
namespace Ui { namespace Ui {
class GenericBox; class GenericBox;
class DropdownMenu; class DropdownMenu;
@ -71,13 +74,15 @@ public:
QString startCommand; QString startCommand;
QByteArray url; QByteArray url;
bool fromMenu = false; bool fromMenu = false;
bool fromSwitch = false;
}; };
void request( void request(
not_null<Window::SessionController*> controller,
const Api::SendAction &action, const Api::SendAction &action,
const QString &botUsername, const QString &botUsername,
const QString &startCommand); const QString &startCommand);
void request( void request(
Window::SessionController *controller, not_null<Window::SessionController*> controller,
const Api::SendAction &action, const Api::SendAction &action,
not_null<UserData*> bot, not_null<UserData*> bot,
const WebViewButton &button); const WebViewButton &button);
@ -100,16 +105,34 @@ public:
} }
void requestAddToMenu( void requestAddToMenu(
const std::optional<Api::SendAction> &action, not_null<UserData*> bot,
const QString &startCommand);
void requestAddToMenu(
not_null<UserData*> bot, not_null<UserData*> bot,
const QString &startCommand, const QString &startCommand,
Window::SessionController *controller = nullptr, Window::SessionController *controller,
PeerTypes chooseTypes = {}); std::optional<Api::SendAction> action,
PeerTypes chooseTypes);
void removeFromMenu(not_null<UserData*> bot); void removeFromMenu(not_null<UserData*> bot);
static void ClearAll(); static void ClearAll();
private: private:
struct Context;
[[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);
void requestWithOptionalConfirm(
not_null<UserData*> bot,
const WebViewButton &button,
const Context &context,
Window::SessionController *controllerForConfirm = nullptr);
void resolve(); void resolve();
void request(const WebViewButton &button); void request(const WebViewButton &button);
void requestSimple(const WebViewButton &button); void requestSimple(const WebViewButton &button);
@ -143,7 +166,7 @@ private:
const not_null<Main::Session*> _session; const not_null<Main::Session*> _session;
std::optional<Api::SendAction> _action; std::unique_ptr<Context> _context;
UserData *_bot = nullptr; UserData *_bot = nullptr;
QString _botUsername; QString _botUsername;
QString _startCommand; QString _startCommand;
@ -155,7 +178,7 @@ private:
uint64 _botsHash = 0; uint64 _botsHash = 0;
mtpRequestId _botsRequestId = 0; mtpRequestId _botsRequestId = 0;
std::optional<Api::SendAction> _addToMenuAction; std::unique_ptr<Context> _addToMenuContext;
UserData *_addToMenuBot = nullptr; UserData *_addToMenuBot = nullptr;
mtpRequestId _addToMenuId = 0; mtpRequestId _addToMenuId = 0;
QString _addToMenuStartCommand; QString _addToMenuStartCommand;
@ -171,6 +194,7 @@ private:
[[nodiscard]] std::unique_ptr<Ui::DropdownMenu> MakeAttachBotsMenu( [[nodiscard]] std::unique_ptr<Ui::DropdownMenu> MakeAttachBotsMenu(
not_null<QWidget*> parent, not_null<QWidget*> parent,
not_null<Window::SessionController*> controller,
not_null<PeerData*> peer, not_null<PeerData*> peer,
Fn<Api::SendAction()> actionFactory, Fn<Api::SendAction()> actionFactory,
Fn<void(bool)> attach); Fn<void(bool)> attach);

View file

@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_chat_participant_status.h" #include "data/data_chat_participant_status.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "inline_bots/bot_attach_web_view.h"
#include "inline_bots/inline_bot_result.h" #include "inline_bots/inline_bot_result.h"
#include "inline_bots/inline_bot_layout_item.h" #include "inline_bots/inline_bot_layout_item.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
@ -453,6 +454,7 @@ void Inner::refreshSwitchPmButton(const CacheEntry *entry) {
if (!entry || entry->switchPmText.isEmpty()) { if (!entry || entry->switchPmText.isEmpty()) {
_switchPmButton.destroy(); _switchPmButton.destroy();
_switchPmStartToken.clear(); _switchPmStartToken.clear();
_switchPmUrl = QByteArray();
} else { } else {
if (!_switchPmButton) { if (!_switchPmButton) {
_switchPmButton.create(this, nullptr, st::switchPmButton); _switchPmButton.create(this, nullptr, st::switchPmButton);
@ -462,6 +464,7 @@ void Inner::refreshSwitchPmButton(const CacheEntry *entry) {
} }
_switchPmButton->setText(rpl::single(entry->switchPmText)); _switchPmButton->setText(rpl::single(entry->switchPmText));
_switchPmStartToken = entry->switchPmStartToken; _switchPmStartToken = entry->switchPmStartToken;
_switchPmUrl = entry->switchPmUrl;
const auto buttonTop = st::stickerPanPadding; const auto buttonTop = st::stickerPanPadding;
_switchPmButton->move(st::inlineResultsLeft - st::roundRadiusSmall, buttonTop); _switchPmButton->move(st::inlineResultsLeft - st::roundRadiusSmall, buttonTop);
if (isRestrictedView()) { if (isRestrictedView()) {
@ -667,9 +670,17 @@ void Inner::repaintItems(crl::time now) {
} }
void Inner::switchPm() { void Inner::switchPm() {
if (_inlineBot && _inlineBot->isBot()) { if (!_inlineBot || !_inlineBot->isBot()) {
return;
} else if (!_switchPmUrl.isEmpty()) {
_inlineBot->session().attachWebView().requestSimple(
_controller,
_inlineBot,
{ .url = _switchPmUrl, .fromSwitch = true });
} else {
_inlineBot->botInfo->startToken = _switchPmStartToken; _inlineBot->botInfo->startToken = _switchPmStartToken;
_inlineBot->botInfo->inlineReturnTo = _currentDialogsEntryState; _inlineBot->botInfo->inlineReturnTo
= _controller->currentDialogsEntryState();
_controller->showPeerHistory( _controller->showPeerHistory(
_inlineBot, _inlineBot,
Window::SectionShow::Way::ClearStack, Window::SectionShow::Way::ClearStack,

View file

@ -54,7 +54,9 @@ using Results = std::vector<std::unique_ptr<Result>>;
struct CacheEntry { struct CacheEntry {
QString nextOffset; QString nextOffset;
QString switchPmText, switchPmStartToken; QString switchPmText;
QString switchPmStartToken;
QByteArray switchPmUrl;
Results results; Results results;
}; };
@ -87,9 +89,6 @@ public:
void setResultSelectedCallback(Fn<void(ResultSelected)> callback) { void setResultSelectedCallback(Fn<void(ResultSelected)> callback) {
_resultSelectedCallback = std::move(callback); _resultSelectedCallback = std::move(callback);
} }
void setCurrentDialogsEntryState(Dialogs::EntryState state) {
_currentDialogsEntryState = state;
}
void setSendMenuType(Fn<SendMenu::Type()> &&callback); void setSendMenuType(Fn<SendMenu::Type()> &&callback);
// Ui::AbstractTooltipShower interface. // Ui::AbstractTooltipShower interface.
@ -160,7 +159,7 @@ private:
object_ptr<Ui::RoundButton> _switchPmButton = { nullptr }; object_ptr<Ui::RoundButton> _switchPmButton = { nullptr };
QString _switchPmStartToken; QString _switchPmStartToken;
Dialogs::EntryState _currentDialogsEntryState; QByteArray _switchPmUrl;
object_ptr<Ui::FlatLabel> _restrictedLabel = { nullptr }; object_ptr<Ui::FlatLabel> _restrictedLabel = { nullptr };

View file

@ -263,10 +263,6 @@ void Widget::setSendMenuType(Fn<SendMenu::Type()> &&callback) {
_inner->setSendMenuType(std::move(callback)); _inner->setSendMenuType(std::move(callback));
} }
void Widget::setCurrentDialogsEntryState(Dialogs::EntryState state) {
_inner->setCurrentDialogsEntryState(state);
}
void Widget::hideAnimated() { void Widget::hideAnimated() {
if (isHidden()) return; if (isHidden()) return;
if (_hiding) return; if (_hiding) return;
@ -384,13 +380,16 @@ void Widget::inlineResultsDone(const MTPmessages_BotResults &result) {
auto entry = it->second.get(); auto entry = it->second.get();
entry->nextOffset = qs(d.vnext_offset().value_or_empty()); entry->nextOffset = qs(d.vnext_offset().value_or_empty());
if (const auto switchPm = d.vswitch_pm()) { if (const auto switchPm = d.vswitch_pm()) {
switchPm->match([&](const MTPDinlineBotSwitchPM &data) { entry->switchPmText = qs(switchPm->data().vtext());
entry->switchPmText = qs(data.vtext()); entry->switchPmStartToken = qs(switchPm->data().vstart_param());
entry->switchPmStartToken = qs(data.vstart_param()); entry->switchPmUrl = QByteArray();
}); } else if (const auto switchWebView = d.vswitch_webview()) {
entry->switchPmText = qs(switchWebView->data().vtext());
entry->switchPmStartToken = QString();
entry->switchPmUrl = switchWebView->data().vurl().v;
} }
if (auto count = v.size()) { if (const auto count = v.size()) {
entry->results.reserve(entry->results.size() + count); entry->results.reserve(entry->results.size() + count);
} }
auto added = 0; auto added = 0;

View file

@ -76,7 +76,6 @@ public:
void setResultSelectedCallback(Fn<void(ResultSelected)> callback); void setResultSelectedCallback(Fn<void(ResultSelected)> callback);
void setSendMenuType(Fn<SendMenu::Type()> &&callback); void setSendMenuType(Fn<SendMenu::Type()> &&callback);
void setCurrentDialogsEntryState(Dialogs::EntryState state);
[[nodiscard]] rpl::producer<bool> requesting() const { [[nodiscard]] rpl::producer<bool> requesting() const {
return _requesting.events(); return _requesting.events();

View file

@ -1341,7 +1341,9 @@ void MainWidget::showHistory(
_controller->window().activate(); _controller->window().activate();
} }
if (!(_history->peer() && _history->peer()->id == peerId) const auto alreadyThatPeer = _history->peer()
&& (_history->peer()->id == peerId);
if (!alreadyThatPeer
&& preventsCloseSection( && preventsCloseSection(
[=] { showHistory(peerId, params, showAtMsgId); }, [=] { showHistory(peerId, params, showAtMsgId); },
params)) { params)) {
@ -1424,17 +1426,24 @@ void MainWidget::showHistory(
return false; return false;
}; };
auto animationParams = animatedShow() ? prepareHistoryAnimation(peerId) : Window::SectionSlideParams(); auto animationParams = animatedShow()
? prepareHistoryAnimation(peerId)
: Window::SectionSlideParams();
if (!back && (way != Way::ClearStack)) { if (!back && (way != Way::ClearStack)) {
// This may modify the current section, for example remove its contents. // This may modify the current section, for example remove its contents.
saveSectionInStack(); saveSectionInStack();
} }
if (_history->peer() && _history->peer()->id != peerId && way != Way::Forward) { if (_history->peer()
&& _history->peer()->id != peerId
&& way != Way::Forward) {
clearBotStartToken(_history->peer()); clearBotStartToken(_history->peer());
} }
_history->showHistory(peerId, showAtMsgId); _history->showHistory(peerId, showAtMsgId);
if (alreadyThatPeer && params.reapplyLocalDraft) {
_history->applyDraft(HistoryWidget::FieldHistoryAction::NewEntry);
}
auto noPeer = !_history->peer(); auto noPeer = !_history->peer();
auto onlyDialogs = noPeer && isOneColumn(); auto onlyDialogs = noPeer && isOneColumn();

View file

@ -318,6 +318,7 @@ Panel::Panel(
Fn<bool(QString)> handleLocalUri, Fn<bool(QString)> handleLocalUri,
Fn<void(QString)> handleInvoice, Fn<void(QString)> handleInvoice,
Fn<void(QByteArray)> sendData, Fn<void(QByteArray)> sendData,
Fn<void(std::vector<QString>, QString)> switchInlineQuery,
Fn<void()> close, Fn<void()> close,
QString phone, QString phone,
MenuButtons menuButtons, MenuButtons menuButtons,
@ -328,6 +329,7 @@ Panel::Panel(
, _handleLocalUri(std::move(handleLocalUri)) , _handleLocalUri(std::move(handleLocalUri))
, _handleInvoice(std::move(handleInvoice)) , _handleInvoice(std::move(handleInvoice))
, _sendData(std::move(sendData)) , _sendData(std::move(sendData))
, _switchInlineQuery(std::move(switchInlineQuery))
, _close(std::move(close)) , _close(std::move(close))
, _phone(phone) , _phone(phone)
, _menuButtons(menuButtons) , _menuButtons(menuButtons)
@ -625,6 +627,8 @@ bool Panel::createWebview() {
_close(); _close();
} else if (command == "web_app_data_send") { } else if (command == "web_app_data_send") {
sendDataMessage(arguments); sendDataMessage(arguments);
} else if (command == "web_app_switch_inline_query") {
switchInlineQueryMessage(arguments);
} else if (command == "web_app_setup_main_button") { } else if (command == "web_app_setup_main_button") {
processMainButtonMessage(arguments); processMainButtonMessage(arguments);
} else if (command == "web_app_setup_back_button") { } else if (command == "web_app_setup_back_button") {
@ -702,6 +706,38 @@ void Panel::sendDataMessage(const QJsonObject &args) {
_sendData(data.toUtf8()); _sendData(data.toUtf8());
} }
void Panel::switchInlineQueryMessage(const QJsonObject &args) {
if (args.isEmpty()) {
_close();
return;
}
const auto query = args["query"].toString();
if (query.isEmpty()) {
LOG(("BotWebView Error: Bad 'query' in switchInlineQueryMessage."));
_close();
return;
}
const auto valid = base::flat_set<QString>{
u"users"_q,
u"bots"_q,
u"groups"_q,
u"channels"_q,
};
auto types = std::vector<QString>();
for (const auto &value : args["chat_types"].toArray()) {
const auto type = value.toString();
if (valid.contains(type)) {
types.push_back(type);
} else {
LOG(("BotWebView Error: "
"Bad chat type in switchInlineQueryMessage: %1.").arg(type));
types.clear();
break;
}
}
_switchInlineQuery(types, query);
}
void Panel::openTgLink(const QJsonObject &args) { void Panel::openTgLink(const QJsonObject &args) {
if (args.isEmpty()) { if (args.isEmpty()) {
_close(); _close();
@ -1129,6 +1165,7 @@ std::unique_ptr<Panel> Show(Args &&args) {
std::move(args.handleLocalUri), std::move(args.handleLocalUri),
std::move(args.handleInvoice), std::move(args.handleInvoice),
std::move(args.sendData), std::move(args.sendData),
std::move(args.switchInlineQuery),
std::move(args.close), std::move(args.close),
args.phone, args.phone,
args.menuButtons, args.menuButtons,

View file

@ -48,6 +48,7 @@ public:
Fn<bool(QString)> handleLocalUri, Fn<bool(QString)> handleLocalUri,
Fn<void(QString)> handleInvoice, Fn<void(QString)> handleInvoice,
Fn<void(QByteArray)> sendData, Fn<void(QByteArray)> sendData,
Fn<void(std::vector<QString>, QString)> switchInlineQuery,
Fn<void()> close, Fn<void()> close,
QString phone, QString phone,
MenuButtons menuButtons, MenuButtons menuButtons,
@ -88,6 +89,7 @@ private:
void hideWebviewProgress(); void hideWebviewProgress();
void setTitle(rpl::producer<QString> title); void setTitle(rpl::producer<QString> title);
void sendDataMessage(const QJsonObject &args); void sendDataMessage(const QJsonObject &args);
void switchInlineQueryMessage(const QJsonObject &args);
void processMainButtonMessage(const QJsonObject &args); void processMainButtonMessage(const QJsonObject &args);
void processBackButtonMessage(const QJsonObject &args); void processBackButtonMessage(const QJsonObject &args);
void openTgLink(const QJsonObject &args); void openTgLink(const QJsonObject &args);
@ -116,6 +118,7 @@ private:
Fn<bool(QString)> _handleLocalUri; Fn<bool(QString)> _handleLocalUri;
Fn<void(QString)> _handleInvoice; Fn<void(QString)> _handleInvoice;
Fn<void(QByteArray)> _sendData; Fn<void(QByteArray)> _sendData;
Fn<void(std::vector<QString>, QString)> _switchInlineQuery;
Fn<void()> _close; Fn<void()> _close;
QString _phone; QString _phone;
bool _closeNeedConfirmation = false; bool _closeNeedConfirmation = false;
@ -147,6 +150,7 @@ struct Args {
Fn<bool(QString)> handleLocalUri; Fn<bool(QString)> handleLocalUri;
Fn<void(QString)> handleInvoice; Fn<void(QString)> handleInvoice;
Fn<void(QByteArray)> sendData; Fn<void(QByteArray)> sendData;
Fn<void(std::vector<QString>, QString)> switchInlineQuery;
Fn<void()> close; Fn<void()> close;
QString phone; QString phone;
MenuButtons menuButtons; MenuButtons menuButtons;

View file

@ -434,6 +434,7 @@ void SessionNavigation::showPeerByLinkResolved(
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().request(
parentController(),
Api::SendAction(history), Api::SendAction(history),
attachBotUsername, attachBotUsername,
info.attachBotToggleCommand.value_or(QString())); info.attachBotToggleCommand.value_or(QString()));
@ -448,13 +449,13 @@ void SessionNavigation::showPeerByLinkResolved(
? contextPeer->asUser() ? contextPeer->asUser()
: nullptr; : nullptr;
bot->session().attachWebView().requestAddToMenu( bot->session().attachWebView().requestAddToMenu(
bot,
*info.attachBotToggleCommand,
parentController(),
(contextUser (contextUser
? Api::SendAction( ? Api::SendAction(
contextUser->owner().history(contextUser)) contextUser->owner().history(contextUser))
: std::optional<Api::SendAction>()), : std::optional<Api::SendAction>()),
bot,
*info.attachBotToggleCommand,
parentController(),
info.attachBotChooseTypes); info.attachBotChooseTypes);
} else { } else {
crl::on_main(this, [=] { crl::on_main(this, [=] {
@ -1150,6 +1151,54 @@ bool SessionController::jumpToChatListEntry(Dialogs::RowDescriptor row) {
return false; return false;
} }
void SessionController::setCurrentDialogsEntryState(
Dialogs::EntryState state) {
_currentDialogsEntryState = state;
}
Dialogs::EntryState SessionController::currentDialogsEntryState() const {
return _currentDialogsEntryState;
}
void SessionController::switchInlineQuery(
Dialogs::EntryState to,
not_null<UserData*> bot,
const QString &query) {
Expects(to.key.owningHistory() != nullptr);
using Section = Dialogs::EntryState::Section;
const auto history = to.key.owningHistory();
const auto textWithTags = TextWithTags{
'@' + bot->username() + ' ' + query,
TextWithTags::Tags(),
};
MessageCursor cursor = { int(textWithTags.text.size()), int(textWithTags.text.size()), QFIXED_MAX };
auto draft = std::make_unique<Data::Draft>(
textWithTags,
to.currentReplyToId,
to.rootId,
cursor,
Data::PreviewState::Allowed);
auto params = Window::SectionShow();
params.reapplyLocalDraft = true;
if (to.section == Section::Scheduled) {
history->setDraft(Data::DraftKey::Scheduled(), std::move(draft));
showSection(
std::make_shared<HistoryView::ScheduledMemento>(history),
params);
} else {
history->setLocalDraft(std::move(draft));
if (to.section == Section::Replies) {
const auto commentId = MsgId();
showRepliesForMessage(history, to.rootId, commentId, params);
} else {
showPeerHistory(history->peer, params);
}
}
}
Dialogs::RowDescriptor SessionController::resolveChatNext( Dialogs::RowDescriptor SessionController::resolveChatNext(
Dialogs::RowDescriptor from) const { Dialogs::RowDescriptor from) const {
return content()->resolveChatNext(from); return content()->resolveChatNext(from);

View file

@ -171,6 +171,7 @@ struct SectionShow {
anim::activation activation = anim::activation::normal; anim::activation activation = anim::activation::normal;
bool thirdColumn = false; bool thirdColumn = false;
bool childColumn = false; bool childColumn = false;
bool reapplyLocalDraft = false;
Origin origin; Origin origin;
}; };
@ -373,6 +374,13 @@ public:
rpl::producer<Dialogs::Key> activeChatValue() const; rpl::producer<Dialogs::Key> activeChatValue() const;
bool jumpToChatListEntry(Dialogs::RowDescriptor row); bool jumpToChatListEntry(Dialogs::RowDescriptor row);
void setCurrentDialogsEntryState(Dialogs::EntryState state);
[[nodiscard]] Dialogs::EntryState currentDialogsEntryState() const;
void switchInlineQuery(
Dialogs::EntryState to,
not_null<UserData*> bot,
const QString &query);
[[nodiscard]] Dialogs::RowDescriptor resolveChatNext( [[nodiscard]] Dialogs::RowDescriptor resolveChatNext(
Dialogs::RowDescriptor from = {}) const; Dialogs::RowDescriptor from = {}) const;
[[nodiscard]] Dialogs::RowDescriptor resolveChatPrevious( [[nodiscard]] Dialogs::RowDescriptor resolveChatPrevious(
@ -635,6 +643,8 @@ private:
int _chatEntryHistoryPosition = -1; int _chatEntryHistoryPosition = -1;
bool _filtersActivated = false; bool _filtersActivated = false;
Dialogs::EntryState _currentDialogsEntryState;
base::Timer _invitePeekTimer; base::Timer _invitePeekTimer;
rpl::variable<FilterId> _activeChatsFilter; rpl::variable<FilterId> _activeChatsFilter;