Allow editing spoiler/caption-above in EditCaptionBox.

This commit is contained in:
John Preston 2024-05-30 00:01:18 +04:00
parent 8c0351be4e
commit 974bf99921
21 changed files with 247 additions and 156 deletions

View file

@ -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<void(mtpRequestId requestId)> done,
Fn<void(const QString &error, mtpRequestId requestId)> fail,
std::optional<bool> spoilerMediaOverride) {
if (spoilerMediaOverride) {
const auto spoiler = *spoilerMediaOverride;
if (const auto media = item->media()) {
auto takeInputMedia = Fn<std::optional<MTPInputMedia>()>(nullptr);
auto takeFileReference = Fn<QByteArray()>(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<void()> 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<std::optional<MTPInputMedia>()>(nullptr);
auto takeFileReference = Fn<QByteArray()>(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<void()> 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<void()> applyUpdates, mtpRequestId id) {

View file

@ -56,6 +56,6 @@ mtpRequestId EditTextMessage(
SendOptions options,
Fn<void(mtpRequestId requestId)> done,
Fn<void(const QString &error, mtpRequestId requestId)> fail,
std::optional<bool> spoilerMediaOverride);
bool spoilered);
} // namespace Api

View file

@ -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;

View file

@ -176,7 +176,7 @@ void ChooseReplacement(
void EditPhotoImage(
not_null<Window::SessionController*> controller,
std::shared_ptr<Data::PhotoMedia> media,
bool wasSpoiler,
bool spoilered,
Fn<void(Ui::PreparedList)> 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<ImageInfo>(&file.information->media);
image->modifications = mods;
@ -231,13 +231,13 @@ EditCaptionBox::EditCaptionBox(
not_null<Window::SessionController*> controller,
not_null<HistoryItem*> item,
TextWithTags &&text,
bool spoilered,
bool invertCaption,
Ui::PreparedList &&list,
Fn<void()> 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<Ui::VerticalLayout>(this))
, _scroll(base::make_unique_q<Ui::ScrollArea>(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<Window::SessionController*> controller,
FullMsgId itemId,
TextWithTags text,
bool spoilered,
bool invertCaption,
Fn<void()> 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<void()> 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<Data::PhotoMedia> media,
FullMsgId itemId,
TextWithTags text,
bool spoilered,
bool invertCaption,
Fn<void()> 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<Ui::SingleFilePreview>(
@ -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)

View file

@ -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<Window::SessionController*> controller,
not_null<HistoryItem*> item,
TextWithTags &&text,
bool spoilered,
bool invertCaption,
Ui::PreparedList &&list,
Fn<void()> saved);
~EditCaptionBox();
@ -45,18 +48,24 @@ public:
not_null<Window::SessionController*> controller,
FullMsgId itemId,
TextWithTags text,
bool spoilered,
bool invertCaption,
Fn<void()> saved);
static void StartMediaReplace(
not_null<Window::SessionController*> controller,
FullMsgId itemId,
Ui::PreparedList &&list,
TextWithTags text,
bool spoilered,
bool invertCaption,
Fn<void()> saved);
static void StartPhotoEdit(
not_null<Window::SessionController*> controller,
std::shared_ptr<Data::PhotoMedia> media,
FullMsgId itemId,
TextWithTags text,
bool spoilered,
bool invertCaption,
Fn<void()> saved);
protected:
@ -107,7 +116,6 @@ private:
const base::unique_qptr<Ui::EmojiButton> _emojiToggle;
base::unique_qptr<Ui::AbstractSinglePreview> _content;
Fn<bool()> _previewHasSpoiler;
base::unique_qptr<ChatHelpers::TabbedPanel> _emojiPanel;
base::unique_qptr<QObject> _emojiFilter;
@ -118,6 +126,7 @@ private:
std::shared_ptr<Data::PhotoMedia> _photoMedia;
Ui::PreparedList _preparedList;
HistoryView::MediaEditManager _mediaEditManager;
mtpRequestId _saveRequestId = 0;

View file

@ -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() {

View file

@ -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) {

View file

@ -661,7 +661,7 @@ private:
MsgId _editMsgId = 0;
std::shared_ptr<Data::PhotoMedia> _photoEditMedia;
bool _canReplaceMedia = false;
HistoryView::MediaEditSpoilerManager _mediaEditSpoiler;
HistoryView::MediaEditManager _mediaEditManager;
HistoryItem *_replyEditMsg = nullptr;
Ui::Text::String _replyEditMsgText;

View file

@ -21,7 +21,7 @@ struct MessageToEdit {
FullMsgId fullId;
Api::SendOptions options;
TextWithTags textWithTags;
std::optional<bool> spoilerMediaOverride;
bool spoilered = false;
};
struct VoiceToSend {
QByteArray bytes;

View file

@ -216,7 +216,7 @@ private:
bool _repaintScheduled : 1 = false;
bool _inClickable : 1 = false;
HistoryView::MediaEditSpoilerManager _mediaEditSpoiler;
HistoryView::MediaEditManager _mediaEditManager;
const not_null<Data::Session*> _data;
const not_null<Ui::IconButton*> _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(); }));
});
});

View file

@ -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<HistoryItem*> item) {
void MediaEditManager::start(
not_null<HistoryItem*> item,
std::optional<bool> spoilered,
std::optional<bool> 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<const HistoryItem*> removed) {
if (removed == _item) {
@ -39,7 +42,7 @@ void MediaEditSpoilerManager::start(not_null<HistoryItem*> 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<Ui::RpWidget*> parent,
Fn<void()> 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<HistoryItem*> 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

View file

@ -24,11 +24,14 @@ class HistoryItem;
namespace HistoryView {
class MediaEditSpoilerManager final {
class MediaEditManager final {
public:
MediaEditSpoilerManager();
MediaEditManager();
void start(not_null<HistoryItem*> item);
void start(
not_null<HistoryItem*> item,
std::optional<bool> spoilered = {},
std::optional<bool> invertCaption = {});
void apply(SendMenu::Action action);
void cancel();
@ -49,6 +52,8 @@ public:
return _item != nullptr;
}
[[nodiscard]] static bool CanBeSpoilered(not_null<HistoryItem*> item);
private:
base::unique_qptr<Ui::PopupMenu> _menu;
HistoryItem *_item = nullptr;

View file

@ -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<HistoryItem*> item,
Api::SendOptions options,
mtpRequestId *const saveEditMsgRequestId,
std::optional<bool> 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();

View file

@ -247,7 +247,7 @@ private:
not_null<HistoryItem*> item,
Api::SendOptions options,
mtpRequestId *const saveEditMsgRequestId,
std::optional<bool> spoilerMediaOverride);
bool spoilered);
void chooseAttach(std::optional<bool> overrideSendImagesAsPhotos);
[[nodiscard]] SendMenu::Details sendMenuDetails() const;
[[nodiscard]] FullReplyTo replyTo() const;

View file

@ -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<HistoryItem*> item,
Api::SendOptions options,
mtpRequestId *const saveEditMsgRequestId,
std::optional<bool> 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();

View file

@ -218,7 +218,7 @@ private:
not_null<HistoryItem*> item,
Api::SendOptions options,
mtpRequestId *const saveEditMsgRequestId,
std::optional<bool> spoilerMediaOverride);
bool spoilered);
void highlightSingleNewMessage(const Data::MessagesSlice &slice);
void chooseAttach();
[[nodiscard]] SendMenu::Details sendMenuDetails() const;

View file

@ -240,7 +240,7 @@ private:
not_null<HistoryItem*> item,
Api::SendOptions options,
mtpRequestId *const saveEditMsgRequestId,
std::optional<bool> spoilerMediaOverride);
bool spoilered);
void chooseAttach(std::optional<bool> 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<HistoryItem*> item,
Api::SendOptions options,
mtpRequestId *const saveEditMsgRequestId,
std::optional<bool> 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();

View file

@ -78,6 +78,10 @@ bool AbstractSingleMediaPreview::canHaveSpoiler() const {
return supportsSpoilers();
}
rpl::producer<bool> 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()) {

View file

@ -41,6 +41,7 @@ public:
void setSpoiler(bool spoiler);
[[nodiscard]] bool hasSpoiler() const;
[[nodiscard]] bool canHaveSpoiler() const;
[[nodiscard]] rpl::producer<bool> spoileredChanges() const;
protected:
virtual bool supportsSpoilers() const = 0;
@ -79,6 +80,7 @@ private:
int _previewHeight = 0;
std::unique_ptr<SpoilerAnimation> _spoiler;
rpl::event_stream<bool> _spoileredChanges;
const int _minThumbH;
const base::unique_qptr<AttachControlsWidget> _controls;

View file

@ -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;
}

View file

@ -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;