mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Multi-select in mini app send prepared.
This commit is contained in:
parent
60f4587d95
commit
7552328cdd
3 changed files with 158 additions and 49 deletions
|
@ -1826,11 +1826,56 @@ void WebViewInstance::botSendPreparedMessage(
|
||||||
});
|
});
|
||||||
struct State {
|
struct State {
|
||||||
QPointer<Ui::BoxContent> preview;
|
QPointer<Ui::BoxContent> preview;
|
||||||
|
QPointer<Ui::BoxContent> choose;
|
||||||
rpl::event_stream<not_null<Data::Thread*>> recipient;
|
rpl::event_stream<not_null<Data::Thread*>> recipient;
|
||||||
bool sent = false;
|
bool sent = false;
|
||||||
};
|
};
|
||||||
const auto state = std::make_shared<State>();
|
const auto state = std::make_shared<State>();
|
||||||
auto recipient = state->recipient.events();
|
auto recipient = state->recipient.events();
|
||||||
|
const auto send = [=](std::vector<not_null<Data::Thread*>> list) {
|
||||||
|
if (state->sent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state->sent = true;
|
||||||
|
const auto failed = std::make_shared<int>();
|
||||||
|
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), [=] {
|
auto box = Box(PreparedPreviewBox, item, std::move(recipient), [=] {
|
||||||
if (state->sent) {
|
if (state->sent) {
|
||||||
return;
|
return;
|
||||||
|
@ -1850,38 +1895,12 @@ void WebViewInstance::botSendPreparedMessage(
|
||||||
chosen,
|
chosen,
|
||||||
tr::lng_inline_switch_choose(),
|
tr::lng_inline_switch_choose(),
|
||||||
nullptr,
|
nullptr,
|
||||||
types);
|
types,
|
||||||
|
send);
|
||||||
|
state->choose = box.data();
|
||||||
panel->showBox(std::move(box));
|
panel->showBox(std::move(box));
|
||||||
}, [=](not_null<Data::Thread*> thread) {
|
}, [=](not_null<Data::Thread*> thread) {
|
||||||
if (state->sent) {
|
send({ thread });
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
box->boxClosing() | rpl::start_with_next([=] {
|
box->boxClosing() | rpl::start_with_next([=] {
|
||||||
if (!state->sent) {
|
if (!state->sent) {
|
||||||
|
|
|
@ -1886,8 +1886,83 @@ object_ptr<Ui::BoxContent> PrepareChooseRecipientBox(
|
||||||
FnMut<bool(not_null<Data::Thread*>)> &&chosen,
|
FnMut<bool(not_null<Data::Thread*>)> &&chosen,
|
||||||
rpl::producer<QString> titleOverride,
|
rpl::producer<QString> titleOverride,
|
||||||
FnMut<void()> &&successCallback,
|
FnMut<void()> &&successCallback,
|
||||||
InlineBots::PeerTypes typesRestriction) {
|
InlineBots::PeerTypes typesRestriction,
|
||||||
const auto weak = std::make_shared<QPointer<Ui::BoxContent>>();
|
Fn<void(std::vector<not_null<Data::Thread*>>)> sendMany) {
|
||||||
|
const auto weak = std::make_shared<QPointer<PeerListBox>>();
|
||||||
|
const auto selectable = (sendMany != nullptr);
|
||||||
|
class Controller final : public ChooseRecipientBoxController {
|
||||||
|
public:
|
||||||
|
using Chosen = not_null<Data::Thread*>;
|
||||||
|
|
||||||
|
Controller(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
FnMut<void(Chosen)> callback,
|
||||||
|
Fn<bool(Chosen)> filter,
|
||||||
|
bool selectable)
|
||||||
|
: ChooseRecipientBoxController({
|
||||||
|
.session = session,
|
||||||
|
.callback = std::move(callback),
|
||||||
|
.filter = filter,
|
||||||
|
.premiumRequiredError = WritePremiumRequiredError,
|
||||||
|
})
|
||||||
|
, _selectable(selectable) {
|
||||||
|
}
|
||||||
|
|
||||||
|
using PeerListController::setSearchNoResultsText;
|
||||||
|
|
||||||
|
void rowClicked(not_null<PeerListRow*> 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<Ui::PopupMenu> rowContextMenu(
|
||||||
|
QWidget *parent,
|
||||||
|
not_null<PeerListRow*> row) override final {
|
||||||
|
if (!_selectable) {
|
||||||
|
return ChooseRecipientBoxController::rowContextMenu(
|
||||||
|
parent,
|
||||||
|
row);
|
||||||
|
}
|
||||||
|
if (!row->checked() && !row->peer()->isForum()) {
|
||||||
|
auto menu = base::make_unique_q<Ui::PopupMenu>(
|
||||||
|
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<bool> hasSelectedChanges() const {
|
||||||
|
return _hasSelectedChanges.events_starting_with(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<Chosen> singleChosen() const {
|
||||||
|
return _singleChosen.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
rpl::event_stream<Chosen> _singleChosen;
|
||||||
|
rpl::event_stream<bool> _hasSelectedChanges;
|
||||||
|
bool _selectable = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
auto callback = [
|
auto callback = [
|
||||||
chosen = std::move(chosen),
|
chosen = std::move(chosen),
|
||||||
success = std::move(successCallback),
|
success = std::move(successCallback),
|
||||||
|
@ -1919,23 +1994,40 @@ object_ptr<Ui::BoxContent> PrepareChooseRecipientBox(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: Fn<bool(not_null<Data::Thread*>)>();
|
: Fn<bool(not_null<Data::Thread*>)>();
|
||||||
|
auto controller = std::make_unique<Controller>(
|
||||||
|
session,
|
||||||
|
std::move(callback),
|
||||||
|
std::move(filter),
|
||||||
|
selectable);
|
||||||
|
const auto raw = controller.get();
|
||||||
auto initBox = [=](not_null<PeerListBox*> box) {
|
auto initBox = [=](not_null<PeerListBox*> box) {
|
||||||
box->addButton(tr::lng_cancel(), [box] {
|
raw->hasSelectedChanges(
|
||||||
box->closeBox();
|
) | 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<PeerData*> peer) -> Controller::Chosen {
|
||||||
|
return peer->owner().history(peer);
|
||||||
|
}) | ranges::to_vector);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
box->addButton(tr::lng_cancel(), [=] {
|
||||||
|
box->closeBox();
|
||||||
|
});
|
||||||
|
}, box->lifetime());
|
||||||
if (titleOverride) {
|
if (titleOverride) {
|
||||||
box->setTitle(std::move(titleOverride));
|
box->setTitle(std::move(titleOverride));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
auto result = Box<PeerListBox>(
|
auto result = Box<PeerListBox>(
|
||||||
std::make_unique<ChooseRecipientBoxController>(ChooseRecipientArgs{
|
std::move(controller),
|
||||||
.session = session,
|
|
||||||
.callback = std::move(callback),
|
|
||||||
.filter = std::move(filter),
|
|
||||||
.premiumRequiredError = WritePremiumRequiredError,
|
|
||||||
}),
|
|
||||||
std::move(initBox));
|
std::move(initBox));
|
||||||
*weak = result.data();
|
*weak = result.data();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2014,9 +2106,7 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||||
}) {
|
}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSearchNoResultsText(const QString &text) {
|
using PeerListController::setSearchNoResultsText;
|
||||||
PeerListController::setSearchNoResultsText(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
void rowClicked(not_null<PeerListRow*> row) override final {
|
void rowClicked(not_null<PeerListRow*> row) override final {
|
||||||
const auto count = delegate()->peerListSelectedRowsCount();
|
const auto count = delegate()->peerListSelectedRowsCount();
|
||||||
|
@ -2034,13 +2124,12 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||||
base::unique_qptr<Ui::PopupMenu> rowContextMenu(
|
base::unique_qptr<Ui::PopupMenu> rowContextMenu(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<PeerListRow*> row) override final {
|
not_null<PeerListRow*> row) override final {
|
||||||
const auto count = delegate()->peerListSelectedRowsCount();
|
if (!row->checked() && !row->peer()->isForum()) {
|
||||||
if (!count && !row->peer()->isForum()) {
|
|
||||||
auto menu = base::make_unique_q<Ui::PopupMenu>(
|
auto menu = base::make_unique_q<Ui::PopupMenu>(
|
||||||
parent,
|
parent,
|
||||||
st::popupMenuWithIcons);
|
st::popupMenuWithIcons);
|
||||||
menu->addAction(tr::lng_bot_choose_chat(tr::now), [=] {
|
menu->addAction(tr::lng_bot_choose_chat(tr::now), [=] {
|
||||||
delegate()->peerListSetRowChecked(row, !row->checked());
|
delegate()->peerListSetRowChecked(row, true);
|
||||||
_hasSelectedChanges.fire(
|
_hasSelectedChanges.fire(
|
||||||
delegate()->peerListSelectedRowsCount() > 0);
|
delegate()->peerListSelectedRowsCount() > 0);
|
||||||
}, &st::menuIconSelect);
|
}, &st::menuIconSelect);
|
||||||
|
@ -2049,7 +2138,7 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<bool> hasSelectedChanges() const{
|
[[nodiscard]] rpl::producer<bool> hasSelectedChanges() const {
|
||||||
return _hasSelectedChanges.events_starting_with(false);
|
return _hasSelectedChanges.events_starting_with(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -144,7 +144,8 @@ object_ptr<Ui::BoxContent> PrepareChooseRecipientBox(
|
||||||
FnMut<bool(not_null<Data::Thread*>)> &&chosen,
|
FnMut<bool(not_null<Data::Thread*>)> &&chosen,
|
||||||
rpl::producer<QString> titleOverride = nullptr,
|
rpl::producer<QString> titleOverride = nullptr,
|
||||||
FnMut<void()> &&successCallback = nullptr,
|
FnMut<void()> &&successCallback = nullptr,
|
||||||
InlineBots::PeerTypes typesRestriction = 0);
|
InlineBots::PeerTypes typesRestriction = 0,
|
||||||
|
Fn<void(std::vector<not_null<Data::Thread*>>)> sendMany = nullptr);
|
||||||
QPointer<Ui::BoxContent> ShowChooseRecipientBox(
|
QPointer<Ui::BoxContent> ShowChooseRecipientBox(
|
||||||
not_null<Window::SessionNavigation*> navigation,
|
not_null<Window::SessionNavigation*> navigation,
|
||||||
FnMut<bool(not_null<Data::Thread*>)> &&chosen,
|
FnMut<bool(not_null<Data::Thread*>)> &&chosen,
|
||||||
|
|
Loading…
Add table
Reference in a new issue