mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-07 23:53:58 +02:00
Add inline bots to ComposeControls.
This commit is contained in:
parent
cf6ca3b1ac
commit
a8564b166b
2 changed files with 227 additions and 11 deletions
|
@ -22,10 +22,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_messages.h"
|
#include "data/data_messages.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
|
#include "data/data_channel.h"
|
||||||
#include "data/stickers/data_stickers.h"
|
#include "data/stickers/data_stickers.h"
|
||||||
#include "data/data_web_page.h"
|
#include "data/data_web_page.h"
|
||||||
#include "storage/storage_account.h"
|
#include "storage/storage_account.h"
|
||||||
#include "facades.h"
|
#include "facades.h"
|
||||||
|
#include "apiwrap.h"
|
||||||
#include "boxes/confirm_box.h"
|
#include "boxes/confirm_box.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
|
@ -537,6 +539,7 @@ ComposeControls::ComposeControls(
|
||||||
|
|
||||||
ComposeControls::~ComposeControls() {
|
ComposeControls::~ComposeControls() {
|
||||||
setTabbedPanel(nullptr);
|
setTabbedPanel(nullptr);
|
||||||
|
session().api().request(_inlineBotResolveRequestId).cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
Main::Session &ComposeControls::session() const {
|
Main::Session &ComposeControls::session() const {
|
||||||
|
@ -559,6 +562,7 @@ void ComposeControls::setHistory(SetHistoryArgs &&args) {
|
||||||
_window->tabbedSelector()->setCurrentPeer(
|
_window->tabbedSelector()->setCurrentPeer(
|
||||||
history ? history->peer.get() : nullptr);
|
history ? history->peer.get() : nullptr);
|
||||||
initWebpageProcess();
|
initWebpageProcess();
|
||||||
|
updateFieldPlaceholder();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComposeControls::move(int x, int y) {
|
void ComposeControls::move(int x, int y) {
|
||||||
|
@ -757,8 +761,7 @@ void ComposeControls::checkAutocomplete() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto peer = _history->peer;
|
const auto peer = _history->peer;
|
||||||
const auto isInlineBot = false;// _inlineBot && !_inlineLookingUpBot;
|
const auto autocomplete = _isInlineBot
|
||||||
const auto autocomplete = isInlineBot
|
|
||||||
? AutocompleteQuery()
|
? AutocompleteQuery()
|
||||||
: ParseMentionHashtagBotCommandQuery(_field);
|
: ParseMentionHashtagBotCommandQuery(_field);
|
||||||
if (!autocomplete.query.isEmpty()) {
|
if (!autocomplete.query.isEmpty()) {
|
||||||
|
@ -878,7 +881,7 @@ void ComposeControls::setTextFromEditingMessage(not_null<HistoryItem*> item) {
|
||||||
|
|
||||||
void ComposeControls::initField() {
|
void ComposeControls::initField() {
|
||||||
_field->setMaxHeight(st::historyComposeFieldMaxHeight);
|
_field->setMaxHeight(st::historyComposeFieldMaxHeight);
|
||||||
_field->setSubmitSettings(Core::App().settings().sendSubmitWay());
|
updateSubmitSettings();
|
||||||
//Ui::Connect(_field, &Ui::InputField::submitted, [=] { send(); });
|
//Ui::Connect(_field, &Ui::InputField::submitted, [=] { send(); });
|
||||||
Ui::Connect(_field, &Ui::InputField::cancelled, [=] { escape(); });
|
Ui::Connect(_field, &Ui::InputField::cancelled, [=] { escape(); });
|
||||||
Ui::Connect(_field, &Ui::InputField::tabbed, [=] { fieldTabbed(); });
|
Ui::Connect(_field, &Ui::InputField::tabbed, [=] { fieldTabbed(); });
|
||||||
|
@ -895,6 +898,13 @@ void ComposeControls::initField() {
|
||||||
InitSpellchecker(_window, _field);
|
InitSpellchecker(_window, _field);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ComposeControls::updateSubmitSettings() {
|
||||||
|
const auto settings = _isInlineBot
|
||||||
|
? Ui::InputField::SubmitSettings::None
|
||||||
|
: Core::App().settings().sendSubmitWay();
|
||||||
|
_field->setSubmitSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
void ComposeControls::initAutocomplete() {
|
void ComposeControls::initAutocomplete() {
|
||||||
const auto insertHashtagOrBotCommand = [=](
|
const auto insertHashtagOrBotCommand = [=](
|
||||||
const QString &string,
|
const QString &string,
|
||||||
|
@ -1001,9 +1011,39 @@ void ComposeControls::updateStickersByEmoji() {
|
||||||
_autocomplete->showStickers(emoji);
|
_autocomplete->showStickers(emoji);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ComposeControls::updateFieldPlaceholder() {
|
||||||
|
if (!isEditingMessage() && _isInlineBot) {
|
||||||
|
_field->setPlaceholder(
|
||||||
|
rpl::single(_inlineBot->botInfo->inlinePlaceholder.mid(1)),
|
||||||
|
_inlineBot->username.size() + 2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_field->setPlaceholder([&] {
|
||||||
|
if (isEditingMessage()) {
|
||||||
|
return tr::lng_edit_message_text();
|
||||||
|
} else if (!_history) {
|
||||||
|
return tr::lng_message_ph();
|
||||||
|
} else if (const auto channel = _history->peer->asChannel()) {
|
||||||
|
if (channel->isBroadcast()) {
|
||||||
|
return session().data().notifySilentPosts(channel)
|
||||||
|
? tr::lng_broadcast_silent_ph()
|
||||||
|
: tr::lng_broadcast_ph();
|
||||||
|
} else if (channel->adminRights() & ChatAdminRight::f_anonymous) {
|
||||||
|
return tr::lng_send_anonymous_ph();
|
||||||
|
} else {
|
||||||
|
return tr::lng_message_ph();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return tr::lng_message_ph();
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
updateSendButtonType();
|
||||||
|
}
|
||||||
|
|
||||||
void ComposeControls::fieldChanged() {
|
void ComposeControls::fieldChanged() {
|
||||||
if (/*!_inlineBot
|
if (!_inlineBot
|
||||||
&& */!_header->isEditingMessage()
|
&& !_header->isEditingMessage()
|
||||||
&& (_textUpdateEvents & TextUpdateEvent::SendTyping)) {
|
&& (_textUpdateEvents & TextUpdateEvent::SendTyping)) {
|
||||||
_sendActionUpdates.fire({ Api::SendProgressType::Typing });
|
_sendActionUpdates.fire({ Api::SendProgressType::Typing });
|
||||||
}
|
}
|
||||||
|
@ -1012,6 +1052,7 @@ void ComposeControls::fieldChanged() {
|
||||||
//_previewCancelled = false;
|
//_previewCancelled = false;
|
||||||
}
|
}
|
||||||
InvokeQueued(_autocomplete.get(), [=] {
|
InvokeQueued(_autocomplete.get(), [=] {
|
||||||
|
updateInlineBotQuery();
|
||||||
updateStickersByEmoji();
|
updateStickersByEmoji();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1073,6 +1114,81 @@ void ComposeControls::initSendButton() {
|
||||||
}, _send->lifetime());
|
}, _send->lifetime());
|
||||||
|
|
||||||
_send->finishAnimating();
|
_send->finishAnimating();
|
||||||
|
|
||||||
|
_send->clicks(
|
||||||
|
) | rpl::filter([=] {
|
||||||
|
return (_send->type() == Ui::SendButton::Type::Cancel);
|
||||||
|
}) | rpl::start_with_next([=] {
|
||||||
|
cancelInlineBot();
|
||||||
|
}, _send->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComposeControls::inlineBotResolveDone(
|
||||||
|
const MTPcontacts_ResolvedPeer &result) {
|
||||||
|
Expects(result.type() == mtpc_contacts_resolvedPeer);
|
||||||
|
|
||||||
|
_inlineBotResolveRequestId = 0;
|
||||||
|
const auto &data = result.c_contacts_resolvedPeer();
|
||||||
|
const auto resolvedBot = [&]() -> UserData* {
|
||||||
|
if (const auto result = session().data().processUsers(data.vusers())) {
|
||||||
|
if (result->isBot()
|
||||||
|
&& !result->botInfo->inlinePlaceholder.isEmpty()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}();
|
||||||
|
session().data().processChats(data.vchats());
|
||||||
|
|
||||||
|
const auto query = ParseInlineBotQuery(&session(), _field);
|
||||||
|
if (_inlineBotUsername == query.username) {
|
||||||
|
applyInlineBotQuery(
|
||||||
|
query.lookingUpBot ? resolvedBot : query.bot,
|
||||||
|
query.query);
|
||||||
|
} else {
|
||||||
|
clearInlineBot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComposeControls::inlineBotResolveFail(
|
||||||
|
const RPCError &error,
|
||||||
|
const QString &username) {
|
||||||
|
_inlineBotResolveRequestId = 0;
|
||||||
|
if (username == _inlineBotUsername) {
|
||||||
|
clearInlineBot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComposeControls::cancelInlineBot() {
|
||||||
|
auto &textWithTags = _field->getTextWithTags();
|
||||||
|
if (textWithTags.text.size() > _inlineBotUsername.size() + 2) {
|
||||||
|
setText({ '@' + _inlineBotUsername + ' ', TextWithTags::Tags() });
|
||||||
|
} else {
|
||||||
|
setText({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComposeControls::clearInlineBot() {
|
||||||
|
if (_inlineBot || _inlineLookingUpBot) {
|
||||||
|
_inlineBot = nullptr;
|
||||||
|
_inlineLookingUpBot = false;
|
||||||
|
inlineBotChanged();
|
||||||
|
_field->finishAnimating();
|
||||||
|
}
|
||||||
|
if (_inlineResults) {
|
||||||
|
_inlineResults->clearInlineBot();
|
||||||
|
}
|
||||||
|
checkAutocomplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComposeControls::inlineBotChanged() {
|
||||||
|
const auto isInlineBot = (_inlineBot && !_inlineLookingUpBot);
|
||||||
|
if (_isInlineBot != isInlineBot) {
|
||||||
|
_isInlineBot = isInlineBot;
|
||||||
|
updateFieldPlaceholder();
|
||||||
|
updateSubmitSettings();
|
||||||
|
checkAutocomplete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComposeControls::initWriteRestriction() {
|
void ComposeControls::initWriteRestriction() {
|
||||||
|
@ -1155,8 +1271,8 @@ void ComposeControls::updateSendButtonType() {
|
||||||
const auto type = [&] {
|
const auto type = [&] {
|
||||||
if (_header->isEditingMessage()) {
|
if (_header->isEditingMessage()) {
|
||||||
return Type::Save;
|
return Type::Save;
|
||||||
//} else if (_isInlineBot) {
|
} else if (_isInlineBot) {
|
||||||
// return Type::Cancel;
|
return Type::Cancel;
|
||||||
} else if (showRecordButton()) {
|
} else if (showRecordButton()) {
|
||||||
return Type::Record;
|
return Type::Record;
|
||||||
}
|
}
|
||||||
|
@ -1319,6 +1435,7 @@ void ComposeControls::editMessage(FullMsgId id) {
|
||||||
if (_autocomplete) {
|
if (_autocomplete) {
|
||||||
InvokeQueued(_autocomplete.get(), [=] { checkAutocomplete(); });
|
InvokeQueued(_autocomplete.get(), [=] { checkAutocomplete(); });
|
||||||
}
|
}
|
||||||
|
updateFieldPlaceholder();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComposeControls::cancelEditMessage() {
|
void ComposeControls::cancelEditMessage() {
|
||||||
|
@ -1326,6 +1443,7 @@ void ComposeControls::cancelEditMessage() {
|
||||||
if (_autocomplete) {
|
if (_autocomplete) {
|
||||||
InvokeQueued(_autocomplete.get(), [=] { checkAutocomplete(); });
|
InvokeQueued(_autocomplete.get(), [=] { checkAutocomplete(); });
|
||||||
}
|
}
|
||||||
|
updateFieldPlaceholder();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComposeControls::replyToMessage(FullMsgId id) {
|
void ComposeControls::replyToMessage(FullMsgId id) {
|
||||||
|
@ -1338,7 +1456,10 @@ void ComposeControls::cancelReplyMessage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ComposeControls::handleCancelRequest() {
|
bool ComposeControls::handleCancelRequest() {
|
||||||
if (isEditingMessage()) {
|
if (_isInlineBot) {
|
||||||
|
cancelInlineBot();
|
||||||
|
return true;
|
||||||
|
} else if (isEditingMessage()) {
|
||||||
cancelEditMessage();
|
cancelEditMessage();
|
||||||
return true;
|
return true;
|
||||||
} else if (_autocomplete && !_autocomplete->isHidden()) {
|
} else if (_autocomplete && !_autocomplete->isHidden()) {
|
||||||
|
@ -1490,6 +1611,7 @@ void ComposeControls::initWebpageProcess() {
|
||||||
}) | rpl::start_with_next([=] {
|
}) | rpl::start_with_next([=] {
|
||||||
checkPreview();
|
checkPreview();
|
||||||
updateStickersByEmoji();
|
updateStickersByEmoji();
|
||||||
|
updateFieldPlaceholder();
|
||||||
}, lifetime);
|
}, lifetime);
|
||||||
|
|
||||||
_window->session().downloaderTaskFinished(
|
_window->session().downloaderTaskFinished(
|
||||||
|
@ -1557,4 +1679,77 @@ bool ComposeControls::isRecording() const {
|
||||||
return _voiceRecordBar->isRecording();
|
return _voiceRecordBar->isRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ComposeControls::updateInlineBotQuery() {
|
||||||
|
if (!_history) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto query = ParseInlineBotQuery(&session(), _field);
|
||||||
|
if (_inlineBotUsername != query.username) {
|
||||||
|
_inlineBotUsername = query.username;
|
||||||
|
auto &api = session().api();
|
||||||
|
if (_inlineBotResolveRequestId) {
|
||||||
|
api.request(_inlineBotResolveRequestId).cancel();
|
||||||
|
_inlineBotResolveRequestId = 0;
|
||||||
|
}
|
||||||
|
if (query.lookingUpBot) {
|
||||||
|
_inlineBot = nullptr;
|
||||||
|
_inlineLookingUpBot = true;
|
||||||
|
const auto username = _inlineBotUsername;
|
||||||
|
_inlineBotResolveRequestId = api.request(
|
||||||
|
MTPcontacts_ResolveUsername(MTP_string(username))
|
||||||
|
).done([=](const MTPcontacts_ResolvedPeer &result) {
|
||||||
|
inlineBotResolveDone(result);
|
||||||
|
}).fail([=](const RPCError &error) {
|
||||||
|
inlineBotResolveFail(error, username);
|
||||||
|
}).send();
|
||||||
|
} else {
|
||||||
|
applyInlineBotQuery(query.bot, query.query);
|
||||||
|
}
|
||||||
|
} else if (query.lookingUpBot) {
|
||||||
|
if (!_inlineLookingUpBot) {
|
||||||
|
applyInlineBotQuery(_inlineBot, query.query);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
applyInlineBotQuery(query.bot, query.query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComposeControls::applyInlineBotQuery(
|
||||||
|
UserData *bot,
|
||||||
|
const QString &query) {
|
||||||
|
if (_history && bot) {
|
||||||
|
if (_inlineBot != bot) {
|
||||||
|
_inlineBot = bot;
|
||||||
|
_inlineLookingUpBot = false;
|
||||||
|
inlineBotChanged();
|
||||||
|
}
|
||||||
|
if (!_inlineResults) {
|
||||||
|
_inlineResults = std::make_unique<InlineBots::Layout::Widget>(
|
||||||
|
_parent,
|
||||||
|
_window);
|
||||||
|
_inlineResults->setResultSelectedCallback([=](
|
||||||
|
InlineBots::Result *result,
|
||||||
|
UserData *bot,
|
||||||
|
Api::SendOptions options) {
|
||||||
|
_inlineResultChosen.fire(InlineChosen{
|
||||||
|
.result = result,
|
||||||
|
.bot = bot,
|
||||||
|
.options = options,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
_inlineResults->requesting(
|
||||||
|
) | rpl::start_with_next([=](bool requesting) {
|
||||||
|
_tabbedSelectorToggle->setLoading(requesting);
|
||||||
|
}, _inlineResults->lifetime());
|
||||||
|
updateOuterGeometry(_wrap->geometry());
|
||||||
|
}
|
||||||
|
_inlineResults->queryInlineBot(_inlineBot, _history->peer, query);
|
||||||
|
if (!_autocomplete->isHidden()) {
|
||||||
|
_autocomplete->hideAnimated();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
clearInlineBot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
|
@ -67,6 +67,7 @@ class ComposeControls final {
|
||||||
public:
|
public:
|
||||||
using FileChosen = ChatHelpers::TabbedSelector::FileChosen;
|
using FileChosen = ChatHelpers::TabbedSelector::FileChosen;
|
||||||
using PhotoChosen = ChatHelpers::TabbedSelector::PhotoChosen;
|
using PhotoChosen = ChatHelpers::TabbedSelector::PhotoChosen;
|
||||||
|
using InlineChosen = ChatHelpers::TabbedSelector::InlineChosen;
|
||||||
|
|
||||||
using MessageToEdit = Controls::MessageToEdit;
|
using MessageToEdit = Controls::MessageToEdit;
|
||||||
using VoiceToSend = Controls::VoiceToSend;
|
using VoiceToSend = Controls::VoiceToSend;
|
||||||
|
@ -105,8 +106,7 @@ public:
|
||||||
[[nodiscard]] rpl::producer<PhotoChosen> photoChosen() const;
|
[[nodiscard]] rpl::producer<PhotoChosen> photoChosen() const;
|
||||||
[[nodiscard]] rpl::producer<Data::MessagePosition> scrollRequests() const;
|
[[nodiscard]] rpl::producer<Data::MessagePosition> scrollRequests() const;
|
||||||
[[nodiscard]] rpl::producer<not_null<QKeyEvent*>> keyEvents() const;
|
[[nodiscard]] rpl::producer<not_null<QKeyEvent*>> keyEvents() const;
|
||||||
[[nodiscard]] auto inlineResultChosen() const
|
[[nodiscard]] rpl::producer<InlineChosen> inlineResultChosen() const;
|
||||||
-> rpl::producer<ChatHelpers::TabbedSelector::InlineChosen>;
|
|
||||||
[[nodiscard]] rpl::producer<SendActionUpdate> sendActionUpdates() const;
|
[[nodiscard]] rpl::producer<SendActionUpdate> sendActionUpdates() const;
|
||||||
|
|
||||||
using MimeDataHook = Fn<bool(
|
using MimeDataHook = Fn<bool(
|
||||||
|
@ -161,6 +161,7 @@ private:
|
||||||
void initWriteRestriction();
|
void initWriteRestriction();
|
||||||
void initVoiceRecordBar();
|
void initVoiceRecordBar();
|
||||||
void initAutocomplete();
|
void initAutocomplete();
|
||||||
|
void updateSubmitSettings();
|
||||||
void updateSendButtonType();
|
void updateSendButtonType();
|
||||||
void updateHeight();
|
void updateHeight();
|
||||||
void updateWrappingVisibility();
|
void updateWrappingVisibility();
|
||||||
|
@ -172,6 +173,7 @@ private:
|
||||||
void orderControls();
|
void orderControls();
|
||||||
void checkAutocomplete();
|
void checkAutocomplete();
|
||||||
void updateStickersByEmoji();
|
void updateStickersByEmoji();
|
||||||
|
void updateFieldPlaceholder();
|
||||||
|
|
||||||
void escape();
|
void escape();
|
||||||
void fieldChanged();
|
void fieldChanged();
|
||||||
|
@ -186,6 +188,19 @@ private:
|
||||||
void drawRestrictedWrite(Painter &p, const QString &error);
|
void drawRestrictedWrite(Painter &p, const QString &error);
|
||||||
void updateOverStates(QPoint pos);
|
void updateOverStates(QPoint pos);
|
||||||
|
|
||||||
|
void cancelInlineBot();
|
||||||
|
void clearInlineBot();
|
||||||
|
void inlineBotChanged();
|
||||||
|
|
||||||
|
// Look in the _field for the inline bot and query string.
|
||||||
|
void updateInlineBotQuery();
|
||||||
|
|
||||||
|
// Request to show results in the emoji panel.
|
||||||
|
void applyInlineBotQuery(UserData *bot, const QString &query);
|
||||||
|
|
||||||
|
void inlineBotResolveDone(const MTPcontacts_ResolvedPeer &result);
|
||||||
|
void inlineBotResolveFail(const RPCError &error, const QString &username);
|
||||||
|
|
||||||
const not_null<QWidget*> _parent;
|
const not_null<QWidget*> _parent;
|
||||||
const not_null<Window::SessionController*> _window;
|
const not_null<Window::SessionController*> _window;
|
||||||
History *_history = nullptr;
|
History *_history = nullptr;
|
||||||
|
@ -213,7 +228,7 @@ private:
|
||||||
rpl::event_stream<> _cancelRequests;
|
rpl::event_stream<> _cancelRequests;
|
||||||
rpl::event_stream<FileChosen> _fileChosen;
|
rpl::event_stream<FileChosen> _fileChosen;
|
||||||
rpl::event_stream<PhotoChosen> _photoChosen;
|
rpl::event_stream<PhotoChosen> _photoChosen;
|
||||||
rpl::event_stream<ChatHelpers::TabbedSelector::InlineChosen> _inlineResultChosen;
|
rpl::event_stream<InlineChosen> _inlineResultChosen;
|
||||||
rpl::event_stream<SendActionUpdate> _sendActionUpdates;
|
rpl::event_stream<SendActionUpdate> _sendActionUpdates;
|
||||||
rpl::event_stream<QString> _sendCommandRequests;
|
rpl::event_stream<QString> _sendCommandRequests;
|
||||||
|
|
||||||
|
@ -223,6 +238,12 @@ private:
|
||||||
//bool _inReplyEditForward = false;
|
//bool _inReplyEditForward = false;
|
||||||
//bool _inClickable = false;
|
//bool _inClickable = false;
|
||||||
|
|
||||||
|
UserData *_inlineBot = nullptr;
|
||||||
|
QString _inlineBotUsername;
|
||||||
|
bool _inlineLookingUpBot = false;
|
||||||
|
mtpRequestId _inlineBotResolveRequestId = 0;
|
||||||
|
bool _isInlineBot = false;
|
||||||
|
|
||||||
rpl::lifetime _uploaderSubscriptions;
|
rpl::lifetime _uploaderSubscriptions;
|
||||||
|
|
||||||
Fn<void()> _raiseEmojiSuggestions;
|
Fn<void()> _raiseEmojiSuggestions;
|
||||||
|
|
Loading…
Add table
Reference in a new issue