From 0eeeb18ecd953e4133bb653be8c85488c39e1d29 Mon Sep 17 00:00:00 2001 From: RadRussianRus Date: Wed, 15 Jul 2020 16:47:26 +0300 Subject: [PATCH] Forward options Fixes #66. --- Telegram/Resources/langs/lang.strings | 14 + Telegram/Resources/langs/rewrites/en.json | 10 + Telegram/Resources/langs/rewrites/it.json | 10 + Telegram/Resources/langs/rewrites/pl.json | 10 + Telegram/Resources/langs/rewrites/pt-br.json | 10 + Telegram/Resources/langs/rewrites/ru.json | 10 + Telegram/Resources/langs/rewrites/tr.json | 10 + Telegram/Resources/langs/rewrites/uk.json | 10 + Telegram/SourceFiles/apiwrap.cpp | 358 +++++++++++++++++- Telegram/SourceFiles/boxes/share_box.cpp | 113 +++++- Telegram/SourceFiles/boxes/share_box.h | 10 +- .../SourceFiles/data/data_media_types.cpp | 8 + Telegram/SourceFiles/data/data_media_types.h | 2 + .../SourceFiles/history/history_widget.cpp | 116 +++++- Telegram/SourceFiles/history/history_widget.h | 3 + Telegram/SourceFiles/kotato/settings.cpp | 5 + Telegram/SourceFiles/kotato/settings.h | 5 + .../SourceFiles/window/window_peer_menu.cpp | 339 +++++++++++++++-- 18 files changed, 988 insertions(+), 55 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 3e41ce07e..d7e287bd5 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2620,4 +2620,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "ktg_settings_forward_chat_on_click" = "Open chat on click"; "ktg_settings_forward_chat_on_click_description" = "You can hold Ctrl to select multiple chats regardless of this option."; +"ktg_forward_menu_quoted" = "Quoted"; +"ktg_forward_menu_unquoted" = "Unquoted with captions"; +"ktg_forward_menu_uncaptioned" = "Unquoted without captions"; + +"ktg_forward_menu_default_albums" = "Preserve albums"; +"ktg_forward_menu_group_all_media" = "Group all media"; +"ktg_forward_menu_separate_messages" = "Separate messages"; + +"ktg_forward_subtitle_unquoted" = "unquoted"; +"ktg_forward_subtitle_uncaptioned" = "uncaptioned"; + +"ktg_forward_subtitle_group_all_media" = "as albums"; +"ktg_forward_subtitle_separate_messages" = "one by one"; + // Keys finished diff --git a/Telegram/Resources/langs/rewrites/en.json b/Telegram/Resources/langs/rewrites/en.json index 8ceee98e8..04a57fa29 100644 --- a/Telegram/Resources/langs/rewrites/en.json +++ b/Telegram/Resources/langs/rewrites/en.json @@ -173,6 +173,16 @@ "ktg_settings_forward_chat_on_click_description": "You can hold Ctrl to select multiple chats regardless of this option.", // New strings + "ktg_forward_menu_quoted": "Quoted", + "ktg_forward_menu_unquoted": "Unquoted with captions", + "ktg_forward_menu_uncaptioned": "Unquoted without captions", + "ktg_forward_menu_default_albums": "Preserve albums", + "ktg_forward_menu_group_all_media": "Group all media", + "ktg_forward_menu_separate_messages": "Separate messages", + "ktg_forward_subtitle_unquoted": "unquoted", + "ktg_forward_subtitle_uncaptioned": "uncaptioned", + "ktg_forward_subtitle_group_all_media": "as albums", + "ktg_forward_subtitle_separate_messages": "one by one", // This string should always be last for better work with Git. "dummy_last_string": "" diff --git a/Telegram/Resources/langs/rewrites/it.json b/Telegram/Resources/langs/rewrites/it.json index 91c0ee9f8..9aad95ed0 100644 --- a/Telegram/Resources/langs/rewrites/it.json +++ b/Telegram/Resources/langs/rewrites/it.json @@ -173,6 +173,16 @@ "ktg_settings_forward_chat_on_click_description": "Puoi tenere premuto Ctrl per selezionare più chat insieme indipendentemente da questa opzione.", // New strings + "ktg_forward_menu_quoted": "Quoted", + "ktg_forward_menu_unquoted": "Unquoted with captions", + "ktg_forward_menu_uncaptioned": "Unquoted without captions", + "ktg_forward_menu_default_albums": "Preserve albums", + "ktg_forward_menu_group_all_media": "Group all media", + "ktg_forward_menu_separate_messages": "Separate messages", + "ktg_forward_subtitle_unquoted": "unquoted", + "ktg_forward_subtitle_uncaptioned": "uncaptioned", + "ktg_forward_subtitle_group_all_media": "as albums", + "ktg_forward_subtitle_separate_messages": "one by one", // This string should always be last for better work with Git. "dummy_last_string": "" diff --git a/Telegram/Resources/langs/rewrites/pl.json b/Telegram/Resources/langs/rewrites/pl.json index eddbfd7c3..75536cbca 100644 --- a/Telegram/Resources/langs/rewrites/pl.json +++ b/Telegram/Resources/langs/rewrites/pl.json @@ -181,6 +181,16 @@ "ktg_settings_forward_chat_on_click_description": "You can hold Ctrl to select multiple chats regardless of this option.", // New strings + "ktg_forward_menu_quoted": "Quoted", + "ktg_forward_menu_unquoted": "Unquoted with captions", + "ktg_forward_menu_uncaptioned": "Unquoted without captions", + "ktg_forward_menu_default_albums": "Preserve albums", + "ktg_forward_menu_group_all_media": "Group all media", + "ktg_forward_menu_separate_messages": "Separate messages", + "ktg_forward_subtitle_unquoted": "unquoted", + "ktg_forward_subtitle_uncaptioned": "uncaptioned", + "ktg_forward_subtitle_group_all_media": "as albums", + "ktg_forward_subtitle_separate_messages": "one by one", // This string should always be last for better work with Git. "dummy_last_string": "" diff --git a/Telegram/Resources/langs/rewrites/pt-br.json b/Telegram/Resources/langs/rewrites/pt-br.json index 922f2f8be..23dfd4787 100644 --- a/Telegram/Resources/langs/rewrites/pt-br.json +++ b/Telegram/Resources/langs/rewrites/pt-br.json @@ -173,6 +173,16 @@ "ktg_settings_forward_chat_on_click_description": "Você pode pressionar Ctrl para selecionar vários chats, independentemente desta opção.", // New strings + "ktg_forward_menu_quoted": "Quoted", + "ktg_forward_menu_unquoted": "Unquoted with captions", + "ktg_forward_menu_uncaptioned": "Unquoted without captions", + "ktg_forward_menu_default_albums": "Preserve albums", + "ktg_forward_menu_group_all_media": "Group all media", + "ktg_forward_menu_separate_messages": "Separate messages", + "ktg_forward_subtitle_unquoted": "unquoted", + "ktg_forward_subtitle_uncaptioned": "uncaptioned", + "ktg_forward_subtitle_group_all_media": "as albums", + "ktg_forward_subtitle_separate_messages": "one by one", // This string should always be last for better work with Git. "dummy_last_string": "" diff --git a/Telegram/Resources/langs/rewrites/ru.json b/Telegram/Resources/langs/rewrites/ru.json index 7f7309ea7..517401c53 100644 --- a/Telegram/Resources/langs/rewrites/ru.json +++ b/Telegram/Resources/langs/rewrites/ru.json @@ -180,6 +180,16 @@ "ktg_settings_forward_chat_on_click_description": "Удерживайте Ctrl для выбора нескольких чатов вне зависимости от этой настройки.", // New strings + "ktg_forward_menu_quoted": "С автором", + "ktg_forward_menu_unquoted": "Без автора с подписями", + "ktg_forward_menu_uncaptioned": "Без автора и подписей", + "ktg_forward_menu_default_albums": "Оригинальные альбомы", + "ktg_forward_menu_group_all_media": "Объединить все медиа", + "ktg_forward_menu_separate_messages": "Отдельные сообщения", + "ktg_forward_subtitle_unquoted": "без автора", + "ktg_forward_subtitle_uncaptioned": "без подписей", + "ktg_forward_subtitle_group_all_media": "альбомами", + "ktg_forward_subtitle_separate_messages": "по одному", // This string should always be last for better work with Git. "dummy_last_string": "" diff --git a/Telegram/Resources/langs/rewrites/tr.json b/Telegram/Resources/langs/rewrites/tr.json index 6a05ba3d9..949b4703a 100644 --- a/Telegram/Resources/langs/rewrites/tr.json +++ b/Telegram/Resources/langs/rewrites/tr.json @@ -173,6 +173,16 @@ "ktg_settings_forward_chat_on_click_description": "Ctrl tuşunu uzun tutarak birden fazla sohbet seçmek için, bu seçenek aktifligine rağmen.", // New strings + "ktg_forward_menu_quoted": "Quoted", + "ktg_forward_menu_unquoted": "Unquoted with captions", + "ktg_forward_menu_uncaptioned": "Unquoted without captions", + "ktg_forward_menu_default_albums": "Preserve albums", + "ktg_forward_menu_group_all_media": "Group all media", + "ktg_forward_menu_separate_messages": "Separate messages", + "ktg_forward_subtitle_unquoted": "unquoted", + "ktg_forward_subtitle_uncaptioned": "uncaptioned", + "ktg_forward_subtitle_group_all_media": "as albums", + "ktg_forward_subtitle_separate_messages": "one by one", // This string should always be last for better work with Git. "dummy_last_string": "" diff --git a/Telegram/Resources/langs/rewrites/uk.json b/Telegram/Resources/langs/rewrites/uk.json index d3a2f7a63..da2f820c3 100644 --- a/Telegram/Resources/langs/rewrites/uk.json +++ b/Telegram/Resources/langs/rewrites/uk.json @@ -180,6 +180,16 @@ "ktg_settings_forward_chat_on_click_description": "Ви можете вибрати декілька чатів утримуючи Ctrl незалежно від цього параметра.", // New strings + "ktg_forward_menu_quoted": "Quoted", + "ktg_forward_menu_unquoted": "Unquoted with captions", + "ktg_forward_menu_uncaptioned": "Unquoted without captions", + "ktg_forward_menu_default_albums": "Preserve albums", + "ktg_forward_menu_group_all_media": "Group all media", + "ktg_forward_menu_separate_messages": "Separate messages", + "ktg_forward_subtitle_unquoted": "unquoted", + "ktg_forward_subtitle_uncaptioned": "uncaptioned", + "ktg_forward_subtitle_group_all_media": "as albums", + "ktg_forward_subtitle_separate_messages": "one by one", // This string should always be last for better work with Git. "dummy_last_string": "" diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 7964c4c71..fe50004cb 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -3890,7 +3890,7 @@ void ApiWrap::forwardMessages( } const auto count = int(items.size()); - const auto genClientSideMessage = action.generateLocal && (count < 2); + const auto genClientSideMessage = action.generateLocal && (count < 2) && cForwardQuoted(); const auto history = action.history; const auto peer = history->peer; @@ -3916,18 +3916,47 @@ void ApiWrap::forwardMessages( auto forwardFrom = items.front()->history()->peer; auto currentGroupId = items.front()->groupId(); + auto isLastGrouped = false; auto ids = QVector(); auto randomIds = QVector(); auto localIds = std::shared_ptr>(); + auto fromIter = items.begin(); + auto toIter = items.begin(); - const auto sendAccumulated = [&] { + const auto needNextGroup = [&] (not_null item) { + if (cForwardAlbumsAsIs()) { + const auto newFrom = item->history()->peer; + const auto newGroupId = item->groupId(); + return forwardFrom != newFrom + || currentGroupId != newGroupId; + } else if (cForwardGrouped()) { + if (item->media() && item->media()->canBeGrouped()) { + return !isLastGrouped; + } else { + return isLastGrouped; + } + } else { + return false; + } + }; + + const auto isGrouped = [&] { + return (cForwardAlbumsAsIs() + && currentGroupId != MessageGroupId()) + || (!cForwardAlbumsAsIs() + && cForwardGrouped() + && items.front()->media() + && items.front()->media()->canBeGrouped()); + }; + + const auto forwardQuoted = [&] { if (shared) { ++shared->requestsLeft; } const auto finalFlags = sendFlags - | (currentGroupId == MessageGroupId() - ? MTPmessages_ForwardMessages::Flag(0) - : MTPmessages_ForwardMessages::Flag::f_grouped); + | (isGrouped() + ? MTPmessages_ForwardMessages::Flag::f_grouped + : MTPmessages_ForwardMessages::Flag(0)); const auto requestType = Data::Histories::RequestType::Send; histories.sendRequest(history, requestType, [=](Fn finish) { history->sendRequestId = request(MTPmessages_ForwardMessages( @@ -3957,6 +3986,308 @@ void ApiWrap::forwardMessages( ).send(); return history->sendRequestId; }); + }; + + const auto forwardQuotedSingle = [&] (not_null item) { + if (shared) { + ++shared->requestsLeft; + } + + auto currentIds = QVector(); + currentIds.push_back(MTP_int(item->id)); + + auto currentRandomId = randomIds.takeFirst(); + auto currentRandomIds = QVector(); + currentRandomIds.push_back(currentRandomId); + + const auto requestType = Data::Histories::RequestType::Send; + histories.sendRequest(history, requestType, [=](Fn finish) { + history->sendRequestId = request(MTPmessages_ForwardMessages( + MTP_flags(sendFlags), + forwardFrom->input, + MTP_vector(currentIds), + MTP_vector(currentRandomIds), + peer->input, + MTP_int(action.options.scheduled) + )).done([=](const MTPUpdates &result) { + applyUpdates(result); + if (shared && !--shared->requestsLeft) { + shared->callback(); + } + finish(); + }).fail([=, ids = localIds](const RPCError &error) { + auto found = false; + for (const auto &[randomId, itemId] : *ids) { + if (currentRandomId == MTP_long(randomId)) { + sendMessageFail(error, peer, randomId, itemId); + found = true; + break; + } + } + if (!found) { + sendMessageFail(error, peer); + } + finish(); + }).afterRequest( + history->sendRequestId + ).send(); + return history->sendRequestId; + }); + }; + + const auto forwardAlbumUnquoted = [&] { + if (shared) { + ++shared->requestsLeft; + } + auto medias = QVector(); + medias.reserve(ids.size()); + + for (auto i = fromIter, e = toIter; i != e; i++) { + const auto item = *i; + const auto media = item->media(); + auto inputMedia = media->photo() + ? MTP_inputMediaPhoto(MTP_flags(0), media->photo()->mtpInput(), MTPint()) + : MTP_inputMediaDocument(MTP_flags(0), media->document()->mtpInput(), MTPint()); + auto caption = cForwardCaptioned() + ? item->originalText() + : TextWithEntities(); + auto sentEntities = Api::EntitiesToMTP( + _session, + caption.entities, + Api::ConvertOption::SkipLocal); + + const auto flags = !sentEntities.v.isEmpty() + ? MTPDinputSingleMedia::Flag::f_entities + : MTPDinputSingleMedia::Flag(0); + + auto randomId = randomIds.takeFirst(); + + medias.push_back(MTP_inputSingleMedia( + MTP_flags(flags), + inputMedia, + randomId, + MTP_string(caption.text), + sentEntities)); + } + + const auto finalFlags = MTPmessages_SendMultiMedia::Flags(0) + | (action.options.silent + ? MTPmessages_SendMultiMedia::Flag::f_silent + : MTPmessages_SendMultiMedia::Flag(0)) + | (action.options.scheduled + ? MTPmessages_SendMultiMedia::Flag::f_schedule_date + : MTPmessages_SendMultiMedia::Flag(0)); + + const auto requestType = Data::Histories::RequestType::Send; + histories.sendRequest(history, requestType, [=](Fn finish) { + history->sendRequestId = request(MTPmessages_SendMultiMedia( + MTP_flags(finalFlags), + peer->input, + MTPint(), + MTP_vector(medias), + MTP_int(action.options.scheduled) + )).done([=](const MTPUpdates &result) { + applyUpdates(result); + if (shared && !--shared->requestsLeft) { + shared->callback(); + } + finish(); + }).fail([=, ids = localIds](const RPCError &error) { + if (ids) { + for (const auto &[randomId, itemId] : *ids) { + sendMessageFail(error, peer, randomId, itemId); + } + } else { + sendMessageFail(error, peer); + } + finish(); + }).afterRequest( + history->sendRequestId + ).send(); + return history->sendRequestId; + }); + }; + + const auto forwardMediaUnquoted = [&] (not_null item) { + if (shared) { + ++shared->requestsLeft; + } + const auto media = item->media(); + + auto newSendFlags = MTPmessages_SendMedia::Flags(0) + | (action.options.silent + ? MTPmessages_SendMedia::Flag::f_silent + : MTPmessages_SendMedia::Flag(0)) + | (action.options.scheduled + ? MTPmessages_SendMedia::Flag::f_schedule_date + : MTPmessages_SendMedia::Flag(0)); + + auto caption = (cForwardCaptioned() + && !media->geoPoint() + && !media->sharedContact()) + ? item->originalText() + : TextWithEntities(); + + auto sentEntities = Api::EntitiesToMTP( + _session, + caption.entities, + Api::ConvertOption::SkipLocal); + if (!sentEntities.v.isEmpty()) { + newSendFlags |= MTPmessages_SendMedia::Flag::f_entities; + } + + auto inputMedia = media->poll() + ? PollDataToInputMedia(media->poll()) + : media->geoPoint() + ? MTP_inputMediaGeoPoint( + MTP_inputGeoPoint( + MTP_double(media->geoPoint()->lat()), + MTP_double(media->geoPoint()->lon()))) + : media->sharedContact() + ? MTP_inputMediaContact( + MTP_string(media->sharedContact()->phoneNumber), + MTP_string(media->sharedContact()->firstName), + MTP_string(media->sharedContact()->lastName), + MTPstring()) + : media->photo() + ? MTP_inputMediaPhoto(MTP_flags(0), media->photo()->mtpInput(), MTPint()) + : MTP_inputMediaDocument(MTP_flags(0), media->document()->mtpInput(), MTPint()); + + const auto requestType = Data::Histories::RequestType::Send; + const auto currentRandomId = randomIds.takeFirst(); + histories.sendRequest(history, requestType, [=](Fn finish) { + history->sendRequestId = request(MTPmessages_SendMedia( + MTP_flags(newSendFlags), + peer->input, + MTPint(), + inputMedia, + MTP_string(caption.text), + currentRandomId, + MTPReplyMarkup(), + sentEntities, + MTP_int(action.options.scheduled) + )).done([=](const MTPUpdates &result) { + applyUpdates(result); + if (shared && !--shared->requestsLeft) { + shared->callback(); + } + finish(); + }).fail([=, ids = localIds](const RPCError &error) { + if (ids) { + auto found = false; + for (const auto &[randomId, itemId] : *ids) { + if (currentRandomId == MTP_long(randomId)) { + sendMessageFail(error, peer, randomId, itemId); + found = true; + break; + } + } + if (!found) { + sendMessageFail(error, peer); + } + } else { + sendMessageFail(error, peer); + } + finish(); + }).afterRequest( + history->sendRequestId + ).send(); + return history->sendRequestId; + }); + }; + + const auto forwardMessageUnquoted = [&] (not_null item) { + if (shared) { + ++shared->requestsLeft; + } + const auto media = item->media(); + + auto newSendFlags = MTPmessages_SendMessage::Flag(0) + | (!media || !media->webpage() + ? MTPmessages_SendMessage::Flag::f_no_webpage + : MTPmessages_SendMessage::Flag(0)) + | (action.options.silent + ? MTPmessages_SendMessage::Flag::f_silent + : MTPmessages_SendMessage::Flag(0)) + | (action.options.scheduled + ? MTPmessages_SendMessage::Flag::f_schedule_date + : MTPmessages_SendMessage::Flag(0)); + + auto sentEntities = Api::EntitiesToMTP( + _session, + item->originalText().entities, + Api::ConvertOption::SkipLocal); + if (!sentEntities.v.isEmpty()) { + newSendFlags |= MTPmessages_SendMessage::Flag::f_entities; + } + + const auto requestType = Data::Histories::RequestType::Send; + const auto currentRandomId = randomIds.takeFirst(); + histories.sendRequest(history, requestType, [=](Fn finish) { + history->sendRequestId = request(MTPmessages_SendMessage( + MTP_flags(newSendFlags), + peer->input, + MTPint(), + MTP_string(item->originalText().text), + currentRandomId, + MTPReplyMarkup(), + sentEntities, + MTP_int(action.options.scheduled) + )).done([=](const MTPUpdates &result) { + applyUpdates(result); + if (shared && !--shared->requestsLeft) { + shared->callback(); + } + finish(); + }).fail([=, ids = localIds](const RPCError &error) { + if (ids) { + auto found = false; + for (const auto &[randomId, itemId] : *ids) { + if (currentRandomId == MTP_long(randomId)) { + sendMessageFail(error, peer, randomId, itemId); + found = true; + break; + } + } + if (!found) { + sendMessageFail(error, peer); + } + } else { + sendMessageFail(error, peer); + } + finish(); + }).afterRequest( + history->sendRequestId + ).send(); + return history->sendRequestId; + }); + }; + + const auto sendAccumulated = [&] { + if (cForwardQuoted()) { + forwardQuoted(); + } else if (isGrouped()) { + forwardAlbumUnquoted(); + } else { + for (auto i = fromIter, e = toIter; i != e; i++) { + const auto item = *i; + const auto media = item->media(); + + if (media && !media->webpage()) { + if (media->poll() + || media->geoPoint() + || media->sharedContact() + || media->photo() + || media->document()) { + forwardMediaUnquoted(item); + } else { + forwardQuotedSingle(item); + } + } else { + forwardMessageUnquoted(item); + } + } + } ids.resize(0); randomIds.resize(0); @@ -3965,7 +4296,8 @@ void ApiWrap::forwardMessages( ids.reserve(count); randomIds.reserve(count); - for (const auto item : items) { + for (auto i = items.begin(), e = items.end(); i != e; /* ++i is in the end */) { + const auto item = *i; const auto randomId = rand_value(); if (genClientSideMessage) { if (const auto message = item->toHistoryMessage()) { @@ -3994,16 +4326,18 @@ void ApiWrap::forwardMessages( localIds->emplace(randomId, newId); } } - const auto newFrom = item->history()->peer; - const auto newGroupId = item->groupId(); - if (forwardFrom != newFrom - || currentGroupId != newGroupId) { + if (needNextGroup(item)) { sendAccumulated(); - forwardFrom = newFrom; - currentGroupId = newGroupId; + forwardFrom = item->history()->peer; + currentGroupId = item->groupId(); + fromIter = i; } ids.push_back(MTP_int(item->id)); randomIds.push_back(MTP_long(randomId)); + if (item->media() && item->media()->canBeGrouped()) { + isLastGrouped = true; + } + toIter = ++i; } sendAccumulated(); _session->data().sendHistoryChangeNotifications(); diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp index deb7f17f2..9fe32b60a 100644 --- a/Telegram/SourceFiles/boxes/share_box.cpp +++ b/Telegram/SourceFiles/boxes/share_box.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/widgets/scroll_area.h" #include "ui/widgets/input_fields.h" +#include "ui/widgets/dropdown_menu.h" #include "ui/wrap/slide_wrap.h" #include "ui/text_options.h" #include "chat_helpers/message_field.h" @@ -41,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_history.h" +#include "styles/style_info.h" class ShareBox::Inner final : public Ui::RpWidget, private base::Subscriber { public: @@ -166,13 +168,15 @@ ShareBox::ShareBox( CopyCallback &©Callback, SubmitCallback &&submitCallback, FilterCallback &&filterCallback, - GoToChatCallback &&goToChatCallback) + GoToChatCallback &&goToChatCallback, + bool hasMedia) : _navigation(navigation) , _api(&_navigation->session().mtp()) , _copyCallback(std::move(copyCallback)) , _submitCallback(std::move(submitCallback)) , _filterCallback(std::move(filterCallback)) , _goToChatCallback(goToChatCallback ? std::move(goToChatCallback) : nullptr) +, _hasMediaMessages(hasMedia) , _select( this, st::contactsMultiSelect, @@ -226,6 +230,7 @@ void ShareBox::prepare() { Ui::SendPendingMoveResizeEvents(_select); setTitle(tr::lng_selected_forward()); + updateAdditionalTitle(); _inner = setInnerWidget( object_ptr( @@ -447,6 +452,9 @@ SendMenu::Type ShareBox::sendMenuType() const { void ShareBox::createButtons() { clearButtons(); + const auto moreButton = addTopButton(st::infoTopBarMenu); + moreButton->setClickedCallback([=] { showMenu(moreButton.data()); }); + if (_hasSelected) { if (_goToChatCallback && _inner->selected().size() == 1) { const auto singleChat = _inner->selected().at(0); @@ -467,6 +475,109 @@ void ShareBox::createButtons() { addButton(tr::lng_cancel(), [=] { closeBox(); }); } +bool ShareBox::showMenu(not_null button) { + if (_menu) { + _menu->hideAnimated(Ui::InnerDropdown::HideOption::IgnoreShow); + return true; + } + + _menu = base::make_unique_q(window()); + const auto weak = _menu.get(); + _menu->setHiddenCallback([=] { + weak->deleteLater(); + if (_menu == weak) { + button->setForceRippled(false); + } + }); + _menu->setShowStartCallback([=] { + if (_menu == weak) { + button->setForceRippled(true); + } + }); + _menu->setHideStartCallback([=] { + if (_menu == weak) { + button->setForceRippled(false); + } + }); + button->installEventFilter(_menu); + + if (!cForwardQuoted()) { + _menu->addAction(tr::ktg_forward_menu_quoted(tr::now), [=] { + cSetForwardQuoted(true); + updateAdditionalTitle(); + }); + } + if (cForwardQuoted() || !cForwardCaptioned()) { + _menu->addAction(tr::ktg_forward_menu_unquoted(tr::now), [=] { + cSetForwardQuoted(false); + cSetForwardCaptioned(true); + updateAdditionalTitle(); + }); + } + if (cForwardQuoted() || cForwardCaptioned()) { + _menu->addAction(tr::ktg_forward_menu_uncaptioned(tr::now), [=] { + cSetForwardQuoted(false); + cSetForwardCaptioned(false); + updateAdditionalTitle(); + }); + } + if (_hasMediaMessages) { + _menu->addSeparator(); + if (!cForwardAlbumsAsIs()) { + _menu->addAction(tr::ktg_forward_menu_default_albums(tr::now), [=] { + cSetForwardAlbumsAsIs(true); + updateAdditionalTitle(); + }); + } + if (cForwardAlbumsAsIs() || !cForwardGrouped()) { + _menu->addAction(tr::ktg_forward_menu_group_all_media(tr::now), [=] { + cSetForwardAlbumsAsIs(false); + cSetForwardGrouped(true); + updateAdditionalTitle(); + }); + } + if (cForwardAlbumsAsIs() || cForwardGrouped()) { + _menu->addAction(tr::ktg_forward_menu_separate_messages(tr::now), [=] { + cSetForwardAlbumsAsIs(false); + cSetForwardGrouped(false); + updateAdditionalTitle(); + }); + } + } + + const auto parentTopLeft = window()->mapToGlobal({ 0, 0 }); + const auto buttonTopLeft = button->mapToGlobal({ 0, 0 }); + const auto parentRect = QRect(parentTopLeft, window()->size()); + const auto buttonRect = QRect(buttonTopLeft, button->size()); + _menu->move( + buttonRect.x() + buttonRect.width() - _menu->width() - parentRect.x(), + buttonRect.y() + buttonRect.height() - parentRect.y() - style::ConvertScale(18)); + _menu->showAnimated(Ui::PanelAnimation::Origin::TopRight); + + return true; +} + +void ShareBox::updateAdditionalTitle() { + QString result; + + if (!cForwardQuoted()) { + result += (cForwardCaptioned() + ? tr::ktg_forward_subtitle_unquoted(tr::now) + : tr::ktg_forward_subtitle_uncaptioned(tr::now)); + } + + if (_hasMediaMessages && !cForwardAlbumsAsIs()) { + if (!result.isEmpty()) { + result += ", "; + } + result += (cForwardGrouped() + ? tr::ktg_forward_subtitle_group_all_media(tr::now) + : tr::ktg_forward_subtitle_separate_messages(tr::now)); + } + + setAdditionalTitle(rpl::single(result)); +} + void ShareBox::applyFilterUpdate(const QString &query) { onScrollToY(0); _inner->updateFilter(query); diff --git a/Telegram/SourceFiles/boxes/share_box.h b/Telegram/SourceFiles/boxes/share_box.h index 0eb63aa40..b1f555ab2 100644 --- a/Telegram/SourceFiles/boxes/share_box.h +++ b/Telegram/SourceFiles/boxes/share_box.h @@ -38,6 +38,7 @@ class IndexedList; namespace Ui { class MultiSelect; class InputField; +class DropdownMenu; struct ScrollToRequest; template class SlideWrap; @@ -67,7 +68,8 @@ public: CopyCallback &©Callback, SubmitCallback &&submitCallback, FilterCallback &&filterCallback, - GoToChatCallback &&goToChatCallback = nullptr); + GoToChatCallback &&goToChatCallback = nullptr, + bool hasMedia = false); protected: void prepare() override; @@ -94,6 +96,8 @@ private: void applyFilterUpdate(const QString &query); void selectedChanged(); void createButtons(); + bool showMenu(not_null button); + void updateAdditionalTitle(); int getTopScrollSkip() const; int getBottomScrollSkip() const; int contentHeight() const; @@ -115,6 +119,8 @@ private: FilterCallback _filterCallback; GoToChatCallback _goToChatCallback; + bool _hasMediaMessages = false; + object_ptr _select; object_ptr> _comment; @@ -136,4 +142,6 @@ private: Ui::Animations::Simple _scrollAnimation; + base::unique_qptr _menu; + }; diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index 7f27a056f..5703c0aa4 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -182,6 +182,10 @@ PollData *Media::poll() const { return nullptr; } +const LocationPoint *Media::geoPoint() const { + return nullptr; +} + bool Media::uploading() const { return false; } @@ -808,6 +812,10 @@ Data::CloudImage *MediaLocation::location() const { return _location; } +const LocationPoint *MediaLocation::geoPoint() const { + return &_point; +} + QString MediaLocation::chatListText() const { return WithCaptionDialogsText(tr::lng_maps_point(tr::now), _title); } diff --git a/Telegram/SourceFiles/data/data_media_types.h b/Telegram/SourceFiles/data/data_media_types.h index caba0e25c..f0fa27b8b 100644 --- a/Telegram/SourceFiles/data/data_media_types.h +++ b/Telegram/SourceFiles/data/data_media_types.h @@ -82,6 +82,7 @@ public: virtual const Invoice *invoice() const; virtual Data::CloudImage *location() const; virtual PollData *poll() const; + virtual const LocationPoint *geoPoint() const; virtual bool uploading() const; virtual Storage::SharedMediaTypesMask sharedMediaTypes() const; @@ -245,6 +246,7 @@ public: std::unique_ptr clone(not_null parent) override; Data::CloudImage *location() const override; + const LocationPoint *geoPoint() const override; QString chatListText() const override; QString notificationText() const override; QString pinnedTextSubstring() const override; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 7f36803b9..238939d2b 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -5172,6 +5172,9 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) { updateField(); } else if (_inReplyEditForward) { if (readyToForward()) { + if (e->button() != Qt::LeftButton) { + return; + } const auto items = std::move(_toForward); session().data().cancelForwarding(_history); auto list = ranges::view::all( @@ -5189,6 +5192,92 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) { } } +void HistoryWidget::contextMenuEvent(QContextMenuEvent *e) { + if (_menu) { + return; + } + const auto hasSecondLayer = (_editMsgId + || _replyToId + || readyToForward() + || _kbReplyTo); + _replyForwardPressed = hasSecondLayer && QRect( + 0, + _field->y() - st::historySendPadding - st::historyReplyHeight, + st::historyReplySkip, + st::historyReplyHeight).contains(e->pos()); + if (_replyForwardPressed && !_fieldBarCancel->isHidden()) { + return; + } else if (_inReplyEditForward) { + if (readyToForward()) { + const auto count = int(_toForward.size()); + auto hasMediaToGroup = false; + + if (count > 1) { + auto grouppableMediaCount = 0; + for (const auto item : _toForward) { + if (item->media() && item->media()->canBeGrouped()) { + grouppableMediaCount++; + } else { + grouppableMediaCount = 0; + } + if (grouppableMediaCount > 1) { + hasMediaToGroup = true; + break; + } + } + } + + _menu = base::make_unique_q(this); + + if (!cForwardQuoted()) { + _menu->addAction(tr::ktg_forward_menu_quoted(tr::now), [=] { + cSetForwardQuoted(true); + updateForwardingTexts(); + }); + } + if (cForwardQuoted() || !cForwardCaptioned()) { + _menu->addAction(tr::ktg_forward_menu_unquoted(tr::now), [=] { + cSetForwardQuoted(false); + cSetForwardCaptioned(true); + updateForwardingTexts(); + }); + } + if (cForwardQuoted() || cForwardCaptioned()) { + _menu->addAction(tr::ktg_forward_menu_uncaptioned(tr::now), [=] { + cSetForwardQuoted(false); + cSetForwardCaptioned(false); + updateForwardingTexts(); + }); + } + if (hasMediaToGroup && count > 1) { + _menu->addSeparator(); + if (!cForwardAlbumsAsIs()) { + _menu->addAction(tr::ktg_forward_menu_default_albums(tr::now), [=] { + cSetForwardAlbumsAsIs(true); + updateForwardingTexts(); + }); + } + if (cForwardAlbumsAsIs() || !cForwardGrouped()) { + _menu->addAction(tr::ktg_forward_menu_group_all_media(tr::now), [=] { + cSetForwardAlbumsAsIs(false); + cSetForwardGrouped(true); + updateForwardingTexts(); + }); + } + if (cForwardAlbumsAsIs() || cForwardGrouped()) { + _menu->addAction(tr::ktg_forward_menu_separate_messages(tr::now), [=] { + cSetForwardAlbumsAsIs(false); + cSetForwardGrouped(false); + updateForwardingTexts(); + }); + } + } + + _menu->popup(QCursor::pos()); + } + } +} + void HistoryWidget::keyPressEvent(QKeyEvent *e) { if (!_history) return; @@ -6319,6 +6408,8 @@ void HistoryWidget::updateForwardingTexts() { auto insertedPeers = base::flat_set>(); auto insertedNames = base::flat_set(); auto fullname = QString(); + auto hasMediaToGroup = false; + auto grouppableMediaCount = 0; auto names = std::vector(); names.reserve(_toForward.size()); for (const auto item : _toForward) { @@ -6339,7 +6430,18 @@ void HistoryWidget::updateForwardingTexts() { } else { Unexpected("Corrupt forwarded information in message."); } + if (!hasMediaToGroup) { + if (item->media() && item->media()->canBeGrouped()) { + grouppableMediaCount++; + } else { + grouppableMediaCount = 0; + } + if (grouppableMediaCount > 1) { + hasMediaToGroup = true; + } + } } + if (names.size() > 2) { from = tr::lng_forwarding_from(tr::now, lt_count, names.size() - 1, lt_user, names[0]); } else if (names.size() < 2) { @@ -6348,10 +6450,20 @@ void HistoryWidget::updateForwardingTexts() { from = tr::lng_forwarding_from_two(tr::now, lt_user, names[0], lt_second_user, names[1]); } - if (count < 2) { + if (count < 2 && cForwardQuoted()) { text = _toForward.front()->inReplyText(); } else { - text = textcmdLink(1, tr::lng_forward_messages(tr::now, lt_count, count)); + text = textcmdLink(1, tr::lng_forward_messages(tr::now, lt_count, count) + + (cForwardQuoted() + ? QString() + : qsl(", ") + (cForwardCaptioned() + ? tr::ktg_forward_subtitle_unquoted(tr::now) + : tr::ktg_forward_subtitle_uncaptioned(tr::now))) + + (cForwardAlbumsAsIs() || !hasMediaToGroup + ? QString() + : qsl(", ") + (cForwardGrouped() + ? tr::ktg_forward_subtitle_group_all_media(tr::now) + : tr::ktg_forward_subtitle_separate_messages(tr::now)))); } } _toForwardFrom.setText(st::msgNameStyle, from, Ui::NameTextOptions()); diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 0de045c87..c31e2d5ac 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -287,6 +287,7 @@ protected: void resizeEvent(QResizeEvent *e) override; void keyPressEvent(QKeyEvent *e) override; void mousePressEvent(QMouseEvent *e) override; + void contextMenuEvent(QContextMenuEvent *e) override; void paintEvent(QPaintEvent *e) override; void leaveEventHook(QEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; @@ -769,4 +770,6 @@ private: int _topDelta = 0; + base::unique_qptr _menu; + }; diff --git a/Telegram/SourceFiles/kotato/settings.cpp b/Telegram/SourceFiles/kotato/settings.cpp index cab06f977..b378aae8b 100644 --- a/Telegram/SourceFiles/kotato/settings.cpp +++ b/Telegram/SourceFiles/kotato/settings.cpp @@ -219,3 +219,8 @@ QString gApiHash; bool gUseEnvApi = true; bool gApiFromStartParams = false; + +bool gForwardQuoted = true; +bool gForwardCaptioned = true; +bool gForwardAlbumsAsIs = true; +bool gForwardGrouped = false; diff --git a/Telegram/SourceFiles/kotato/settings.h b/Telegram/SourceFiles/kotato/settings.h index 3b133ba50..51f74fb2f 100644 --- a/Telegram/SourceFiles/kotato/settings.h +++ b/Telegram/SourceFiles/kotato/settings.h @@ -129,3 +129,8 @@ DeclareSetting(int, ApiId); DeclareSetting(QString, ApiHash); DeclareSetting(bool, UseEnvApi); DeclareSetting(bool, ApiFromStartParams); + +DeclareSetting(bool, ForwardQuoted); +DeclareSetting(bool, ForwardCaptioned); +DeclareSetting(bool, ForwardAlbumsAsIs); +DeclareSetting(bool, ForwardGrouped); diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 987876fa4..4a50c2345 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwidget.h" #include "mainwindow.h" #include "api/api_chat_filters.h" +#include "api/api_text_entities.h" #include "mtproto/mtproto_config.h" #include "history/history.h" #include "history/history_item.h" @@ -1045,6 +1046,15 @@ QPointer ShowForwardMessagesBox( FnMut submitCallback; }; struct MsgIdsGroup { + MsgIdsGroup() = default; + MsgIdsGroup(not_null item, MTPint fullId, bool isGrouped = false) + : grouped(isGrouped) { + add(item, fullId); + } + void add(not_null item, MTPint fullId) { + items.push_back(item); + ids.push_back(fullId); + } HistoryItemsList items; QVector ids; bool grouped = false; @@ -1058,6 +1068,23 @@ QPointer ShowForwardMessagesBox( && firstItem->media() && (firstItem->media()->game() != nullptr); const auto canCopyLink = items.size() == 1 && (firstItem->hasDirectLink() || isGame); + auto hasMediaForGrouping = false; + + if (items.size() > 1) { + auto grouppableMediaCount = 0; + for (const auto item : history->owner().idsToItems(items)) { + if (item->media() && item->media()->canBeGrouped()) { + grouppableMediaCount++; + } else { + grouppableMediaCount = 0; + } + if (grouppableMediaCount > 1) { + hasMediaForGrouping = true; + break; + } + } + } + const auto data = std::make_shared(history->peer, std::move(items), std::move(successCallback)); auto copyCallback = [=]() { @@ -1128,37 +1155,261 @@ QPointer ShowForwardMessagesBox( : MTPmessages_ForwardMessages::Flag(0)); const auto groupedSendFlags = sendFlags | MTPmessages_ForwardMessages::Flag::f_grouped; + // Regroup messages if needed auto groupedMsgIds = QVector(); for (const auto fullId : data->msgIds) { - auto item = navigation->session().data().message(fullId); - auto group = owner->groups().find(item); + const auto item = navigation->session().data().message(fullId); + const auto group = owner->groups().find(item); + const auto canBeGrouped = hasMediaForGrouping && item->media() && item->media()->canBeGrouped(); - if (groupedMsgIds.size()) { - auto prevItem = groupedMsgIds.back().items.back(); - auto prevGroup = owner->groups().find(prevItem); - if (prevGroup == group) { - groupedMsgIds.back().items.push_back(item); - groupedMsgIds.back().ids.push_back(MTP_int(fullId.msg)); - continue; + if (groupedMsgIds.size() > 0) { + auto lastGroup = &groupedMsgIds.back(); + + if (cForwardAlbumsAsIs()) { + if (owner->groups().find(lastGroup->items.back()) == group) { + lastGroup->add(item, MTP_int(fullId.msg)); + continue; + } + } else { + if (lastGroup->grouped) { + if (lastGroup->items.size() < 10 && canBeGrouped) { + lastGroup->add(item, MTP_int(fullId.msg)); + continue; + } + } else { + if (!canBeGrouped) { + lastGroup->add(item, MTP_int(fullId.msg)); + continue; + } + } } } - MsgIdsGroup msgIdGroupInst; - msgIdGroupInst.items.push_back(item); - msgIdGroupInst.ids.push_back(MTP_int(fullId.msg)); - msgIdGroupInst.grouped = (group != nullptr); - groupedMsgIds.push_back(msgIdGroupInst); + groupedMsgIds.push_back(MsgIdsGroup( + item, + MTP_int(fullId.msg), + cForwardAlbumsAsIs() + ? (group != nullptr) + : canBeGrouped + ? cForwardGrouped() + : false)); } - auto generateRandom = [&] (int size) { + + const auto generateRandom = [&] (int size) { auto result = QVector(size); for (auto &value : result) { value = rand_value(); } return result; }; + const auto checkAndClose = [=] (mtpRequestId requestId) { + data->requests.remove(requestId); + if (data->requests.empty()) { + Ui::Toast::Show(tr::lng_share_done(tr::now)); + Ui::hideLayer(); + } + }; auto &api = owner->session().api(); auto &histories = owner->histories(); const auto requestType = Data::Histories::RequestType::Send; + + const auto forwardQuoted = [&] ( + MsgIdsGroup &&group, + not_null history, + MTPmessages_ForwardMessages::Flags flags) { + histories.sendRequest(history, requestType, [=](Fn finish) { + auto &api = history->session().api(); + history->sendRequestId = api.request(MTPmessages_ForwardMessages( + MTP_flags(flags), + data->peer->input, + MTP_vector(group.ids), + MTP_vector(generateRandom(group.ids.size())), + history->peer->input, + MTP_int(options.scheduled) + )).done([=](const MTPUpdates &updates, mtpRequestId requestId) { + history->session().api().applyUpdates(updates); + checkAndClose(requestId); + finish(); + }).fail([=](const RPCError &error) { + finish(); + }).afterRequest(history->sendRequestId).send(); + return history->sendRequestId; + }); + data->requests.insert(history->sendRequestId); + }; + + const auto forwardAlbumUnquoted = [&] (MsgIdsGroup &&group, not_null history) { + auto medias = QVector(); + medias.reserve(group.items.size()); + + auto randomIds = generateRandom(group.items.size()); + + for (const auto item : group.items) { + const auto media = item->media(); + const auto inputMedia = media->photo() + ? MTP_inputMediaPhoto(MTP_flags(0), media->photo()->mtpInput(), MTPint()) + : MTP_inputMediaDocument(MTP_flags(0), media->document()->mtpInput(), MTPint()); + const auto caption = cForwardCaptioned() + ? item->originalText() + : TextWithEntities(); + const auto sentEntities = Api::EntitiesToMTP( + session, + caption.entities, + Api::ConvertOption::SkipLocal); + + const auto flags = !sentEntities.v.isEmpty() + ? MTPDinputSingleMedia::Flag::f_entities + : MTPDinputSingleMedia::Flag(0); + + const auto randomId = randomIds.takeFirst(); + + medias.push_back(MTP_inputSingleMedia( + MTP_flags(flags), + inputMedia, + randomId, + MTP_string(caption.text), + sentEntities)); + } + + const auto flags = MTPmessages_SendMultiMedia::Flags(0) + | (options.silent + ? MTPmessages_SendMultiMedia::Flag::f_silent + : MTPmessages_SendMultiMedia::Flag(0)) + | (options.scheduled + ? MTPmessages_SendMultiMedia::Flag::f_schedule_date + : MTPmessages_SendMultiMedia::Flag(0)); + + histories.sendRequest(history, requestType, [=](Fn finish) { + auto &api = history->session().api(); + history->sendRequestId = api.request(MTPmessages_SendMultiMedia( + MTP_flags(flags), + history->peer->input, + MTPint(), + MTP_vector(medias), + MTP_int(options.scheduled) + )).done([=](const MTPUpdates &updates, mtpRequestId requestId) { + history->session().api().applyUpdates(updates); + checkAndClose(requestId); + finish(); + }).fail([=](const RPCError &error) { + finish(); + }).afterRequest(history->sendRequestId).send(); + return history->sendRequestId; + }); + data->requests.insert(history->sendRequestId); + }; + + const auto forwardMediaUnquoted = [&] (not_null item, not_null history) { + const auto media = item->media(); + + auto newSendFlags = MTPmessages_SendMedia::Flags(0) + | (options.silent + ? MTPmessages_SendMedia::Flag::f_silent + : MTPmessages_SendMedia::Flag(0)) + | (options.scheduled + ? MTPmessages_SendMedia::Flag::f_schedule_date + : MTPmessages_SendMedia::Flag(0)); + + const auto caption = (cForwardCaptioned() + && !media->geoPoint() + && !media->sharedContact()) + ? item->originalText() + : TextWithEntities(); + + const auto sentEntities = Api::EntitiesToMTP( + session, + caption.entities, + Api::ConvertOption::SkipLocal); + if (!sentEntities.v.isEmpty()) { + newSendFlags |= MTPmessages_SendMedia::Flag::f_entities; + } + + const auto inputMedia = media->poll() + ? PollDataToInputMedia(media->poll()) + : media->geoPoint() + ? MTP_inputMediaGeoPoint( + MTP_inputGeoPoint( + MTP_double(media->geoPoint()->lat()), + MTP_double(media->geoPoint()->lon()))) + : media->sharedContact() + ? MTP_inputMediaContact( + MTP_string(media->sharedContact()->phoneNumber), + MTP_string(media->sharedContact()->firstName), + MTP_string(media->sharedContact()->lastName), + MTPstring()) + : media->photo() + ? MTP_inputMediaPhoto(MTP_flags(0), media->photo()->mtpInput(), MTPint()) + : MTP_inputMediaDocument(MTP_flags(0), media->document()->mtpInput(), MTPint()); + + histories.sendRequest(history, requestType, [=](Fn finish) { + auto &api = history->session().api(); + history->sendRequestId = api.request(MTPmessages_SendMedia( + MTP_flags(newSendFlags), + history->peer->input, + MTPint(), + inputMedia, + MTP_string(caption.text), + rand_value(), + MTPReplyMarkup(), + sentEntities, + MTP_int(options.scheduled) + )).done([=](const MTPUpdates &updates, mtpRequestId requestId) { + history->session().api().applyUpdates(updates); + checkAndClose(requestId); + finish(); + }).fail([=](const RPCError &error) { + finish(); + }).afterRequest(history->sendRequestId).send(); + return history->sendRequestId; + }); + data->requests.insert(history->sendRequestId); + }; + + const auto forwardMessageUnquoted = [&] (not_null item, not_null history) { + const auto media = item->media(); + + auto newSendFlags = MTPmessages_SendMessage::Flag(0) + | (!media || !media->webpage() + ? MTPmessages_SendMessage::Flag::f_no_webpage + : MTPmessages_SendMessage::Flag(0)) + | (options.silent + ? MTPmessages_SendMessage::Flag::f_silent + : MTPmessages_SendMessage::Flag(0)) + | (options.scheduled + ? MTPmessages_SendMessage::Flag::f_schedule_date + : MTPmessages_SendMessage::Flag(0)); + + const auto sentEntities = Api::EntitiesToMTP( + session, + item->originalText().entities, + Api::ConvertOption::SkipLocal); + if (!sentEntities.v.isEmpty()) { + newSendFlags |= MTPmessages_SendMessage::Flag::f_entities; + } + + histories.sendRequest(history, requestType, [=](Fn finish) { + auto &api = history->session().api(); + history->sendRequestId = api.request(MTPmessages_SendMessage( + MTP_flags(newSendFlags), + history->peer->input, + MTPint(), + MTP_string(item->originalText().text), + rand_value(), + MTPReplyMarkup(), + sentEntities, + MTP_int(options.scheduled) + )).done([=](const MTPUpdates &updates, mtpRequestId requestId) { + history->session().api().applyUpdates(updates); + checkAndClose(requestId); + finish(); + }).fail([=](const RPCError &error) { + finish(); + }).afterRequest(history->sendRequestId).send(); + return history->sendRequestId; + }); + data->requests.insert(history->sendRequestId); + }; + for (const auto peer : result) { const auto history = owner->history(peer); if (!comment.text.isEmpty()) { @@ -1168,30 +1419,39 @@ QPointer ShowForwardMessagesBox( message.action.clearDraft = false; api.sendMessage(std::move(message)); } - for (auto group : groupedMsgIds) { - histories.sendRequest(history, requestType, [=](Fn finish) { - auto &api = history->session().api(); - history->sendRequestId = api.request(MTPmessages_ForwardMessages( - MTP_flags(group.grouped ? groupedSendFlags : sendFlags), - data->peer->input, - MTP_vector(group.ids), - MTP_vector(generateRandom(group.ids.size())), - peer->input, - MTP_int(options.scheduled) - )).done([=](const MTPUpdates &updates, mtpRequestId requestId) { - history->session().api().applyUpdates(updates); - data->requests.remove(requestId); - if (data->requests.empty()) { - Ui::Toast::Show(tr::lng_share_done(tr::now)); - Ui::hideLayer(); + for (auto &group : groupedMsgIds) { + if (cForwardQuoted()) { + // Forward regrouped messages as is + const auto flags = group.grouped ? groupedSendFlags : sendFlags; + forwardQuoted(std::move(group), history, flags); + } else if (group.grouped) { + // Sending albums without author + forwardAlbumUnquoted(std::move(group), history); + } else { + for (const auto item : group.items) { + const auto media = item->media(); + + if (media && !media->webpage()) { + if (media->poll() + || media->geoPoint() + || media->sharedContact() + || media->photo() + || media->document()) { + // Send media messages without author + forwardMediaUnquoted(item, history); + } else { + // Forward message if type doesn't support forwarding unquoted + forwardQuoted( + MsgIdsGroup(item, MTP_int(item->fullId().msg)), + history, + sendFlags); + } + } else { + // Send messages without author + forwardMessageUnquoted(item, history); } - finish(); - }).fail([=](const RPCError &error) { - finish(); - }).afterRequest(history->sendRequestId).send(); - return history->sendRequestId; - }); - data->requests.insert(history->sendRequestId); + } + } } } if (data->submitCallback && !cForwardRetainSelection()) { @@ -1212,7 +1472,8 @@ QPointer ShowForwardMessagesBox( std::move(copyLinkCallback), std::move(submitCallback), std::move(filterCallback), - std::move(goToChatCallback))); + std::move(goToChatCallback), + hasMediaForGrouping)); return weak->data(); }