From 974bf999212d79d91a8c954da6506f3dafc3147b Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 30 May 2024 00:01:18 +0400 Subject: [PATCH] Allow editing spoiler/caption-above in EditCaptionBox. --- Telegram/SourceFiles/api/api_editing.cpp | 155 +++++++++--------- Telegram/SourceFiles/api/api_editing.h | 2 +- Telegram/SourceFiles/api/api_sending.cpp | 1 + .../SourceFiles/boxes/edit_caption_box.cpp | 70 ++++++-- Telegram/SourceFiles/boxes/edit_caption_box.h | 11 +- Telegram/SourceFiles/boxes/send_files_box.cpp | 11 +- .../SourceFiles/history/history_widget.cpp | 26 +-- Telegram/SourceFiles/history/history_widget.h | 2 +- .../view/controls/compose_controls_common.h | 2 +- .../history_view_compose_controls.cpp | 25 ++- ...istory_view_compose_media_edit_manager.cpp | 43 +++-- .../history_view_compose_media_edit_manager.h | 11 +- .../view/history_view_replies_section.cpp | 6 +- .../view/history_view_replies_section.h | 2 +- .../view/history_view_scheduled_section.cpp | 6 +- .../view/history_view_scheduled_section.h | 2 +- .../business/settings_shortcut_messages.cpp | 8 +- .../attach_abstract_single_media_preview.cpp | 5 + .../attach_abstract_single_media_preview.h | 2 + .../ui/chat/attach/attach_prepare.cpp | 12 ++ .../ui/chat/attach/attach_prepare.h | 1 + 21 files changed, 247 insertions(+), 156 deletions(-) diff --git a/Telegram/SourceFiles/api/api_editing.cpp b/Telegram/SourceFiles/api/api_editing.cpp index 60dfb4c69..b48ead0b8 100644 --- a/Telegram/SourceFiles/api/api_editing.cpp +++ b/Telegram/SourceFiles/api/api_editing.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_histories.h" #include "data/data_session.h" #include "data/data_web_page.h" +#include "history/view/controls/history_view_compose_media_edit_manager.h" #include "history/history.h" #include "lang/lang_keys.h" #include "main/main_session.h" @@ -255,85 +256,85 @@ mtpRequestId EditTextMessage( SendOptions options, Fn done, Fn fail, - std::optional spoilerMediaOverride) { - if (spoilerMediaOverride) { - const auto spoiler = *spoilerMediaOverride; - if (const auto media = item->media()) { - auto takeInputMedia = Fn()>(nullptr); - auto takeFileReference = Fn(nullptr); - if (const auto photo = media->photo()) { - using Flag = MTPDinputMediaPhoto::Flag; - const auto flags = Flag() - | (media->ttlSeconds() ? Flag::f_ttl_seconds : Flag()) - | (spoiler ? Flag::f_spoiler : Flag()); - takeInputMedia = [=] { - return MTP_inputMediaPhoto( - MTP_flags(flags), - photo->mtpInput(), - MTP_int(media->ttlSeconds())); - }; - takeFileReference = [=] { return photo->fileReference(); }; - } else if (const auto document = media->document()) { - using Flag = MTPDinputMediaDocument::Flag; - const auto flags = Flag() - | (media->ttlSeconds() ? Flag::f_ttl_seconds : Flag()) - | (spoiler ? Flag::f_spoiler : Flag()); - takeInputMedia = [=] { - return MTP_inputMediaDocument( - MTP_flags(flags), - document->mtpInput(), - MTP_int(media->ttlSeconds()), - MTPstring()); // query - }; - takeFileReference = [=] { return document->fileReference(); }; - } - - const auto usedFileReference = takeFileReference - ? takeFileReference() - : QByteArray(); - const auto origin = item->fullId(); - const auto api = &item->history()->session().api(); - const auto performRequest = [=]( - const auto &repeatRequest, - mtpRequestId originalRequestId) -> mtpRequestId { - const auto handleReference = [=]( - const QString &error, - mtpRequestId requestId) { - if (error.startsWith(u"FILE_REFERENCE_"_q)) { - api->refreshFileReference(origin, [=](const auto &) { - if (takeFileReference && - (takeFileReference() != usedFileReference)) { - repeatRequest( - repeatRequest, - originalRequestId - ? originalRequestId - : requestId); - } else { - fail(error, requestId); - } - }); - } else { - fail(error, requestId); - } - }; - const auto callback = [=]( - Fn applyUpdates, - mtpRequestId requestId) { - applyUpdates(); - done(originalRequestId ? originalRequestId : requestId); - }; - const auto requestId = EditMessage( - item, - caption, - webpage, - options, - callback, - handleReference, - takeInputMedia ? takeInputMedia() : std::nullopt); - return originalRequestId ? originalRequestId : requestId; + bool spoilered) { + const auto media = item->media(); + if (media + && HistoryView::MediaEditManager::CanBeSpoilered(item) + && spoilered != media->hasSpoiler()) { + auto takeInputMedia = Fn()>(nullptr); + auto takeFileReference = Fn(nullptr); + if (const auto photo = media->photo()) { + using Flag = MTPDinputMediaPhoto::Flag; + const auto flags = Flag() + | (media->ttlSeconds() ? Flag::f_ttl_seconds : Flag()) + | (spoilered ? Flag::f_spoiler : Flag()); + takeInputMedia = [=] { + return MTP_inputMediaPhoto( + MTP_flags(flags), + photo->mtpInput(), + MTP_int(media->ttlSeconds())); }; - return performRequest(performRequest, 0); + takeFileReference = [=] { return photo->fileReference(); }; + } else if (const auto document = media->document()) { + using Flag = MTPDinputMediaDocument::Flag; + const auto flags = Flag() + | (media->ttlSeconds() ? Flag::f_ttl_seconds : Flag()) + | (spoilered ? Flag::f_spoiler : Flag()); + takeInputMedia = [=] { + return MTP_inputMediaDocument( + MTP_flags(flags), + document->mtpInput(), + MTP_int(media->ttlSeconds()), + MTPstring()); // query + }; + takeFileReference = [=] { return document->fileReference(); }; } + + const auto usedFileReference = takeFileReference + ? takeFileReference() + : QByteArray(); + const auto origin = item->fullId(); + const auto api = &item->history()->session().api(); + const auto performRequest = [=]( + const auto &repeatRequest, + mtpRequestId originalRequestId) -> mtpRequestId { + const auto handleReference = [=]( + const QString &error, + mtpRequestId requestId) { + if (error.startsWith(u"FILE_REFERENCE_"_q)) { + api->refreshFileReference(origin, [=](const auto &) { + if (takeFileReference && + (takeFileReference() != usedFileReference)) { + repeatRequest( + repeatRequest, + originalRequestId + ? originalRequestId + : requestId); + } else { + fail(error, requestId); + } + }); + } else { + fail(error, requestId); + } + }; + const auto callback = [=]( + Fn applyUpdates, + mtpRequestId requestId) { + applyUpdates(); + done(originalRequestId ? originalRequestId : requestId); + }; + const auto requestId = EditMessage( + item, + caption, + webpage, + options, + callback, + handleReference, + takeInputMedia ? takeInputMedia() : std::nullopt); + return originalRequestId ? originalRequestId : requestId; + }; + return performRequest(performRequest, 0); } const auto callback = [=](Fn applyUpdates, mtpRequestId id) { diff --git a/Telegram/SourceFiles/api/api_editing.h b/Telegram/SourceFiles/api/api_editing.h index c8d0f6c50..630e1cd8d 100644 --- a/Telegram/SourceFiles/api/api_editing.h +++ b/Telegram/SourceFiles/api/api_editing.h @@ -56,6 +56,6 @@ mtpRequestId EditTextMessage( SendOptions options, Fn done, Fn fail, - std::optional spoilerMediaOverride); + bool spoilered); } // namespace Api diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp index 8ab3d8bc7..ade419248 100644 --- a/Telegram/SourceFiles/api/api_sending.cpp +++ b/Telegram/SourceFiles/api/api_sending.cpp @@ -514,6 +514,7 @@ void SendConfirmedFile( edition.ttl = 0; edition.mtpMedia = &media; edition.textWithEntities = caption; + edition.invertMedia = file->to.options.invertCaption; edition.useSameViews = true; edition.useSameForwards = true; edition.useSameMarkup = true; diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index bb9914f09..d46121c2f 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -176,7 +176,7 @@ void ChooseReplacement( void EditPhotoImage( not_null controller, std::shared_ptr media, - bool wasSpoiler, + bool spoilered, Fn done) { const auto large = media ? media->image(Data::PhotoSize::Large) @@ -199,7 +199,7 @@ void EditPhotoImage( using ImageInfo = Ui::PreparedFileInformation::Image; auto &file = list.files.front(); - file.spoiler = wasSpoiler; + file.spoiler = spoilered; const auto image = std::get_if(&file.information->media); image->modifications = mods; @@ -231,13 +231,13 @@ EditCaptionBox::EditCaptionBox( not_null controller, not_null item, TextWithTags &&text, + bool spoilered, + bool invertCaption, Ui::PreparedList &&list, Fn saved) : _controller(controller) , _historyItem(item) -, _isAllowedEditMedia(item->media() - ? item->media()->allowsEditMedia() - : false) +, _isAllowedEditMedia(item->media() && item->media()->allowsEditMedia()) , _albumType(ComputeAlbumType(item)) , _controls(base::make_unique_q(this)) , _scroll(base::make_unique_q(this, st::boxScroll)) @@ -255,6 +255,8 @@ EditCaptionBox::EditCaptionBox( Expects(item->media() != nullptr); Expects(item->media()->allowsEditCaption()); + _mediaEditManager.start(item, spoilered, invertCaption); + _controller->session().data().itemRemoved( _historyItem->fullId() ) | rpl::start_with_next([=] { @@ -268,6 +270,8 @@ void EditCaptionBox::StartMediaReplace( not_null controller, FullMsgId itemId, TextWithTags text, + bool spoilered, + bool invertCaption, Fn saved) { const auto session = &controller->session(); const auto item = session->data().message(itemId); @@ -279,6 +283,8 @@ void EditCaptionBox::StartMediaReplace( controller, item, std::move(text), + spoilered, + invertCaption, std::move(list), std::move(saved))); }; @@ -293,6 +299,8 @@ void EditCaptionBox::StartMediaReplace( FullMsgId itemId, Ui::PreparedList &&list, TextWithTags text, + bool spoilered, + bool invertCaption, Fn saved) { const auto session = &controller->session(); const auto item = session->data().message(itemId); @@ -326,6 +334,8 @@ void EditCaptionBox::StartMediaReplace( controller, item, std::move(text), + spoilered, + invertCaption, std::move(list), std::move(saved))); } @@ -336,14 +346,15 @@ void EditCaptionBox::StartPhotoEdit( std::shared_ptr media, FullMsgId itemId, TextWithTags text, + bool spoilered, + bool invertCaption, Fn saved) { const auto session = &controller->session(); const auto item = session->data().message(itemId); if (!item) { return; } - const auto hasSpoiler = item->media() && item->media()->hasSpoiler(); - EditPhotoImage(controller, media, hasSpoiler, [=]( + EditPhotoImage(controller, media, spoilered, [=]( Ui::PreparedList &&list) mutable { const auto item = session->data().message(itemId); if (!item) { @@ -353,6 +364,8 @@ void EditCaptionBox::StartPhotoEdit( controller, item, std::move(text), + spoilered, + invertCaption, std::move(list), std::move(saved))); }); @@ -363,10 +376,29 @@ void EditCaptionBox::prepare() { addButton(tr::lng_cancel(), [=] { closeBox(); }); const auto details = crl::guard(this, [=] { - return SendMenu::Details(); + auto result = SendMenu::Details(); + const auto allWithSpoilers = ranges::all_of( + _preparedList.files, + &Ui::PreparedFile::spoiler); + result.spoiler = !_preparedList.hasSpoilerMenu(!_asFile) + ? SendMenu::SpoilerState::None + : allWithSpoilers + ? SendMenu::SpoilerState::Enabled + : SendMenu::SpoilerState::Possible; + const auto canMoveCaption = _preparedList.canMoveCaption( + false, + !_asFile + ) && _field && HasSendText(_field); + result.caption = !canMoveCaption + ? SendMenu::CaptionState::None + : _mediaEditManager.invertCaption() + ? SendMenu::CaptionState::Above + : SendMenu::CaptionState::Below; + return result; }); const auto callback = [=](SendMenu::Action action, const auto &) { - + _mediaEditManager.apply(action); + rebuildPreview(); }; SendMenu::SetupMenuAndShortcuts( button, @@ -402,7 +434,6 @@ void EditCaptionBox::rebuildPreview() { applyChanges(); - _previewHasSpoiler = nullptr; if (_preparedList.files.empty()) { const auto media = _historyItem->media(); const auto photo = media->photo(); @@ -436,7 +467,13 @@ void EditCaptionBox::rebuildPreview() { _isPhoto = (media && media->isPhoto()); const auto withCheckbox = _isPhoto && CanBeCompressed(_albumType); if (media && (!withCheckbox || !_asFile)) { - _previewHasSpoiler = [media] { return media->hasSpoiler(); }; + media->spoileredChanges( + ) | rpl::start_with_next([=](bool spoilered) { + _mediaEditManager.apply({ .type = spoilered + ? SendMenu::ActionType::SpoilerOn + : SendMenu::ActionType::SpoilerOff + }); + }, media->lifetime()); _content.reset(media); } else { _content.reset(Ui::CreateChild( @@ -763,10 +800,7 @@ bool EditCaptionBox::setPreparedList(Ui::PreparedList &&list) { } bool EditCaptionBox::hasSpoiler() const { - return _preparedList.files.empty() - ? (_historyItem->media() - && _historyItem->media()->hasSpoiler()) - : _preparedList.files.front().spoiler; + return _mediaEditManager.spoilered(); } void EditCaptionBox::captionResized() { @@ -875,8 +909,8 @@ bool EditCaptionBox::validateLength(const QString &text) const { } void EditCaptionBox::applyChanges() { - if (!_preparedList.files.empty() && _previewHasSpoiler) { - _preparedList.files.front().spoiler = _previewHasSpoiler(); + if (!_preparedList.files.empty()) { + _preparedList.files.front().spoiler = _mediaEditManager.spoilered(); } } @@ -905,7 +939,7 @@ void EditCaptionBox::save() { auto options = Api::SendOptions(); options.scheduled = item->isScheduled() ? item->date() : 0; options.shortcutId = item->shortcutId(); - //options.invertCaption = _invertCaption; + options.invertCaption = _mediaEditManager.invertCaption(); if (!_preparedList.files.empty()) { if ((_albumType != Ui::AlbumType::None) diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.h b/Telegram/SourceFiles/boxes/edit_caption_box.h index b3a10f43f..af72a250f 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.h +++ b/Telegram/SourceFiles/boxes/edit_caption_box.h @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "history/view/controls/history_view_compose_media_edit_manager.h" #include "ui/layers/box_content.h" #include "ui/chat/attach/attach_prepare.h" @@ -37,6 +38,8 @@ public: not_null controller, not_null item, TextWithTags &&text, + bool spoilered, + bool invertCaption, Ui::PreparedList &&list, Fn saved); ~EditCaptionBox(); @@ -45,18 +48,24 @@ public: not_null controller, FullMsgId itemId, TextWithTags text, + bool spoilered, + bool invertCaption, Fn saved); static void StartMediaReplace( not_null controller, FullMsgId itemId, Ui::PreparedList &&list, TextWithTags text, + bool spoilered, + bool invertCaption, Fn saved); static void StartPhotoEdit( not_null controller, std::shared_ptr media, FullMsgId itemId, TextWithTags text, + bool spoilered, + bool invertCaption, Fn saved); protected: @@ -107,7 +116,6 @@ private: const base::unique_qptr _emojiToggle; base::unique_qptr _content; - Fn _previewHasSpoiler; base::unique_qptr _emojiPanel; base::unique_qptr _emojiFilter; @@ -118,6 +126,7 @@ private: std::shared_ptr _photoMedia; Ui::PreparedList _preparedList; + HistoryView::MediaEditManager _mediaEditManager; mtpRequestId _saveRequestId = 0; diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index 0656488ee..112e7543a 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -595,16 +595,7 @@ bool SendFilesBox::hasSendMenu(const SendMenu::Details &details) const { } bool SendFilesBox::hasSpoilerMenu() const { - const auto allAreVideo = !ranges::any_of(_list.files, [](const auto &f) { - using Type = Ui::PreparedFile::Type; - return (f.type != Type::Video); - }); - const auto allAreMedia = !ranges::any_of(_list.files, [](const auto &f) { - using Type = Ui::PreparedFile::Type; - return (f.type != Type::Photo) && (f.type != Type::Video); - }); - return allAreVideo - || (allAreMedia && _sendWay.current().sendImagesAsPhotos()); + return _list.hasSpoilerMenu(_sendWay.current().sendImagesAsPhotos()); } void SendFilesBox::applyBlockChanges() { diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index bcbada572..44c65a048 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -328,7 +328,7 @@ HistoryWidget::HistoryWidget( || action.type == ActionType::CaptionDown || action.type == ActionType::SpoilerOn || action.type == ActionType::SpoilerOff) { - _mediaEditSpoiler.apply(action); + _mediaEditManager.apply(action); } else if (action.type == ActionType::Send) { send(action.options); } else { @@ -2677,7 +2677,7 @@ void HistoryWidget::setEditMsgId(MsgId msgId) { unregisterDraftSources(); _editMsgId = msgId; if (!msgId) { - _mediaEditSpoiler.cancel(); + _mediaEditManager.cancel(); _canReplaceMedia = false; if (_preview) { _preview->setDisabled(false); @@ -2749,6 +2749,8 @@ bool HistoryWidget::updateReplaceMediaButton() { controller(), { _history->peer->id, _editMsgId }, _field->getTextWithTags(), + _mediaEditManager.spoilered(), + _mediaEditManager.invertCaption(), crl::guard(_list, [=] { cancelEdit(); })); }); }); @@ -4065,10 +4067,10 @@ void HistoryWidget::saveEditMsg() { item, sending, webPageDraft, - { .invertCaption = _mediaEditSpoiler.invertCaption() }, + { .invertCaption = _mediaEditManager.invertCaption() }, done, fail, - _mediaEditSpoiler.spoilered()); + _mediaEditManager.spoilered()); } void HistoryWidget::hideChildWidgets() { @@ -4228,7 +4230,7 @@ SendMenu::Details HistoryWidget::sendMenuDetails() const { SendMenu::Details HistoryWidget::saveMenuDetails() const { return (_editMsgId && _replyEditMsg) - ? _mediaEditSpoiler.sendMenuDetails(HasSendText(_field)) + ? _mediaEditManager.sendMenuDetails(HasSendText(_field)) : SendMenu::Details(); } @@ -5613,6 +5615,8 @@ bool HistoryWidget::confirmSendingFiles( { _history->peer->id, _editMsgId }, std::move(list), _field->getTextWithTags(), + _mediaEditManager.spoilered(), + _mediaEditManager.invertCaption(), crl::guard(_list, [=] { cancelEdit(); })); return true; } @@ -6599,7 +6603,7 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) { if (_editMsgId && (_inDetails || _inPhotoEdit) && (e->button() == Qt::RightButton)) { - _mediaEditSpoiler.showMenu( + _mediaEditManager.showMenu( _list, [=] { mouseMoveEvent(nullptr); }, HasSendText(_field)); @@ -6609,6 +6613,8 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) { _photoEditMedia, { _history->peer->id, _editMsgId }, _field->getTextWithTags(), + _mediaEditManager.spoilered(), + _mediaEditManager.invertCaption(), crl::guard(_list, [=] { cancelEdit(); })); } else if (!_inDetails) { return; @@ -8186,7 +8192,7 @@ void HistoryWidget::updateReplyEditTexts(bool force) { ? _replyEditMsg->media() : nullptr; if (_editMsgId && _replyEditMsg) { - _mediaEditSpoiler.start(_replyEditMsg); + _mediaEditManager.start(_replyEditMsg); } _canReplaceMedia = editMedia && editMedia->allowsEditMedia(); _photoEditMedia = (_canReplaceMedia @@ -8281,12 +8287,12 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { ? drawMsgText->media() : nullptr; const auto hasPreview = media && media->hasReplyPreview(); - const auto preview = _mediaEditSpoiler - ? _mediaEditSpoiler.mediaPreview() + const auto preview = _mediaEditManager + ? _mediaEditManager.mediaPreview() : hasPreview ? media->replyPreview() : nullptr; - const auto spoilered = _mediaEditSpoiler.spoilered(); + const auto spoilered = _mediaEditManager.spoilered(); if (!spoilered) { _replySpoiler = nullptr; } else if (!_replySpoiler) { diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index f829cf797..e9453a621 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -661,7 +661,7 @@ private: MsgId _editMsgId = 0; std::shared_ptr _photoEditMedia; bool _canReplaceMedia = false; - HistoryView::MediaEditSpoilerManager _mediaEditSpoiler; + HistoryView::MediaEditManager _mediaEditManager; HistoryItem *_replyEditMsg = nullptr; Ui::Text::String _replyEditMsgText; diff --git a/Telegram/SourceFiles/history/view/controls/compose_controls_common.h b/Telegram/SourceFiles/history/view/controls/compose_controls_common.h index 01237072b..e51bae134 100644 --- a/Telegram/SourceFiles/history/view/controls/compose_controls_common.h +++ b/Telegram/SourceFiles/history/view/controls/compose_controls_common.h @@ -21,7 +21,7 @@ struct MessageToEdit { FullMsgId fullId; Api::SendOptions options; TextWithTags textWithTags; - std::optional spoilerMediaOverride; + bool spoilered = false; }; struct VoiceToSend { QByteArray bytes; diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 6c985bbf0..ac381222c 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -216,7 +216,7 @@ private: bool _repaintScheduled : 1 = false; bool _inClickable : 1 = false; - HistoryView::MediaEditSpoilerManager _mediaEditSpoiler; + HistoryView::MediaEditManager _mediaEditManager; const not_null _data; const not_null _cancel; @@ -409,7 +409,7 @@ void FieldHeader::init() { } } else if (!isLeftButton) { if (inPreviewRect && isEditingMessage()) { - _mediaEditSpoiler.showMenu( + _mediaEditManager.showMenu( this, [=] { update(); }, _hasSendText()); @@ -587,12 +587,12 @@ void FieldHeader::paintEditOrReplyToMessage(Painter &p) { const auto media = _shownMessage->media(); _shownMessageHasPreview = media && media->hasReplyPreview(); - const auto preview = _mediaEditSpoiler - ? _mediaEditSpoiler.mediaPreview() + const auto preview = _mediaEditManager + ? _mediaEditManager.mediaPreview() : _shownMessageHasPreview ? media->replyPreview() : nullptr; - const auto spoilered = _mediaEditSpoiler.spoilered(); + const auto spoilered = _mediaEditManager.spoilered(); if (!spoilered) { _shownPreviewSpoiler = nullptr; } else if (!_shownPreviewSpoiler) { @@ -737,7 +737,7 @@ void FieldHeader::editMessage(FullMsgId id, bool photoEditAllowed) { _photoEditAllowed = photoEditAllowed; _editMsgId = id; if (!photoEditAllowed) { - _mediaEditSpoiler.cancel(); + _mediaEditManager.cancel(); _inPhotoEdit = false; _inPhotoEditOver.stop(); } @@ -784,9 +784,9 @@ MessageToEdit FieldHeader::queryToEdit() { .options = { .scheduled = item->isScheduled() ? item->date() : 0, .shortcutId = item->shortcutId(), - .invertCaption = _mediaEditSpoiler.invertCaption(), + .invertCaption = _mediaEditManager.invertCaption(), }, - .spoilerMediaOverride = _mediaEditSpoiler.spoilered(), + .spoilered = _mediaEditManager.spoilered(), }; } @@ -1138,11 +1138,14 @@ bool ComposeControls::confirmMediaEdit(Ui::PreparedList &list) { if (!isEditingMessage() || !_regularWindow) { return false; } else if (_canReplaceMedia) { + const auto queryToEdit = _header->queryToEdit(); EditCaptionBox::StartMediaReplace( _regularWindow, _editingId, std::move(list), _field->getTextWithTags(), + queryToEdit.spoilered, + queryToEdit.options.invertCaption, crl::guard(_wrap.get(), [=] { cancelEditMessage(); })); } else { _show->showToast(tr::lng_edit_caption_attach(tr::now)); @@ -1401,11 +1404,14 @@ void ComposeControls::init() { _header->editPhotoRequests( ) | rpl::start_with_next([=] { + const auto queryToEdit = _header->queryToEdit(); EditCaptionBox::StartPhotoEdit( _regularWindow, _photoEditMedia, _editingId, _field->getTextWithTags(), + queryToEdit.spoilered, + queryToEdit.options.invertCaption, crl::guard(_wrap.get(), [=] { cancelEditMessage(); })); }, _wrap->lifetime()); @@ -2943,10 +2949,13 @@ bool ComposeControls::updateReplaceMediaButton() { const auto hideDuration = st::historyReplaceMedia.ripple.hideDuration; _replaceMedia->setClickedCallback([=] { base::call_delayed(hideDuration, _wrap.get(), [=] { + const auto queryToEdit = _header->queryToEdit(); EditCaptionBox::StartMediaReplace( _regularWindow, _editingId, _field->getTextWithTags(), + queryToEdit.spoilered, + queryToEdit.options.invertCaption, crl::guard(_wrap.get(), [=] { cancelEditMessage(); })); }); }); diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_media_edit_manager.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_media_edit_manager.cpp index f20681a0f..dd2fa9893 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_media_edit_manager.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_media_edit_manager.cpp @@ -21,16 +21,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace HistoryView { -MediaEditSpoilerManager::MediaEditSpoilerManager() = default; +MediaEditManager::MediaEditManager() = default; -void MediaEditSpoilerManager::start(not_null item) { +void MediaEditManager::start( + not_null item, + std::optional spoilered, + std::optional invertCaption) { const auto media = item->media(); if (!media) { return; } _item = item; - _spoilered = media->hasSpoiler(); - _invertCaption = item->invertMedia(); + _spoilered = spoilered.value_or(media->hasSpoiler()); + _invertCaption = invertCaption.value_or(item->invertMedia()); _lifetime = item->history()->owner().itemRemoved( ) | rpl::start_with_next([=](not_null removed) { if (removed == _item) { @@ -39,7 +42,7 @@ void MediaEditSpoilerManager::start(not_null item) { }); } -void MediaEditSpoilerManager::apply(SendMenu::Action action) { +void MediaEditManager::apply(SendMenu::Action action) { using Type = SendMenu::Action::Type; if (action.type == Type::CaptionUp) { _invertCaption = true; @@ -52,13 +55,13 @@ void MediaEditSpoilerManager::apply(SendMenu::Action action) { } } -void MediaEditSpoilerManager::cancel() { +void MediaEditManager::cancel() { _menu = nullptr; _item = nullptr; _lifetime.destroy(); } -void MediaEditSpoilerManager::showMenu( +void MediaEditManager::showMenu( not_null parent, Fn finished, bool hasCaptionText) { @@ -88,7 +91,7 @@ void MediaEditSpoilerManager::showMenu( _menu->popup(position); } -[[nodiscard]] Image *MediaEditSpoilerManager::mediaPreview() { +Image *MediaEditManager::mediaPreview() { if (const auto media = _item ? _item->media() : nullptr) { if (const auto photo = media->photo()) { return photo->getReplyPreview( @@ -105,15 +108,15 @@ void MediaEditSpoilerManager::showMenu( return nullptr; } -bool MediaEditSpoilerManager::spoilered() const { +bool MediaEditManager::spoilered() const { return _spoilered; } -bool MediaEditSpoilerManager::invertCaption() const { +bool MediaEditManager::invertCaption() const { return _invertCaption; } -SendMenu::Details MediaEditSpoilerManager::sendMenuDetails( +SendMenu::Details MediaEditManager::sendMenuDetails( bool hasCaptionText) const { const auto media = _item ? _item->media() : nullptr; if (!media) { @@ -122,10 +125,12 @@ SendMenu::Details MediaEditSpoilerManager::sendMenuDetails( const auto editingMedia = media->allowsEditMedia(); const auto editPhoto = editingMedia ? media->photo() : nullptr; const auto editDocument = editingMedia ? media->document() : nullptr; - const auto canSaveSpoiler = (editPhoto && !editPhoto->isNull()) - || (editDocument + const auto canSaveSpoiler = CanBeSpoilered(_item); + const auto canMoveCaption = media->allowsEditCaption() + && hasCaptionText + && (editPhoto + || editDocument && (editDocument->isVideoFile() || editDocument->isGifv())); - const auto canMoveCaption = media->allowsEditCaption() && hasCaptionText; return { .spoiler = (!canSaveSpoiler ? SendMenu::SpoilerState::None @@ -140,4 +145,14 @@ SendMenu::Details MediaEditSpoilerManager::sendMenuDetails( }; } +bool MediaEditManager::CanBeSpoilered(not_null item) { + const auto media = item ? item->media() : nullptr; + const auto editingMedia = media && media->allowsEditMedia(); + const auto editPhoto = editingMedia ? media->photo() : nullptr; + const auto editDocument = editingMedia ? media->document() : nullptr; + return (editPhoto && !editPhoto->isNull()) + || (editDocument + && (editDocument->isVideoFile() || editDocument->isGifv())); +} + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_media_edit_manager.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_media_edit_manager.h index 0f9f49e2b..0ca33f854 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_media_edit_manager.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_media_edit_manager.h @@ -24,11 +24,14 @@ class HistoryItem; namespace HistoryView { -class MediaEditSpoilerManager final { +class MediaEditManager final { public: - MediaEditSpoilerManager(); + MediaEditManager(); - void start(not_null item); + void start( + not_null item, + std::optional spoilered = {}, + std::optional invertCaption = {}); void apply(SendMenu::Action action); void cancel(); @@ -49,6 +52,8 @@ public: return _item != nullptr; } + [[nodiscard]] static bool CanBeSpoilered(not_null item); + private: base::unique_qptr _menu; HistoryItem *_item = nullptr; diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 7f7ded51a..27ff0e4ff 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -740,7 +740,7 @@ void RepliesWidget::setupComposeControls() { _composeControls->editRequests( ) | rpl::start_with_next([=](auto data) { if (const auto item = session().data().message(data.fullId)) { - const auto spoiler = data.spoilerMediaOverride; + const auto spoiler = data.spoilered; edit(item, data.options, saveEditMsgRequestId, spoiler); } }, lifetime()); @@ -1213,7 +1213,7 @@ void RepliesWidget::edit( not_null item, Api::SendOptions options, mtpRequestId *const saveEditMsgRequestId, - std::optional spoilerMediaOverride) { + bool spoilered) { if (*saveEditMsgRequestId) { return; } @@ -1282,7 +1282,7 @@ void RepliesWidget::edit( options, crl::guard(this, done), crl::guard(this, fail), - spoilerMediaOverride); + spoilered); _composeControls->hidePanelsAnimated(); doSetInnerFocus(); diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.h b/Telegram/SourceFiles/history/view/history_view_replies_section.h index 54f02ef16..37c8540fd 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.h +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.h @@ -247,7 +247,7 @@ private: not_null item, Api::SendOptions options, mtpRequestId *const saveEditMsgRequestId, - std::optional spoilerMediaOverride); + bool spoilered); void chooseAttach(std::optional overrideSendImagesAsPhotos); [[nodiscard]] SendMenu::Details sendMenuDetails() const; [[nodiscard]] FullReplyTo replyTo() const; diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 6535958be..bd4a1123b 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -320,7 +320,7 @@ void ScheduledWidget::setupComposeControls() { ) | rpl::start_with_next([=](auto data) { if (const auto item = session().data().message(data.fullId)) { if (item->isScheduled()) { - const auto spoiler = data.spoilerMediaOverride; + const auto spoiler = data.spoilered; edit(item, data.options, saveEditMsgRequestId, spoiler); } } @@ -733,7 +733,7 @@ void ScheduledWidget::edit( not_null item, Api::SendOptions options, mtpRequestId *const saveEditMsgRequestId, - std::optional spoilerMediaOverride) { + bool spoilered) { if (*saveEditMsgRequestId) { return; } @@ -802,7 +802,7 @@ void ScheduledWidget::edit( options, crl::guard(this, done), crl::guard(this, fail), - spoilerMediaOverride); + spoilered); _composeControls->hidePanelsAnimated(); _composeControls->focus(); diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h index e79304691..b2b0efa41 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h @@ -218,7 +218,7 @@ private: not_null item, Api::SendOptions options, mtpRequestId *const saveEditMsgRequestId, - std::optional spoilerMediaOverride); + bool spoilered); void highlightSingleNewMessage(const Data::MessagesSlice &slice); void chooseAttach(); [[nodiscard]] SendMenu::Details sendMenuDetails() const; diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp index ccbb1aea2..96c6cb841 100644 --- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp +++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp @@ -240,7 +240,7 @@ private: not_null item, Api::SendOptions options, mtpRequestId *const saveEditMsgRequestId, - std::optional spoilerMediaOverride); + bool spoilered); void chooseAttach(std::optional overrideSendImagesAsPhotos); [[nodiscard]] FullReplyTo replyTo() const; void doSetInnerFocus(); @@ -675,7 +675,7 @@ void ShortcutMessages::setupComposeControls() { ) | rpl::start_with_next([=](auto data) { if (const auto item = _session->data().message(data.fullId)) { if (item->isBusinessShortcut()) { - const auto spoiler = data.spoilerMediaOverride; + const auto spoiler = data.spoilered; edit(item, data.options, saveEditMsgRequestId, spoiler); } } @@ -1221,7 +1221,7 @@ void ShortcutMessages::edit( not_null item, Api::SendOptions options, mtpRequestId *const saveEditMsgRequestId, - std::optional spoilerMediaOverride) { + bool spoilered) { if (*saveEditMsgRequestId) { return; } @@ -1290,7 +1290,7 @@ void ShortcutMessages::edit( options, crl::guard(this, done), crl::guard(this, fail), - spoilerMediaOverride); + spoilered); _composeControls->hidePanelsAnimated(); doSetInnerFocus(); diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.cpp index 29f38cdde..01b94a9d1 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.cpp @@ -78,6 +78,10 @@ bool AbstractSingleMediaPreview::canHaveSpoiler() const { return supportsSpoilers(); } +rpl::producer AbstractSingleMediaPreview::spoileredChanges() const { + return _spoileredChanges.events(); +} + void AbstractSingleMediaPreview::preparePreview(QImage preview) { auto maxW = 0; auto maxH = 0; @@ -275,6 +279,7 @@ void AbstractSingleMediaPreview::showContextMenu(QPoint position) { ? tr::lng_context_disable_spoiler(tr::now) : tr::lng_context_spoiler_effect(tr::now), [=] { setSpoiler(!spoilered); + _spoileredChanges.fire_copy(!spoilered); }, spoilered ? &icons.menuSpoilerOff : &icons.menuSpoiler); if (_menu->empty()) { diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.h b/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.h index 901815136..0f5f8e784 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_media_preview.h @@ -41,6 +41,7 @@ public: void setSpoiler(bool spoiler); [[nodiscard]] bool hasSpoiler() const; [[nodiscard]] bool canHaveSpoiler() const; + [[nodiscard]] rpl::producer spoileredChanges() const; protected: virtual bool supportsSpoilers() const = 0; @@ -79,6 +80,7 @@ private: int _previewHeight = 0; std::unique_ptr _spoiler; + rpl::event_stream _spoileredChanges; const int _minThumbH; const base::unique_qptr _controls; diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp index ebdf9ac6f..1a70c69b6 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp @@ -237,6 +237,18 @@ bool PreparedList::hasSticker() const { return ranges::any_of(files, &PreparedFile::isSticker); } +bool PreparedList::hasSpoilerMenu(bool compress) const { + const auto allAreVideo = !ranges::any_of(files, [](const auto &f) { + using Type = Ui::PreparedFile::Type; + return (f.type != Type::Video); + }); + const auto allAreMedia = !ranges::any_of(files, [](const auto &f) { + using Type = Ui::PreparedFile::Type; + return (f.type != Type::Photo) && (f.type != Type::Video); + }); + return allAreVideo || (allAreMedia && compress); +} + int MaxAlbumItems() { return kMaxAlbumCount; } diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h index e467cc611..30210e3d6 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h @@ -123,6 +123,7 @@ struct PreparedList { [[nodiscard]] bool hasSendImagesAsPhotosOption(bool slowmode) const; [[nodiscard]] bool canHaveEditorHintLabel() const; [[nodiscard]] bool hasSticker() const; + [[nodiscard]] bool hasSpoilerMenu(bool compress) const; Error error = Error::None; QString errorData;