diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp index e54976e4e..7a2f79071 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp @@ -1826,11 +1826,56 @@ void WebViewInstance::botSendPreparedMessage( }); struct State { QPointer preview; + QPointer choose; rpl::event_stream> recipient; bool sent = false; }; const auto state = std::make_shared(); auto recipient = state->recipient.events(); + const auto send = [=](std::vector> list) { + if (state->sent) { + return; + } + state->sent = true; + const auto failed = std::make_shared(); + const auto count = int(list.size()); + const auto weak1 = state->preview; + const auto weak2 = state->choose; + const auto close = [=] { + if (const auto strong = weak1.data()) { + strong->closeBox(); + } + if (const auto strong = weak2.data()) { + strong->closeBox(); + } + }; + const auto done = [=](bool success) { + if (*failed < 0) { + return; + } + if (success) { + *failed = -1; + if (const auto strong2 = weak2.data()) { + strong2->showToast({ tr::lng_share_done(tr::now) }); + } else if (const auto strong1 = weak1.data()) { + strong1->showToast({ tr::lng_share_done(tr::now) }); + } + base::call_delayed(Ui::Toast::kDefaultDuration, close); + callback(QString()); + } else if (++*failed == count) { + close(); + callback(u"MESSAGE_SEND_FAILED"_q); + } + }; + for (const auto &thread : list) { + bot->session().api().sendInlineResult( + bot, + parsed.get(), + Api::SendAction(thread), + std::nullopt, + done); + } + }; auto box = Box(PreparedPreviewBox, item, std::move(recipient), [=] { if (state->sent) { return; @@ -1850,38 +1895,12 @@ void WebViewInstance::botSendPreparedMessage( chosen, tr::lng_inline_switch_choose(), nullptr, - types); + types, + send); + state->choose = box.data(); panel->showBox(std::move(box)); }, [=](not_null thread) { - if (state->sent) { - return; - } - state->sent = true; - const auto weak = state->preview; - const auto done = [=](bool success) { - if (success) { - if (const auto strong = weak.data()) { - strong->showToast({ tr::lng_share_done(tr::now) }); - } - base::call_delayed(Ui::Toast::kDefaultDuration, [weak] { - if (const auto strong = weak.data()) { - strong->closeBox(); - } - }); - callback(QString()); - } else { - if (const auto strong = weak.data()) { - strong->closeBox(); - } - callback(u"MESSAGE_SEND_FAILED"_q); - } - }; - bot->session().api().sendInlineResult( - bot, - parsed.get(), - Api::SendAction(thread), - std::nullopt, - done); + send({ thread }); }); box->boxClosing() | rpl::start_with_next([=] { if (!state->sent) { diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 5d0c2bc12..2ea988a8e 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -1886,8 +1886,83 @@ object_ptr PrepareChooseRecipientBox( FnMut)> &&chosen, rpl::producer titleOverride, FnMut &&successCallback, - InlineBots::PeerTypes typesRestriction) { - const auto weak = std::make_shared>(); + InlineBots::PeerTypes typesRestriction, + Fn>)> sendMany) { + const auto weak = std::make_shared>(); + const auto selectable = (sendMany != nullptr); + class Controller final : public ChooseRecipientBoxController { + public: + using Chosen = not_null; + + Controller( + not_null session, + FnMut callback, + Fn filter, + bool selectable) + : ChooseRecipientBoxController({ + .session = session, + .callback = std::move(callback), + .filter = filter, + .premiumRequiredError = WritePremiumRequiredError, + }) + , _selectable(selectable) { + } + + using PeerListController::setSearchNoResultsText; + + void rowClicked(not_null row) override final { + if (!_selectable) { + return ChooseRecipientBoxController::rowClicked(row); + } + const auto count = delegate()->peerListSelectedRowsCount(); + if (showLockedError(row) || (count && row->peer()->isForum())) { + return; + } else if (row->peer()->isForum()) { + ChooseRecipientBoxController::rowClicked(row); + } else { + delegate()->peerListSetRowChecked(row, !row->checked()); + _hasSelectedChanges.fire( + delegate()->peerListSelectedRowsCount() > 0); + } + } + + base::unique_qptr rowContextMenu( + QWidget *parent, + not_null row) override final { + if (!_selectable) { + return ChooseRecipientBoxController::rowContextMenu( + parent, + row); + } + if (!row->checked() && !row->peer()->isForum()) { + auto menu = base::make_unique_q( + parent, + st::popupMenuWithIcons); + menu->addAction(tr::lng_bot_choose_chat(tr::now), [=] { + delegate()->peerListSetRowChecked(row, true); + _hasSelectedChanges.fire( + delegate()->peerListSelectedRowsCount() > 0); + }, &st::menuIconSelect); + return menu; + } + return nullptr; + } + + [[nodiscard]] rpl::producer hasSelectedChanges() const { + return _hasSelectedChanges.events_starting_with(false); + } + + [[nodiscard]] rpl::producer singleChosen() const { + return _singleChosen.events(); + } + + private: + rpl::event_stream _singleChosen; + rpl::event_stream _hasSelectedChanges; + bool _selectable = false; + + }; + auto callback = [ chosen = std::move(chosen), success = std::move(successCallback), @@ -1919,23 +1994,40 @@ object_ptr PrepareChooseRecipientBox( } } : Fn)>(); + auto controller = std::make_unique( + session, + std::move(callback), + std::move(filter), + selectable); + const auto raw = controller.get(); auto initBox = [=](not_null box) { - box->addButton(tr::lng_cancel(), [box] { - box->closeBox(); - }); + raw->hasSelectedChanges( + ) | rpl::start_with_next([=](bool shown) { + box->clearButtons(); + if (shown) { + box->addButton(tr::lng_send_button(), [=] { + const auto peers = box->collectSelectedRows(); + sendMany(ranges::views::all( + peers + ) | ranges::views::transform([&]( + not_null peer) -> Controller::Chosen { + return peer->owner().history(peer); + }) | ranges::to_vector); + }); + } + box->addButton(tr::lng_cancel(), [=] { + box->closeBox(); + }); + }, box->lifetime()); if (titleOverride) { box->setTitle(std::move(titleOverride)); } }; auto result = Box( - std::make_unique(ChooseRecipientArgs{ - .session = session, - .callback = std::move(callback), - .filter = std::move(filter), - .premiumRequiredError = WritePremiumRequiredError, - }), + std::move(controller), std::move(initBox)); *weak = result.data(); + return result; } @@ -2014,9 +2106,7 @@ QPointer ShowForwardMessagesBox( }) { } - void setSearchNoResultsText(const QString &text) { - PeerListController::setSearchNoResultsText(text); - } + using PeerListController::setSearchNoResultsText; void rowClicked(not_null row) override final { const auto count = delegate()->peerListSelectedRowsCount(); @@ -2034,13 +2124,12 @@ QPointer ShowForwardMessagesBox( base::unique_qptr rowContextMenu( QWidget *parent, not_null row) override final { - const auto count = delegate()->peerListSelectedRowsCount(); - if (!count && !row->peer()->isForum()) { + if (!row->checked() && !row->peer()->isForum()) { auto menu = base::make_unique_q( parent, st::popupMenuWithIcons); menu->addAction(tr::lng_bot_choose_chat(tr::now), [=] { - delegate()->peerListSetRowChecked(row, !row->checked()); + delegate()->peerListSetRowChecked(row, true); _hasSelectedChanges.fire( delegate()->peerListSelectedRowsCount() > 0); }, &st::menuIconSelect); @@ -2049,7 +2138,7 @@ QPointer ShowForwardMessagesBox( return nullptr; } - [[nodiscard]] rpl::producer hasSelectedChanges() const{ + [[nodiscard]] rpl::producer hasSelectedChanges() const { return _hasSelectedChanges.events_starting_with(false); } diff --git a/Telegram/SourceFiles/window/window_peer_menu.h b/Telegram/SourceFiles/window/window_peer_menu.h index e1714257b..6ef96c9f5 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.h +++ b/Telegram/SourceFiles/window/window_peer_menu.h @@ -144,7 +144,8 @@ object_ptr PrepareChooseRecipientBox( FnMut)> &&chosen, rpl::producer titleOverride = nullptr, FnMut &&successCallback = nullptr, - InlineBots::PeerTypes typesRestriction = 0); + InlineBots::PeerTypes typesRestriction = 0, + Fn>)> sendMany = nullptr); QPointer ShowChooseRecipientBox( not_null navigation, FnMut)> &&chosen,