Prepared messages sharing from miniapp.

This commit is contained in:
John Preston 2024-11-08 23:19:29 +04:00
parent 3d77bff0c9
commit 2d1fb0562d
20 changed files with 465 additions and 63 deletions

View file

@ -1040,6 +1040,8 @@ PRIVATE
info/info_wrap_widget.h
inline_bots/bot_attach_web_view.cpp
inline_bots/bot_attach_web_view.h
inline_bots/inline_bot_confirm_prepared.cpp
inline_bots/inline_bot_confirm_prepared.h
inline_bots/inline_bot_layout_internal.cpp
inline_bots/inline_bot_layout_internal.h
inline_bots/inline_bot_layout_item.cpp

View file

@ -3425,6 +3425,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_bot_emoji_status_text" = "Do you want to set this emoji status suggested by {bot}?";
"lng_bot_emoji_status_access_text" = "{bot} requests access to set your **emoji status**. You will be able to revoke this access in the profile page of {name}.";
"lng_bot_emoji_status_access_allow" = "Allow";
"lng_bot_share_prepared_title" = "Share Message";
"lng_bot_share_prepared_about" = "{bot} mini app suggests you to send this message to a chat you select.";
"lng_bot_share_prepared_button" = "Share With...";
"lng_bot_status_users#one" = "{count} monthly user";
"lng_bot_status_users#other" = "{count} monthly users";

View file

@ -3947,7 +3947,8 @@ void ApiWrap::sendInlineResult(
not_null<UserData*> bot,
not_null<InlineBots::Result*> data,
const SendAction &action,
std::optional<MsgId> localMessageId) {
std::optional<MsgId> localMessageId,
Fn<void(bool)> done) {
sendAction(action);
const auto history = action.history;
@ -4027,11 +4028,13 @@ void ApiWrap::sendInlineResult(
history->finishSavingCloudDraft(
topicRootId,
UnixtimeFromMsgId(response.outerMsgId));
done(true);
}, [=](const MTP::Error &error, const MTP::Response &response) {
sendMessageFail(error, peer, randomId, newId);
history->finishSavingCloudDraft(
topicRootId,
UnixtimeFromMsgId(response.outerMsgId));
done(false);
});
finishForwarding(action);
}

View file

@ -361,7 +361,8 @@ public:
not_null<UserData*> bot,
not_null<InlineBots::Result*> data,
const SendAction &action,
std::optional<MsgId> localMessageId);
std::optional<MsgId> localMessageId,
Fn<void(bool)> done = nullptr);
void sendMessageFail(
const MTP::Error &error,
not_null<PeerData*> peer,

View file

@ -712,6 +712,14 @@ not_null<HistoryItem*> History::addNewLocalMessage(
true);
}
not_null<HistoryItem*> History::addNewLocalMessage(
not_null<HistoryItem*> item) {
Expects(item->history() == this);
Expects(item->isLocal());
return addNewItem(item, true);
}
not_null<HistoryItem*> History::addSponsoredMessage(
MsgId id,
Data::SponsoredFrom from,

View file

@ -180,6 +180,7 @@ public:
not_null<HistoryItem*> addNewLocalMessage(
HistoryItemCommonFields &&fields,
not_null<GameData*> game);
not_null<HistoryItem*> addNewLocalMessage(not_null<HistoryItem*> item);
not_null<HistoryItem*> addSponsoredMessage(
MsgId id,

View file

@ -14,28 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace {
[[nodiscard]] InlineBots::PeerTypes PeerTypesFromMTP(
const MTPvector<MTPInlineQueryPeerType> &types) {
using namespace InlineBots;
auto result = PeerTypes(0);
for (const auto &type : types.v) {
result |= type.match([&](const MTPDinlineQueryPeerTypePM &data) {
return PeerType::User;
}, [&](const MTPDinlineQueryPeerTypeChat &data) {
return PeerType::Group;
}, [&](const MTPDinlineQueryPeerTypeMegagroup &data) {
return PeerType::Group;
}, [&](const MTPDinlineQueryPeerTypeBroadcast &data) {
return PeerType::Broadcast;
}, [&](const MTPDinlineQueryPeerTypeBotPM &data) {
return PeerType::Bot;
}, [&](const MTPDinlineQueryPeerTypeSameBotPM &data) {
return PeerType();
});
}
return result;
}
[[nodiscard]] RequestPeerQuery RequestPeerQueryFromTL(
const MTPDkeyboardButtonRequestPeer &query) {
using Type = RequestPeerQuery::Type;
@ -76,6 +54,28 @@ namespace {
} // namespace
InlineBots::PeerTypes PeerTypesFromMTP(
const MTPvector<MTPInlineQueryPeerType> &types) {
using namespace InlineBots;
auto result = PeerTypes(0);
for (const auto &type : types.v) {
result |= type.match([&](const MTPDinlineQueryPeerTypePM &data) {
return PeerType::User;
}, [&](const MTPDinlineQueryPeerTypeChat &data) {
return PeerType::Group;
}, [&](const MTPDinlineQueryPeerTypeMegagroup &data) {
return PeerType::Group;
}, [&](const MTPDinlineQueryPeerTypeBroadcast &data) {
return PeerType::Broadcast;
}, [&](const MTPDinlineQueryPeerTypeBotPM &data) {
return PeerType::Bot;
}, [&](const MTPDinlineQueryPeerTypeSameBotPM &data) {
return PeerType();
});
}
return result;
}
HistoryMessageMarkupButton::HistoryMessageMarkupButton(
Type type,
const QString &text,

View file

@ -19,6 +19,9 @@ enum class PeerType : uint8;
using PeerTypes = base::flags<PeerType>;
} // namespace InlineBots
[[nodiscard]] InlineBots::PeerTypes PeerTypesFromMTP(
const MTPvector<MTPInlineQueryPeerType> &types);
enum class ReplyMarkupFlag : uint32 {
None = (1U << 0),
ForceReply = (1U << 1),

View file

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/qthelp_url.h"
#include "base/random.h"
#include "base/timer_rpl.h"
#include "base/unixtime.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/share_box.h"
#include "chat_helpers/stickers_lottie.h"
@ -38,6 +39,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h"
#include "history/history_item.h"
#include "info/profile/info_profile_values.h"
#include "inline_bots/inline_bot_result.h"
#include "inline_bots/inline_bot_confirm_prepared.h"
#include "iv/iv_instance.h"
#include "lang/lang_keys.h"
#include "main/main_app_config.h"
@ -1730,6 +1733,88 @@ void WebViewInstance::botInvokeCustomMethod(
}).send();
}
void WebViewInstance::botSendPreparedMessage(
Ui::BotWebView::SendPreparedMessageRequest request) {
const auto bot = _bot;
const auto id = request.id;
const auto panel = _panel.get();
const auto weak = base::make_weak(panel);
const auto callback = request.callback;
if (!panel) {
callback(u"UNKNOWN_ERROR"_q);
return;
}
_session->api().request(MTPmessages_GetPreparedInlineMessage(
bot->inputUser,
MTP_string(request.id)
)).done([=](const MTPmessages_PreparedInlineMessage &result) {
const auto panel = weak.get();
const auto &data = result.data();
bot->owner().processUsers(data.vusers());
const auto parsed = std::shared_ptr<Result>(Result::Create(
&bot->session(),
data.vquery_id().v,
data.vresult()));
if (!parsed || !panel) {
callback(u"UNKNOWN_ERROR"_q);
return;
}
const auto types = PeerTypesFromMTP(data.vpeer_types());
const auto history = bot->owner().history(bot->session().user());
const auto item = parsed->makeMessage(history, {
.id = bot->owner().nextNonHistoryEntryId(),
.flags = MessageFlag::FakeHistoryItem,
.from = bot->session().userPeerId(),
.date = base::unixtime::now(),
.viaBotId = peerToUser(bot->id),
});
struct State {
QPointer<Ui::BoxContent> preview;
bool sent = false;
};
const auto state = std::make_shared<State>();
auto box = Box(PreparedPreviewBox, item, [=] {
const auto chosen = [=](not_null<Data::Thread*> thread) {
auto action = Api::SendAction(thread);
const auto done = [=](bool success) {
if (success) {
callback(QString());
} else {
callback(u"MESSAGE_SEND_FAILED"_q);
}
};
bot->session().api().sendInlineResult(
bot,
parsed.get(),
action,
std::nullopt,
done);
state->sent = true;
if (const auto strong = state->preview.data()) {
strong->closeBox();
}
return true;
};
auto box = Window::PrepareChooseRecipientBox(
&bot->session(),
chosen,
tr::lng_inline_switch_choose(),
nullptr,
types);
panel->showBox(std::move(box));
});
box->boxClosing() | rpl::start_with_next([=] {
if (!state->sent) {
callback("USER_DECLINED");
}
}, box->lifetime());
state->preview = box.data();
panel->showBox(std::move(box));
}).fail([=] {
callback(u"MESSAGE_EXPIRED"_q);
}).send();
}
void WebViewInstance::botSetEmojiStatus(
Ui::BotWebView::SetEmojiStatusRequest request) {
const auto bot = _bot;

View file

@ -265,6 +265,8 @@ private:
void botSharePhone(Fn<void(bool shared)> callback) override;
void botInvokeCustomMethod(
Ui::BotWebView::CustomMethodRequest request) override;
void botSendPreparedMessage(
Ui::BotWebView::SendPreparedMessageRequest request) override;
void botSetEmojiStatus(
Ui::BotWebView::SetEmojiStatusRequest request) override;
void botOpenPrivacyPolicy() override;

View file

@ -0,0 +1,196 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "inline_bots/inline_bot_confirm_prepared.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "history/admin_log/history_admin_log_item.h"
#include "history/view/history_view_element.h"
#include "history/history.h"
#include "history/history_item.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "ui/chat/chat_style.h"
#include "ui/chat/chat_theme.h"
#include "ui/effects/path_shift_gradient.h"
#include "ui/layers/generic_box.h"
#include "ui/painter.h"
#include "ui/vertical_list.h"
#include "window/themes/window_theme.h"
#include "window/section_widget.h"
#include "styles/style_chat.h"
namespace InlineBots {
namespace {
using namespace HistoryView;
class PreviewDelegate final : public DefaultElementDelegate {
public:
PreviewDelegate(
not_null<QWidget*> parent,
not_null<Ui::ChatStyle*> st,
Fn<void()> update);
bool elementAnimationsPaused() override;
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
Context elementContext() override;
private:
const not_null<QWidget*> _parent;
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
};
class PreviewWrap final : public Ui::RpWidget {
public:
PreviewWrap(not_null<QWidget*> parent, not_null<HistoryItem*> item);
~PreviewWrap();
private:
void paintEvent(QPaintEvent *e) override;
void resizeTo(int width);
void prepare(not_null<HistoryItem*> item);
const not_null<History*> _history;
const std::unique_ptr<Ui::ChatTheme> _theme;
const std::unique_ptr<Ui::ChatStyle> _style;
const std::unique_ptr<PreviewDelegate> _delegate;
AdminLog::OwnedItem _item;
QPoint _position;
};
PreviewDelegate::PreviewDelegate(
not_null<QWidget*> parent,
not_null<Ui::ChatStyle*> st,
Fn<void()> update)
: _parent(parent)
, _pathGradient(MakePathShiftGradient(st, update)) {
}
bool PreviewDelegate::elementAnimationsPaused() {
return _parent->window()->isActiveWindow();
}
auto PreviewDelegate::elementPathShiftGradient()
-> not_null<Ui::PathShiftGradient*> {
return _pathGradient.get();
}
Context PreviewDelegate::elementContext() {
return Context::History;
}
PreviewWrap::PreviewWrap(
not_null<QWidget*> parent,
not_null<HistoryItem*> item)
: RpWidget(parent)
, _history(item->history())
, _theme(Window::Theme::DefaultChatThemeOn(lifetime()))
, _style(std::make_unique<Ui::ChatStyle>(
_history->session().colorIndicesValue()))
, _delegate(std::make_unique<PreviewDelegate>(
parent,
_style.get(),
[=] { update(); }))
, _position(0, st::msgMargin.bottom()) {
_style->apply(_theme.get());
using namespace HistoryView;
_history->owner().viewRepaintRequest(
) | rpl::start_with_next([=](not_null<const Element*> view) {
if (view == _item.get()) {
update();
}
}, lifetime());
_history->session().downloaderTaskFinished() | rpl::start_with_next([=] {
update();
}, lifetime());
prepare(item);
}
PreviewWrap::~PreviewWrap() {
_item = {};
}
void PreviewWrap::prepare(not_null<HistoryItem*> item) {
_item = AdminLog::OwnedItem(_delegate.get(), item);
if (width() >= st::msgMinWidth) {
resizeTo(width());
}
widthValue(
) | rpl::filter([=](int width) {
return width >= st::msgMinWidth;
}) | rpl::start_with_next([=](int width) {
resizeTo(width);
}, lifetime());
}
void PreviewWrap::resizeTo(int width) {
const auto height = _position.y()
+ _item->resizeGetHeight(width)
+ _position.y()
+ st::msgServiceMargin.top()
+ st::msgServiceGiftBoxTopSkip
- st::msgServiceMargin.bottom();
resize(width, height);
}
void PreviewWrap::paintEvent(QPaintEvent *e) {
auto p = Painter(this);
const auto clip = e->rect();
if (!clip.isEmpty()) {
p.setClipRect(clip);
Window::SectionWidget::PaintBackground(
p,
_theme.get(),
QSize(width(), window()->height()),
clip);
}
auto context = _theme->preparePaintContext(
_style.get(),
rect(),
e->rect(),
!window()->isActiveWindow());
p.translate(_position);
_item->draw(p, context);
}
} // namesace
void PreparedPreviewBox(
not_null<Ui::GenericBox*> box,
not_null<HistoryItem*> item,
Fn<void()> share) {
box->setTitle(tr::lng_bot_share_prepared_title());
const auto container = box->verticalLayout();
container->add(object_ptr<PreviewWrap>(container, item));
const auto bot = item->viaBot();
const auto name = bot ? bot->name() : u"Bot"_q;
Ui::AddDividerText(
container,
tr::lng_bot_share_prepared_about(lt_bot, rpl::single(name))),
box->addButton(tr::lng_bot_share_prepared_button(), share);
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
item->history()->owner().itemRemoved(
) | rpl::start_with_next([=](not_null<const HistoryItem*> removed) {
if (removed == item) {
box->closeBox();
}
}, box->lifetime());
}
} // namespace InlineBots

View file

@ -0,0 +1,21 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace Ui {
class GenericBox;
} // namespace Ui
namespace InlineBots {
void PreparedPreviewBox(
not_null<Ui::GenericBox*> box,
not_null<HistoryItem*> item,
Fn<void()> share);
} // namespace InlineBots

View file

@ -378,14 +378,20 @@ bool Result::hasThumbDisplay() const {
void Result::addToHistory(
not_null<History*> history,
HistoryItemCommonFields &&fields) const {
fields.flags |= MessageFlag::FromInlineBot;
history->addNewLocalMessage(makeMessage(history, std::move(fields)));
}
not_null<HistoryItem*> Result::makeMessage(
not_null<History*> history,
HistoryItemCommonFields &&fields) const {
fields.flags |= MessageFlag::FromInlineBot | MessageFlag::Local;
if (_replyMarkup) {
fields.markup = *_replyMarkup;
if (!fields.markup.isNull()) {
fields.flags |= MessageFlag::HasReplyMarkup;
}
}
sendData->addToHistory(this, history, std::move(fields));
return sendData->makeMessage(this, history, std::move(fields));
}
QString Result::getErrorOnSend(not_null<History*> history) const {

View file

@ -66,6 +66,9 @@ public:
void addToHistory(
not_null<History*> history,
HistoryItemCommonFields &&fields) const;
[[nodiscard]] not_null<HistoryItem*> makeMessage(
not_null<History*> history,
HistoryItemCommonFields &&fields) const;
QString getErrorOnSend(not_null<History*> history) const;
// interface for Layout:: usage

View file

@ -28,7 +28,7 @@ QString SendData::getLayoutDescription(const Result *owner) const {
return owner->_description;
}
void SendDataCommon::addToHistory(
not_null<HistoryItem*> SendDataCommon::makeMessage(
const Result *owner,
not_null<History*> history,
HistoryItemCommonFields &&fields) const {
@ -36,7 +36,7 @@ void SendDataCommon::addToHistory(
if (fields.replyTo) {
fields.flags |= MessageFlag::HasReplyInfo;
}
history->addNewLocalMessage(
return history->makeMessage(
std::move(fields),
std::move(distinct.text),
std::move(distinct.media));
@ -96,14 +96,14 @@ QString SendContact::getLayoutDescription(const Result *owner) const {
return result;
}
void SendPhoto::addToHistory(
not_null<HistoryItem*> SendPhoto::makeMessage(
const Result *owner,
not_null<History*> history,
HistoryItemCommonFields &&fields) const {
history->addNewLocalMessage(
return history->makeMessage(
std::move(fields),
_photo,
{ _message, _entities });
TextWithEntities{ _message, _entities });
}
QString SendPhoto::getErrorOnSend(
@ -113,14 +113,14 @@ QString SendPhoto::getErrorOnSend(
return Data::RestrictionError(history->peer, type).value_or(QString());
}
void SendFile::addToHistory(
not_null<HistoryItem*> SendFile::makeMessage(
const Result *owner,
not_null<History*> history,
HistoryItemCommonFields &&fields) const {
history->addNewLocalMessage(
return history->makeMessage(
std::move(fields),
_document,
{ _message, _entities });
TextWithEntities{ _message, _entities });
}
QString SendFile::getErrorOnSend(
@ -130,11 +130,11 @@ QString SendFile::getErrorOnSend(
return Data::RestrictionError(history->peer, type).value_or(QString());
}
void SendGame::addToHistory(
not_null<HistoryItem*> SendGame::makeMessage(
const Result *owner,
not_null<History*> history,
HistoryItemCommonFields &&fields) const {
history->addNewLocalMessage(std::move(fields), _game);
return history->addNewLocalMessage(std::move(fields), _game);
}
QString SendGame::getErrorOnSend(

View file

@ -40,7 +40,7 @@ public:
virtual bool isValid() const = 0;
virtual void addToHistory(
virtual not_null<HistoryItem*> makeMessage(
const Result *owner,
not_null<History*> history,
HistoryItemCommonFields &&fields) const = 0;
@ -75,7 +75,7 @@ public:
};
virtual SentMessageFields getSentMessageFields() const = 0;
void addToHistory(
not_null<HistoryItem*> makeMessage(
const Result *owner,
not_null<History*> history,
HistoryItemCommonFields &&fields) const override;
@ -236,7 +236,7 @@ public:
return _photo != nullptr;
}
void addToHistory(
not_null<HistoryItem*> makeMessage(
const Result *owner,
not_null<History*> history,
HistoryItemCommonFields &&fields) const override;
@ -270,7 +270,7 @@ public:
return _document != nullptr;
}
void addToHistory(
not_null<HistoryItem*> makeMessage(
const Result *owner,
not_null<History*> history,
HistoryItemCommonFields &&fields) const override;
@ -298,7 +298,7 @@ public:
return _game != nullptr;
}
void addToHistory(
not_null<HistoryItem*> makeMessage(
const Result *owner,
not_null<History*> history,
HistoryItemCommonFields &&fields) const override;

View file

@ -183,7 +183,8 @@ struct Panel::WebviewWithLifetime {
Webview::WindowConfig config = Webview::WindowConfig());
Webview::Window window;
QPointer<RpWidget> lastHidingBox;
std::vector<QPointer<RpWidget>> boxes;
rpl::lifetime boxesLifetime;
rpl::lifetime lifetime;
};
@ -824,6 +825,8 @@ bool Panel::createWebview(const Webview::ThemeParams &params) {
processHeaderColor(arguments);
} else if (command == "web_app_set_bottom_bar_color") {
processBottomBarColor(arguments);
} else if (command == "web_app_send_prepared_message") {
processSendMessageRequest(arguments);
} else if (command == "web_app_set_emoji_status") {
processEmojiStatusRequest(arguments);
} else if (command == "web_app_request_emoji_status_access") {
@ -967,6 +970,27 @@ void Panel::switchInlineQueryMessage(const QJsonObject &args) {
_delegate->botSwitchInlineQuery(types, query);
}
void Panel::processSendMessageRequest(const QJsonObject &args) {
if (args.isEmpty()) {
_delegate->botClose();
return;
}
const auto id = args["id"].toString();
auto callback = crl::guard(this, [=](QString error) {
if (error.isEmpty()) {
postEvent("prepared_message_sent");
} else {
postEvent(
"prepared_message_failed",
u"{ error: \"%1\" }"_q.arg(error));
}
});
_delegate->botSendPreparedMessage({
.id = id,
.callback = std::move(callback),
});
}
void Panel::processEmojiStatusRequest(const QJsonObject &args) {
if (args.isEmpty()) {
_delegate->botClose();
@ -1502,9 +1526,11 @@ void Panel::layoutButtons() {
return button && !button->isHidden();
};
const auto any = shown(_mainButton) || shown(_secondaryButton);
_webviewBottom->setVisible(!any && !_fullscreen.current());
_webviewBottom->setVisible(!any
&& !_fullscreen.current()
&& !_layerShown);
if (any) {
_bottomButtonsBg->show();
_bottomButtonsBg->setVisible(!_layerShown);
const auto one = shown(_mainButton)
? _mainButton.get()
@ -1570,7 +1596,9 @@ void Panel::layoutButtons() {
} else if (_bottomButtonsBg) {
_bottomButtonsBg->hide();
}
_footerHeight = any
_footerHeight = _layerShown
? 0
: any
? _bottomButtonsBg->height()
: _fullscreen.current()
? 0
@ -1586,25 +1614,34 @@ void Panel::showBox(
LayerOptions options,
anim::type animated) {
if (const auto widget = _webview ? _webview->window.widget() : nullptr) {
_layerShown = true;
const auto hideNow = !widget->isHidden();
if (hideNow || _webview->lastHidingBox) {
const auto raw = _webview->lastHidingBox = box.data();
box->boxClosing(
) | rpl::start_with_next([=] {
const auto raw = box.data();
_webview->boxes.push_back(raw);
raw->boxClosing(
) | rpl::filter([=] {
return _webview != nullptr;
}) | rpl::start_with_next([=] {
auto &list = _webview->boxes;
list.erase(ranges::remove_if(list, [&](QPointer<RpWidget> b) {
return !b || (b == raw);
}), end(list));
if (list.empty()) {
_webview->boxesLifetime.destroy();
_layerShown = false;
const auto widget = _webview
? _webview->window.widget()
: nullptr;
if (widget
&& widget->isHidden()
&& _webview->lastHidingBox == raw) {
if (widget && widget->isHidden()) {
widget->show();
_webviewBottom->setVisible(!_fullscreen.current());
layoutButtons();
}
}, _webview->lifetime);
if (hideNow) {
widget->hide();
_webviewBottom->hide();
}
}, _webview->boxesLifetime);
if (hideNow) {
widget->hide();
layoutButtons();
}
}
const auto raw = box.data();

View file

@ -57,6 +57,11 @@ struct SetEmojiStatusRequest {
Fn<void(QString)> callback;
};
struct SendPreparedMessageRequest {
QString id = 0;
Fn<void(QString)> callback;
};
class Delegate {
public:
virtual Webview::ThemeParams botThemeParams() = 0;
@ -76,6 +81,8 @@ public:
virtual void botSharePhone(Fn<void(bool shared)> callback) = 0;
virtual void botInvokeCustomMethod(CustomMethodRequest request) = 0;
virtual void botSetEmojiStatus(SetEmojiStatusRequest request) = 0;
virtual void botSendPreparedMessage(
SendPreparedMessageRequest request) = 0;
virtual void botOpenPrivacyPolicy() = 0;
virtual void botClose() = 0;
};
@ -132,6 +139,7 @@ private:
void setTitle(rpl::producer<QString> title);
void sendDataMessage(const QJsonObject &args);
void switchInlineQueryMessage(const QJsonObject &args);
void processSendMessageRequest(const QJsonObject &args);
void processEmojiStatusRequest(const QJsonObject &args);
void processEmojiStatusAccessRequest();
void processButtonMessage(
@ -195,6 +203,7 @@ private:
rpl::lifetime _headerColorLifetime;
rpl::lifetime _bottomBarColorLifetime;
rpl::variable<bool> _fullscreen = false;
bool _layerShown = false;
bool _webviewProgress = false;
bool _themeUpdateScheduled = false;
bool _hiddenForPayment = false;

View file

@ -1881,8 +1881,8 @@ void BlockSenderFromRepliesBox(
Window::ClearReply{ id });
}
QPointer<Ui::BoxContent> ShowChooseRecipientBox(
not_null<Window::SessionNavigation*> navigation,
object_ptr<Ui::BoxContent> PrepareChooseRecipientBox(
not_null<Main::Session*> session,
FnMut<bool(not_null<Data::Thread*>)> &&chosen,
rpl::producer<QString> titleOverride,
FnMut<void()> &&successCallback,
@ -1927,15 +1927,30 @@ QPointer<Ui::BoxContent> ShowChooseRecipientBox(
box->setTitle(std::move(titleOverride));
}
};
*weak = navigation->parentController()->show(Box<PeerListBox>(
auto result = Box<PeerListBox>(
std::make_unique<ChooseRecipientBoxController>(ChooseRecipientArgs{
.session = &navigation->session(),
.session = session,
.callback = std::move(callback),
.filter = std::move(filter),
.premiumRequiredError = WritePremiumRequiredError,
}),
std::move(initBox)));
return weak->data();
std::move(initBox));
*weak = result.data();
return result;
}
QPointer<Ui::BoxContent> ShowChooseRecipientBox(
not_null<Window::SessionNavigation*> navigation,
FnMut<bool(not_null<Data::Thread*>)> &&chosen,
rpl::producer<QString> titleOverride,
FnMut<void()> &&successCallback,
InlineBots::PeerTypes typesRestriction) {
return navigation->parentController()->show(PrepareChooseRecipientBox(
&navigation->session(),
std::move(chosen),
std::move(titleOverride),
std::move(successCallback),
typesRestriction));
}
QPointer<Ui::BoxContent> ShowForwardMessagesBox(

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "api/api_common.h"
#include "base/object_ptr.h"
#include "menu/menu_send.h"
#include "data/data_poll.h"
#include "ui/widgets/menu/menu_add_action_callback.h"
@ -138,6 +139,12 @@ Fn<void()> DeleteAndLeaveHandler(
not_null<Window::SessionController*> controller,
not_null<PeerData*> peer);
object_ptr<Ui::BoxContent> PrepareChooseRecipientBox(
not_null<Main::Session*> session,
FnMut<bool(not_null<Data::Thread*>)> &&chosen,
rpl::producer<QString> titleOverride = nullptr,
FnMut<void()> &&successCallback = nullptr,
InlineBots::PeerTypes typesRestriction = 0);
QPointer<Ui::BoxContent> ShowChooseRecipientBox(
not_null<Window::SessionNavigation*> navigation,
FnMut<bool(not_null<Data::Thread*>)> &&chosen,