mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-05-29 19:23:58 +02:00
Allow sending videos with covers.
This commit is contained in:
parent
6a415cf232
commit
e05bb75b8a
42 changed files with 571 additions and 104 deletions
|
@ -3883,6 +3883,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_choose_image" = "Choose an image";
|
"lng_choose_image" = "Choose an image";
|
||||||
"lng_choose_file" = "Choose a file";
|
"lng_choose_file" = "Choose a file";
|
||||||
"lng_choose_files" = "Choose Files";
|
"lng_choose_files" = "Choose Files";
|
||||||
|
"lng_choose_cover" = "Choose video cover";
|
||||||
|
"lng_choose_cover_bad" = "Can't use this file as a caption.";
|
||||||
"lng_game_tag" = "Game";
|
"lng_game_tag" = "Game";
|
||||||
|
|
||||||
"lng_context_new_window" = "Open in new window";
|
"lng_context_new_window" = "Open in new window";
|
||||||
|
@ -4028,6 +4030,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_context_disable_spoiler" = "Remove Spoiler";
|
"lng_context_disable_spoiler" = "Remove Spoiler";
|
||||||
"lng_context_make_paid" = "Make This Content Paid";
|
"lng_context_make_paid" = "Make This Content Paid";
|
||||||
"lng_context_change_price" = "Change Price";
|
"lng_context_change_price" = "Change Price";
|
||||||
|
"lng_context_edit_cover" = "Edit Cover";
|
||||||
|
"lng_context_clear_cover" = "Clear Cover";
|
||||||
|
|
||||||
"lng_context_mention" = "Mention";
|
"lng_context_mention" = "Mention";
|
||||||
"lng_context_search_from" = "Search messages";
|
"lng_context_search_from" = "Search messages";
|
||||||
|
|
|
@ -74,6 +74,7 @@ struct MessageToSend {
|
||||||
struct RemoteFileInfo {
|
struct RemoteFileInfo {
|
||||||
MTPInputFile file;
|
MTPInputFile file;
|
||||||
std::optional<MTPInputFile> thumb;
|
std::optional<MTPInputFile> thumb;
|
||||||
|
std::optional<MTPInputPhoto> videoCover;
|
||||||
std::vector<MTPInputDocument> attachedStickers;
|
std::vector<MTPInputDocument> attachedStickers;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -276,16 +276,22 @@ mtpRequestId EditTextMessage(
|
||||||
takeFileReference = [=] { return photo->fileReference(); };
|
takeFileReference = [=] { return photo->fileReference(); };
|
||||||
} else if (const auto document = media->document()) {
|
} else if (const auto document = media->document()) {
|
||||||
using Flag = MTPDinputMediaDocument::Flag;
|
using Flag = MTPDinputMediaDocument::Flag;
|
||||||
|
const auto videoCover = media->videoCover();
|
||||||
|
const auto videoTimestamp = media->videoTimestamp();
|
||||||
const auto flags = Flag()
|
const auto flags = Flag()
|
||||||
| (media->ttlSeconds() ? Flag::f_ttl_seconds : Flag())
|
| (media->ttlSeconds() ? Flag::f_ttl_seconds : Flag())
|
||||||
| (spoilered ? Flag::f_spoiler : Flag());
|
| (spoilered ? Flag::f_spoiler : Flag())
|
||||||
|
| (videoTimestamp ? Flag::f_video_timestamp : Flag())
|
||||||
|
| (videoCover ? Flag::f_video_cover : Flag());
|
||||||
takeInputMedia = [=] {
|
takeInputMedia = [=] {
|
||||||
return MTP_inputMediaDocument(
|
return MTP_inputMediaDocument(
|
||||||
MTP_flags(flags),
|
MTP_flags(flags),
|
||||||
document->mtpInput(),
|
document->mtpInput(),
|
||||||
MTPInputPhoto(), // video_cover
|
(videoCover
|
||||||
|
? videoCover->mtpInput()
|
||||||
|
: MTPInputPhoto()),
|
||||||
MTP_int(media->ttlSeconds()),
|
MTP_int(media->ttlSeconds()),
|
||||||
MTPint(), // video_timestamp
|
MTP_int(videoTimestamp),
|
||||||
MTPstring()); // query
|
MTPstring()); // query
|
||||||
};
|
};
|
||||||
takeFileReference = [=] { return document->fileReference(); };
|
takeFileReference = [=] { return document->fileReference(); };
|
||||||
|
|
|
@ -111,7 +111,8 @@ MTPInputMedia PrepareUploadedDocument(
|
||||||
| (info.thumb ? Flag::f_thumb : Flag())
|
| (info.thumb ? Flag::f_thumb : Flag())
|
||||||
| (item->groupId() ? Flag::f_nosound_video : Flag())
|
| (item->groupId() ? Flag::f_nosound_video : Flag())
|
||||||
| (info.attachedStickers.empty() ? Flag::f_stickers : Flag())
|
| (info.attachedStickers.empty() ? Flag::f_stickers : Flag())
|
||||||
| (ttlSeconds ? Flag::f_ttl_seconds : Flag());
|
| (ttlSeconds ? Flag::f_ttl_seconds : Flag())
|
||||||
|
| (info.videoCover ? Flag::f_video_cover : Flag());
|
||||||
const auto document = item->media()->document();
|
const auto document = item->media()->document();
|
||||||
return MTP_inputMediaUploadedDocument(
|
return MTP_inputMediaUploadedDocument(
|
||||||
MTP_flags(flags),
|
MTP_flags(flags),
|
||||||
|
@ -121,7 +122,7 @@ MTPInputMedia PrepareUploadedDocument(
|
||||||
ComposeSendingDocumentAttributes(document),
|
ComposeSendingDocumentAttributes(document),
|
||||||
MTP_vector<MTPInputDocument>(
|
MTP_vector<MTPInputDocument>(
|
||||||
ranges::to<QVector<MTPInputDocument>>(info.attachedStickers)),
|
ranges::to<QVector<MTPInputDocument>>(info.attachedStickers)),
|
||||||
MTPInputPhoto(), // video_cover
|
info.videoCover.value_or(MTPInputPhoto()),
|
||||||
MTP_int(0), // video_timestamp
|
MTP_int(0), // video_timestamp
|
||||||
MTP_int(ttlSeconds));
|
MTP_int(ttlSeconds));
|
||||||
}
|
}
|
||||||
|
|
|
@ -549,10 +549,11 @@ void SendConfirmedFile(
|
||||||
using Flag = MTPDmessageMediaDocument::Flag;
|
using Flag = MTPDmessageMediaDocument::Flag;
|
||||||
return MTP_messageMediaDocument(
|
return MTP_messageMediaDocument(
|
||||||
MTP_flags(Flag::f_document
|
MTP_flags(Flag::f_document
|
||||||
| (file->spoiler ? Flag::f_spoiler : Flag())),
|
| (file->spoiler ? Flag::f_spoiler : Flag())
|
||||||
|
| (file->videoCover ? Flag::f_video_cover : Flag())),
|
||||||
file->document,
|
file->document,
|
||||||
MTPVector<MTPDocument>(), // alt_documents
|
MTPVector<MTPDocument>(), // alt_documents
|
||||||
MTPPhoto(), // video_cover
|
file->videoCover ? file->videoCover->photo : MTPPhoto(),
|
||||||
MTPint(), // video_timestamp
|
MTPint(), // video_timestamp
|
||||||
MTPint());
|
MTPint());
|
||||||
} else if (file->type == SendMediaType::Audio) {
|
} else if (file->type == SendMediaType::Audio) {
|
||||||
|
@ -561,10 +562,11 @@ void SendConfirmedFile(
|
||||||
return MTP_messageMediaDocument(
|
return MTP_messageMediaDocument(
|
||||||
MTP_flags(Flag::f_document
|
MTP_flags(Flag::f_document
|
||||||
| Flag::f_voice
|
| Flag::f_voice
|
||||||
| (ttlSeconds ? Flag::f_ttl_seconds : Flag())),
|
| (ttlSeconds ? Flag::f_ttl_seconds : Flag())
|
||||||
|
| (file->videoCover ? Flag::f_video_cover : Flag())),
|
||||||
file->document,
|
file->document,
|
||||||
MTPVector<MTPDocument>(), // alt_documents
|
MTPVector<MTPDocument>(), // alt_documents
|
||||||
MTPPhoto(), // video_cover
|
file->videoCover ? file->videoCover->photo : MTPPhoto(),
|
||||||
MTPint(), // video_timestamp
|
MTPint(), // video_timestamp
|
||||||
MTP_int(ttlSeconds));
|
MTP_int(ttlSeconds));
|
||||||
} else if (file->type == SendMediaType::Round) {
|
} else if (file->type == SendMediaType::Round) {
|
||||||
|
|
|
@ -3578,6 +3578,18 @@ void ApiWrap::editMedia(
|
||||||
file.path,
|
file.path,
|
||||||
file.content,
|
file.content,
|
||||||
std::move(file.information),
|
std::move(file.information),
|
||||||
|
(file.videoCover
|
||||||
|
? std::make_unique<FileLoadTask>(
|
||||||
|
&session(),
|
||||||
|
file.videoCover->path,
|
||||||
|
file.videoCover->content,
|
||||||
|
std::move(file.videoCover->information),
|
||||||
|
nullptr,
|
||||||
|
SendMediaType::Photo,
|
||||||
|
to,
|
||||||
|
TextWithTags(),
|
||||||
|
false)
|
||||||
|
: nullptr),
|
||||||
type,
|
type,
|
||||||
to,
|
to,
|
||||||
caption,
|
caption,
|
||||||
|
@ -3619,6 +3631,19 @@ void ApiWrap::sendFiles(
|
||||||
file.path,
|
file.path,
|
||||||
file.content,
|
file.content,
|
||||||
std::move(file.information),
|
std::move(file.information),
|
||||||
|
(file.videoCover
|
||||||
|
? std::make_unique<FileLoadTask>(
|
||||||
|
&session(),
|
||||||
|
file.videoCover->path,
|
||||||
|
file.videoCover->content,
|
||||||
|
std::move(file.videoCover->information),
|
||||||
|
nullptr,
|
||||||
|
SendMediaType::Photo,
|
||||||
|
to,
|
||||||
|
TextWithTags(),
|
||||||
|
false,
|
||||||
|
nullptr)
|
||||||
|
: nullptr),
|
||||||
uploadWithType,
|
uploadWithType,
|
||||||
to,
|
to,
|
||||||
caption,
|
caption,
|
||||||
|
@ -3644,11 +3669,13 @@ void ApiWrap::sendFile(
|
||||||
auto caption = TextWithTags();
|
auto caption = TextWithTags();
|
||||||
const auto spoiler = false;
|
const auto spoiler = false;
|
||||||
const auto information = nullptr;
|
const auto information = nullptr;
|
||||||
|
const auto videoCover = nullptr;
|
||||||
_fileLoader->addTask(std::make_unique<FileLoadTask>(
|
_fileLoader->addTask(std::make_unique<FileLoadTask>(
|
||||||
&session(),
|
&session(),
|
||||||
QString(),
|
QString(),
|
||||||
fileContent,
|
fileContent,
|
||||||
information,
|
information,
|
||||||
|
videoCover,
|
||||||
type,
|
type,
|
||||||
to,
|
to,
|
||||||
caption,
|
caption,
|
||||||
|
@ -4142,19 +4169,30 @@ void ApiWrap::uploadAlbumMedia(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto &fields = document->c_document();
|
const auto &fields = document->c_document();
|
||||||
|
const auto mtpCover = data.vvideo_cover();
|
||||||
|
const auto cover = (mtpCover && mtpCover->type() == mtpc_photo)
|
||||||
|
? &(mtpCover->c_photo())
|
||||||
|
: (const MTPDphoto*)nullptr;
|
||||||
using Flag = MTPDinputMediaDocument::Flag;
|
using Flag = MTPDinputMediaDocument::Flag;
|
||||||
const auto flags = Flag()
|
const auto flags = Flag()
|
||||||
| (data.vttl_seconds() ? Flag::f_ttl_seconds : Flag())
|
| (data.vttl_seconds() ? Flag::f_ttl_seconds : Flag())
|
||||||
| (spoiler ? Flag::f_spoiler : Flag());
|
| (spoiler ? Flag::f_spoiler : Flag())
|
||||||
|
| (data.vvideo_timestamp() ? Flag::f_video_timestamp : Flag())
|
||||||
|
| (cover ? Flag::f_video_cover : Flag());
|
||||||
const auto media = MTP_inputMediaDocument(
|
const auto media = MTP_inputMediaDocument(
|
||||||
MTP_flags(flags),
|
MTP_flags(flags),
|
||||||
MTP_inputDocument(
|
MTP_inputDocument(
|
||||||
fields.vid(),
|
fields.vid(),
|
||||||
fields.vaccess_hash(),
|
fields.vaccess_hash(),
|
||||||
fields.vfile_reference()),
|
fields.vfile_reference()),
|
||||||
MTPInputPhoto(), // video_cover
|
(cover
|
||||||
|
? MTP_inputPhoto(
|
||||||
|
cover->vid(),
|
||||||
|
cover->vaccess_hash(),
|
||||||
|
cover->vfile_reference())
|
||||||
|
: MTPInputPhoto()),
|
||||||
|
MTP_int(data.vvideo_timestamp().value_or_empty()),
|
||||||
MTP_int(data.vttl_seconds().value_or_empty()),
|
MTP_int(data.vttl_seconds().value_or_empty()),
|
||||||
MTPint(), // video_timestamp
|
|
||||||
MTPstring()); // query
|
MTPstring()); // query
|
||||||
sendAlbumWithUploaded(item, groupId, media);
|
sendAlbumWithUploaded(item, groupId, media);
|
||||||
} break;
|
} break;
|
||||||
|
|
|
@ -467,13 +467,16 @@ void EditCaptionBox::rebuildPreview() {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const auto &file = _preparedList.files.front();
|
const auto &file = _preparedList.files.front();
|
||||||
|
const auto isVideoFile = file.isVideoFile();
|
||||||
const auto media = Ui::SingleMediaPreview::Create(
|
const auto media = Ui::SingleMediaPreview::Create(
|
||||||
this,
|
this,
|
||||||
st::defaultComposeControls,
|
st::defaultComposeControls,
|
||||||
gifPaused,
|
gifPaused,
|
||||||
file,
|
file,
|
||||||
[] { return true; },
|
[=](Ui::AttachActionType type) {
|
||||||
|
return (type != Ui::AttachActionType::EditCover)
|
||||||
|
|| isVideoFile;
|
||||||
|
},
|
||||||
Ui::AttachControls::Type::EditOnly);
|
Ui::AttachControls::Type::EditOnly);
|
||||||
_isPhoto = (media && media->isPhoto());
|
_isPhoto = (media && media->isPhoto());
|
||||||
const auto withCheckbox = _isPhoto && CanBeCompressed(_albumType);
|
const auto withCheckbox = _isPhoto && CanBeCompressed(_albumType);
|
||||||
|
@ -719,7 +722,7 @@ void EditCaptionBox::setupPhotoEditorEventHandler() {
|
||||||
controller->uiShow(),
|
controller->uiShow(),
|
||||||
&_preparedList.files.front(),
|
&_preparedList.files.front(),
|
||||||
st::sendMediaPreviewSize,
|
st::sendMediaPreviewSize,
|
||||||
[=] { rebuildPreview(); });
|
[=](bool ok) { if (ok) rebuildPreview(); });
|
||||||
} else {
|
} else {
|
||||||
EditPhotoImage(_controller, _photoMedia, hasSpoiler(), [=](
|
EditPhotoImage(_controller, _photoMedia, hasSpoiler(), [=](
|
||||||
Ui::PreparedList &&list) {
|
Ui::PreparedList &&list) {
|
||||||
|
|
|
@ -242,7 +242,7 @@ SendFilesBox::Block::Block(
|
||||||
int till,
|
int till,
|
||||||
Fn<bool()> gifPaused,
|
Fn<bool()> gifPaused,
|
||||||
SendFilesWay way,
|
SendFilesWay way,
|
||||||
Fn<bool()> canToggleSpoiler)
|
Fn<bool(const Ui::PreparedFile &, Ui::AttachActionType)> actionAllowed)
|
||||||
: _items(items)
|
: _items(items)
|
||||||
, _from(from)
|
, _from(from)
|
||||||
, _till(till) {
|
, _till(till) {
|
||||||
|
@ -260,7 +260,9 @@ SendFilesBox::Block::Block(
|
||||||
st,
|
st,
|
||||||
my,
|
my,
|
||||||
way,
|
way,
|
||||||
std::move(canToggleSpoiler));
|
[=](int index, Ui::AttachActionType type) {
|
||||||
|
return actionAllowed((*_items)[from + index], type);
|
||||||
|
});
|
||||||
_preview.reset(preview);
|
_preview.reset(preview);
|
||||||
} else {
|
} else {
|
||||||
const auto media = Ui::SingleMediaPreview::Create(
|
const auto media = Ui::SingleMediaPreview::Create(
|
||||||
|
@ -268,7 +270,9 @@ SendFilesBox::Block::Block(
|
||||||
st,
|
st,
|
||||||
gifPaused,
|
gifPaused,
|
||||||
first,
|
first,
|
||||||
std::move(canToggleSpoiler));
|
[=](Ui::AttachActionType type) {
|
||||||
|
return actionAllowed((*_items)[from], type);
|
||||||
|
});
|
||||||
if (media) {
|
if (media) {
|
||||||
_isSingleMedia = true;
|
_isSingleMedia = true;
|
||||||
_preview.reset(media);
|
_preview.reset(media);
|
||||||
|
@ -344,6 +348,38 @@ rpl::producer<int> SendFilesBox::Block::itemModifyRequest() const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<int> SendFilesBox::Block::itemEditCoverRequest() const {
|
||||||
|
using namespace rpl::mappers;
|
||||||
|
|
||||||
|
const auto preview = _preview.get();
|
||||||
|
const auto from = _from;
|
||||||
|
if (_isAlbum) {
|
||||||
|
const auto album = static_cast<Ui::AlbumPreview*>(preview);
|
||||||
|
return album->thumbEditCoverRequested() | rpl::map(_1 + from);
|
||||||
|
} else if (_isSingleMedia) {
|
||||||
|
const auto media = static_cast<Ui::SingleMediaPreview*>(preview);
|
||||||
|
return media->editCoverRequests() | rpl::map_to(from);
|
||||||
|
} else {
|
||||||
|
return rpl::never<int>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<int> SendFilesBox::Block::itemClearCoverRequest() const {
|
||||||
|
using namespace rpl::mappers;
|
||||||
|
|
||||||
|
const auto preview = _preview.get();
|
||||||
|
const auto from = _from;
|
||||||
|
if (_isAlbum) {
|
||||||
|
const auto album = static_cast<Ui::AlbumPreview*>(preview);
|
||||||
|
return album->thumbClearCoverRequested() | rpl::map(_1 + from);
|
||||||
|
} else if (_isSingleMedia) {
|
||||||
|
const auto media = static_cast<Ui::SingleMediaPreview*>(preview);
|
||||||
|
return media->clearCoverRequests() | rpl::map_to(from);
|
||||||
|
} else {
|
||||||
|
return rpl::never<int>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rpl::producer<> SendFilesBox::Block::orderUpdated() const {
|
rpl::producer<> SendFilesBox::Block::orderUpdated() const {
|
||||||
if (_isAlbum) {
|
if (_isAlbum) {
|
||||||
const auto album = static_cast<Ui::AlbumPreview*>(_preview.get());
|
const auto album = static_cast<Ui::AlbumPreview*>(_preview.get());
|
||||||
|
@ -1008,7 +1044,16 @@ void SendFilesBox::pushBlock(int from, int till) {
|
||||||
till,
|
till,
|
||||||
gifPaused,
|
gifPaused,
|
||||||
_sendWay.current(),
|
_sendWay.current(),
|
||||||
[=] { return !hasPrice(); });
|
[=](const Ui::PreparedFile &file, Ui::AttachActionType type) {
|
||||||
|
return (type == Ui::AttachActionType::ToggleSpoiler)
|
||||||
|
? !hasPrice()
|
||||||
|
: (type == Ui::AttachActionType::EditCover)
|
||||||
|
? (file.isVideoFile()
|
||||||
|
&& _captionToPeer
|
||||||
|
&& (_captionToPeer->isBroadcast()
|
||||||
|
|| _captionToPeer->isSelf()))
|
||||||
|
: (file.videoCover != nullptr);
|
||||||
|
});
|
||||||
auto &block = _blocks.back();
|
auto &block = _blocks.back();
|
||||||
const auto widget = _inner->add(
|
const auto widget = _inner->add(
|
||||||
block.takeWidget(),
|
block.takeWidget(),
|
||||||
|
@ -1129,7 +1174,79 @@ void SendFilesBox::pushBlock(int from, int till) {
|
||||||
show,
|
show,
|
||||||
&_list.files[index],
|
&_list.files[index],
|
||||||
st::sendMediaPreviewSize,
|
st::sendMediaPreviewSize,
|
||||||
[=] { refreshAllAfterChanges(from); });
|
[=](bool ok) { if (ok) refreshAllAfterChanges(from); });
|
||||||
|
}, widget->lifetime());
|
||||||
|
|
||||||
|
block.itemEditCoverRequest(
|
||||||
|
) | rpl::start_with_next([=, show = _show](int index) {
|
||||||
|
applyBlockChanges();
|
||||||
|
|
||||||
|
const auto replace = [=](Ui::PreparedList list) {
|
||||||
|
if (list.files.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto &entry = _list.files[index];
|
||||||
|
const auto video = entry.information
|
||||||
|
? std::get_if<Ui::PreparedFileInformation::Video>(
|
||||||
|
&entry.information->media)
|
||||||
|
: nullptr;
|
||||||
|
if (!video) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto old = std::shared_ptr<Ui::PreparedFile>(
|
||||||
|
std::move(entry.videoCover));
|
||||||
|
entry.videoCover = std::make_unique<Ui::PreparedFile>(
|
||||||
|
std::move(list.files.front()));
|
||||||
|
Editor::OpenWithPreparedFile(
|
||||||
|
this,
|
||||||
|
show,
|
||||||
|
entry.videoCover.get(),
|
||||||
|
st::sendMediaPreviewSize,
|
||||||
|
crl::guard(this, [=](bool ok) {
|
||||||
|
if (!ok) {
|
||||||
|
_list.files[index].videoCover = old
|
||||||
|
? std::make_unique<Ui::PreparedFile>(
|
||||||
|
std::move(*old))
|
||||||
|
: nullptr;
|
||||||
|
}
|
||||||
|
refreshAllAfterChanges(from);
|
||||||
|
}),
|
||||||
|
video->thumbnail.size());
|
||||||
|
};
|
||||||
|
const auto checkResult = [=](const Ui::PreparedList &list) {
|
||||||
|
if (list.files.empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (list.files.front().type != Ui::PreparedFile::Type::Photo) {
|
||||||
|
show->showToast(tr::lng_choose_cover_bad(tr::now));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
const auto callback = [=](FileDialog::OpenResult &&result) {
|
||||||
|
const auto premium = _show->session().premium();
|
||||||
|
FileDialogCallback(
|
||||||
|
std::move(result),
|
||||||
|
checkResult,
|
||||||
|
replace,
|
||||||
|
premium,
|
||||||
|
show);
|
||||||
|
};
|
||||||
|
|
||||||
|
FileDialog::GetOpenPath(
|
||||||
|
this,
|
||||||
|
tr::lng_choose_cover(tr::now),
|
||||||
|
FileDialog::ImagesFilter(),
|
||||||
|
crl::guard(this, callback));
|
||||||
|
}, widget->lifetime());
|
||||||
|
|
||||||
|
block.itemClearCoverRequest(
|
||||||
|
) | rpl::start_with_next([=](int index) {
|
||||||
|
applyBlockChanges();
|
||||||
|
refreshAllAfterChanges(from, [&] {
|
||||||
|
auto &entry = _list.files[index];
|
||||||
|
entry.videoCover = nullptr;
|
||||||
|
});
|
||||||
}, widget->lifetime());
|
}, widget->lifetime());
|
||||||
|
|
||||||
block.orderUpdated() | rpl::start_with_next([=]{
|
block.orderUpdated() | rpl::start_with_next([=]{
|
||||||
|
|
|
@ -153,7 +153,9 @@ private:
|
||||||
int till,
|
int till,
|
||||||
Fn<bool()> gifPaused,
|
Fn<bool()> gifPaused,
|
||||||
Ui::SendFilesWay way,
|
Ui::SendFilesWay way,
|
||||||
Fn<bool()> canToggleSpoiler);
|
Fn<bool(
|
||||||
|
const Ui::PreparedFile &,
|
||||||
|
Ui::AttachActionType)> actionAllowed);
|
||||||
Block(Block &&other) = default;
|
Block(Block &&other) = default;
|
||||||
Block &operator=(Block &&other) = default;
|
Block &operator=(Block &&other) = default;
|
||||||
|
|
||||||
|
@ -164,6 +166,8 @@ private:
|
||||||
[[nodiscard]] rpl::producer<int> itemDeleteRequest() const;
|
[[nodiscard]] rpl::producer<int> itemDeleteRequest() const;
|
||||||
[[nodiscard]] rpl::producer<int> itemReplaceRequest() const;
|
[[nodiscard]] rpl::producer<int> itemReplaceRequest() const;
|
||||||
[[nodiscard]] rpl::producer<int> itemModifyRequest() const;
|
[[nodiscard]] rpl::producer<int> itemModifyRequest() const;
|
||||||
|
[[nodiscard]] rpl::producer<int> itemEditCoverRequest() const;
|
||||||
|
[[nodiscard]] rpl::producer<int> itemClearCoverRequest() const;
|
||||||
[[nodiscard]] rpl::producer<> orderUpdated() const;
|
[[nodiscard]] rpl::producer<> orderUpdated() const;
|
||||||
|
|
||||||
void setSendWay(Ui::SendFilesWay way);
|
void setSendWay(Ui::SendFilesWay way);
|
||||||
|
|
|
@ -329,6 +329,7 @@ not_null<DocumentData*> GenerateLocalSticker(
|
||||||
path,
|
path,
|
||||||
QByteArray(),
|
QByteArray(),
|
||||||
nullptr,
|
nullptr,
|
||||||
|
nullptr,
|
||||||
SendMediaType::File,
|
SendMediaType::File,
|
||||||
FileLoadTo(0, {}, {}, 0),
|
FileLoadTo(0, {}, {}, 0),
|
||||||
{},
|
{},
|
||||||
|
|
|
@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_streaming.h"
|
#include "data/data_streaming.h"
|
||||||
#include "data/data_document_media.h"
|
#include "data/data_document_media.h"
|
||||||
#include "data/data_reply_preview.h"
|
#include "data/data_reply_preview.h"
|
||||||
|
#include "data/data_web_page.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "inline_bots/inline_bot_layout_item.h"
|
#include "inline_bots/inline_bot_layout_item.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
@ -1881,3 +1882,18 @@ void DocumentData::collectLocalData(not_null<DocumentData*> local) {
|
||||||
session().local().writeFileLocation(mediaKey(), _location);
|
session().local().writeFileLocation(mediaKey(), _location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PhotoData *LookupVideoCover(
|
||||||
|
not_null<DocumentData*> document,
|
||||||
|
HistoryItem *item) {
|
||||||
|
const auto media = item ? item->media() : nullptr;
|
||||||
|
if (const auto webpage = media ? media->webpage() : nullptr) {
|
||||||
|
if (webpage->document == document && webpage->photoIsVideoCover) {
|
||||||
|
return webpage->photo;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return (media && media->document() == document)
|
||||||
|
? media->videoCover()
|
||||||
|
: nullptr;
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_cloud_file.h"
|
#include "data/data_cloud_file.h"
|
||||||
#include "core/file_location.h"
|
#include "core/file_location.h"
|
||||||
|
|
||||||
|
class HistoryItem;
|
||||||
|
class PhotoData;
|
||||||
enum class ChatRestriction;
|
enum class ChatRestriction;
|
||||||
class mtpFileLoader;
|
class mtpFileLoader;
|
||||||
|
|
||||||
|
@ -398,6 +400,10 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] PhotoData *LookupVideoCover(
|
||||||
|
not_null<DocumentData*> document,
|
||||||
|
HistoryItem *item);
|
||||||
|
|
||||||
VoiceWaveform documentWaveformDecode(const QByteArray &encoded5bit);
|
VoiceWaveform documentWaveformDecode(const QByteArray &encoded5bit);
|
||||||
QByteArray documentWaveformEncode5bit(const VoiceWaveform &waveform);
|
QByteArray documentWaveformEncode5bit(const VoiceWaveform &waveform);
|
||||||
|
|
||||||
|
|
|
@ -1302,7 +1302,11 @@ bool MediaFile::updateSentMedia(const MTPMessageMedia &media) {
|
||||||
"or with ttl_seconds in updateSentMedia()"));
|
"or with ttl_seconds in updateSentMedia()"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
parent()->history()->owner().documentConvert(_document, *content);
|
const auto owner = &parent()->history()->owner();
|
||||||
|
owner->documentConvert(_document, *content);
|
||||||
|
if (const auto cover = _videoCover ? data.vvideo_cover() : nullptr) {
|
||||||
|
owner->photoConvert(_videoCover, *cover);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1334,7 +1338,6 @@ std::unique_ptr<HistoryView::Media> MediaFile::createView(
|
||||||
message,
|
message,
|
||||||
realParent,
|
realParent,
|
||||||
_document,
|
_document,
|
||||||
_videoCover,
|
|
||||||
_spoiler);
|
_spoiler);
|
||||||
}
|
}
|
||||||
} else if (_document->isAnimation() || _document->isVideoFile()) {
|
} else if (_document->isAnimation() || _document->isVideoFile()) {
|
||||||
|
@ -1342,7 +1345,6 @@ std::unique_ptr<HistoryView::Media> MediaFile::createView(
|
||||||
message,
|
message,
|
||||||
realParent,
|
realParent,
|
||||||
_document,
|
_document,
|
||||||
_videoCover,
|
|
||||||
_spoiler);
|
_spoiler);
|
||||||
} else if (_document->isTheme() && _document->hasThumbnail()) {
|
} else if (_document->isTheme() && _document->hasThumbnail()) {
|
||||||
return std::make_unique<HistoryView::ThemeDocument>(
|
return std::make_unique<HistoryView::ThemeDocument>(
|
||||||
|
@ -2611,7 +2613,6 @@ std::unique_ptr<HistoryView::Media> MediaStory::createView(
|
||||||
message,
|
message,
|
||||||
realParent,
|
realParent,
|
||||||
story->document(),
|
story->document(),
|
||||||
nullptr,
|
|
||||||
spoiler);
|
spoiler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,15 @@ QSizeF FlipSizeByRotation(const QSizeF &size, int angle) {
|
||||||
return (((angle / 90) % 2) == 1) ? size.transposed() : size;
|
return (((angle / 90) % 2) == 1) ? size.transposed() : size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] QRectF OriginalCrop(QSize outer, QSize inner) {
|
||||||
|
const auto size = inner.scaled(outer, Qt::KeepAspectRatio);
|
||||||
|
return QRectF(
|
||||||
|
(outer.width() - size.width()) / 2,
|
||||||
|
(outer.height() - size.height()) / 2,
|
||||||
|
size.width(),
|
||||||
|
size.height());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Crop::Crop(
|
Crop::Crop(
|
||||||
|
@ -60,6 +69,8 @@ Crop::Crop(
|
||||||
, _data(std::move(data))
|
, _data(std::move(data))
|
||||||
, _cropOriginal(modifications.crop.isValid()
|
, _cropOriginal(modifications.crop.isValid()
|
||||||
? modifications.crop
|
? modifications.crop
|
||||||
|
: !_data.exactSize.isEmpty()
|
||||||
|
? OriginalCrop(_imageSize, _data.exactSize)
|
||||||
: QRectF(QPoint(), _imageSize))
|
: QRectF(QPoint(), _imageSize))
|
||||||
, _angle(modifications.angle)
|
, _angle(modifications.angle)
|
||||||
, _flipped(modifications.flipped)
|
, _flipped(modifications.flipped)
|
||||||
|
|
|
@ -32,6 +32,7 @@ struct EditorData {
|
||||||
|
|
||||||
TextWithEntities about;
|
TextWithEntities about;
|
||||||
QString confirm;
|
QString confirm;
|
||||||
|
QSize exactSize;
|
||||||
CropType cropType = CropType::Rect;
|
CropType cropType = CropType::Rect;
|
||||||
bool keepAspectRatio = false;
|
bool keepAspectRatio = false;
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,22 +26,26 @@ void OpenWithPreparedFile(
|
||||||
std::shared_ptr<ChatHelpers::Show> show,
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
not_null<Ui::PreparedFile*> file,
|
not_null<Ui::PreparedFile*> file,
|
||||||
int previewWidth,
|
int previewWidth,
|
||||||
Fn<void()> &&doneCallback) {
|
Fn<void(bool ok)> &&doneCallback,
|
||||||
|
QSize exactSize) {
|
||||||
using ImageInfo = Ui::PreparedFileInformation::Image;
|
using ImageInfo = Ui::PreparedFileInformation::Image;
|
||||||
const auto image = std::get_if<ImageInfo>(&file->information->media);
|
const auto image = std::get_if<ImageInfo>(&file->information->media);
|
||||||
if (!image) {
|
if (!image) {
|
||||||
|
doneCallback(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto photoType = (file->type == Ui::PreparedFile::Type::Photo);
|
const auto photoType = (file->type == Ui::PreparedFile::Type::Photo);
|
||||||
const auto modifiedFileType = (file->type == Ui::PreparedFile::Type::File)
|
const auto modifiedFileType = (file->type == Ui::PreparedFile::Type::File)
|
||||||
&& !image->modifications.empty();
|
&& !image->modifications.empty();
|
||||||
if (!photoType && !modifiedFileType) {
|
if (!photoType && !modifiedFileType) {
|
||||||
|
doneCallback(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto sideLimit = PhotoSideLimit();
|
const auto sideLimit = PhotoSideLimit();
|
||||||
auto callback = [=, done = std::move(doneCallback)](
|
const auto accepted = std::make_shared<bool>();
|
||||||
const PhotoModifications &mods) {
|
auto callback = [=](const PhotoModifications &mods) {
|
||||||
|
*accepted = true;
|
||||||
image->modifications = mods;
|
image->modifications = mods;
|
||||||
Storage::UpdateImageDetails(*file, previewWidth, sideLimit);
|
Storage::UpdateImageDetails(*file, previewWidth, sideLimit);
|
||||||
{
|
{
|
||||||
|
@ -51,7 +55,7 @@ void OpenWithPreparedFile(
|
||||||
? PreparedFile::Type::Photo
|
? PreparedFile::Type::Photo
|
||||||
: PreparedFile::Type::File;
|
: PreparedFile::Type::File;
|
||||||
}
|
}
|
||||||
done();
|
doneCallback(true);
|
||||||
};
|
};
|
||||||
auto copy = image->data;
|
auto copy = image->data;
|
||||||
const auto fileImage = std::make_shared<Image>(std::move(copy));
|
const auto fileImage = std::make_shared<Image>(std::move(copy));
|
||||||
|
@ -60,10 +64,16 @@ void OpenWithPreparedFile(
|
||||||
show,
|
show,
|
||||||
show,
|
show,
|
||||||
fileImage,
|
fileImage,
|
||||||
image->modifications);
|
image->modifications,
|
||||||
|
EditorData{ .exactSize = exactSize, .keepAspectRatio = true });
|
||||||
const auto raw = editor.get();
|
const auto raw = editor.get();
|
||||||
auto layer = std::make_unique<LayerWidget>(parent, std::move(editor));
|
auto layer = std::make_unique<LayerWidget>(parent, std::move(editor));
|
||||||
InitEditorLayer(layer.get(), raw, std::move(callback));
|
InitEditorLayer(layer.get(), raw, std::move(callback));
|
||||||
|
QObject::connect(layer.get(), &QObject::destroyed, [=] {
|
||||||
|
if (!*accepted) {
|
||||||
|
doneCallback(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
show->showLayer(std::move(layer), Ui::LayerOption::KeepOther);
|
show->showLayer(std::move(layer), Ui::LayerOption::KeepOther);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,8 @@ void OpenWithPreparedFile(
|
||||||
std::shared_ptr<ChatHelpers::Show> show,
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
not_null<Ui::PreparedFile*> file,
|
not_null<Ui::PreparedFile*> file,
|
||||||
int previewWidth,
|
int previewWidth,
|
||||||
Fn<void()> &&doneCallback);
|
Fn<void(bool ok)> &&doneCallback,
|
||||||
|
QSize exactSize = {});
|
||||||
|
|
||||||
void PrepareProfilePhoto(
|
void PrepareProfilePhoto(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
|
|
|
@ -143,11 +143,10 @@ Gif::Gif(
|
||||||
not_null<Element*> parent,
|
not_null<Element*> parent,
|
||||||
not_null<HistoryItem*> realParent,
|
not_null<HistoryItem*> realParent,
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
PhotoData *videoCover,
|
|
||||||
bool spoiler)
|
bool spoiler)
|
||||||
: File(parent, realParent)
|
: File(parent, realParent)
|
||||||
, _data(document)
|
, _data(document)
|
||||||
, _videoCover(videoCover)
|
, _videoCover(LookupVideoCover(document, realParent))
|
||||||
, _storyId(realParent->media()
|
, _storyId(realParent->media()
|
||||||
? realParent->media()->storyId()
|
? realParent->media()->storyId()
|
||||||
: FullStoryId())
|
: FullStoryId())
|
||||||
|
@ -1805,12 +1804,18 @@ void Gif::validateGroupedCache(
|
||||||
|
|
||||||
ensureDataMediaCreated();
|
ensureDataMediaCreated();
|
||||||
|
|
||||||
const auto good = _dataMedia->goodThumbnail();
|
const auto good = _videoCoverMedia
|
||||||
const auto thumb = _dataMedia->thumbnail();
|
? _videoCoverMedia->image(Data::PhotoSize::Large)
|
||||||
|
: _dataMedia->goodThumbnail();
|
||||||
|
const auto thumb = _videoCoverMedia
|
||||||
|
? nullptr
|
||||||
|
: _dataMedia->thumbnail();
|
||||||
const auto image = good
|
const auto image = good
|
||||||
? good
|
? good
|
||||||
: thumb
|
: thumb
|
||||||
? thumb
|
? thumb
|
||||||
|
: _videoCoverMedia
|
||||||
|
? _videoCoverMedia->thumbnailInline()
|
||||||
: _dataMedia->thumbnailInline();
|
: _dataMedia->thumbnailInline();
|
||||||
const auto blur = !good
|
const auto blur = !good
|
||||||
&& (!thumb
|
&& (!thumb
|
||||||
|
|
|
@ -54,7 +54,6 @@ public:
|
||||||
not_null<Element*> parent,
|
not_null<Element*> parent,
|
||||||
not_null<HistoryItem*> realParent,
|
not_null<HistoryItem*> realParent,
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
PhotoData *videoCover,
|
|
||||||
bool spoiler);
|
bool spoiler);
|
||||||
~Gif();
|
~Gif();
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,6 @@ std::unique_ptr<Media> CreateAttach(
|
||||||
parent,
|
parent,
|
||||||
parent->data(),
|
parent->data(),
|
||||||
document,
|
document,
|
||||||
photo,
|
|
||||||
spoiler);
|
spoiler);
|
||||||
} else if (document->isWallPaper() || document->isTheme()) {
|
} else if (document->isWallPaper() || document->isTheme()) {
|
||||||
return std::make_unique<ThemeDocument>(
|
return std::make_unique<ThemeDocument>(
|
||||||
|
|
|
@ -37,9 +37,7 @@ QByteArray SessionSettings::serialize() const {
|
||||||
+ _groupStickersSectionHidden.size() * sizeof(quint64)
|
+ _groupStickersSectionHidden.size() * sizeof(quint64)
|
||||||
+ sizeof(qint32) * 4
|
+ sizeof(qint32) * 4
|
||||||
+ Serialize::bytearraySize(autoDownload)
|
+ Serialize::bytearraySize(autoDownload)
|
||||||
+ sizeof(qint32) * 4
|
+ sizeof(qint32) * 11
|
||||||
+ sizeof(qint32) * 5
|
|
||||||
+ sizeof(qint32)
|
|
||||||
+ (_mutePeriods.size() * sizeof(quint64))
|
+ (_mutePeriods.size() * sizeof(quint64))
|
||||||
+ sizeof(qint32) * 2
|
+ sizeof(qint32) * 2
|
||||||
+ _hiddenPinnedMessages.size() * (sizeof(quint64) * 3)
|
+ _hiddenPinnedMessages.size() * (sizeof(quint64) * 3)
|
||||||
|
@ -69,6 +67,7 @@ QByteArray SessionSettings::serialize() const {
|
||||||
<< qint32(_archiveCollapsed.current() ? 1 : 0)
|
<< qint32(_archiveCollapsed.current() ? 1 : 0)
|
||||||
<< qint32(_archiveInMainMenu.current() ? 1 : 0)
|
<< qint32(_archiveInMainMenu.current() ? 1 : 0)
|
||||||
<< qint32(_skipArchiveInSearch.current() ? 1 : 0)
|
<< qint32(_skipArchiveInSearch.current() ? 1 : 0)
|
||||||
|
<< qint32(0) // old _mediaLastPlaybackPosition.size());
|
||||||
<< qint32(0) // very old _hiddenPinnedMessages.size());
|
<< qint32(0) // very old _hiddenPinnedMessages.size());
|
||||||
<< qint32(_dialogsFiltersEnabled ? 1 : 0)
|
<< qint32(_dialogsFiltersEnabled ? 1 : 0)
|
||||||
<< qint32(_supportAllSilent ? 1 : 0)
|
<< qint32(_supportAllSilent ? 1 : 0)
|
||||||
|
|
|
@ -2328,11 +2328,22 @@ void OverlayWidget::assignMediaPointer(DocumentData *document) {
|
||||||
_quality = Core::App().settings().videoQuality();
|
_quality = Core::App().settings().videoQuality();
|
||||||
_chosenQuality = _document->chooseQuality(_message, _quality);
|
_chosenQuality = _document->chooseQuality(_message, _quality);
|
||||||
_documentMedia = _document->createMediaView();
|
_documentMedia = _document->createMediaView();
|
||||||
_documentMedia->goodThumbnailWanted();
|
_videoCover = LookupVideoCover(_document, _message);
|
||||||
_documentMedia->thumbnailWanted(fileOrigin());
|
if (_videoCover) {
|
||||||
|
_videoCoverMedia = _videoCover->createMediaView();
|
||||||
|
_videoCoverMedia->wanted(
|
||||||
|
Data::PhotoSize::Large,
|
||||||
|
fileOrigin());
|
||||||
|
} else {
|
||||||
|
_videoCoverMedia = nullptr;
|
||||||
|
_documentMedia->goodThumbnailWanted();
|
||||||
|
_documentMedia->thumbnailWanted(fileOrigin());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
_chosenQuality = nullptr;
|
_chosenQuality = nullptr;
|
||||||
_documentMedia = nullptr;
|
_documentMedia = nullptr;
|
||||||
|
_videoCover = nullptr;
|
||||||
|
_videoCoverMedia = nullptr;
|
||||||
}
|
}
|
||||||
_documentLoadingTo = QString();
|
_documentLoadingTo = QString();
|
||||||
}
|
}
|
||||||
|
@ -2346,6 +2357,8 @@ void OverlayWidget::assignMediaPointer(not_null<PhotoData*> photo) {
|
||||||
_document = nullptr;
|
_document = nullptr;
|
||||||
_documentMedia = nullptr;
|
_documentMedia = nullptr;
|
||||||
_documentLoadingTo = QString();
|
_documentLoadingTo = QString();
|
||||||
|
_videoCover = nullptr;
|
||||||
|
_videoCoverMedia = nullptr;
|
||||||
if (_photo != photo) {
|
if (_photo != photo) {
|
||||||
_flip = {};
|
_flip = {};
|
||||||
_photo = photo;
|
_photo = photo;
|
||||||
|
@ -3988,13 +4001,19 @@ void OverlayWidget::initStreamingThumbnail() {
|
||||||
}
|
}
|
||||||
return thumbnail;
|
return thumbnail;
|
||||||
};
|
};
|
||||||
const auto good = _document
|
const auto good = _videoCover
|
||||||
|
? _videoCoverMedia->image(Data::PhotoSize::Large)
|
||||||
|
: _document
|
||||||
? _documentMedia->goodThumbnail()
|
? _documentMedia->goodThumbnail()
|
||||||
: _photoMedia->image(Data::PhotoSize::Large);
|
: _photoMedia->image(Data::PhotoSize::Large);
|
||||||
const auto thumbnail = _document
|
const auto thumbnail = _videoCover
|
||||||
|
? _videoCoverMedia->image(Data::PhotoSize::Small)
|
||||||
|
: _document
|
||||||
? _documentMedia->thumbnail()
|
? _documentMedia->thumbnail()
|
||||||
: computePhotoThumbnail();
|
: computePhotoThumbnail();
|
||||||
const auto blurred = _document
|
const auto blurred = _videoCover
|
||||||
|
? _videoCoverMedia->thumbnailInline()
|
||||||
|
: _document
|
||||||
? _documentMedia->thumbnailInline()
|
? _documentMedia->thumbnailInline()
|
||||||
: _photoMedia->thumbnailInline();
|
: _photoMedia->thumbnailInline();
|
||||||
const auto size = _photo
|
const auto size = _photo
|
||||||
|
|
|
@ -561,10 +561,12 @@ private:
|
||||||
PhotoData *_photo = nullptr;
|
PhotoData *_photo = nullptr;
|
||||||
DocumentData *_document = nullptr;
|
DocumentData *_document = nullptr;
|
||||||
DocumentData *_chosenQuality = nullptr;
|
DocumentData *_chosenQuality = nullptr;
|
||||||
|
PhotoData *_videoCover = nullptr;
|
||||||
Media::VideoQuality _quality;
|
Media::VideoQuality _quality;
|
||||||
QString _documentLoadingTo;
|
QString _documentLoadingTo;
|
||||||
std::shared_ptr<Data::PhotoMedia> _photoMedia;
|
std::shared_ptr<Data::PhotoMedia> _photoMedia;
|
||||||
std::shared_ptr<Data::DocumentMedia> _documentMedia;
|
std::shared_ptr<Data::DocumentMedia> _documentMedia;
|
||||||
|
std::shared_ptr<Data::PhotoMedia> _videoCoverMedia;
|
||||||
base::flat_set<std::shared_ptr<Data::PhotoMedia>> _preloadPhotos;
|
base::flat_set<std::shared_ptr<Data::PhotoMedia>> _preloadPhotos;
|
||||||
base::flat_set<std::shared_ptr<Data::DocumentMedia>> _preloadDocuments;
|
base::flat_set<std::shared_ptr<Data::DocumentMedia>> _preloadDocuments;
|
||||||
int _rotation = 0;
|
int _rotation = 0;
|
||||||
|
|
|
@ -486,6 +486,7 @@ Video::Video(
|
||||||
MediaOptions options)
|
MediaOptions options)
|
||||||
: RadialProgressItem(delegate, parent)
|
: RadialProgressItem(delegate, parent)
|
||||||
, _data(video)
|
, _data(video)
|
||||||
|
, _videoCover(LookupVideoCover(video, parent))
|
||||||
, _duration(Ui::FormatDurationText(_data->duration() / 1000))
|
, _duration(Ui::FormatDurationText(_data->duration() / 1000))
|
||||||
, _spoiler(options.spoiler ? std::make_unique<Ui::SpoilerAnimation>([=] {
|
, _spoiler(options.spoiler ? std::make_unique<Ui::SpoilerAnimation>([=] {
|
||||||
delegate->repaintItem(this);
|
delegate->repaintItem(this);
|
||||||
|
@ -493,7 +494,13 @@ Video::Video(
|
||||||
, _pinned(options.pinned)
|
, _pinned(options.pinned)
|
||||||
, _story(options.story) {
|
, _story(options.story) {
|
||||||
setDocumentLinks(_data);
|
setDocumentLinks(_data);
|
||||||
_data->loadThumbnail(parent->fullId());
|
if (!_videoCover) {
|
||||||
|
_data->loadThumbnail(parent->fullId());
|
||||||
|
} else if (_videoCover->inlineThumbnailBytes().isEmpty()
|
||||||
|
&& (_videoCover->hasExact(Data::PhotoSize::Small)
|
||||||
|
|| _videoCover->hasExact(Data::PhotoSize::Thumbnail))) {
|
||||||
|
_videoCover->load(Data::PhotoSize::Small, parent->fullId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Video::~Video() = default;
|
Video::~Video() = default;
|
||||||
|
@ -516,9 +523,19 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const
|
||||||
ensureDataMediaCreated();
|
ensureDataMediaCreated();
|
||||||
|
|
||||||
const auto selected = (selection == FullSelection);
|
const auto selected = (selection == FullSelection);
|
||||||
const auto blurred = _dataMedia->thumbnailInline();
|
const auto blurred = _videoCover
|
||||||
const auto thumbnail = _spoiler ? nullptr : _dataMedia->thumbnail();
|
? _videoCoverMedia->thumbnailInline()
|
||||||
const auto good = _spoiler ? nullptr : _dataMedia->goodThumbnail();
|
: _dataMedia->thumbnailInline();
|
||||||
|
const auto thumbnail = _spoiler
|
||||||
|
? nullptr
|
||||||
|
: _videoCover
|
||||||
|
? _videoCoverMedia->image(Data::PhotoSize::Small)
|
||||||
|
: _dataMedia->thumbnail();
|
||||||
|
const auto good = _spoiler
|
||||||
|
? nullptr
|
||||||
|
: _videoCover
|
||||||
|
? _videoCoverMedia->image(Data::PhotoSize::Large)
|
||||||
|
: _dataMedia->goodThumbnail();
|
||||||
|
|
||||||
bool loaded = dataLoaded(), displayLoading = _data->displayLoading();
|
bool loaded = dataLoaded(), displayLoading = _data->displayLoading();
|
||||||
if (displayLoading) {
|
if (displayLoading) {
|
||||||
|
@ -626,12 +643,17 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const
|
||||||
}
|
}
|
||||||
|
|
||||||
void Video::ensureDataMediaCreated() const {
|
void Video::ensureDataMediaCreated() const {
|
||||||
if (_dataMedia) {
|
if (_dataMedia && (!_videoCover || _videoCoverMedia)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_dataMedia = _data->createMediaView();
|
_dataMedia = _data->createMediaView();
|
||||||
_dataMedia->goodThumbnailWanted();
|
if (_videoCover) {
|
||||||
_dataMedia->thumbnailWanted(parent()->fullId());
|
_videoCoverMedia = _videoCover->createMediaView();
|
||||||
|
_videoCover->load(Data::PhotoSize::Large, parent()->fullId());
|
||||||
|
} else {
|
||||||
|
_dataMedia->goodThumbnailWanted();
|
||||||
|
_dataMedia->thumbnailWanted(parent()->fullId());
|
||||||
|
}
|
||||||
delegate()->registerHeavyItem(this);
|
delegate()->registerHeavyItem(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -316,7 +316,9 @@ private:
|
||||||
void updateStatusText();
|
void updateStatusText();
|
||||||
|
|
||||||
const not_null<DocumentData*> _data;
|
const not_null<DocumentData*> _data;
|
||||||
|
PhotoData *_videoCover = nullptr;
|
||||||
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
|
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
|
||||||
|
mutable std::shared_ptr<Data::PhotoMedia> _videoCoverMedia;
|
||||||
StatusText _status;
|
StatusText _status;
|
||||||
|
|
||||||
QString _duration;
|
QString _duration;
|
||||||
|
|
|
@ -335,6 +335,11 @@ void Uploader::upload(
|
||||||
if (file->type == SendMediaType::ThemeFile) {
|
if (file->type == SendMediaType::ThemeFile) {
|
||||||
document->checkWallPaperProperties();
|
document->checkWallPaperProperties();
|
||||||
}
|
}
|
||||||
|
if (file->videoCover) {
|
||||||
|
session().data().processPhoto(
|
||||||
|
file->videoCover->photo,
|
||||||
|
file->videoCover->photoThumbs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_queue.push_back({ itemId, file });
|
_queue.push_back({ itemId, file });
|
||||||
if (!_nextTimer.isActive()) {
|
if (!_nextTimer.isActive()) {
|
||||||
|
@ -348,9 +353,26 @@ void Uploader::failed(FullMsgId itemId) {
|
||||||
const auto entry = std::move(*i);
|
const auto entry = std::move(*i);
|
||||||
_queue.erase(i);
|
_queue.erase(i);
|
||||||
notifyFailed(entry);
|
notifyFailed(entry);
|
||||||
|
} else if (const auto coverId = _videoIdToCoverId.take(itemId)) {
|
||||||
|
if (const auto video = _videoWaitingCover.take(*coverId)) {
|
||||||
|
const auto document = session().data().document(video->id);
|
||||||
|
if (document->uploading()) {
|
||||||
|
document->status = FileUploadFailed;
|
||||||
|
}
|
||||||
|
_documentFailed.fire_copy(video->fullId);
|
||||||
|
}
|
||||||
|
failed(*coverId);
|
||||||
|
} else if (const auto video = _videoWaitingCover.take(itemId)) {
|
||||||
|
_videoIdToCoverId.remove(video->fullId);
|
||||||
|
const auto document = session().data().document(video->id);
|
||||||
|
if (document->uploading()) {
|
||||||
|
document->status = FileUploadFailed;
|
||||||
|
}
|
||||||
|
_documentFailed.fire_copy(video->fullId);
|
||||||
}
|
}
|
||||||
cancelRequests(itemId);
|
cancelRequests(itemId);
|
||||||
maybeFinishFront();
|
maybeFinishFront();
|
||||||
|
|
||||||
crl::on_main(this, [=] {
|
crl::on_main(this, [=] {
|
||||||
maybeSend();
|
maybeSend();
|
||||||
});
|
});
|
||||||
|
@ -854,7 +876,8 @@ void Uploader::finishFront() {
|
||||||
MTP_int(entry.parts->size()),
|
MTP_int(entry.parts->size()),
|
||||||
MTP_string(photoFilename),
|
MTP_string(photoFilename),
|
||||||
MTP_bytes(md5));
|
MTP_bytes(md5));
|
||||||
_photoReady.fire({
|
auto ready = UploadedMedia{
|
||||||
|
.id = entry.file->id,
|
||||||
.fullId = entry.itemId,
|
.fullId = entry.itemId,
|
||||||
.info = {
|
.info = {
|
||||||
.file = file,
|
.file = file,
|
||||||
|
@ -862,7 +885,13 @@ void Uploader::finishFront() {
|
||||||
},
|
},
|
||||||
.options = options,
|
.options = options,
|
||||||
.edit = edit,
|
.edit = edit,
|
||||||
});
|
};
|
||||||
|
const auto i = _videoWaitingCover.find(entry.itemId);
|
||||||
|
if (i != end(_videoWaitingCover)) {
|
||||||
|
uploadCoverAsPhoto(i->second.fullId, std::move(ready));
|
||||||
|
} else {
|
||||||
|
_photoReady.fire(std::move(ready));
|
||||||
|
}
|
||||||
} else if (entry.file->type == SendMediaType::File
|
} else if (entry.file->type == SendMediaType::File
|
||||||
|| entry.file->type == SendMediaType::ThemeFile
|
|| entry.file->type == SendMediaType::ThemeFile
|
||||||
|| entry.file->type == SendMediaType::Audio
|
|| entry.file->type == SendMediaType::Audio
|
||||||
|
@ -892,7 +921,8 @@ void Uploader::finishFront() {
|
||||||
MTP_string(thumbFilename),
|
MTP_string(thumbFilename),
|
||||||
MTP_bytes(thumbMd5));
|
MTP_bytes(thumbMd5));
|
||||||
}();
|
}();
|
||||||
_documentReady.fire({
|
auto ready = UploadedMedia{
|
||||||
|
.id = entry.file->id,
|
||||||
.fullId = entry.itemId,
|
.fullId = entry.itemId,
|
||||||
.info = {
|
.info = {
|
||||||
.file = file,
|
.file = file,
|
||||||
|
@ -901,7 +931,12 @@ void Uploader::finishFront() {
|
||||||
},
|
},
|
||||||
.options = options,
|
.options = options,
|
||||||
.edit = edit,
|
.edit = edit,
|
||||||
});
|
};
|
||||||
|
if (entry.file->videoCover) {
|
||||||
|
uploadVideoCover(std::move(ready), entry.file->videoCover);
|
||||||
|
} else {
|
||||||
|
_documentReady.fire(std::move(ready));
|
||||||
|
}
|
||||||
} else if (entry.file->type == SendMediaType::Secure) {
|
} else if (entry.file->type == SendMediaType::Secure) {
|
||||||
_secureReady.fire({
|
_secureReady.fire({
|
||||||
entry.itemId,
|
entry.itemId,
|
||||||
|
@ -916,4 +951,54 @@ void Uploader::partFailed(const MTP::Error &error, mtpRequestId requestId) {
|
||||||
failed(request.itemId);
|
failed(request.itemId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Uploader::uploadVideoCover(
|
||||||
|
UploadedMedia &&video,
|
||||||
|
std::shared_ptr<FilePrepareResult> videoCover) {
|
||||||
|
const auto coverId = FullMsgId(
|
||||||
|
videoCover->to.peer,
|
||||||
|
session().data().nextLocalMessageId());
|
||||||
|
_videoIdToCoverId.emplace(video.fullId, coverId);
|
||||||
|
_videoWaitingCover.emplace(coverId, std::move(video));
|
||||||
|
|
||||||
|
upload(coverId, videoCover);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Uploader::uploadCoverAsPhoto(
|
||||||
|
FullMsgId videoId,
|
||||||
|
UploadedMedia &&cover) {
|
||||||
|
const auto coverId = cover.fullId;
|
||||||
|
_api->request(MTPmessages_UploadMedia(
|
||||||
|
MTP_flags(0),
|
||||||
|
MTPstring(), // business_connection_id
|
||||||
|
session().data().peer(videoId.peer)->input,
|
||||||
|
MTP_inputMediaUploadedPhoto(
|
||||||
|
MTP_flags(0),
|
||||||
|
cover.info.file,
|
||||||
|
MTP_vector<MTPInputDocument>(0),
|
||||||
|
MTP_int(0))
|
||||||
|
)).done([=](const MTPMessageMedia &result) {
|
||||||
|
result.match([&](const MTPDmessageMediaPhoto &data) {
|
||||||
|
const auto photo = data.vphoto();
|
||||||
|
if (!photo || photo->type() != mtpc_photo) {
|
||||||
|
failed(coverId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto &fields = photo->c_photo();
|
||||||
|
if (const auto coverId = _videoIdToCoverId.take(videoId)) {
|
||||||
|
if (auto video = _videoWaitingCover.take(*coverId)) {
|
||||||
|
video->info.videoCover = MTP_inputPhoto(
|
||||||
|
fields.vid(),
|
||||||
|
fields.vaccess_hash(),
|
||||||
|
fields.vfile_reference());
|
||||||
|
_documentReady.fire(std::move(*video));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [&](const auto &) {
|
||||||
|
failed(coverId);
|
||||||
|
});
|
||||||
|
}).fail([=] {
|
||||||
|
failed(coverId);
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Storage
|
} // namespace Storage
|
||||||
|
|
|
@ -29,6 +29,7 @@ namespace Storage {
|
||||||
constexpr auto kUseBigFilesFrom = 30 * 1024 * 1024;
|
constexpr auto kUseBigFilesFrom = 30 * 1024 * 1024;
|
||||||
|
|
||||||
struct UploadedMedia {
|
struct UploadedMedia {
|
||||||
|
uint64 id = 0;
|
||||||
FullMsgId fullId;
|
FullMsgId fullId;
|
||||||
Api::RemoteFileInfo info;
|
Api::RemoteFileInfo info;
|
||||||
Api::SendOptions options;
|
Api::SendOptions options;
|
||||||
|
@ -134,6 +135,11 @@ private:
|
||||||
void partFailed(const MTP::Error &error, mtpRequestId requestId);
|
void partFailed(const MTP::Error &error, mtpRequestId requestId);
|
||||||
Request finishRequest(mtpRequestId requestId);
|
Request finishRequest(mtpRequestId requestId);
|
||||||
|
|
||||||
|
void uploadVideoCover(
|
||||||
|
UploadedMedia &&video,
|
||||||
|
std::shared_ptr<FilePrepareResult> videoCover);
|
||||||
|
void uploadCoverAsPhoto(FullMsgId videoId, UploadedMedia &&cover);
|
||||||
|
|
||||||
void processPhotoProgress(FullMsgId itemId);
|
void processPhotoProgress(FullMsgId itemId);
|
||||||
void processPhotoFailed(FullMsgId itemId);
|
void processPhotoFailed(FullMsgId itemId);
|
||||||
void processDocumentProgress(FullMsgId itemId);
|
void processDocumentProgress(FullMsgId itemId);
|
||||||
|
@ -163,6 +169,9 @@ private:
|
||||||
crl::time _latestDcIndexRemoved = 0;
|
crl::time _latestDcIndexRemoved = 0;
|
||||||
std::vector<Request> _pendingFromRemovedDcIndices;
|
std::vector<Request> _pendingFromRemovedDcIndices;
|
||||||
|
|
||||||
|
base::flat_map<FullMsgId, FullMsgId> _videoIdToCoverId;
|
||||||
|
base::flat_map<FullMsgId, UploadedMedia> _videoWaitingCover;
|
||||||
|
|
||||||
FullMsgId _pausedId;
|
FullMsgId _pausedId;
|
||||||
base::Timer _nextTimer, _stopSessionsTimer;
|
base::Timer _nextTimer, _stopSessionsTimer;
|
||||||
|
|
||||||
|
|
|
@ -468,6 +468,7 @@ FileLoadTask::FileLoadTask(
|
||||||
const QString &filepath,
|
const QString &filepath,
|
||||||
const QByteArray &content,
|
const QByteArray &content,
|
||||||
std::unique_ptr<Ui::PreparedFileInformation> information,
|
std::unique_ptr<Ui::PreparedFileInformation> information,
|
||||||
|
std::unique_ptr<FileLoadTask> videoCover,
|
||||||
SendMediaType type,
|
SendMediaType type,
|
||||||
const FileLoadTo &to,
|
const FileLoadTo &to,
|
||||||
const TextWithTags &caption,
|
const TextWithTags &caption,
|
||||||
|
@ -481,6 +482,7 @@ FileLoadTask::FileLoadTask(
|
||||||
, _album(std::move(album))
|
, _album(std::move(album))
|
||||||
, _filepath(filepath)
|
, _filepath(filepath)
|
||||||
, _content(content)
|
, _content(content)
|
||||||
|
, _videoCover(std::move(videoCover))
|
||||||
, _information(std::move(information))
|
, _information(std::move(information))
|
||||||
, _type(type)
|
, _type(type)
|
||||||
, _caption(caption)
|
, _caption(caption)
|
||||||
|
@ -688,6 +690,15 @@ void FileLoadTask::process(Args &&args) {
|
||||||
.spoiler = _spoiler,
|
.spoiler = _spoiler,
|
||||||
.album = _album,
|
.album = _album,
|
||||||
});
|
});
|
||||||
|
if (const auto cover = _videoCover.get()) {
|
||||||
|
cover->process();
|
||||||
|
if (const auto &result = cover->peekResult()) {
|
||||||
|
if (result->type == SendMediaType::Photo
|
||||||
|
&& !result->fileparts.empty()) {
|
||||||
|
_result->videoCover = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QString filename, filemime;
|
QString filename, filemime;
|
||||||
qint64 filesize = 0;
|
qint64 filesize = 0;
|
||||||
|
@ -1073,8 +1084,8 @@ void FileLoadTask::finish() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FilePrepareResult *FileLoadTask::peekResult() const {
|
const std::shared_ptr<FilePrepareResult> &FileLoadTask::peekResult() const {
|
||||||
return _result.get();
|
return _result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Ui::PreparedFileInformation> FileLoadTask::readMediaInformation(
|
std::unique_ptr<Ui::PreparedFileInformation> FileLoadTask::readMediaInformation(
|
||||||
|
|
|
@ -196,6 +196,8 @@ struct FilePrepareResult {
|
||||||
|
|
||||||
std::vector<MTPInputDocument> attachedStickers;
|
std::vector<MTPInputDocument> attachedStickers;
|
||||||
|
|
||||||
|
std::shared_ptr<FilePrepareResult> videoCover;
|
||||||
|
|
||||||
void setFileData(const QByteArray &filedata);
|
void setFileData(const QByteArray &filedata);
|
||||||
void setThumbData(const QByteArray &thumbdata);
|
void setThumbData(const QByteArray &thumbdata);
|
||||||
|
|
||||||
|
@ -222,6 +224,7 @@ public:
|
||||||
const QString &filepath,
|
const QString &filepath,
|
||||||
const QByteArray &content,
|
const QByteArray &content,
|
||||||
std::unique_ptr<Ui::PreparedFileInformation> information,
|
std::unique_ptr<Ui::PreparedFileInformation> information,
|
||||||
|
std::unique_ptr<FileLoadTask> videoCover,
|
||||||
SendMediaType type,
|
SendMediaType type,
|
||||||
const FileLoadTo &to,
|
const FileLoadTo &to,
|
||||||
const TextWithTags &caption,
|
const TextWithTags &caption,
|
||||||
|
@ -252,7 +255,8 @@ public:
|
||||||
}
|
}
|
||||||
void finish() override;
|
void finish() override;
|
||||||
|
|
||||||
FilePrepareResult *peekResult() const;
|
[[nodiscard]] auto peekResult() const
|
||||||
|
-> const std::shared_ptr<FilePrepareResult> &;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool CheckForSong(
|
static bool CheckForSong(
|
||||||
|
@ -281,6 +285,7 @@ private:
|
||||||
const std::shared_ptr<SendingAlbum> _album;
|
const std::shared_ptr<SendingAlbum> _album;
|
||||||
QString _filepath;
|
QString _filepath;
|
||||||
QByteArray _content;
|
QByteArray _content;
|
||||||
|
std::unique_ptr<FileLoadTask> _videoCover;
|
||||||
std::unique_ptr<Ui::PreparedFileInformation> _information;
|
std::unique_ptr<Ui::PreparedFileInformation> _information;
|
||||||
crl::time _duration = 0;
|
crl::time _duration = 0;
|
||||||
VoiceWaveform _waveform;
|
VoiceWaveform _waveform;
|
||||||
|
|
|
@ -362,10 +362,22 @@ void UpdateImageDetails(
|
||||||
|
|
||||||
bool ApplyModifications(PreparedList &list) {
|
bool ApplyModifications(PreparedList &list) {
|
||||||
auto applied = false;
|
auto applied = false;
|
||||||
for (auto &file : list.files) {
|
const auto apply = [&](PreparedFile &file, QSize strictSize = {}) {
|
||||||
const auto image = std::get_if<Image>(&file.information->media);
|
const auto image = std::get_if<Image>(&file.information->media);
|
||||||
|
const auto guard = gsl::finally([&] {
|
||||||
|
if (!image || strictSize.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
applied = true;
|
||||||
|
file.path = QString();
|
||||||
|
file.content = QByteArray();
|
||||||
|
image->data = image->data.scaled(
|
||||||
|
strictSize,
|
||||||
|
Qt::IgnoreAspectRatio,
|
||||||
|
Qt::SmoothTransformation);
|
||||||
|
});
|
||||||
if (!image || !image->modifications) {
|
if (!image || !image->modifications) {
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
applied = true;
|
applied = true;
|
||||||
file.path = QString();
|
file.path = QString();
|
||||||
|
@ -373,6 +385,16 @@ bool ApplyModifications(PreparedList &list) {
|
||||||
image->data = Editor::ImageModified(
|
image->data = Editor::ImageModified(
|
||||||
std::move(image->data),
|
std::move(image->data),
|
||||||
image->modifications);
|
image->modifications);
|
||||||
|
};
|
||||||
|
for (auto &file : list.files) {
|
||||||
|
apply(file);
|
||||||
|
if (const auto cover = file.videoCover.get()) {
|
||||||
|
const auto video = file.information
|
||||||
|
? std::get_if<Ui::PreparedFileInformation::Video>(
|
||||||
|
&file.information->media)
|
||||||
|
: nullptr;
|
||||||
|
apply(*cover, video ? video->thumbnail.size() : QSize());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return applied;
|
return applied;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,10 +34,10 @@ AbstractSingleMediaPreview::AbstractSingleMediaPreview(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
const style::ComposeControls &st,
|
const style::ComposeControls &st,
|
||||||
AttachControls::Type type,
|
AttachControls::Type type,
|
||||||
Fn<bool()> canToggleSpoiler)
|
Fn<bool(AttachActionType)> actionAllowed)
|
||||||
: AbstractSinglePreview(parent)
|
: AbstractSinglePreview(parent)
|
||||||
, _st(st)
|
, _st(st)
|
||||||
, _canToggleSpoiler(std::move(canToggleSpoiler))
|
, _actionAllowed(std::move(actionAllowed))
|
||||||
, _minThumbH(st::sendBoxAlbumGroupSize.height()
|
, _minThumbH(st::sendBoxAlbumGroupSize.height()
|
||||||
+ st::sendBoxAlbumGroupSkipTop * 2)
|
+ st::sendBoxAlbumGroupSkipTop * 2)
|
||||||
, _controls(base::make_unique_q<AttachControlsWidget>(this, type)) {
|
, _controls(base::make_unique_q<AttachControlsWidget>(this, type)) {
|
||||||
|
@ -57,6 +57,14 @@ rpl::producer<> AbstractSingleMediaPreview::modifyRequests() const {
|
||||||
return _photoEditorRequests.events();
|
return _photoEditorRequests.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<> AbstractSingleMediaPreview::editCoverRequests() const {
|
||||||
|
return _editCoverRequests.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> AbstractSingleMediaPreview::clearCoverRequests() const {
|
||||||
|
return _clearCoverRequests.events();
|
||||||
|
}
|
||||||
|
|
||||||
void AbstractSingleMediaPreview::setSendWay(SendFilesWay way) {
|
void AbstractSingleMediaPreview::setSendWay(SendFilesWay way) {
|
||||||
_sendWay = way;
|
_sendWay = way;
|
||||||
update();
|
update();
|
||||||
|
@ -112,7 +120,7 @@ void AbstractSingleMediaPreview::preparePreview(QImage preview) {
|
||||||
preview = Images::Prepare(
|
preview = Images::Prepare(
|
||||||
std::move(preview),
|
std::move(preview),
|
||||||
QSize(maxW, maxH) * ratio,
|
QSize(maxW, maxH) * ratio,
|
||||||
{ .options = Images::Option::Blur, .outer = { maxW, maxH } });
|
{ .outer = { maxW, maxH } });
|
||||||
}
|
}
|
||||||
auto originalWidth = preview.width();
|
auto originalWidth = preview.width();
|
||||||
auto originalHeight = preview.height();
|
auto originalHeight = preview.height();
|
||||||
|
@ -273,24 +281,33 @@ void AbstractSingleMediaPreview::applyCursor(style::cursor cursor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbstractSingleMediaPreview::showContextMenu(QPoint position) {
|
void AbstractSingleMediaPreview::showContextMenu(QPoint position) {
|
||||||
if (!_canToggleSpoiler()
|
|
||||||
|| !_sendWay.sendImagesAsPhotos()
|
|
||||||
|| !supportsSpoilers()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_menu = base::make_unique_q<Ui::PopupMenu>(
|
_menu = base::make_unique_q<Ui::PopupMenu>(
|
||||||
this,
|
this,
|
||||||
_st.tabbed.menu);
|
_st.tabbed.menu);
|
||||||
|
|
||||||
const auto &icons = _st.tabbed.icons;
|
const auto &icons = _st.tabbed.icons;
|
||||||
const auto spoilered = hasSpoiler();
|
if (_actionAllowed(AttachActionType::ToggleSpoiler)
|
||||||
_menu->addAction(spoilered
|
&& _sendWay.sendImagesAsPhotos()
|
||||||
? tr::lng_context_disable_spoiler(tr::now)
|
&& supportsSpoilers()) {
|
||||||
: tr::lng_context_spoiler_effect(tr::now), [=] {
|
const auto spoilered = hasSpoiler();
|
||||||
setSpoiler(!spoilered);
|
_menu->addAction(spoilered
|
||||||
_spoileredChanges.fire_copy(!spoilered);
|
? tr::lng_context_disable_spoiler(tr::now)
|
||||||
}, spoilered ? &icons.menuSpoilerOff : &icons.menuSpoiler);
|
: tr::lng_context_spoiler_effect(tr::now), [=] {
|
||||||
|
setSpoiler(!spoilered);
|
||||||
|
_spoileredChanges.fire_copy(!spoilered);
|
||||||
|
}, spoilered ? &icons.menuSpoilerOff : &icons.menuSpoiler);
|
||||||
|
}
|
||||||
|
if (_actionAllowed(AttachActionType::EditCover)) {
|
||||||
|
_menu->addAction(tr::lng_context_edit_cover(tr::now), [=] {
|
||||||
|
_editCoverRequests.fire({});
|
||||||
|
}, &st::menuIconEdit);
|
||||||
|
|
||||||
|
if (_actionAllowed(AttachActionType::ClearCover)) {
|
||||||
|
_menu->addAction(tr::lng_context_clear_cover(tr::now), [=] {
|
||||||
|
_clearCoverRequests.fire({});
|
||||||
|
}, &st::menuIconCancel);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (_menu->empty()) {
|
if (_menu->empty()) {
|
||||||
_menu = nullptr;
|
_menu = nullptr;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -27,7 +27,7 @@ public:
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
const style::ComposeControls &st,
|
const style::ComposeControls &st,
|
||||||
AttachControls::Type type,
|
AttachControls::Type type,
|
||||||
Fn<bool()> canToggleSpoiler);
|
Fn<bool(AttachActionType)> actionAllowed);
|
||||||
~AbstractSingleMediaPreview();
|
~AbstractSingleMediaPreview();
|
||||||
|
|
||||||
void setSendWay(SendFilesWay way);
|
void setSendWay(SendFilesWay way);
|
||||||
|
@ -36,6 +36,8 @@ public:
|
||||||
[[nodiscard]] rpl::producer<> deleteRequests() const override;
|
[[nodiscard]] rpl::producer<> deleteRequests() const override;
|
||||||
[[nodiscard]] rpl::producer<> editRequests() const override;
|
[[nodiscard]] rpl::producer<> editRequests() const override;
|
||||||
[[nodiscard]] rpl::producer<> modifyRequests() const override;
|
[[nodiscard]] rpl::producer<> modifyRequests() const override;
|
||||||
|
[[nodiscard]] rpl::producer<> editCoverRequests() const;
|
||||||
|
[[nodiscard]] rpl::producer<> clearCoverRequests() const;
|
||||||
|
|
||||||
[[nodiscard]] bool isPhoto() const;
|
[[nodiscard]] bool isPhoto() const;
|
||||||
|
|
||||||
|
@ -74,7 +76,7 @@ private:
|
||||||
|
|
||||||
const style::ComposeControls &_st;
|
const style::ComposeControls &_st;
|
||||||
SendFilesWay _sendWay;
|
SendFilesWay _sendWay;
|
||||||
Fn<bool()> _canToggleSpoiler;
|
Fn<bool(AttachActionType)> _actionAllowed;
|
||||||
bool _animated = false;
|
bool _animated = false;
|
||||||
QPixmap _preview;
|
QPixmap _preview;
|
||||||
QPixmap _previewBlurred;
|
QPixmap _previewBlurred;
|
||||||
|
@ -89,6 +91,8 @@ private:
|
||||||
const int _minThumbH;
|
const int _minThumbH;
|
||||||
const base::unique_qptr<AttachControlsWidget> _controls;
|
const base::unique_qptr<AttachControlsWidget> _controls;
|
||||||
rpl::event_stream<> _photoEditorRequests;
|
rpl::event_stream<> _photoEditorRequests;
|
||||||
|
rpl::event_stream<> _editCoverRequests;
|
||||||
|
rpl::event_stream<> _clearCoverRequests;
|
||||||
|
|
||||||
style::cursor _cursor = style::cur_default;
|
style::cursor _cursor = style::cur_default;
|
||||||
bool _pressed = false;
|
bool _pressed = false;
|
||||||
|
|
|
@ -38,11 +38,11 @@ AlbumPreview::AlbumPreview(
|
||||||
const style::ComposeControls &st,
|
const style::ComposeControls &st,
|
||||||
gsl::span<Ui::PreparedFile> items,
|
gsl::span<Ui::PreparedFile> items,
|
||||||
SendFilesWay way,
|
SendFilesWay way,
|
||||||
Fn<bool()> canToggleSpoiler)
|
Fn<bool(int, AttachActionType)> actionAllowed)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
, _st(st)
|
, _st(st)
|
||||||
, _sendWay(way)
|
, _sendWay(way)
|
||||||
, _canToggleSpoiler(std::move(canToggleSpoiler))
|
, _actionAllowed(std::move(actionAllowed))
|
||||||
, _dragTimer([=] { switchToDrag(); }) {
|
, _dragTimer([=] { switchToDrag(); }) {
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
prepareThumbs(items);
|
prepareThumbs(items);
|
||||||
|
@ -582,19 +582,31 @@ void AlbumPreview::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
void AlbumPreview::showContextMenu(
|
void AlbumPreview::showContextMenu(
|
||||||
not_null<AlbumThumbnail*> thumb,
|
not_null<AlbumThumbnail*> thumb,
|
||||||
QPoint position) {
|
QPoint position) {
|
||||||
if (!_canToggleSpoiler() || !_sendWay.sendImagesAsPhotos()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_menu = base::make_unique_q<Ui::PopupMenu>(
|
_menu = base::make_unique_q<Ui::PopupMenu>(
|
||||||
this,
|
this,
|
||||||
st::popupMenuWithIcons);
|
st::popupMenuWithIcons);
|
||||||
|
|
||||||
const auto spoilered = thumb->hasSpoiler();
|
const auto index = orderIndex(thumb);
|
||||||
_menu->addAction(spoilered
|
if (_actionAllowed(index, AttachActionType::ToggleSpoiler)
|
||||||
? tr::lng_context_disable_spoiler(tr::now)
|
&& _sendWay.sendImagesAsPhotos()) {
|
||||||
: tr::lng_context_spoiler_effect(tr::now), [=] {
|
const auto spoilered = thumb->hasSpoiler();
|
||||||
thumb->setSpoiler(!spoilered);
|
_menu->addAction(spoilered
|
||||||
}, spoilered ? &st::menuIconSpoilerOff : &st::menuIconSpoiler);
|
? tr::lng_context_disable_spoiler(tr::now)
|
||||||
|
: tr::lng_context_spoiler_effect(tr::now), [=] {
|
||||||
|
thumb->setSpoiler(!spoilered);
|
||||||
|
}, spoilered ? &st::menuIconSpoilerOff : &st::menuIconSpoiler);
|
||||||
|
}
|
||||||
|
if (_actionAllowed(index, AttachActionType::EditCover)) {
|
||||||
|
_menu->addAction(tr::lng_context_edit_cover(tr::now), [=] {
|
||||||
|
_thumbEditCoverRequested.fire_copy(index);
|
||||||
|
}, &st::menuIconEdit);
|
||||||
|
|
||||||
|
if (_actionAllowed(index, AttachActionType::ClearCover)) {
|
||||||
|
_menu->addAction(tr::lng_context_clear_cover(tr::now), [=] {
|
||||||
|
_thumbClearCoverRequested.fire_copy(index);
|
||||||
|
}, &st::menuIconCancel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_menu->empty()) {
|
if (_menu->empty()) {
|
||||||
_menu = nullptr;
|
_menu = nullptr;
|
||||||
|
|
|
@ -29,7 +29,7 @@ public:
|
||||||
const style::ComposeControls &st,
|
const style::ComposeControls &st,
|
||||||
gsl::span<Ui::PreparedFile> items,
|
gsl::span<Ui::PreparedFile> items,
|
||||||
SendFilesWay way,
|
SendFilesWay way,
|
||||||
Fn<bool()> canToggleSpoiler);
|
Fn<bool(int, AttachActionType)> actionAllowed);
|
||||||
~AlbumPreview();
|
~AlbumPreview();
|
||||||
|
|
||||||
void setSendWay(SendFilesWay way);
|
void setSendWay(SendFilesWay way);
|
||||||
|
@ -42,15 +42,18 @@ public:
|
||||||
[[nodiscard]] rpl::producer<int> thumbDeleted() const {
|
[[nodiscard]] rpl::producer<int> thumbDeleted() const {
|
||||||
return _thumbDeleted.events();
|
return _thumbDeleted.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<int> thumbChanged() const {
|
[[nodiscard]] rpl::producer<int> thumbChanged() const {
|
||||||
return _thumbChanged.events();
|
return _thumbChanged.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<int> thumbModified() const {
|
[[nodiscard]] rpl::producer<int> thumbModified() const {
|
||||||
return _thumbModified.events();
|
return _thumbModified.events();
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] rpl::producer<int> thumbEditCoverRequested() const {
|
||||||
|
return _thumbEditCoverRequested.events();
|
||||||
|
}
|
||||||
|
[[nodiscard]] rpl::producer<int> thumbClearCoverRequested() const {
|
||||||
|
return _thumbClearCoverRequested.events();
|
||||||
|
}
|
||||||
[[nodiscard]] rpl::producer<> orderUpdated() const {
|
[[nodiscard]] rpl::producer<> orderUpdated() const {
|
||||||
return _orderUpdated.events();
|
return _orderUpdated.events();
|
||||||
}
|
}
|
||||||
|
@ -101,7 +104,7 @@ private:
|
||||||
|
|
||||||
const style::ComposeControls &_st;
|
const style::ComposeControls &_st;
|
||||||
SendFilesWay _sendWay;
|
SendFilesWay _sendWay;
|
||||||
Fn<bool()> _canToggleSpoiler;
|
Fn<bool(int, AttachActionType)> _actionAllowed;
|
||||||
style::cursor _cursor = style::cur_default;
|
style::cursor _cursor = style::cur_default;
|
||||||
std::vector<int> _order;
|
std::vector<int> _order;
|
||||||
std::vector<QSize> _itemsShownDimensions;
|
std::vector<QSize> _itemsShownDimensions;
|
||||||
|
@ -124,6 +127,8 @@ private:
|
||||||
rpl::event_stream<int> _thumbDeleted;
|
rpl::event_stream<int> _thumbDeleted;
|
||||||
rpl::event_stream<int> _thumbChanged;
|
rpl::event_stream<int> _thumbChanged;
|
||||||
rpl::event_stream<int> _thumbModified;
|
rpl::event_stream<int> _thumbModified;
|
||||||
|
rpl::event_stream<int> _thumbEditCoverRequested;
|
||||||
|
rpl::event_stream<int> _thumbClearCoverRequested;
|
||||||
rpl::event_stream<> _orderUpdated;
|
rpl::event_stream<> _orderUpdated;
|
||||||
|
|
||||||
base::unique_qptr<PopupMenu> _menu;
|
base::unique_qptr<PopupMenu> _menu;
|
||||||
|
|
|
@ -35,7 +35,7 @@ AlbumThumbnail::AlbumThumbnail(
|
||||||
Fn<void()> deleteCallback)
|
Fn<void()> deleteCallback)
|
||||||
: _st(st)
|
: _st(st)
|
||||||
, _layout(layout)
|
, _layout(layout)
|
||||||
, _fullPreview(file.preview)
|
, _fullPreview(file.videoCover ? file.videoCover->preview : file.preview)
|
||||||
, _shrinkSize(int(std::ceil(st::roundRadiusLarge / 1.4)))
|
, _shrinkSize(int(std::ceil(st::roundRadiusLarge / 1.4)))
|
||||||
, _isPhoto(file.type == PreparedFile::Type::Photo)
|
, _isPhoto(file.type == PreparedFile::Type::Photo)
|
||||||
, _isVideo(file.type == PreparedFile::Type::Video)
|
, _isVideo(file.type == PreparedFile::Type::Video)
|
||||||
|
|
|
@ -36,8 +36,15 @@ ItemSingleMediaPreview::ItemSingleMediaPreview(
|
||||||
Fn<bool()> gifPaused,
|
Fn<bool()> gifPaused,
|
||||||
not_null<HistoryItem*> item,
|
not_null<HistoryItem*> item,
|
||||||
AttachControls::Type type)
|
AttachControls::Type type)
|
||||||
: AbstractSingleMediaPreview(parent, st, type, [] { return true; })
|
: AbstractSingleMediaPreview(parent, st, type, [=](AttachActionType type) {
|
||||||
|
if (type == AttachActionType::EditCover) {
|
||||||
|
return _isVideoFile;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
, _gifPaused(std::move(gifPaused))
|
, _gifPaused(std::move(gifPaused))
|
||||||
|
, _isVideoFile(item->media()->document()
|
||||||
|
&& item->media()->document()->isVideoFile())
|
||||||
, _fullId(item->fullId()) {
|
, _fullId(item->fullId()) {
|
||||||
const auto media = item->media();
|
const auto media = item->media();
|
||||||
Assert(media != nullptr);
|
Assert(media != nullptr);
|
||||||
|
|
|
@ -56,6 +56,7 @@ private:
|
||||||
void startStreamedPlayer();
|
void startStreamedPlayer();
|
||||||
|
|
||||||
const Fn<bool()> _gifPaused;
|
const Fn<bool()> _gifPaused;
|
||||||
|
const bool _isVideoFile;
|
||||||
const FullMsgId _fullId;
|
const FullMsgId _fullId;
|
||||||
|
|
||||||
std::shared_ptr<::Data::PhotoMedia> _photoMedia;
|
std::shared_ptr<::Data::PhotoMedia> _photoMedia;
|
||||||
|
|
|
@ -42,6 +42,15 @@ bool PreparedFile::isSticker() const {
|
||||||
&& Core::IsMimeSticker(information->filemime);
|
&& Core::IsMimeSticker(information->filemime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PreparedFile::isVideoFile() const {
|
||||||
|
Expects(information != nullptr);
|
||||||
|
|
||||||
|
using Video = Ui::PreparedFileInformation::Video;
|
||||||
|
return (type == PreparedFile::Type::Video)
|
||||||
|
&& v::is<Video>(information->media)
|
||||||
|
&& !v::get<Video>(information->media).isGifv;
|
||||||
|
}
|
||||||
|
|
||||||
bool PreparedFile::isGifv() const {
|
bool PreparedFile::isGifv() const {
|
||||||
Expects(information != nullptr);
|
Expects(information != nullptr);
|
||||||
|
|
||||||
|
|
|
@ -74,12 +74,14 @@ struct PreparedFile {
|
||||||
[[nodiscard]] bool canBeInAlbumType(AlbumType album) const;
|
[[nodiscard]] bool canBeInAlbumType(AlbumType album) const;
|
||||||
[[nodiscard]] AlbumType albumType(bool sendImagesAsPhotos) const;
|
[[nodiscard]] AlbumType albumType(bool sendImagesAsPhotos) const;
|
||||||
[[nodiscard]] bool isSticker() const;
|
[[nodiscard]] bool isSticker() const;
|
||||||
|
[[nodiscard]] bool isVideoFile() const;
|
||||||
[[nodiscard]] bool isGifv() const;
|
[[nodiscard]] bool isGifv() const;
|
||||||
|
|
||||||
QString path;
|
QString path;
|
||||||
QByteArray content;
|
QByteArray content;
|
||||||
int64 size = 0;
|
int64 size = 0;
|
||||||
std::unique_ptr<Ui::PreparedFileInformation> information;
|
std::unique_ptr<PreparedFileInformation> information;
|
||||||
|
std::unique_ptr<PreparedFile> videoCover;
|
||||||
QImage preview;
|
QImage preview;
|
||||||
QSize shownDimensions;
|
QSize shownDimensions;
|
||||||
QSize originalDimensions;
|
QSize originalDimensions;
|
||||||
|
|
|
@ -11,6 +11,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
|
||||||
|
enum class AttachActionType {
|
||||||
|
ToggleSpoiler,
|
||||||
|
EditCover,
|
||||||
|
ClearCover,
|
||||||
|
};
|
||||||
|
|
||||||
enum class AttachButtonType {
|
enum class AttachButtonType {
|
||||||
Edit,
|
Edit,
|
||||||
Delete,
|
Delete,
|
||||||
|
|
|
@ -19,7 +19,7 @@ SingleMediaPreview *SingleMediaPreview::Create(
|
||||||
const style::ComposeControls &st,
|
const style::ComposeControls &st,
|
||||||
Fn<bool()> gifPaused,
|
Fn<bool()> gifPaused,
|
||||||
const PreparedFile &file,
|
const PreparedFile &file,
|
||||||
Fn<bool()> canToggleSpoiler,
|
Fn<bool(AttachActionType)> actionAllowed,
|
||||||
AttachControls::Type type) {
|
AttachControls::Type type) {
|
||||||
auto preview = QImage();
|
auto preview = QImage();
|
||||||
auto animated = false;
|
auto animated = false;
|
||||||
|
@ -32,7 +32,9 @@ SingleMediaPreview *SingleMediaPreview::Create(
|
||||||
hasModifications = !image->modifications.empty();
|
hasModifications = !image->modifications.empty();
|
||||||
} else if (const auto video = std::get_if<PreparedFileInformation::Video>(
|
} else if (const auto video = std::get_if<PreparedFileInformation::Video>(
|
||||||
&file.information->media)) {
|
&file.information->media)) {
|
||||||
preview = video->thumbnail;
|
preview = file.videoCover
|
||||||
|
? file.videoCover->preview
|
||||||
|
: video->thumbnail;
|
||||||
animated = true;
|
animated = true;
|
||||||
animationPreview = video->isGifv;
|
animationPreview = video->isGifv;
|
||||||
}
|
}
|
||||||
|
@ -53,7 +55,7 @@ SingleMediaPreview *SingleMediaPreview::Create(
|
||||||
file.spoiler,
|
file.spoiler,
|
||||||
animationPreview ? file.path : QString(),
|
animationPreview ? file.path : QString(),
|
||||||
type,
|
type,
|
||||||
std::move(canToggleSpoiler));
|
std::move(actionAllowed));
|
||||||
}
|
}
|
||||||
|
|
||||||
SingleMediaPreview::SingleMediaPreview(
|
SingleMediaPreview::SingleMediaPreview(
|
||||||
|
@ -66,8 +68,8 @@ SingleMediaPreview::SingleMediaPreview(
|
||||||
bool spoiler,
|
bool spoiler,
|
||||||
const QString &animatedPreviewPath,
|
const QString &animatedPreviewPath,
|
||||||
AttachControls::Type type,
|
AttachControls::Type type,
|
||||||
Fn<bool()> canToggleSpoiler)
|
Fn<bool(AttachActionType)> actionAllowed)
|
||||||
: AbstractSingleMediaPreview(parent, st, type, std::move(canToggleSpoiler))
|
: AbstractSingleMediaPreview(parent, st, type, std::move(actionAllowed))
|
||||||
, _gifPaused(std::move(gifPaused))
|
, _gifPaused(std::move(gifPaused))
|
||||||
, _sticker(sticker) {
|
, _sticker(sticker) {
|
||||||
Expects(!preview.isNull());
|
Expects(!preview.isNull());
|
||||||
|
|
|
@ -25,7 +25,7 @@ public:
|
||||||
const style::ComposeControls &st,
|
const style::ComposeControls &st,
|
||||||
Fn<bool()> gifPaused,
|
Fn<bool()> gifPaused,
|
||||||
const PreparedFile &file,
|
const PreparedFile &file,
|
||||||
Fn<bool()> canToggleSpoiler,
|
Fn<bool(AttachActionType)> actionAllowed,
|
||||||
AttachControls::Type type = AttachControls::Type::Full);
|
AttachControls::Type type = AttachControls::Type::Full);
|
||||||
|
|
||||||
SingleMediaPreview(
|
SingleMediaPreview(
|
||||||
|
@ -38,7 +38,7 @@ public:
|
||||||
bool spoiler,
|
bool spoiler,
|
||||||
const QString &animatedPreviewPath,
|
const QString &animatedPreviewPath,
|
||||||
AttachControls::Type type,
|
AttachControls::Type type,
|
||||||
Fn<bool()> canToggleSpoiler);
|
Fn<bool(AttachActionType)> actionAllowed);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool supportsSpoilers() const override;
|
bool supportsSpoilers() const override;
|
||||||
|
|
Loading…
Add table
Reference in a new issue