Correctly check media when editing files.

This commit is contained in:
John Preston 2020-10-20 11:19:48 +03:00
parent c4af731b19
commit a614ccad97
16 changed files with 155 additions and 187 deletions

View File

@ -4228,18 +4228,11 @@ void ApiWrap::sendFiles(
tasks.reserve(list.files.size());
for (auto &file : list.files) {
if (album) {
switch (file.type) {
case Ui::PreparedFile::AlbumType::Photo:
type = (type == SendMediaType::File)
? type
: SendMediaType::Photo;
break;
case Ui::PreparedFile::AlbumType::Music:
case Ui::PreparedFile::AlbumType::Video:
case Ui::PreparedFile::AlbumType::File:
if (file.type == Ui::PreparedFile::Type::Photo
&& type != SendMediaType::File) {
type = SendMediaType::Photo;
} else {
type = SendMediaType::File;
break;
default: Unexpected("AlbumType in ApiWrap::sendFiles.");
}
}
tasks.push_back(std::make_unique<FileLoadTask>(

View File

@ -106,10 +106,23 @@ EditCaptionBox::EditCaptionBox(
Expects(item->media()->allowsEditCaption());
_isAllowedEditMedia = item->media()->allowsEditMedia();
_isAlbum = !item->groupId().empty();
auto dimensions = QSize();
const auto media = item->media();
if (!item->groupId().empty()) {
if (media->photo()) {
_albumType = Ui::AlbumType::PhotoVideo;
} else if (const auto document = media->document()) {
if (document->isVideoFile()) {
_albumType = Ui::AlbumType::PhotoVideo;
} else if (document->isSong()) {
_albumType = Ui::AlbumType::Music;
} else {
_albumType = Ui::AlbumType::File;
}
}
}
if (const auto photo = media->photo()) {
_photoMedia = photo->createMediaView();
_photoMedia->wanted(PhotoSize::Large, _msgId);
@ -503,7 +516,8 @@ void EditCaptionBox::updateEditPreview() {
if (const auto image = std::get_if<Info::Image>(fileMedia)) {
shouldAsDoc = !Ui::ValidateThumbDimensions(
image->data.width(),
image->data.height());
image->data.height()
) || (_albumType == Ui::AlbumType::File);
if (shouldAsDoc) {
docPhotoSize.setWidth(image->data.width());
docPhotoSize.setHeight(image->data.height());
@ -554,7 +568,7 @@ void EditCaptionBox::updateEditPreview() {
_doc = true;
}
const auto showCheckbox = _photo && !_isAlbum;
const auto showCheckbox = _photo && (_albumType == Ui::AlbumType::None);
_wayWrap->toggle(showCheckbox, anim::type::instant);
if (!_doc) {
@ -604,14 +618,10 @@ void EditCaptionBox::createEditMediaButton() {
if (Core::IsMimeSticker(mime)) {
showError(tr::lng_edit_media_invalid_file);
return false;
} else if (_isAlbum) { // #TODO files edit in file-albums
if (!Core::IsMimeAcceptedForAlbum(mime)
|| file.type == Ui::PreparedFile::AlbumType::File
|| file.type == Ui::PreparedFile::AlbumType::Music
|| file.type == Ui::PreparedFile::AlbumType::None) {
showError(tr::lng_edit_media_album_error);
return false;
}
} else if (_albumType != Ui::AlbumType::None
&& !file.canBeInAlbumType(_albumType)) {
showError(tr::lng_edit_media_album_error);
return false;
}
return true;
};
@ -622,14 +632,13 @@ void EditCaptionBox::createEditMediaButton() {
st::sendMediaPreviewSize);
if (list) {
_preparedList = std::move(*list);
updateEditPreview();
setPreparedList(std::move(*list));
}
};
const auto buttonCallback = [=] {
const auto filters = _isAlbum
? FileDialog::AlbumFilesFilter()
const auto filters = (_albumType == Ui::AlbumType::PhotoVideo)
? FileDialog::PhotoVideoFilesFilter()
: FileDialog::AllFilesFilter();
FileDialog::GetOpenPath(
this,
@ -676,7 +685,7 @@ void EditCaptionBox::prepare() {
if (action == Ui::InputField::MimeAction::Check) {
if (!data->hasText() && !_isAllowedEditMedia) {
return false;
} else if (Storage::ValidateDragData(data, _isAlbum)) {
} else if (Storage::ValidateEditMediaDragData(data, _albumType)) {
return true;
}
return data->hasText();
@ -700,40 +709,32 @@ void EditCaptionBox::prepare() {
}
bool EditCaptionBox::fileFromClipboard(not_null<const QMimeData*> data) {
return setPreparedList(ListFromMimeData(data));
}
bool EditCaptionBox::setPreparedList(Ui::PreparedList &&list) {
if (!_isAllowedEditMedia) {
return false;
}
using Error = Ui::PreparedList::Error;
using AlbumType = Ui::PreparedFile::AlbumType;
auto list = ListFromMimeData(data);
using Type = Ui::PreparedFile::Type;
if (list.error != Error::None || list.files.empty()) {
return false;
}
const auto file = &list.files.front();
if (_isAlbum && (file->type == AlbumType::File
|| file->type == AlbumType::None
|| file->type == AlbumType::Music)) {
const auto imageAsDoc = [&] {
using Info = Ui::PreparedFileInformation;
const auto fileMedia = &file->information->media;
if (const auto image = std::get_if<Info::Image>(fileMedia)) {
return !Ui::ValidateThumbDimensions(
image->data.width(),
image->data.height());
}
return false;
}();
if (!data->hasText() || imageAsDoc) {
Ui::show(
Box<InformBox>(tr::lng_edit_media_album_error(tr::now)),
Ui::LayerOption::KeepOther);
auto file = &list.files.front();
const auto invalidForAlbum = (_albumType != Ui::AlbumType::None)
&& !file->canBeInAlbumType(_albumType);
if (_albumType == Ui::AlbumType::PhotoVideo) {
using Video = Ui::PreparedFileInformation::Video;
if (const auto video = std::get_if<Video>(&file->information->media)) {
video->isGifv = false;
}
}
if (invalidForAlbum) {
Ui::Toast::Show(tr::lng_edit_media_album_error(tr::now));
return false;
}
_photo = _isImage = (file->type == AlbumType::Photo);
_photo = _isImage = (file->type == Type::Photo);
_preparedList = std::move(list);
updateEditPreview();
return true;
@ -783,7 +784,7 @@ void EditCaptionBox::setupDragArea() {
auto enterFilter = [=](not_null<const QMimeData*> data) {
return !_isAllowedEditMedia
? false
: Storage::ValidateDragData(data, _isAlbum);
: Storage::ValidateEditMediaDragData(data, _albumType);
};
// Avoid both drag areas appearing at one time.
auto computeState = [=](const QMimeData *data) {

View File

@ -33,6 +33,7 @@ class InputField;
class EmojiButton;
class IconButton;
class Checkbox;
enum class AlbumType;
} // namespace Ui
namespace Window {
@ -93,6 +94,7 @@ private:
int errorTopSkip() const;
void createEditMediaButton();
bool setPreparedList(Ui::PreparedList &&list);
inline QString getNewMediaPath() {
return _preparedList.files.empty()
@ -138,8 +140,8 @@ private:
object_ptr<Ui::IconButton> _editMedia = nullptr;
Ui::SlideWrap<Ui::RpWidget> *_wayWrap = nullptr;
QString _newMediaPath;
Ui::AlbumType _albumType = Ui::AlbumType();
bool _isAllowedEditMedia = false;
bool _isAlbum = false;
bool _asFile = false;
rpl::event_stream<> _editMediaClicks;

View File

@ -473,7 +473,7 @@ void SendFilesBox::preparePreview() {
void SendFilesBox::generatePreviewFrom(int fromBlock) {
Expects(fromBlock <= _blocks.size());
using Type = Ui::PreparedFile::AlbumType;
using Type = Ui::PreparedFile::Type;
const auto eraseFrom = _blocks.begin() + fromBlock;
for (auto i = eraseFrom; i != _blocks.end(); ++i) {
@ -813,7 +813,7 @@ void SendFilesBox::refreshTitleText() {
if (count > 1) {
const auto imagesCount = ranges::count(
_list.files,
Ui::PreparedFile::AlbumType::Photo,
Ui::PreparedFile::Type::Photo,
&Ui::PreparedFile::type);
_titleText = (imagesCount == count)
? tr::lng_send_images_selected(tr::now, lt_count, count)

View File

@ -329,8 +329,9 @@ QString ImagesOrAllFilter() {
return ImagesFilter() + u";;"_q + AllFilesFilter();
}
QString AlbumFilesFilter() {
return u"Image and Video Files (*.png *.jpg *.jpeg *.mp4 *.mov)"_q;
QString PhotoVideoFilesFilter() {
return u"Image and Video Files (*.png *.jpg *.jpeg *.mp4 *.mov);;"_q
+ AllFilesFilter();
}
namespace internal {

View File

@ -93,7 +93,7 @@ void GetFolder(
[[nodiscard]] QString ImagesFilter();
[[nodiscard]] QString AllOrImagesFilter();
[[nodiscard]] QString ImagesOrAllFilter();
[[nodiscard]] QString AlbumFilesFilter();
[[nodiscard]] QString PhotoVideoFilesFilter();
namespace internal {

View File

@ -111,7 +111,7 @@ bool IsMimeSticker(const QString &mime) {
|| IsMimeStickerAnimated(mime);
}
bool IsMimeAcceptedForAlbum(const QString &mime) {
bool IsMimeAcceptedForPhotoVideoAlbum(const QString &mime) {
return (mime == u"image/jpeg"_q)
|| (mime == u"image/png"_q)
|| (mime == u"video/mp4"_q)

View File

@ -42,7 +42,7 @@ private:
[[nodiscard]] bool IsMimeStickerAnimated(const QString &mime);
[[nodiscard]] bool IsMimeSticker(const QString &mime);
[[nodiscard]] bool IsMimeAcceptedForAlbum(const QString &mime);
[[nodiscard]] bool IsMimeAcceptedForPhotoVideoAlbum(const QString &mime);
[[nodiscard]] bool FileIsImage(const QString &name, const QString &mime);

View File

@ -4226,14 +4226,15 @@ void HistoryWidget::sendingFilesConfirmed(
action.replyTo = replyToId();
action.options = options;
action.clearDraft = false;
if ((groups.empty() || groups.size() > 1) && !caption.text.isEmpty()) {
if ((groups.size() != 1 || !groups.front().sentWithCaption())
&& !caption.text.isEmpty()) {
auto message = Api::MessageToSend(_history);
message.textWithTags = base::take(caption);
message.action = action;
session().api().sendMessage(std::move(message));
}
for (auto &group : groups) {
const auto album = group.grouped
const auto album = (group.type != Ui::AlbumType::None)
? std::make_shared<SendingAlbum>()
: nullptr;
session().api().sendFiles(

View File

@ -671,14 +671,15 @@ void RepliesWidget::sendingFilesConfirmed(
action.replyTo = replyTo ? replyTo : _rootId;
action.options = options;
action.clearDraft = false;
if ((groups.empty() || groups.size() > 1) && !caption.text.isEmpty()) {
if ((groups.size() != 1 || !groups.front().sentWithCaption())
&& !caption.text.isEmpty()) {
auto message = Api::MessageToSend(_history);
message.textWithTags = base::take(caption);
message.action = action;
session().api().sendMessage(std::move(message));
}
for (auto &group : groups) {
const auto album = group.grouped
const auto album = (group.type != Ui::AlbumType::None)
? std::make_shared<SendingAlbum>()
: nullptr;
session().api().sendFiles(

View File

@ -407,14 +407,15 @@ void ScheduledWidget::sendingFilesConfirmed(
auto action = Api::SendAction(_history);
action.options = options;
action.clearDraft = false;
if ((groups.empty() || groups.size() > 1) && !caption.text.isEmpty()) {
if ((groups.size() != 1 || !groups.front().sentWithCaption())
&& !caption.text.isEmpty()) {
auto message = Api::MessageToSend(_history);
message.textWithTags = base::take(caption);
message.action = action;
session().api().sendMessage(std::move(message));
}
for (auto &group : groups) {
const auto album = group.grouped
const auto album = (group.type != Ui::AlbumType::None)
? std::make_shared<SendingAlbum>()
: nullptr;
session().api().sendFiles(

View File

@ -80,19 +80,21 @@ void PrepareDetailsInParallel(PreparedList &result, int previewWidth) {
} // namespace
bool ValidateDragData(not_null<const QMimeData*> data, bool isAlbum) {
bool ValidateEditMediaDragData(
not_null<const QMimeData*> data,
Ui::AlbumType albumType) {
if (data->urls().size() > 1) {
return false;
} else if (data->hasImage()) {
return true;
return (albumType != Ui::AlbumType::Music);
}
if (isAlbum && data->hasUrls()) {
if (albumType == Ui::AlbumType::PhotoVideo && data->hasUrls()) {
const auto url = data->urls().front();
if (url.isLocalFile()) {
using namespace Core;
const auto info = QFileInfo(Platform::File::UrlToLocal(url));
return IsMimeAcceptedForAlbum(MimeTypeForFile(info).name());
return IsMimeAcceptedForPhotoVideoAlbum(MimeTypeForFile(info).name());
}
}
@ -241,86 +243,6 @@ std::optional<PreparedList> PreparedFileFromFilesDialog(
} else {
return list;
}
//if (!result.remoteContent.isEmpty()) {
// auto list = PrepareMediaFromImage(
// QImage(),
// std::move(result.remoteContent),
// previewWidth);
// const auto mimeFile = list.files.front().information->filemime;
// if (Core::IsMimeSticker(mimeFile)) {
// errorCallback(tr::lng_edit_media_invalid_file);
// return std::nullopt;
// }
// if (isAlbum) {
// const auto file = &list.files.front();
// if (!Core::IsMimeAcceptedForAlbum(mimeFile)
// || file->type == PreparedFile::AlbumType::File
// || file->type == PreparedFile::AlbumType::Music
// || file->type == PreparedFile::AlbumType::None) {
// errorCallback(tr::lng_edit_media_album_error);
// return std::nullopt;
// }
// }
// Ensures(list.files.size() == 1);
// return list;
//} else if (!result.paths.isEmpty()) {
// auto temp = PrepareMediaList(result.paths, previewWidth);
// const auto isSingleFile = (temp.files.size() == 1);
// if (temp.error != PreparedList::Error::None) {
// errorCallback(tr::lng_send_media_invalid_files);
// return std::nullopt;
// }
// auto filteredFiles = ranges::view::all(
// temp.files
// ) | ranges::view::filter([&](const auto &file) {
// const auto info = QFileInfo(file.path);
// if (Core::IsMimeSticker(Core::MimeTypeForFile(info).name())) {
// if (isSingleFile) {
// errorCallback(tr::lng_edit_media_invalid_file);
// }
// return false;
// }
// if (!isAlbum) {
// return true;
// }
// using Info = PreparedFileInformation;
// const auto media = &file.information->media;
// const auto valid = v::match(*media, [](const Info::Image &data) {
// return Ui::ValidateThumbDimensions(
// data.data.width(),
// data.data.height())
// && !data.animated;
// }, [](Info::Video &data) {
// data.isGifv = false;
// return true;
// }, [](auto &&other) {
// return false;
// });
// if (!valid && isSingleFile) {
// errorCallback(tr::lng_edit_media_album_error);
// }
// return valid;
// }) | ranges::view::transform([](auto &file) {
// return std::move(file);
// }) | ranges::to_vector;
// if (!filteredFiles.size()) {
// if (!isSingleFile) {
// errorCallback(tr::lng_send_media_invalid_files);
// }
// return std::nullopt;
// }
// auto list = PreparedList(temp.error, temp.errorData);
// list.files = std::move(filteredFiles);
// return list;
//}
//return std::nullopt;
}
void PrepareDetails(PreparedFile &file, int previewWidth) {
@ -351,9 +273,9 @@ void PrepareDetails(PreparedFile &file, int previewWidth) {
Qt::SmoothTransformation));
Assert(!file.preview.isNull());
file.preview.setDevicePixelRatio(cRetinaFactor());
file.type = PreparedFile::AlbumType::Photo;
file.type = PreparedFile::Type::Photo;
} else if (Core::IsMimeSticker(file.information->filemime)) {
file.type = PreparedFile::AlbumType::None;
file.type = PreparedFile::Type::None;
}
} else if (const auto video = std::get_if<Video>(
&file.information->media)) {
@ -365,10 +287,10 @@ void PrepareDetails(PreparedFile &file, int previewWidth) {
Qt::SmoothTransformation);
Assert(!file.preview.isNull());
file.preview.setDevicePixelRatio(cRetinaFactor());
file.type = PreparedFile::AlbumType::Video;
file.type = PreparedFile::Type::Video;
}
} else if (const auto song = std::get_if<Song>(&file.information->media)) {
file.type = PreparedFile::AlbumType::Music;
file.type = PreparedFile::Type::Music;
}
}

View File

@ -14,6 +14,7 @@ namespace Ui {
struct PreparedFileInformation;
struct PreparedFile;
struct PreparedList;
enum class AlbumType;
} // namespace Ui
namespace Storage {
@ -31,7 +32,9 @@ std::optional<Ui::PreparedList> PreparedFileFromFilesDialog(
Fn<void(tr::phrase<>)> errorCallback,
int previewWidth);
MimeDataState ComputeMimeDataState(const QMimeData *data);
bool ValidateDragData(not_null<const QMimeData*> data, bool isAlbum);
bool ValidateEditMediaDragData(
not_null<const QMimeData*> data,
Ui::AlbumType albumType);
Ui::PreparedList PrepareMediaList(const QList<QUrl> &files, int previewWidth);
Ui::PreparedList PrepareMediaList(const QStringList &files, int previewWidth);
Ui::PreparedList PrepareMediaFromImage(

View File

@ -29,7 +29,7 @@ AlbumThumbnail::AlbumThumbnail(
: _layout(layout)
, _fullPreview(file.preview)
, _shrinkSize(int(std::ceil(st::historyMessageRadius / 1.4)))
, _isVideo(file.type == PreparedFile::AlbumType::Video)
, _isVideo(file.type == PreparedFile::Type::Video)
, _buttonsRect(st::sendBoxAlbumGroupRadius, st::callFingerprintBg) {
Expects(!_fullPreview.isNull());

View File

@ -26,6 +26,41 @@ PreparedFile &PreparedFile::operator=(PreparedFile &&other) = default;
PreparedFile::~PreparedFile() = default;
bool PreparedFile::canBeInAlbumType(AlbumType album) const {
return CanBeInAlbumType(type, album);
}
AlbumType PreparedFile::albumType(bool sendImagesAsPhotos) const {
switch (type) {
case Type::Photo:
return sendImagesAsPhotos ? AlbumType::PhotoVideo : AlbumType::File;
case Type::Video:
return AlbumType::PhotoVideo;
case Type::Music:
return AlbumType::Music;
case Type::File:
return AlbumType::File;
case Type::None:
return AlbumType::None;
}
Unexpected("PreparedFile::type in PreparedFile::albumType().");
}
bool CanBeInAlbumType(PreparedFile::Type type, AlbumType album) {
Expects(album != AlbumType::None);
using Type = PreparedFile::Type;
switch (album) {
case AlbumType::PhotoVideo:
return (type == Type::Photo) || (type == Type::Video);
case AlbumType::Music:
return (type == Type::Music);
case AlbumType::File:
return (type == Type::Photo) || (type == Type::File);
}
Unexpected("AlbumType in CanBeInAlbumType.");
}
PreparedList PreparedList::Reordered(
PreparedList &&list,
std::vector<int> order) {
@ -73,7 +108,7 @@ bool PreparedList::canBeSentInSlowmodeWith(const PreparedList &other) const {
return false;
}
using Type = PreparedFile::AlbumType;
using Type = PreparedFile::Type;
auto &&all = ranges::view::concat(files, other.files);
const auto has = [&](Type type) {
return ranges::contains(all, type, &PreparedFile::type);
@ -117,11 +152,11 @@ bool PreparedList::canAddCaption(bool sendingAlbum) const {
}
const auto hasFiles = ranges::contains(
files,
PreparedFile::AlbumType::File,
PreparedFile::Type::File,
&PreparedFile::type);
const auto hasNotGrouped = ranges::contains(
files,
PreparedFile::AlbumType::None,
PreparedFile::Type::None,
&PreparedFile::type);
return !hasFiles && !hasNotGrouped;
}
@ -130,7 +165,7 @@ bool PreparedList::hasGroupOption(bool slowmode) const {
if (slowmode || files.size() < 2) {
return false;
}
using Type = PreparedFile::AlbumType;
using Type = PreparedFile::Type;
auto lastType = Type::None;
for (const auto &file : files) {
if ((file.type == lastType)
@ -148,7 +183,7 @@ bool PreparedList::hasGroupOption(bool slowmode) const {
}
bool PreparedList::hasSendImagesAsPhotosOption(bool slowmode) const {
using Type = PreparedFile::AlbumType;
using Type = PreparedFile::Type;
return slowmode
? ((files.size() == 1) && (files.front().type == Type::Photo))
: ranges::contains(files, Type::Photo, &PreparedFile::type);
@ -174,42 +209,33 @@ std::vector<PreparedGroup> DivideByGroups(
auto group = Ui::PreparedList();
enum class GroupType {
PhotoVideo,
File,
Music,
None,
};
// For groupType Type::Video means media album,
// Type::File means file album,
// Type::None means no grouping.
using Type = Ui::PreparedFile::AlbumType;
auto groupType = GroupType::None;
using Type = Ui::PreparedFile::Type;
auto groupType = AlbumType::None;
auto result = std::vector<PreparedGroup>();
auto pushGroup = [&] {
result.push_back(PreparedGroup{
.list = base::take(group),
.grouped = (groupType != GroupType::None)
.type = groupType,
});
};
for (auto i = 0; i != list.files.size(); ++i) {
auto &file = list.files[i];
const auto fileGroupType = (file.type == Type::Music)
? (groupFiles ? GroupType::Music : GroupType::None)
? (groupFiles ? AlbumType::Music : AlbumType::None)
: (file.type == Type::Video)
? (groupFiles ? GroupType::PhotoVideo : GroupType::None)
? (groupFiles ? AlbumType::PhotoVideo : AlbumType::None)
: (file.type == Type::Photo)
? ((groupFiles && sendImagesAsPhotos)
? GroupType::PhotoVideo
? AlbumType::PhotoVideo
: (groupFiles && !sendImagesAsPhotos)
? GroupType::File
: GroupType::None)
? AlbumType::File
: AlbumType::None)
: (file.type == Type::File)
? (groupFiles ? GroupType::File : GroupType::None)
: GroupType::None;
? (groupFiles ? AlbumType::File : AlbumType::None)
: AlbumType::None;
if ((!group.files.empty() && groupType != fileGroupType)
|| ((groupType != GroupType::None)
|| ((groupType != AlbumType::None)
&& (group.files.size() == Ui::MaxAlbumItems()))) {
pushGroup();
}

View File

@ -36,6 +36,13 @@ struct PreparedFileInformation {
std::variant<v::null_t, Image, Song, Video> media;
};
enum class AlbumType {
None,
PhotoVideo,
Music,
File,
};
struct PreparedFile {
// File-s can be grouped if 'groupFiles'.
// File-s + Photo-s can be grouped if 'groupFiles && !sendImagesAsPhotos'.
@ -43,12 +50,12 @@ struct PreparedFile {
// Photo-s + Video-s can be grouped if 'groupFiles && sendImagesAsPhotos'.
// Video-s can be grouped if 'groupFiles'.
// Music-s can be grouped if 'groupFiles'.
enum class AlbumType {
File,
enum class Type {
None,
Photo,
Video,
Music,
None,
File,
};
PreparedFile(const QString &path);
@ -56,15 +63,20 @@ struct PreparedFile {
PreparedFile &operator=(PreparedFile &&other);
~PreparedFile();
[[nodiscard]] bool canBeInAlbumType(AlbumType album) const;
[[nodiscard]] AlbumType albumType(bool sendImagesAsPhotos) const;
QString path;
QByteArray content;
int size = 0;
std::unique_ptr<Ui::PreparedFileInformation> information;
QImage preview;
QSize shownDimensions;
AlbumType type = AlbumType::File;
Type type = Type::File;
};
[[nodiscard]] bool CanBeInAlbumType(PreparedFile::Type type, AlbumType album);
struct PreparedList {
enum class Error {
None,
@ -104,7 +116,12 @@ struct PreparedList {
struct PreparedGroup {
PreparedList list;
bool grouped = false;
AlbumType type = AlbumType::None;
[[nodiscard]] bool sentWithCaption() const {
return (list.files.size() == 1)
|| (type == AlbumType::PhotoVideo);
}
};
[[nodiscard]] std::vector<PreparedGroup> DivideByGroups(