diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index 2f49c1ea6..d5590e840 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -1116,7 +1116,8 @@ void ShareInviteLinkBox(not_null peer, const QString &link) { auto submitCallback = [=]( std::vector> &&result, TextWithTags &&comment, - Api::SendOptions options) { + Api::SendOptions options, + Data::ForwardOptions) { if (*sending || result.empty()) { return; } diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp index 4ea2bffe7..0a5827971 100644 --- a/Telegram/SourceFiles/boxes/share_box.cpp +++ b/Telegram/SourceFiles/boxes/share_box.cpp @@ -15,11 +15,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/storage_account.h" #include "ui/boxes/confirm_box.h" #include "apiwrap.h" +#include "ui/chat/forward_options_box.h" #include "ui/toast/toast.h" +#include "ui/widgets/checkbox.h" #include "ui/widgets/multi_select.h" #include "ui/widgets/buttons.h" #include "ui/widgets/scroll_area.h" #include "ui/widgets/input_fields.h" +#include "ui/widgets/menu/menu_action.h" +#include "ui/widgets/popup_menu.h" #include "ui/wrap/slide_wrap.h" #include "ui/text/text_options.h" #include "chat_helpers/message_field.h" @@ -40,6 +44,44 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_chat.h" +#include "styles/style_menu_icons.h" +#include "styles/style_media_player.h" + +namespace { + +class ForwardOptionItem final : public Ui::Menu::Action { +public: + using Ui::Menu::Action::Action; + + void init(bool checked) { + enableMouseSelecting(); + + AbstractButton::setDisabled(true); + + _checkView = std::make_unique(st::defaultCheck, false); + _checkView->checkedChanges( + ) | rpl::start_with_next([=](bool checked) { + setIcon(checked ? &st::mediaPlayerMenuCheck : nullptr); + }, lifetime()); + + _checkView->setChecked(checked, anim::type::normal); + AbstractButton::clicks( + ) | rpl::start_with_next([=] { + _checkView->setChecked( + !_checkView->checked(), + anim::type::normal); + }, lifetime()); + } + + not_null checkView() const { + return _checkView.get(); + } + +private: + std::unique_ptr _checkView; +}; + +} // namespace class ShareBox::Inner final : public Ui::RpWidget { public: @@ -442,17 +484,68 @@ SendMenu::Type ShareBox::sendMenuType() const { : SendMenu::Type::Scheduled; } +void ShareBox::showMenu(not_null parent) { + if (_menu) { + _menu = nullptr; + return; + } + _menu.emplace(parent, st::popupMenuWithIcons); + + if (_descriptor.forwardOptions.show) { + auto createView = [&](rpl::producer &&text, bool checked) { + auto item = base::make_unique_q( + _menu->menu(), + st::popupMenuWithIcons.menu, + new QAction(QString(), _menu->menu()), + 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(); + _menu->addAction(std::move(item)); + return view; + }; + Ui::FillForwardOptions( + std::move(createView), + _descriptor.forwardOptions.messagesCount, + _forwardOptions, + [=](Ui::ForwardOptions value) { _forwardOptions = value; }, + _menu->lifetime()); + + _menu->addSeparator(); + } + + const auto result = SendMenu::FillSendMenu( + _menu.get(), + sendMenuType(), + [=] { submitSilent(); }, + [=] { submitScheduled(); }); + const auto success = (result == SendMenu::FillMenuResult::Success); + if (_descriptor.forwardOptions.show || success) { + _menu->setForcedOrigin(Ui::PanelAnimation::Origin::BottomRight); + _menu->popup(QCursor::pos()); + } +} + void ShareBox::createButtons() { clearButtons(); if (_hasSelected) { const auto send = addButton(tr::lng_share_confirm(), [=] { submit({}); }); - SendMenu::SetupMenuAndShortcuts( - send, - [=] { return sendMenuType(); }, - [=] { submitSilent(); }, - [=] { submitScheduled(); }); + _forwardOptions.hasCaptions = _descriptor.forwardOptions.hasCaptions; + + send->setAcceptBoth(); + send->clicks( + ) | rpl::start_with_next([=](Qt::MouseButton button) { + if (button == Qt::RightButton) { + showMenu(send); + } + }, send->lifetime()); } else if (_descriptor.copyCallback) { addButton(_copyLinkText.value(), [=] { copyLink(); }); } @@ -488,10 +581,17 @@ void ShareBox::innerSelectedChanged(PeerData *peer, bool checked) { void ShareBox::submit(Api::SendOptions options) { if (const auto onstack = _descriptor.submitCallback) { + const auto forwardOptions = (_forwardOptions.hasCaptions + && _forwardOptions.dropCaptions) + ? Data::ForwardOptions::NoNamesAndCaptions + : _forwardOptions.dropNames + ? Data::ForwardOptions::NoSenderNames + : Data::ForwardOptions::PreserveInfo; onstack( _inner->selected(), _comment->entity()->getTextWithAppliedMarkdown(), - options); + options, + forwardOptions); } } diff --git a/Telegram/SourceFiles/boxes/share_box.h b/Telegram/SourceFiles/boxes/share_box.h index fc262b2cc..83a360964 100644 --- a/Telegram/SourceFiles/boxes/share_box.h +++ b/Telegram/SourceFiles/boxes/share_box.h @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/abstract_box.h" #include "base/observer.h" #include "base/timer.h" +#include "ui/chat/forward_options_box.h" #include "ui/effects/animations.h" #include "ui/effects/round_checkbox.h" #include "mtproto/sender.h" @@ -41,12 +42,17 @@ class Row; class IndexedList; } // namespace Dialogs +namespace Data { +enum class ForwardOptions; +} // namespace Data + namespace Ui { class MultiSelect; class InputField; struct ScrollToRequest; template class SlideWrap; +class PopupMenu; } // namespace Ui QString AppendShareGameScoreUrl( @@ -63,7 +69,8 @@ public: using SubmitCallback = Fn>&&, TextWithTags&&, - Api::SendOptions)>; + Api::SendOptions, + Data::ForwardOptions option)>; using FilterCallback = Fn; struct Descriptor { @@ -79,6 +86,11 @@ public: const style::MultiSelect *stMultiSelect = nullptr; const style::InputField *stComment = nullptr; const style::PeerList *st = nullptr; + struct { + int messagesCount = 0; + bool show = false; + bool hasCaptions = false; + } forwardOptions; }; ShareBox(QWidget*, Descriptor &&descriptor); @@ -119,6 +131,8 @@ private: mtpRequestId requestId); void peopleFail(const MTP::Error &error, mtpRequestId requestId); + void showMenu(not_null parent); + Descriptor _descriptor; MTP::Sender _api; @@ -126,6 +140,9 @@ private: object_ptr> _comment; object_ptr _bottomWidget; + base::unique_qptr _menu; + Ui::ForwardOptions _forwardOptions; + class Inner; QPointer _inner; diff --git a/Telegram/SourceFiles/calls/group/calls_group_settings.cpp b/Telegram/SourceFiles/calls/group/calls_group_settings.cpp index a7a011b6c..df09d59ca 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_settings.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_settings.cpp @@ -129,7 +129,8 @@ object_ptr ShareInviteLinkBox( auto submitCallback = [=]( std::vector> &&result, TextWithTags &&comment, - Api::SendOptions options) { + Api::SendOptions options, + Data::ForwardOptions) { if (*sending || result.empty()) { return; } diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 818794cd5..f741da5e6 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -209,6 +209,18 @@ void FastShareMessage(not_null item) { && (item->media()->game() != nullptr); const auto canCopyLink = item->hasDirectLink() || isGame; + const auto items = owner->idsToItems(data->msgIds); + const auto hasCaptions = ranges::any_of(items, [](auto item) { + return item->media() + && !item->originalText().text.isEmpty() + && item->media()->allowsEditCaption(); + }); + const auto hasOnlyForcedForwardedInfo = hasCaptions + ? false + : ranges::all_of(items, [](auto item) { + return item->media() && item->media()->forceForwardedInfo(); + }); + auto copyCallback = [=]() { if (const auto item = owner->message(data->msgIds[0])) { if (item->hasDirectLink()) { @@ -235,7 +247,8 @@ void FastShareMessage(not_null item) { auto submitCallback = [=]( std::vector> &&result, TextWithTags &&comment, - Api::SendOptions options) { + Api::SendOptions options, + Data::ForwardOptions forwardOptions) { if (!data->requests.empty()) { return; // Share clicked already. } @@ -274,6 +287,12 @@ void FastShareMessage(not_null item) { | MTPmessages_ForwardMessages::Flag::f_with_my_score | (options.scheduled ? MTPmessages_ForwardMessages::Flag::f_schedule_date + : MTPmessages_ForwardMessages::Flag(0)) + | ((forwardOptions != Data::ForwardOptions::PreserveInfo) + ? MTPmessages_ForwardMessages::Flag::f_drop_author + : MTPmessages_ForwardMessages::Flag(0)) + | ((forwardOptions == Data::ForwardOptions::NoNamesAndCaptions) + ? MTPmessages_ForwardMessages::Flag::f_drop_media_captions : MTPmessages_ForwardMessages::Flag(0)); auto msgIds = QVector(); msgIds.reserve(data->msgIds.size()); @@ -346,7 +365,13 @@ void FastShareMessage(not_null item) { .copyCallback = std::move(copyLinkCallback), .submitCallback = std::move(submitCallback), .filterCallback = std::move(filterCallback), - .navigation = App::wnd()->sessionController() })); + .navigation = App::wnd()->sessionController(), + .forwardOptions = { + .messagesCount = int(data->msgIds.size()), + .show = !hasOnlyForcedForwardedInfo, + .hasCaptions = hasCaptions, + }, + })); } void RequestDependentMessageData( diff --git a/Telegram/SourceFiles/ui/chat/forward_options_box.cpp b/Telegram/SourceFiles/ui/chat/forward_options_box.cpp index ee1f668d4..c619ee719 100644 --- a/Telegram/SourceFiles/ui/chat/forward_options_box.cpp +++ b/Telegram/SourceFiles/ui/chat/forward_options_box.cpp @@ -15,6 +15,56 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { +void FillForwardOptions( + Fn( + rpl::producer &&, + bool)> createView, + int count, + ForwardOptions options, + Fn optionsChanged, + rpl::lifetime &lifetime) { + Expects(optionsChanged != nullptr); + + const auto names = createView( + (count == 1 + ? tr::lng_forward_show_sender + : tr::lng_forward_show_senders)(), + !options.dropNames); + const auto captions = options.hasCaptions + ? createView( + (count == 1 + ? tr::lng_forward_show_caption + : tr::lng_forward_show_captions)(), + !options.dropCaptions).get() + : nullptr; + + const auto notify = [=] { + optionsChanged({ + .dropNames = !names->checked(), + .hasCaptions = options.hasCaptions, + .dropCaptions = (captions && !captions->checked()), + }); + }; + names->checkedChanges( + ) | rpl::start_with_next([=](bool showNames) { + if (showNames && captions && !captions->checked()) { + captions->setChecked(true, anim::type::normal); + } else { + notify(); + } + }, lifetime); + if (captions) { + captions->checkedChanges( + ) | rpl::start_with_next([=](bool showCaptions) { + if (!showCaptions && names->checked()) { + names->setChecked(false, anim::type::normal); + } else { + notify(); + } + }, lifetime); + } +} + void ForwardOptionsBox( not_null box, int count, @@ -45,51 +95,23 @@ void ForwardOptionsBox( st::boxRowPadding.left(), st::boxRowPadding.right(), st::boxRowPadding.bottom()); - const auto names = box->addRow( - object_ptr( - box.get(), - (count == 1 - ? tr::lng_forward_show_sender - : tr::lng_forward_show_senders)(), - !options.dropNames, - st::defaultBoxCheckbox), - checkboxPadding); - const auto captions = options.hasCaptions - ? box->addRow( + + auto createView = [&](rpl::producer &&text, bool checked) { + return box->addRow( object_ptr( box.get(), - (count == 1 - ? tr::lng_forward_show_caption - : tr::lng_forward_show_captions)(), - !options.dropCaptions, + std::move(text), + checked, st::defaultBoxCheckbox), - checkboxPadding) - : nullptr; - const auto notify = [=] { - optionsChanged({ - .dropNames = !names->checked(), - .hasCaptions = options.hasCaptions, - .dropCaptions = (captions && !captions->checked()), - }); + checkboxPadding)->checkView(); }; - names->checkedChanges( - ) | rpl::start_with_next([=](bool showNames) { - if (showNames && captions && !captions->checked()) { - captions->setChecked(true); - } else { - notify(); - } - }, names->lifetime()); - if (captions) { - captions->checkedChanges( - ) | rpl::start_with_next([=](bool showCaptions) { - if (!showCaptions && names->checked()) { - names->setChecked(false); - } else { - notify(); - } - }, captions->lifetime()); - } + FillForwardOptions( + std::move(createView), + count, + options, + std::move(optionsChanged), + box->lifetime()); + box->addRow( object_ptr( box.get(), diff --git a/Telegram/SourceFiles/ui/chat/forward_options_box.h b/Telegram/SourceFiles/ui/chat/forward_options_box.h index 521a8fdfe..057c3f348 100644 --- a/Telegram/SourceFiles/ui/chat/forward_options_box.h +++ b/Telegram/SourceFiles/ui/chat/forward_options_box.h @@ -11,12 +11,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { +class AbstractCheckView; + struct ForwardOptions { bool dropNames = false; bool hasCaptions = false; bool dropCaptions = false; }; +void FillForwardOptions( + Fn( + rpl::producer &&, + bool)> createView, + int count, + ForwardOptions options, + Fn optionsChanged, + rpl::lifetime &lifetime); + void ForwardOptionsBox( not_null box, int count,