mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 14:17:12 +02:00
Added ability to forward messages to multiple chats.
This commit is contained in:
parent
bd8e7fdddd
commit
2c1e7bfcb6
2 changed files with 293 additions and 19 deletions
|
@ -7,6 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "window/window_peer_menu.h"
|
||||
|
||||
#include "menu/menu_check_item.h"
|
||||
#include "boxes/share_box.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "api/api_chat_participants.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
|
@ -1604,30 +1609,299 @@ QPointer<Ui::BoxContent> ShowChooseRecipientBox(
|
|||
QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
Data::ForwardDraft &&draft,
|
||||
FnMut<void()> &&successCallback) {
|
||||
auto chosen = [navigation, draft = std::move(draft)](
|
||||
not_null<Data::Thread*> thread) mutable {
|
||||
const auto content = navigation->parentController()->content();
|
||||
const auto peer = thread->peer();
|
||||
if (peer->isSelf()
|
||||
&& !draft.ids.empty()
|
||||
&& draft.ids.front().peer != peer->id) {
|
||||
ForwardToSelf(navigation, draft);
|
||||
return true;
|
||||
Fn<void()> &&successCallback) {
|
||||
const auto msgIds = draft.ids;
|
||||
|
||||
class ListBox final : public PeerListBox {
|
||||
public:
|
||||
using PeerListBox::PeerListBox;
|
||||
|
||||
void setBottomSkip(int bottomSkip) {
|
||||
PeerListBox::setInnerBottomSkip(bottomSkip);
|
||||
}
|
||||
return content->setForwardDraft(thread, std::move(draft));
|
||||
|
||||
[[nodiscard]] rpl::producer<> focusRequests() const {
|
||||
return _focusRequests.events();
|
||||
}
|
||||
|
||||
[[nodiscard]] Data::ForwardOptions forwardOptionsData() const {
|
||||
return (_forwardOptions.hasCaptions
|
||||
&& _forwardOptions.dropCaptions)
|
||||
? Data::ForwardOptions::NoNamesAndCaptions
|
||||
: _forwardOptions.dropNames
|
||||
? Data::ForwardOptions::NoSenderNames
|
||||
: Data::ForwardOptions::PreserveInfo;
|
||||
}
|
||||
[[nodiscard]] Ui::ForwardOptions forwardOptions() const {
|
||||
return _forwardOptions;
|
||||
}
|
||||
void setForwardOptions(Ui::ForwardOptions forwardOptions) {
|
||||
_forwardOptions = forwardOptions;
|
||||
}
|
||||
|
||||
private:
|
||||
rpl::event_stream<> _focusRequests;
|
||||
Ui::ForwardOptions _forwardOptions;
|
||||
|
||||
};
|
||||
return ShowChooseRecipientBox(
|
||||
navigation,
|
||||
std::move(chosen),
|
||||
nullptr,
|
||||
std::move(successCallback));
|
||||
|
||||
class Controller final : public ChooseRecipientBoxController {
|
||||
public:
|
||||
using Chosen = not_null<Data::Thread*>;
|
||||
|
||||
Controller(not_null<Main::Session*> session)
|
||||
: ChooseRecipientBoxController(
|
||||
session,
|
||||
[=](Chosen thread) mutable { _singleChosen.fire_copy(thread); },
|
||||
nullptr) {
|
||||
}
|
||||
|
||||
void rowClicked(not_null<PeerListRow*> row) override final {
|
||||
const auto count = delegate()->peerListSelectedRowsCount();
|
||||
if (count && row->peer()->isForum()) {
|
||||
return;
|
||||
} else if (!count || row->peer()->isForum()) {
|
||||
ChooseRecipientBoxController::rowClicked(row);
|
||||
} else if (count) {
|
||||
delegate()->peerListSetRowChecked(row, !row->checked());
|
||||
_hasSelectedChanges.fire(
|
||||
delegate()->peerListSelectedRowsCount() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
base::unique_qptr<Ui::PopupMenu> rowContextMenu(
|
||||
QWidget *parent,
|
||||
not_null<PeerListRow*> row) override final {
|
||||
const auto count = delegate()->peerListSelectedRowsCount();
|
||||
if (!count && !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, !row->checked());
|
||||
_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;
|
||||
|
||||
};
|
||||
|
||||
const auto session = &navigation->session();
|
||||
struct State {
|
||||
not_null<ListBox*> box;
|
||||
not_null<Controller*> controller;
|
||||
base::unique_qptr<Ui::PopupMenu> menu;
|
||||
};
|
||||
const auto state = [&] {
|
||||
auto controller = std::make_unique<Controller>(session);
|
||||
const auto controllerRaw = controller.get();
|
||||
auto box = Box<ListBox>(std::move(controller), nullptr);
|
||||
const auto boxRaw = box.data();
|
||||
navigation->parentController()->show(
|
||||
std::move(box),
|
||||
Ui::LayerOption::KeepOther);
|
||||
auto state = State{ boxRaw, controllerRaw };
|
||||
return boxRaw->lifetime().make_state<State>(std::move(state));
|
||||
}();
|
||||
|
||||
{ // Chosen a single.
|
||||
auto chosen = [navigation, draft = std::move(draft)](
|
||||
not_null<Data::Thread*> thread) mutable {
|
||||
const auto content = navigation->parentController()->content();
|
||||
const auto peer = thread->peer();
|
||||
if (peer->isSelf()
|
||||
&& !draft.ids.empty()
|
||||
&& draft.ids.front().peer != peer->id) {
|
||||
ForwardToSelf(navigation, draft);
|
||||
return true;
|
||||
}
|
||||
return content->setForwardDraft(thread, std::move(draft));
|
||||
};
|
||||
auto callback = [=, chosen = std::move(chosen)](
|
||||
Controller::Chosen thread) mutable {
|
||||
const auto weak = Ui::MakeWeak(state->box);
|
||||
if (!chosen(thread)) {
|
||||
return;
|
||||
} else if (const auto strong = weak.get()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
if (successCallback) {
|
||||
successCallback();
|
||||
}
|
||||
};
|
||||
state->controller->singleChosen(
|
||||
) | rpl::start_with_next(std::move(callback), state->box->lifetime());
|
||||
}
|
||||
|
||||
const auto comment = Ui::CreateChild<Ui::SlideWrap<Ui::InputField>>(
|
||||
state->box.get(),
|
||||
object_ptr<Ui::InputField>(
|
||||
state->box,
|
||||
st::shareComment,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
tr::lng_photos_comment()),
|
||||
st::shareCommentPadding);
|
||||
|
||||
const auto send = ShareBox::DefaultForwardCallback(
|
||||
std::make_shared<Window::Show>(navigation),
|
||||
session->data().message(msgIds.front())->history(),
|
||||
msgIds);
|
||||
|
||||
const auto submit = [=](Api::SendOptions options) {
|
||||
const auto peers = state->box->collectSelectedRows();
|
||||
send(
|
||||
ranges::views::all(
|
||||
peers
|
||||
) | ranges::views::transform([&](
|
||||
not_null<PeerData*> peer) -> Controller::Chosen {
|
||||
return peer->owner().history(peer);
|
||||
}) | ranges::to_vector,
|
||||
comment->entity()->getTextWithAppliedMarkdown(),
|
||||
options,
|
||||
state->box->forwardOptionsData());
|
||||
if (successCallback) {
|
||||
successCallback();
|
||||
}
|
||||
};
|
||||
|
||||
const auto sendMenuType = [=] {
|
||||
const auto selected = state->box->collectSelectedRows();
|
||||
return ranges::all_of(selected, HistoryView::CanScheduleUntilOnline)
|
||||
? SendMenu::Type::ScheduledToUser
|
||||
: ((selected.size() == 1) && selected.front()->isSelf())
|
||||
? SendMenu::Type::Reminder
|
||||
: SendMenu::Type::Scheduled;
|
||||
};
|
||||
|
||||
const auto showForwardOptions = true;
|
||||
const auto showMenu = [=](not_null<Ui::RpWidget*> parent) {
|
||||
if (state->menu) {
|
||||
state->menu = nullptr;
|
||||
return;
|
||||
}
|
||||
state->menu.emplace(parent, st::popupMenuWithIcons);
|
||||
|
||||
if (showForwardOptions) {
|
||||
auto createView = [&](
|
||||
rpl::producer<QString> &&text,
|
||||
bool checked) {
|
||||
auto item = base::make_unique_q<Menu::ItemWithCheck>(
|
||||
state->menu->menu(),
|
||||
st::popupMenuWithIcons.menu,
|
||||
Ui::CreateChild<QAction>(state->menu->menu().get()),
|
||||
nullptr,
|
||||
nullptr);
|
||||
std::move(
|
||||
text
|
||||
) | rpl::start_with_next([action = item->action()](
|
||||
QString text) {
|
||||
action->setText(text);
|
||||
}, item->lifetime());
|
||||
item->init(checked);
|
||||
const auto view = item->checkView();
|
||||
state->menu->addAction(std::move(item));
|
||||
return view;
|
||||
};
|
||||
Ui::FillForwardOptions(
|
||||
std::move(createView),
|
||||
msgIds.size(),
|
||||
state->box->forwardOptions(),
|
||||
[=](Ui::ForwardOptions o) {
|
||||
state->box->setForwardOptions(o);
|
||||
},
|
||||
state->menu->lifetime());
|
||||
|
||||
state->menu->addSeparator();
|
||||
}
|
||||
const auto type = sendMenuType();
|
||||
const auto result = SendMenu::FillSendMenu(
|
||||
state->menu.get(),
|
||||
type,
|
||||
SendMenu::DefaultSilentCallback(submit),
|
||||
SendMenu::DefaultScheduleCallback(state->box, type, submit));
|
||||
const auto success = (result == SendMenu::FillMenuResult::Success);
|
||||
if (showForwardOptions || success) {
|
||||
state->menu->setForcedVerticalOrigin(
|
||||
Ui::PopupMenu::VerticalOrigin::Bottom);
|
||||
state->menu->popup(QCursor::pos());
|
||||
}
|
||||
};
|
||||
|
||||
comment->hide(anim::type::instant);
|
||||
comment->toggleOn(state->controller->hasSelectedChanges());
|
||||
|
||||
rpl::combine(
|
||||
state->box->sizeValue(),
|
||||
comment->heightValue()
|
||||
) | rpl::start_with_next([=](const QSize &size, int commentHeight) {
|
||||
comment->moveToLeft(0, size.height() - commentHeight);
|
||||
comment->resizeToWidth(size.width());
|
||||
|
||||
state->box->setBottomSkip(comment->isHidden() ? 0 : commentHeight);
|
||||
}, comment->lifetime());
|
||||
|
||||
const auto field = comment->entity();
|
||||
|
||||
QObject::connect(field, &Ui::InputField::submitted, [=] {
|
||||
submit({});
|
||||
});
|
||||
const auto show = std::make_shared<Ui::BoxShow>(state->box);
|
||||
if (show->valid()) {
|
||||
InitMessageFieldHandlers(session, show, field, nullptr);
|
||||
}
|
||||
field->setSubmitSettings(Core::App().settings().sendSubmitWay());
|
||||
|
||||
Ui::SendPendingMoveResizeEvents(comment);
|
||||
|
||||
state->box->focusRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
if (!comment->isHidden()) {
|
||||
comment->entity()->setFocusFast();
|
||||
}
|
||||
}, comment->lifetime());
|
||||
|
||||
state->controller->hasSelectedChanges(
|
||||
) | rpl::start_with_next([=](bool shown) {
|
||||
state->box->clearButtons();
|
||||
if (shown) {
|
||||
const auto send = state->box->addButton(
|
||||
tr::lng_send_button(),
|
||||
[=] { submit({}); });
|
||||
send->setAcceptBoth();
|
||||
send->clicks(
|
||||
) | rpl::start_with_next([=](Qt::MouseButton button) {
|
||||
if (button == Qt::RightButton) {
|
||||
showMenu(send);
|
||||
}
|
||||
}, send->lifetime());
|
||||
}
|
||||
state->box->addButton(tr::lng_cancel(), [=] {
|
||||
state->box->closeBox();
|
||||
});
|
||||
}, state->box->lifetime());
|
||||
|
||||
return QPointer<Ui::BoxContent>(state->box);
|
||||
}
|
||||
|
||||
QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
MessageIdsList &&items,
|
||||
FnMut<void()> &&successCallback) {
|
||||
Fn<void()> &&successCallback) {
|
||||
return ShowForwardMessagesBox(
|
||||
navigation,
|
||||
Data::ForwardDraft{ .ids = std::move(items) },
|
||||
|
|
|
@ -122,11 +122,11 @@ QPointer<Ui::BoxContent> ShowChooseRecipientBox(
|
|||
QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
Data::ForwardDraft &&draft,
|
||||
FnMut<void()> &&successCallback = nullptr);
|
||||
Fn<void()> &&successCallback = nullptr);
|
||||
QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
MessageIdsList &&items,
|
||||
FnMut<void()> &&successCallback = nullptr);
|
||||
Fn<void()> &&successCallback = nullptr);
|
||||
QPointer<Ui::BoxContent> ShowShareUrlBox(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
const QString &url,
|
||||
|
|
Loading…
Add table
Reference in a new issue