diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 7c152ada8a..b5ead24a13 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2511,6 +2511,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_date_input_month" = "Month"; "lng_date_input_year" = "Year"; +"lng_forward_header" = "Forward message"; +"lng_forward_title" = "Forward Message"; +"lng_forward_many_title#one" = "Forward {count} Message"; +"lng_forward_many_title#other" = "Forward {count} Messages"; +"lng_forward_about" = "You can remove the sender's name from this message as if it was sent by you."; +"lng_forward_about_many#one" = "You can remove senders' name from {count} forwarded message as if it was sent by you."; +"lng_forward_about_many#other" = "You can remove senders' names from {count} forwarded messages as if they were sent by you."; +"lng_forward_show_sender" = "Show sender's name"; +"lng_forward_show_senders" = "Show senders' names"; +"lng_forward_show_caption" = "Show caption"; +"lng_forward_show_captions" = "Show captions"; +"lng_forward_change_recipient" = "Change recipient"; +"lng_forward_sender_names_removed" = "Sender names removed"; + "lng_passport_title" = "Telegram passport"; "lng_passport_request1" = "{bot} requests access to your personal data"; "lng_passport_request2" = "to sign you up for their services"; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 65e99ddd84..ce0cbfcfbd 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -3580,9 +3580,11 @@ void ApiWrap::sendAction(const SendAction &action) { void ApiWrap::finishForwarding(const SendAction &action) { const auto history = action.history; - auto toForward = history->validateForwardDraft(); - if (!toForward.empty()) { - const auto error = GetErrorTextForSending(history->peer, toForward); + auto toForward = history->resolveForwardDraft(); + if (!toForward.items.empty()) { + const auto error = GetErrorTextForSending( + history->peer, + toForward.items); if (!error.isEmpty()) { return; } @@ -3600,10 +3602,10 @@ void ApiWrap::finishForwarding(const SendAction &action) { } void ApiWrap::forwardMessages( - HistoryItemsList &&items, + Data::ResolvedForwardDraft &&draft, const SendAction &action, FnMut &&successCallback) { - Expects(!items.empty()); + Expects(!draft.items.empty()); auto &histories = _session->data().histories(); @@ -3618,8 +3620,10 @@ void ApiWrap::forwardMessages( shared->callback = std::move(successCallback); } - const auto count = int(items.size()); - const auto genClientSideMessage = action.generateLocal && (count < 2); + const auto count = int(draft.items.size()); + const auto genClientSideMessage = action.generateLocal + && (count < 2) + && (draft.options == Data::ForwardOptions::PreserveInfo); const auto history = action.history; const auto peer = history->peer; @@ -3640,8 +3644,14 @@ void ApiWrap::forwardMessages( } else { flags |= MessageFlag::LocalHistoryEntry; } + if (draft.options != Data::ForwardOptions::PreserveInfo) { + sendFlags |= MTPmessages_ForwardMessages::Flag::f_drop_author; + } + if (draft.options == Data::ForwardOptions::NoNamesAndCaptions) { + sendFlags |= MTPmessages_ForwardMessages::Flag::f_drop_media_captions; + } - auto forwardFrom = items.front()->history()->peer; + auto forwardFrom = draft.items.front()->history()->peer; auto ids = QVector(); auto randomIds = QVector(); auto localIds = std::shared_ptr>(); @@ -3688,7 +3698,7 @@ void ApiWrap::forwardMessages( ids.reserve(count); randomIds.reserve(count); - for (const auto item : items) { + for (const auto item : draft.items) { const auto randomId = openssl::RandomValue(); if (genClientSideMessage) { if (const auto message = item->toHistoryMessage()) { diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 42f751ed4b..9b7257ff2a 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -29,6 +29,7 @@ class Session; namespace Data { struct UpdatedFileReferences; class WallPaper; +struct ResolvedForwardDraft; } // namespace Data namespace InlineBots { @@ -319,7 +320,7 @@ public: void sendAction(const SendAction &action); void finishForwarding(const SendAction &action); void forwardMessages( - HistoryItemsList &&items, + Data::ResolvedForwardDraft &&draft, const SendAction &action, FnMut &&successCallback = nullptr); void shareContact( diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index 9f12f3407b..d76c8601ad 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -201,7 +201,7 @@ bool Media::canBeGrouped() const { return false; } -QString Media::chatListText() const { +QString Media::chatListText(DrawInDialog way) const { auto result = notificationText(); return result.isEmpty() ? QString() @@ -341,10 +341,11 @@ QString MediaPhoto::notificationText() const { parent()->originalText().text); } -QString MediaPhoto::chatListText() const { - return WithCaptionDialogsText( - tr::lng_in_dlg_photo(tr::now), - parent()->originalText().text); +QString MediaPhoto::chatListText(DrawInDialog way) const { + const auto caption = (way == DrawInDialog::WithoutSenderAndCaption) + ? QString() + : parent()->originalText().text; + return WithCaptionDialogsText(tr::lng_in_dlg_photo(tr::now), caption); } QString MediaPhoto::pinnedTextSubstring() const { @@ -510,9 +511,9 @@ bool MediaFile::replyPreviewLoaded() const { return _document->replyPreviewLoaded(); } -QString MediaFile::chatListText() const { +QString MediaFile::chatListText(DrawInDialog way) const { if (const auto sticker = _document->sticker()) { - return Media::chatListText(); + return Media::chatListText(way); } const auto type = [&] { using namespace Ui::Text; @@ -532,7 +533,10 @@ QString MediaFile::chatListText() const { } return tr::lng_in_dlg_file(tr::now); }(); - return WithCaptionDialogsText(type, parent()->originalText().text); + const auto caption = (way == DrawInDialog::WithoutSenderAndCaption) + ? QString() + : parent()->originalText().text; + return WithCaptionDialogsText(type, caption); } QString MediaFile::notificationText() const { @@ -851,7 +855,7 @@ Data::CloudImage *MediaLocation::location() const { return _location; } -QString MediaLocation::chatListText() const { +QString MediaLocation::chatListText(DrawInDialog way) const { return WithCaptionDialogsText(tr::lng_maps_point(tr::now), _title); } @@ -1045,7 +1049,7 @@ bool MediaWebPage::replyPreviewLoaded() const { return true; } -QString MediaWebPage::chatListText() const { +QString MediaWebPage::chatListText(DrawInDialog way) const { return notificationText(); } diff --git a/Telegram/SourceFiles/data/data_media_types.h b/Telegram/SourceFiles/data/data_media_types.h index 1549328b21..49cbfffa44 100644 --- a/Telegram/SourceFiles/data/data_media_types.h +++ b/Telegram/SourceFiles/data/data_media_types.h @@ -27,6 +27,7 @@ namespace HistoryView { enum class Context : char; class Element; class Media; +enum class DrawInDialog; } // namespace HistoryView namespace Data { @@ -72,6 +73,8 @@ public: not_null parent() const; + using DrawInDialog = HistoryView::DrawInDialog; + virtual std::unique_ptr clone(not_null parent) = 0; virtual DocumentData *document() const; @@ -92,7 +95,7 @@ public: virtual bool replyPreviewLoaded() const; // Returns text with link-start and link-end commands for service-color highlighting. // Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text" - virtual QString chatListText() const; + virtual QString chatListText(DrawInDialog way) const; virtual QString notificationText() const = 0; virtual QString pinnedTextSubstring() const = 0; virtual TextForMimeData clipboardText() const = 0; @@ -148,7 +151,7 @@ public: bool hasReplyPreview() const override; Image *replyPreview() const override; bool replyPreviewLoaded() const override; - QString chatListText() const override; + QString chatListText(DrawInDialog way) const override; QString notificationText() const override; QString pinnedTextSubstring() const override; TextForMimeData clipboardText() const override; @@ -186,7 +189,7 @@ public: bool hasReplyPreview() const override; Image *replyPreview() const override; bool replyPreviewLoaded() const override; - QString chatListText() const override; + QString chatListText(DrawInDialog way) const override; QString notificationText() const override; QString pinnedTextSubstring() const override; TextForMimeData clipboardText() const override; @@ -252,7 +255,7 @@ public: std::unique_ptr clone(not_null parent) override; Data::CloudImage *location() const override; - QString chatListText() const override; + QString chatListText(DrawInDialog way) const override; QString notificationText() const override; QString pinnedTextSubstring() const override; TextForMimeData clipboardText() const override; @@ -320,7 +323,7 @@ public: bool hasReplyPreview() const override; Image *replyPreview() const override; bool replyPreviewLoaded() const override; - QString chatListText() const override; + QString chatListText(DrawInDialog way) const override; QString notificationText() const override; QString pinnedTextSubstring() const override; TextForMimeData clipboardText() const override; diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 12e82c7d2f..ccc0beaf0d 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -336,16 +336,27 @@ void History::draftSavedToCloud() { session().local().writeDrafts(this); } -HistoryItemsList History::validateForwardDraft() { - auto result = owner().idsToItems(_forwardDraft); - if (result.size() != _forwardDraft.size()) { - setForwardDraft(owner().itemsToIds(result)); +Data::ResolvedForwardDraft History::resolveForwardDraft( + const Data::ForwardDraft &draft) const { + return Data::ResolvedForwardDraft{ + .items = owner().idsToItems(draft.ids), + .options = draft.options, + }; +} + +Data::ResolvedForwardDraft History::resolveForwardDraft() { + auto result = resolveForwardDraft(_forwardDraft); + if (result.items.size() != _forwardDraft.ids.size()) { + setForwardDraft({ + .ids = owner().itemsToIds(result.items), + .options = result.options, + }); } return result; } -void History::setForwardDraft(MessageIdsList &&items) { - _forwardDraft = std::move(items); +void History::setForwardDraft(Data::ForwardDraft &&draft) { + _forwardDraft = std::move(draft); } HistoryItem *History::createItem( diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 9f1939f685..2b93ed4b00 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -33,6 +33,23 @@ struct Draft; class Session; class Folder; class ChatFilter; + +enum class ForwardOptions { + PreserveInfo, + NoSenderNames, + NoNamesAndCaptions, +}; + +struct ForwardDraft { + MessageIdsList ids; + ForwardOptions options = ForwardOptions::PreserveInfo; +}; + +struct ResolvedForwardDraft { + HistoryItemsList items; + ForwardOptions options = ForwardOptions::PreserveInfo; +}; + } // namespace Data namespace Dialogs { @@ -354,11 +371,13 @@ public: void applyCloudDraft(); void draftSavedToCloud(); - const MessageIdsList &forwardDraft() const { + [[nodiscard]] const Data::ForwardDraft &forwardDraft() const { return _forwardDraft; } - HistoryItemsList validateForwardDraft(); - void setForwardDraft(MessageIdsList &&items); + [[nodiscard]] Data::ResolvedForwardDraft resolveForwardDraft( + const Data::ForwardDraft &draft) const; + [[nodiscard]] Data::ResolvedForwardDraft resolveForwardDraft(); + void setForwardDraft(Data::ForwardDraft &&draft); History *migrateSibling() const; [[nodiscard]] bool useTopPromotion() const; @@ -598,7 +617,7 @@ private: Data::HistoryDrafts _drafts; TimeId _acceptCloudDraftsAfter = 0; int _savingCloudDraftRequests = 0; - MessageIdsList _forwardDraft; + Data::ForwardDraft _forwardDraft; QString _topPromotedMessage; QString _topPromotedType; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 9d37710dc1..553bf54aa7 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -936,20 +936,19 @@ QString HistoryItem::notificationText() const { } QString HistoryItem::inDialogsText(DrawInDialog way) const { - auto getText = [this]() { + const auto plainText = [&] { if (_media) { if (_groupId) { return textcmdLink(1, TextUtilities::Clean(tr::lng_in_dlg_album(tr::now))); } - return _media->chatListText(); + return _media->chatListText(way); } else if (!emptyText()) { return TextUtilities::Clean(_text.toString()); } return QString(); - }; - const auto plainText = getText(); + }(); const auto sender = [&]() -> PeerData* { - if (isPost() || isEmpty() || (way == DrawInDialog::WithoutSender)) { + if (isPost() || isEmpty() || (way != DrawInDialog::Normal)) { return nullptr; } else if (!_history->peer->isUser() || out()) { return displayFrom(); diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 66fa8b370f..813c2e734a 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -52,6 +52,11 @@ enum class CursorState : char; enum class PointState : char; enum class Context : char; class ElementDelegate; +enum class DrawInDialog { + Normal, + WithoutSender, + WithoutSenderAndCaption, +}; } // namespace HistoryView struct HiddenSenderInfo; @@ -291,10 +296,7 @@ public: } [[nodiscard]] virtual QString notificationText() const; - enum class DrawInDialog { - Normal, - WithoutSender, - }; + using DrawInDialog = HistoryView::DrawInDialog; // Returns text with link-start and link-end commands for service-color highlighting. // Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text" diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 5d02c5ed71..24e3bd4c5c 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/ripple_animation.h" #include "ui/text/text_utilities.h" // Ui::Text::ToUpper #include "ui/text/format_values.h" +#include "ui/chat/forward_options_box.h" #include "ui/chat/message_bar.h" #include "ui/chat/attach/attach_send_files_way.h" #include "ui/image/image.h" @@ -3285,7 +3286,7 @@ void HistoryWidget::send(Api::SendOptions options) { if (_canSendMessages) { const auto error = GetErrorTextForSending( _peer, - _toForward, + _toForward.items, message.textWithTags, options.scheduled); if (!error.isEmpty()) { @@ -3819,7 +3820,7 @@ QRect HistoryWidget::floatPlayerAvailableRect() { } bool HistoryWidget::readyToForward() const { - return _canSendMessages && !_toForward.empty(); + return _canSendMessages && !_toForward.items.empty(); } bool HistoryWidget::hasSilentToggle() const { @@ -4741,11 +4742,11 @@ void HistoryWidget::itemRemoved(not_null item) { toggleKeyboard(); _kbReplyTo = nullptr; } - auto found = ranges::find(_toForward, item); - if (found != _toForward.end()) { - _toForward.erase(found); + auto found = ranges::find(_toForward.items, item); + if (found != _toForward.items.end()) { + _toForward.items.erase(found); updateForwardingTexts(); - if (_toForward.empty()) { + if (_toForward.items.empty()) { updateControlsVisibility(); updateControlsGeometry(); } @@ -5316,14 +5317,63 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) { updateField(); } else if (_inReplyEditForward) { if (readyToForward()) { - const auto items = std::move(_toForward); - session().data().cancelForwarding(_history); - auto list = ranges::views::all( - items - ) | ranges::views::transform( - &HistoryItem::fullId - ) | ranges::to_vector; - Window::ShowForwardMessagesBox(controller(), std::move(list)); + using Options = Data::ForwardOptions; + const auto now = _toForward.options; + const auto count = _toForward.items.size(); + const auto dropNames = (now != Options::PreserveInfo); + const auto hasCaptions = [&] { + for (const auto item : _toForward.items) { + if (const auto media = item->media()) { + if (!item->originalText().text.isEmpty() + && (media->photo() || media->document()) + && !media->webpage()) { + return true; + } + } + } + return false; + }(); + const auto dropCaptions = (now == Options::NoNamesAndCaptions); + const auto weak = Ui::MakeWeak(this); + const auto changeRecipient = crl::guard(weak, [=] { + if (_toForward.items.empty()) { + return; + } + const auto draft = std::move(_toForward); + session().data().cancelForwarding(_history); + auto list = session().data().itemsToIds(draft.items); + Window::ShowForwardMessagesBox(controller(), { + .ids = session().data().itemsToIds(draft.items), + .options = draft.options, + }); + }); + const auto optionsChanged = crl::guard(weak, [=]( + Ui::ForwardOptions options) { + const auto newOptions = (options.hasCaptions + && options.dropCaptions) + ? Options::NoNamesAndCaptions + : options.dropNames + ? Options::NoSenderNames + : Options::PreserveInfo; + if (_history && _toForward.options != newOptions) { + _toForward.options = newOptions; + _history->setForwardDraft({ + .ids = session().data().itemsToIds(_toForward.items), + .options = newOptions, + }); + updateField(); + } + }); + controller()->show(Box( + Ui::ForwardOptionsBox, + count, + Ui::ForwardOptions{ + .dropNames = dropNames, + .hasCaptions = hasCaptions, + .dropCaptions = dropCaptions, + }, + optionsChanged, + changeRecipient)); } else { Ui::showPeerHistory(_peer, _editMsgId ? _editMsgId : replyToId()); } @@ -5964,7 +6014,7 @@ void HistoryWidget::replyToMessage(not_null item) { crl::guard(this, [=] { controller()->content()->setForwardDraft( _peer->id, - { 1, itemId }); + { .ids = { 1, itemId } }); }))); } return; @@ -6617,10 +6667,10 @@ void HistoryWidget::updateReplyEditTexts(bool force) { void HistoryWidget::updateForwarding() { if (_history) { - _toForward = _history->validateForwardDraft(); + _toForward = _history->resolveForwardDraft(); updateForwardingTexts(); } else { - _toForward.clear(); + _toForward = {}; } updateControlsVisibility(); updateControlsGeometry(); @@ -6629,13 +6679,17 @@ void HistoryWidget::updateForwarding() { void HistoryWidget::updateForwardingTexts() { int32 version = 0; QString from, text; - if (const auto count = int(_toForward.size())) { + const auto keepNames = (_toForward.options + == Data::ForwardOptions::PreserveInfo); + const auto keepCaptions = (_toForward.options + != Data::ForwardOptions::NoNamesAndCaptions); + if (const auto count = int(_toForward.items.size())) { auto insertedPeers = base::flat_set>(); auto insertedNames = base::flat_set(); auto fullname = QString(); auto names = std::vector(); - names.reserve(_toForward.size()); - for (const auto item : _toForward) { + names.reserve(_toForward.items.size()); + for (const auto item : _toForward.items) { if (const auto from = item->senderOriginal()) { if (!insertedPeers.contains(from)) { insertedPeers.emplace(from); @@ -6654,7 +6708,9 @@ void HistoryWidget::updateForwardingTexts() { Unexpected("Corrupt forwarded information in message."); } } - if (names.size() > 2) { + if (!keepNames) { + from = tr::lng_forward_sender_names_removed(tr::now); + } else 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) { from = fullname; @@ -6663,7 +6719,9 @@ void HistoryWidget::updateForwardingTexts() { } if (count < 2) { - text = _toForward.front()->inReplyText(); + text = _toForward.items.front()->inDialogsText(keepCaptions + ? HistoryItem::DrawInDialog::WithoutSender + : HistoryItem::DrawInDialog::WithoutSenderAndCaption); } else { text = textcmdLink(1, tr::lng_forward_messages(tr::now, lt_count, count)); } @@ -6673,19 +6731,25 @@ void HistoryWidget::updateForwardingTexts() { st::messageTextStyle, text, Ui::DialogTextOptions()); - _toForwardNameVersion = version; + _toForwardNameVersion = keepNames ? version : keepCaptions ? -1 : -2; } void HistoryWidget::checkForwardingInfo() { - if (!_toForward.empty()) { - auto version = 0; - for (const auto item : _toForward) { - if (const auto from = item->senderOriginal()) { - version += from->nameVersion; - } else if (const auto info = item->hiddenForwardedInfo()) { - ++version; - } else { - Unexpected("Corrupt forwarded information in message."); + if (!_toForward.items.empty()) { + const auto keepNames = (_toForward.options + == Data::ForwardOptions::PreserveInfo); + const auto keepCaptions = (_toForward.options + != Data::ForwardOptions::NoNamesAndCaptions); + auto version = keepNames ? 0 : keepCaptions ? -1 : -2; + if (keepNames) { + for (const auto item : _toForward.items) { + if (const auto from = item->senderOriginal()) { + version += from->nameVersion; + } else if (const auto info = item->hiddenForwardedInfo()) { + ++version; + } else { + Unexpected("Corrupt forwarded information in message."); + } } } if (version != _toForwardNameVersion) { @@ -6772,9 +6836,9 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { auto forwardLeft = st::historyReplySkip; st::historyForwardIcon.paint(p, st::historyReplyIconPosition + QPoint(0, backy), width()); if (!drawWebPagePreview) { - const auto firstItem = _toForward.front(); + const auto firstItem = _toForward.items.front(); const auto firstMedia = firstItem->media(); - const auto preview = (_toForward.size() < 2 && firstMedia && firstMedia->hasReplyPreview()) + const auto preview = (_toForward.items.size() < 2 && firstMedia && firstMedia->hasReplyPreview()) ? firstMedia->replyPreview() : nullptr; if (preview) { diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index f8e7920601..ae3ff7bd84 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "history/history_drag_area.h" +#include "history/history.h" #include "ui/widgets/tooltip.h" #include "mainwidget.h" #include "chat_helpers/bot_command.h" @@ -607,7 +608,7 @@ private: Ui::Text::String _replyToName; int _replyToNameVersion = 0; - HistoryItemsList _toForward; + Data::ResolvedForwardDraft _toForward; Ui::Text::String _toForwardFrom, _toForwardText; int _toForwardNameVersion = 0; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 99c42e76fa..3d26b8ee2b 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -502,20 +502,20 @@ void MainWidget::floatPlayerDoubleClickEvent( _controller->showPeerHistoryAtItem(item); } -bool MainWidget::setForwardDraft(PeerId peerId, MessageIdsList &&items) { +bool MainWidget::setForwardDraft(PeerId peerId, Data::ForwardDraft &&draft) { Expects(peerId != 0); const auto peer = session().data().peer(peerId); const auto error = GetErrorTextForSending( peer, - session().data().idsToItems(items), + session().data().idsToItems(draft.ids), true); if (!error.isEmpty()) { Ui::show(Box(error), Ui::LayerOption::KeepOther); return false; } - peer->owner().history(peer)->setForwardDraft(std::move(items)); + peer->owner().history(peer)->setForwardDraft(std::move(draft)); _controller->showPeerHistory( peer, SectionShow::Way::Forward, @@ -603,7 +603,10 @@ void MainWidget::onFilesOrForwardDrop( Expects(peerId != 0); if (data->hasFormat(qsl("application/x-td-forward"))) { - if (!setForwardDraft(peerId, session().data().takeMimeForwardIds())) { + auto draft = Data::ForwardDraft{ + .ids = session().data().takeMimeForwardIds(), + }; + if (!setForwardDraft(peerId, std::move(draft))) { // We've already released the mouse button, so the forwarding is cancelled. if (_hider) { _hider->startHide(); @@ -704,9 +707,9 @@ void MainWidget::hiderLayer(base::unique_qptr hider) { floatPlayerCheckVisibility(); } -void MainWidget::showForwardLayer(MessageIdsList &&items) { - auto callback = [=, items = std::move(items)](PeerId peer) mutable { - return setForwardDraft(peer, std::move(items)); +void MainWidget::showForwardLayer(Data::ForwardDraft &&draft) { + auto callback = [=, draft = std::move(draft)](PeerId peer) mutable { + return setForwardDraft(peer, std::move(draft)); }; hiderLayer(base::make_unique_q( this, diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 3fd19aed59..7266b413ea 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -39,6 +39,7 @@ class Session; namespace Data { class WallPaper; +struct ForwardDraft; } // namespace Data namespace Dialogs { @@ -159,12 +160,12 @@ public: int32 dlgsWidth() const; - void showForwardLayer(MessageIdsList &&items); + void showForwardLayer(Data::ForwardDraft &&draft); void showSendPathsLayer(); void shareUrlLayer(const QString &url, const QString &text); void inlineSwitchLayer(const QString &botAndQuery); void hiderLayer(base::unique_qptr h); - bool setForwardDraft(PeerId peer, MessageIdsList &&items); + bool setForwardDraft(PeerId peer, Data::ForwardDraft &&draft); bool shareUrl( PeerId peerId, const QString &url, diff --git a/Telegram/SourceFiles/ui/chat/forward_options_box.cpp b/Telegram/SourceFiles/ui/chat/forward_options_box.cpp new file mode 100644 index 0000000000..8e346d4b60 --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/forward_options_box.cpp @@ -0,0 +1,106 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/chat/forward_options_box.h" + +#include "ui/widgets/checkbox.h" +#include "ui/widgets/labels.h" +#include "lang/lang_keys.h" +#include "styles/style_layers.h" +#include "styles/style_boxes.h" + +namespace Ui { + +void ForwardOptionsBox( + not_null box, + int count, + ForwardOptions options, + Fn optionsChanged, + Fn changeRecipient) { + Expects(optionsChanged != nullptr); + Expects(changeRecipient != nullptr); + + box->setTitle((count == 1) + ? tr::lng_forward_title() + : tr::lng_forward_many_title( + lt_count, + rpl::single(count) | tr::to_count())); + box->addButton(tr::lng_box_done(), [=] { + box->closeBox(); + }); + box->addRow( + object_ptr( + box.get(), + (count == 1 + ? tr::lng_forward_about() + : tr::lng_forward_about_many( + lt_count, + rpl::single(count) | tr::to_count())), + st::boxLabel), + st::boxRowPadding); + const auto checkboxPadding = style::margins( + st::boxRowPadding.left(), + 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( + object_ptr( + box.get(), + (count == 1 + ? tr::lng_forward_show_caption + : tr::lng_forward_show_captions)(), + !options.dropCaptions, + st::defaultBoxCheckbox), + checkboxPadding) + : 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); + } 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()); + } + box->addRow( + object_ptr( + box.get(), + tr::lng_forward_change_recipient(tr::now)), + checkboxPadding + )->setClickedCallback([=] { + box->closeBox(); + changeRecipient(); + }); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/forward_options_box.h b/Telegram/SourceFiles/ui/chat/forward_options_box.h new file mode 100644 index 0000000000..521a8fdfed --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/forward_options_box.h @@ -0,0 +1,27 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "ui/layers/generic_box.h" + +namespace Ui { + +struct ForwardOptions { + bool dropNames = false; + bool hasCaptions = false; + bool dropCaptions = false; +}; + +void ForwardOptionsBox( + not_null box, + int count, + ForwardOptions options, + Fn optionsChanged, + Fn changeRecipient); + +} // namespace Ui diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 8a1a867da1..101a667cd4 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -963,27 +963,29 @@ void BlockSenderFromRepliesBox( QPointer ShowForwardMessagesBox( not_null navigation, - MessageIdsList &&items, + Data::ForwardDraft &&draft, FnMut &&successCallback) { const auto weak = std::make_shared>(); auto callback = [ - ids = std::move(items), + draft = std::move(draft), callback = std::move(successCallback), weak, navigation ](not_null peer) mutable { + const auto content = navigation->parentController()->content(); if (peer->isSelf()) { - auto items = peer->owner().idsToItems(ids); - if (!items.empty()) { + const auto history = peer->owner().history(peer); + auto resolved = history->resolveForwardDraft(draft); + if (!resolved.items.empty()) { const auto api = &peer->session().api(); auto action = Api::SendAction(peer->owner().history(peer)); action.clearDraft = false; action.generateLocal = false; - api->forwardMessages(std::move(items), action, [] { + api->forwardMessages(std::move(resolved), action, [] { Ui::Toast::Show(tr::lng_share_done(tr::now)); }); } - } else if (!navigation->parentController()->content()->setForwardDraft(peer->id, std::move(ids))) { + } else if (!content->setForwardDraft(peer->id, std::move(draft))) { return; } if (const auto strong = *weak) { @@ -1006,6 +1008,16 @@ QPointer ShowForwardMessagesBox( return weak->data(); } +QPointer ShowForwardMessagesBox( + not_null navigation, + MessageIdsList &&items, + FnMut &&successCallback) { + return ShowForwardMessagesBox( + navigation, + Data::ForwardDraft{ .ids = std::move(items) }, + std::move(successCallback)); +} + QPointer ShowSendNowMessagesBox( not_null navigation, not_null history, diff --git a/Telegram/SourceFiles/window/window_peer_menu.h b/Telegram/SourceFiles/window/window_peer_menu.h index c513e5ed28..b0412fb5ee 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.h +++ b/Telegram/SourceFiles/window/window_peer_menu.h @@ -21,6 +21,7 @@ class GenericBox; namespace Data { class Folder; class Session; +struct ForwardDraft; } // namespace Data namespace Dialogs { @@ -94,6 +95,10 @@ void ToggleHistoryArchived(not_null history, bool archived); Fn ClearHistoryHandler(not_null peer); Fn DeleteAndLeaveHandler(not_null peer); +QPointer ShowForwardMessagesBox( + not_null navigation, + Data::ForwardDraft &&draft, + FnMut &&successCallback = nullptr); QPointer ShowForwardMessagesBox( not_null navigation, MessageIdsList &&items, diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index c04e73fa96..8fc8005e76 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -139,6 +139,8 @@ PRIVATE ui/chat/chat_style.h ui/chat/chat_theme.cpp ui/chat/chat_theme.h + ui/chat/forward_options_box.cpp + ui/chat/forward_options_box.h ui/chat/group_call_bar.cpp ui/chat/group_call_bar.h ui/chat/group_call_userpics.cpp