From 2d1fb0562db32b64e762407b2ea801ce025c711f Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 8 Nov 2024 23:19:29 +0400 Subject: [PATCH] Prepared messages sharing from miniapp. --- Telegram/CMakeLists.txt | 2 + Telegram/Resources/langs/lang.strings | 3 + Telegram/SourceFiles/apiwrap.cpp | 5 +- Telegram/SourceFiles/apiwrap.h | 3 +- Telegram/SourceFiles/history/history.cpp | 8 + Telegram/SourceFiles/history/history.h | 1 + .../history/history_item_reply_markup.cpp | 44 ++-- .../history/history_item_reply_markup.h | 3 + .../inline_bots/bot_attach_web_view.cpp | 85 ++++++++ .../inline_bots/bot_attach_web_view.h | 2 + .../inline_bot_confirm_prepared.cpp | 196 ++++++++++++++++++ .../inline_bots/inline_bot_confirm_prepared.h | 21 ++ .../inline_bots/inline_bot_result.cpp | 10 +- .../inline_bots/inline_bot_result.h | 3 + .../inline_bots/inline_bot_send_data.cpp | 20 +- .../inline_bots/inline_bot_send_data.h | 10 +- .../ui/chat/attach/attach_bot_webview.cpp | 69 ++++-- .../ui/chat/attach/attach_bot_webview.h | 9 + .../SourceFiles/window/window_peer_menu.cpp | 27 ++- .../SourceFiles/window/window_peer_menu.h | 7 + 20 files changed, 465 insertions(+), 63 deletions(-) create mode 100644 Telegram/SourceFiles/inline_bots/inline_bot_confirm_prepared.cpp create mode 100644 Telegram/SourceFiles/inline_bots/inline_bot_confirm_prepared.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 8ea6aa9ae..956115e4b 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -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 diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 9c0aa15d6..1525b0fed 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -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"; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index b8d51732b..f4779b1f7 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -3947,7 +3947,8 @@ void ApiWrap::sendInlineResult( not_null bot, not_null data, const SendAction &action, - std::optional localMessageId) { + std::optional localMessageId, + Fn 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); } diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 7cff89c71..f25368293 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -361,7 +361,8 @@ public: not_null bot, not_null data, const SendAction &action, - std::optional localMessageId); + std::optional localMessageId, + Fn done = nullptr); void sendMessageFail( const MTP::Error &error, not_null peer, diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index f78b3f906..250fcb831 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -712,6 +712,14 @@ not_null History::addNewLocalMessage( true); } +not_null History::addNewLocalMessage( + not_null item) { + Expects(item->history() == this); + Expects(item->isLocal()); + + return addNewItem(item, true); +} + not_null History::addSponsoredMessage( MsgId id, Data::SponsoredFrom from, diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 73695785f..7685e0ba4 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -180,6 +180,7 @@ public: not_null addNewLocalMessage( HistoryItemCommonFields &&fields, not_null game); + not_null addNewLocalMessage(not_null item); not_null addSponsoredMessage( MsgId id, diff --git a/Telegram/SourceFiles/history/history_item_reply_markup.cpp b/Telegram/SourceFiles/history/history_item_reply_markup.cpp index 5cd532ad5..319a77238 100644 --- a/Telegram/SourceFiles/history/history_item_reply_markup.cpp +++ b/Telegram/SourceFiles/history/history_item_reply_markup.cpp @@ -14,28 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace { -[[nodiscard]] InlineBots::PeerTypes PeerTypesFromMTP( - const MTPvector &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 &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, diff --git a/Telegram/SourceFiles/history/history_item_reply_markup.h b/Telegram/SourceFiles/history/history_item_reply_markup.h index 8d1d4e937..77895c3aa 100644 --- a/Telegram/SourceFiles/history/history_item_reply_markup.h +++ b/Telegram/SourceFiles/history/history_item_reply_markup.h @@ -19,6 +19,9 @@ enum class PeerType : uint8; using PeerTypes = base::flags; } // namespace InlineBots +[[nodiscard]] InlineBots::PeerTypes PeerTypesFromMTP( + const MTPvector &types); + enum class ReplyMarkupFlag : uint32 { None = (1U << 0), ForceReply = (1U << 1), diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp index e1858aa95..17983983f 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp @@ -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::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 preview; + bool sent = false; + }; + const auto state = std::make_shared(); + auto box = Box(PreparedPreviewBox, item, [=] { + const auto chosen = [=](not_null 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; diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h index 0d7ae73d4..46e1487c4 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h @@ -265,6 +265,8 @@ private: void botSharePhone(Fn 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; diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_confirm_prepared.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_confirm_prepared.cpp new file mode 100644 index 000000000..07aae0515 --- /dev/null +++ b/Telegram/SourceFiles/inline_bots/inline_bot_confirm_prepared.cpp @@ -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 parent, + not_null st, + Fn update); + + bool elementAnimationsPaused() override; + not_null elementPathShiftGradient() override; + Context elementContext() override; + +private: + const not_null _parent; + const std::unique_ptr _pathGradient; + +}; + +class PreviewWrap final : public Ui::RpWidget { +public: + PreviewWrap(not_null parent, not_null item); + ~PreviewWrap(); + +private: + void paintEvent(QPaintEvent *e) override; + + void resizeTo(int width); + void prepare(not_null item); + + const not_null _history; + const std::unique_ptr _theme; + const std::unique_ptr _style; + const std::unique_ptr _delegate; + AdminLog::OwnedItem _item; + QPoint _position; + +}; + +PreviewDelegate::PreviewDelegate( + not_null parent, + not_null st, + Fn update) +: _parent(parent) +, _pathGradient(MakePathShiftGradient(st, update)) { +} + +bool PreviewDelegate::elementAnimationsPaused() { + return _parent->window()->isActiveWindow(); +} + +auto PreviewDelegate::elementPathShiftGradient() +-> not_null { + return _pathGradient.get(); +} + +Context PreviewDelegate::elementContext() { + return Context::History; +} + +PreviewWrap::PreviewWrap( + not_null parent, + not_null item) +: RpWidget(parent) +, _history(item->history()) +, _theme(Window::Theme::DefaultChatThemeOn(lifetime())) +, _style(std::make_unique( + _history->session().colorIndicesValue())) +, _delegate(std::make_unique( + 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 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 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 box, + not_null item, + Fn share) { + box->setTitle(tr::lng_bot_share_prepared_title()); + const auto container = box->verticalLayout(); + container->add(object_ptr(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 removed) { + if (removed == item) { + box->closeBox(); + } + }, box->lifetime()); +} + +} // namespace InlineBots diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_confirm_prepared.h b/Telegram/SourceFiles/inline_bots/inline_bot_confirm_prepared.h new file mode 100644 index 000000000..606bc31c2 --- /dev/null +++ b/Telegram/SourceFiles/inline_bots/inline_bot_confirm_prepared.h @@ -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 box, + not_null item, + Fn share); + +} // namespace InlineBots diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp index 612d35769..7c487f843 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp @@ -378,14 +378,20 @@ bool Result::hasThumbDisplay() const { void Result::addToHistory( not_null history, HistoryItemCommonFields &&fields) const { - fields.flags |= MessageFlag::FromInlineBot; + history->addNewLocalMessage(makeMessage(history, std::move(fields))); +} + +not_null Result::makeMessage( + not_null 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) const { diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.h b/Telegram/SourceFiles/inline_bots/inline_bot_result.h index cc7e090ee..e0b89e3d9 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.h +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.h @@ -66,6 +66,9 @@ public: void addToHistory( not_null history, HistoryItemCommonFields &&fields) const; + [[nodiscard]] not_null makeMessage( + not_null history, + HistoryItemCommonFields &&fields) const; QString getErrorOnSend(not_null history) const; // interface for Layout:: usage diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp index 0b8e9d8b7..714516b39 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp @@ -28,7 +28,7 @@ QString SendData::getLayoutDescription(const Result *owner) const { return owner->_description; } -void SendDataCommon::addToHistory( +not_null SendDataCommon::makeMessage( const Result *owner, not_null 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 SendPhoto::makeMessage( const Result *owner, not_null 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 SendFile::makeMessage( const Result *owner, not_null 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 SendGame::makeMessage( const Result *owner, not_null history, HistoryItemCommonFields &&fields) const { - history->addNewLocalMessage(std::move(fields), _game); + return history->addNewLocalMessage(std::move(fields), _game); } QString SendGame::getErrorOnSend( diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h index 502cc006e..9c53b6207 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h +++ b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h @@ -40,7 +40,7 @@ public: virtual bool isValid() const = 0; - virtual void addToHistory( + virtual not_null makeMessage( const Result *owner, not_null history, HistoryItemCommonFields &&fields) const = 0; @@ -75,7 +75,7 @@ public: }; virtual SentMessageFields getSentMessageFields() const = 0; - void addToHistory( + not_null makeMessage( const Result *owner, not_null history, HistoryItemCommonFields &&fields) const override; @@ -236,7 +236,7 @@ public: return _photo != nullptr; } - void addToHistory( + not_null makeMessage( const Result *owner, not_null history, HistoryItemCommonFields &&fields) const override; @@ -270,7 +270,7 @@ public: return _document != nullptr; } - void addToHistory( + not_null makeMessage( const Result *owner, not_null history, HistoryItemCommonFields &&fields) const override; @@ -298,7 +298,7 @@ public: return _game != nullptr; } - void addToHistory( + not_null makeMessage( const Result *owner, not_null history, HistoryItemCommonFields &&fields) const override; diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp index da2a8a5b8..9da6c01eb 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp @@ -183,7 +183,8 @@ struct Panel::WebviewWithLifetime { Webview::WindowConfig config = Webview::WindowConfig()); Webview::Window window; - QPointer lastHidingBox; + std::vector> boxes; + rpl::lifetime boxesLifetime; rpl::lifetime lifetime; }; @@ -824,6 +825,8 @@ bool Panel::createWebview(const Webview::ThemeParams ¶ms) { 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 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(); diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h index 97da49a60..1f15dac3c 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h @@ -57,6 +57,11 @@ struct SetEmojiStatusRequest { Fn callback; }; +struct SendPreparedMessageRequest { + QString id = 0; + Fn callback; +}; + class Delegate { public: virtual Webview::ThemeParams botThemeParams() = 0; @@ -76,6 +81,8 @@ public: virtual void botSharePhone(Fn 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 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 _fullscreen = false; + bool _layerShown = false; bool _webviewProgress = false; bool _themeUpdateScheduled = false; bool _hiddenForPayment = false; diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 9d01be276..764ebba2e 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -1881,8 +1881,8 @@ void BlockSenderFromRepliesBox( Window::ClearReply{ id }); } -QPointer ShowChooseRecipientBox( - not_null navigation, +object_ptr PrepareChooseRecipientBox( + not_null session, FnMut)> &&chosen, rpl::producer titleOverride, FnMut &&successCallback, @@ -1927,15 +1927,30 @@ QPointer ShowChooseRecipientBox( box->setTitle(std::move(titleOverride)); } }; - *weak = navigation->parentController()->show(Box( + auto result = Box( std::make_unique(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 ShowChooseRecipientBox( + not_null navigation, + FnMut)> &&chosen, + rpl::producer titleOverride, + FnMut &&successCallback, + InlineBots::PeerTypes typesRestriction) { + return navigation->parentController()->show(PrepareChooseRecipientBox( + &navigation->session(), + std::move(chosen), + std::move(titleOverride), + std::move(successCallback), + typesRestriction)); } QPointer ShowForwardMessagesBox( diff --git a/Telegram/SourceFiles/window/window_peer_menu.h b/Telegram/SourceFiles/window/window_peer_menu.h index 8369e3228..e1714257b 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.h +++ b/Telegram/SourceFiles/window/window_peer_menu.h @@ -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 DeleteAndLeaveHandler( not_null controller, not_null peer); +object_ptr PrepareChooseRecipientBox( + not_null session, + FnMut)> &&chosen, + rpl::producer titleOverride = nullptr, + FnMut &&successCallback = nullptr, + InlineBots::PeerTypes typesRestriction = 0); QPointer ShowChooseRecipientBox( not_null navigation, FnMut)> &&chosen,