mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-08 08:04:08 +02:00
Allow adding any amount of files in SendFilesBox.
This commit is contained in:
parent
0539cc9448
commit
843fba61ee
7 changed files with 306 additions and 226 deletions
|
@ -50,6 +50,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/text/text_options.h"
|
#include "ui/text/text_options.h"
|
||||||
#include "ui/chat/attach/attach_prepare.h"
|
#include "ui/chat/attach/attach_prepare.h"
|
||||||
#include "ui/controls/emoji_button.h"
|
#include "ui/controls/emoji_button.h"
|
||||||
|
#include "ui/toast/toast.h"
|
||||||
#include "ui/cached_round_corners.h"
|
#include "ui/cached_round_corners.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "confirm_box.h"
|
#include "confirm_box.h"
|
||||||
|
@ -485,7 +486,8 @@ void EditCaptionBox::updateEditPreview() {
|
||||||
const auto fileinfo = QFileInfo(file->path);
|
const auto fileinfo = QFileInfo(file->path);
|
||||||
const auto filename = fileinfo.fileName();
|
const auto filename = fileinfo.fileName();
|
||||||
|
|
||||||
_isImage = Core::FileIsImage(filename, file->mime);
|
const auto mime = file->information->filemime;
|
||||||
|
_isImage = Core::FileIsImage(filename, mime);
|
||||||
_isAudio = false;
|
_isAudio = false;
|
||||||
_animated = false;
|
_animated = false;
|
||||||
_photo = false;
|
_photo = false;
|
||||||
|
@ -525,7 +527,7 @@ void EditCaptionBox::updateEditPreview() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto getExt = [&] {
|
const auto getExt = [&] {
|
||||||
auto patterns = Core::MimeTypeForName(file->mime).globPatterns();
|
auto patterns = Core::MimeTypeForName(mime).globPatterns();
|
||||||
if (!patterns.isEmpty()) {
|
if (!patterns.isEmpty()) {
|
||||||
return patterns.front().replace('*', QString());
|
return patterns.front().replace('*', QString());
|
||||||
}
|
}
|
||||||
|
@ -588,14 +590,33 @@ void EditCaptionBox::updateEditMediaButton() {
|
||||||
|
|
||||||
void EditCaptionBox::createEditMediaButton() {
|
void EditCaptionBox::createEditMediaButton() {
|
||||||
const auto callback = [=](FileDialog::OpenResult &&result) {
|
const auto callback = [=](FileDialog::OpenResult &&result) {
|
||||||
auto showBoxErrorCallback = [](tr::phrase<> t) {
|
auto showError = [](tr::phrase<> t) {
|
||||||
Ui::show(Box<InformBox>(t(tr::now)), Ui::LayerOption::KeepOther);
|
Ui::Toast::Show(t(tr::now));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const auto checkResult = [=](const Ui::PreparedList &list) {
|
||||||
|
if (list.files.size() != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto &file = list.files.front();
|
||||||
|
const auto mime = file.information->filemime;
|
||||||
|
if (Core::IsMimeSticker(mime)) {
|
||||||
|
showError(tr::lng_edit_media_invalid_file);
|
||||||
|
return false;
|
||||||
|
} else if (_isAlbum) { // #TODO edit in file-albums
|
||||||
|
if (!Core::IsMimeAcceptedForAlbum(mime)
|
||||||
|
|| file.type == Ui::PreparedFile::AlbumType::File
|
||||||
|
|| file.type == Ui::PreparedFile::AlbumType::None) {
|
||||||
|
showError(tr::lng_edit_media_album_error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
auto list = Storage::PreparedFileFromFilesDialog(
|
auto list = Storage::PreparedFileFromFilesDialog(
|
||||||
std::move(result),
|
std::move(result),
|
||||||
_isAlbum,
|
checkResult,
|
||||||
std::move(showBoxErrorCallback),
|
showError,
|
||||||
st::sendMediaPreviewSize);
|
st::sendMediaPreviewSize);
|
||||||
|
|
||||||
if (list) {
|
if (list) {
|
||||||
|
|
|
@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/text/format_values.h"
|
#include "ui/text/format_values.h"
|
||||||
#include "ui/grouped_layout.h"
|
#include "ui/grouped_layout.h"
|
||||||
#include "ui/text/text_options.h"
|
#include "ui/text/text_options.h"
|
||||||
|
#include "ui/toast/toast.h"
|
||||||
#include "ui/controls/emoji_button.h"
|
#include "ui/controls/emoji_button.h"
|
||||||
#include "lottie/lottie_single_player.h"
|
#include "lottie/lottie_single_player.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
|
@ -70,17 +71,17 @@ inline bool IsSingleItem(const Ui::PreparedList &list) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileDialogCallback(
|
void FileDialogCallback(
|
||||||
FileDialog::OpenResult &&result,
|
FileDialog::OpenResult &&result,
|
||||||
bool isAlbum,
|
Fn<bool(const Ui::PreparedList&)> checkResult,
|
||||||
Fn<void(Ui::PreparedList)> callback) {
|
Fn<void(Ui::PreparedList)> callback) {
|
||||||
auto showBoxErrorCallback = [](tr::phrase<> text) {
|
auto showError = [](tr::phrase<> text) {
|
||||||
Ui::show(Box<InformBox>(text(tr::now)), Ui::LayerOption::KeepOther);
|
Ui::Toast::Show(text(tr::now));
|
||||||
};
|
};
|
||||||
|
|
||||||
auto list = Storage::PreparedFileFromFilesDialog(
|
auto list = Storage::PreparedFileFromFilesDialog(
|
||||||
std::move(result),
|
std::move(result),
|
||||||
isAlbum,
|
checkResult,
|
||||||
std::move(showBoxErrorCallback),
|
showError,
|
||||||
st::sendMediaPreviewSize);
|
st::sendMediaPreviewSize);
|
||||||
|
|
||||||
if (!list) {
|
if (!list) {
|
||||||
|
@ -212,12 +213,13 @@ SendFilesBox::SendFilesBox(
|
||||||
, _inner(
|
, _inner(
|
||||||
_scroll->setOwnedWidget(
|
_scroll->setOwnedWidget(
|
||||||
object_ptr<Ui::VerticalLayout>(_scroll.data()))) {
|
object_ptr<Ui::VerticalLayout>(_scroll.data()))) {
|
||||||
|
enqueueNextPrepare();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendFilesBox::initPreview() {
|
void SendFilesBox::initPreview() {
|
||||||
using namespace rpl::mappers;
|
using namespace rpl::mappers;
|
||||||
|
|
||||||
setupControls();
|
refreshControls();
|
||||||
|
|
||||||
updateBoxSize();
|
updateBoxSize();
|
||||||
|
|
||||||
|
@ -260,7 +262,6 @@ void SendFilesBox::addThumbButtonHandlers(not_null<Ui::ScrollArea*> wrap) {
|
||||||
// const auto callback = [=](FileDialog::OpenResult &&result) {
|
// const auto callback = [=](FileDialog::OpenResult &&result) {
|
||||||
// FileDialogCallback(
|
// FileDialogCallback(
|
||||||
// std::move(result),
|
// std::move(result),
|
||||||
// true,
|
|
||||||
// [=] (auto list) {
|
// [=] (auto list) {
|
||||||
// _list.files[index] = std::move(list.files.front());
|
// _list.files[index] = std::move(list.files.front());
|
||||||
// applyAlbumOrder();
|
// applyAlbumOrder();
|
||||||
|
@ -277,12 +278,38 @@ void SendFilesBox::addThumbButtonHandlers(not_null<Ui::ScrollArea*> wrap) {
|
||||||
// FileDialog::GetOpenPath(
|
// FileDialog::GetOpenPath(
|
||||||
// this,
|
// this,
|
||||||
// tr::lng_choose_file(tr::now),
|
// tr::lng_choose_file(tr::now),
|
||||||
// FileDialog::AlbumFilesFilter(),
|
// FileDialog::AllOrImagesFilter(),
|
||||||
// crl::guard(this, callback));
|
// crl::guard(this, callback));
|
||||||
|
|
||||||
//}, _albumPreview->lifetime());
|
//}, _albumPreview->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SendFilesBox::enqueueNextPrepare() {
|
||||||
|
if (_preparing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (!_list.filesToProcess.empty()
|
||||||
|
&& _list.filesToProcess.front().information) {
|
||||||
|
addFile(std::move(_list.filesToProcess.front()));
|
||||||
|
_list.filesToProcess.pop_front();
|
||||||
|
}
|
||||||
|
if (_list.filesToProcess.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto file = std::move(_list.filesToProcess.front());
|
||||||
|
_list.filesToProcess.pop_front();
|
||||||
|
const auto weak = Ui::MakeWeak(this);
|
||||||
|
_preparing = true;
|
||||||
|
crl::async([weak, file = std::move(file)]() mutable {
|
||||||
|
Storage::PrepareDetails(file, st::sendMediaPreviewSize);
|
||||||
|
crl::on_main([weak, file = std::move(file)]() mutable {
|
||||||
|
if (weak) {
|
||||||
|
weak->addPreparedAsyncFile(std::move(file));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void SendFilesBox::setupShadows() {
|
void SendFilesBox::setupShadows() {
|
||||||
using namespace rpl::mappers;
|
using namespace rpl::mappers;
|
||||||
|
|
||||||
|
@ -369,33 +396,42 @@ void SendFilesBox::setupDragArea() {
|
||||||
};
|
};
|
||||||
areas.document->setDroppedCallback(droppedCallback(false));
|
areas.document->setDroppedCallback(droppedCallback(false));
|
||||||
areas.photo->setDroppedCallback(droppedCallback(true));
|
areas.photo->setDroppedCallback(droppedCallback(true));
|
||||||
_albumChanged.events(
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
areas.document->raise();
|
|
||||||
areas.photo->raise();
|
|
||||||
}, lifetime());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendFilesBox::refreshAllAfterAlbumChanges() {
|
void SendFilesBox::refreshAllAfterChanges(int fromItem) {
|
||||||
preparePreview();
|
auto fromBlock = 0;
|
||||||
|
for (auto count = int(_blocks.size()); fromBlock != count; ++fromBlock) {
|
||||||
|
if (_blocks[fromBlock].tillIndex() >= fromItem) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
generatePreviewFrom(fromBlock);
|
||||||
|
_inner->resizeToWidth(st::boxWideWidth);
|
||||||
|
refreshControls();
|
||||||
captionResized();
|
captionResized();
|
||||||
_albumChanged.fire({});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendFilesBox::openDialogToAddFileToAlbum() {
|
void SendFilesBox::openDialogToAddFileToAlbum() {
|
||||||
|
const auto checkResult = [=](const Ui::PreparedList &list) {
|
||||||
|
if (_sendLimit != SendLimit::One) {
|
||||||
|
return true;
|
||||||
|
} else if (!_list.canBeSentInSlowmodeWith(list)) {
|
||||||
|
Ui::Toast::Show(tr::lng_slowmode_no_many(tr::now));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
const auto callback = [=](FileDialog::OpenResult &&result) {
|
const auto callback = [=](FileDialog::OpenResult &&result) {
|
||||||
FileDialogCallback(
|
FileDialogCallback(
|
||||||
std::move(result),
|
std::move(result),
|
||||||
true,
|
checkResult,
|
||||||
[=] (auto list) {
|
[=](Ui::PreparedList list) { addFiles(std::move(list)); });
|
||||||
addFiles(std::move(list));
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
FileDialog::GetOpenPaths(
|
FileDialog::GetOpenPaths(
|
||||||
this,
|
this,
|
||||||
tr::lng_choose_file(tr::now),
|
tr::lng_choose_file(tr::now),
|
||||||
FileDialog::AlbumFilesFilter(),
|
FileDialog::AllOrImagesFilter(),
|
||||||
crl::guard(this, callback));
|
crl::guard(this, callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -455,7 +491,11 @@ void SendFilesBox::generatePreviewFrom(int fromBlock) {
|
||||||
|
|
||||||
using Type = Ui::PreparedFile::AlbumType;
|
using Type = Ui::PreparedFile::AlbumType;
|
||||||
|
|
||||||
_blocks.erase(_blocks.begin() + fromBlock, _blocks.end());
|
const auto eraseFrom = _blocks.begin() + fromBlock;
|
||||||
|
for (auto i = eraseFrom; i != _blocks.end(); ++i) {
|
||||||
|
i->applyAlbumOrder();
|
||||||
|
}
|
||||||
|
_blocks.erase(eraseFrom, _blocks.end());
|
||||||
|
|
||||||
const auto fromItem = _blocks.empty() ? 0 : _blocks.back().tillIndex();
|
const auto fromItem = _blocks.empty() ? 0 : _blocks.back().tillIndex();
|
||||||
Assert(fromItem <= _list.files.size());
|
Assert(fromItem <= _list.files.size());
|
||||||
|
@ -498,12 +538,10 @@ void SendFilesBox::generatePreviewFrom(int fromBlock) {
|
||||||
if (albumStart >= 0) {
|
if (albumStart >= 0) {
|
||||||
pushBlock(albumStart, _list.files.size());
|
pushBlock(albumStart, _list.files.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
_scroll->scrollToY(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendFilesBox::setupControls() {
|
void SendFilesBox::refreshControls() {
|
||||||
setupTitleText();
|
refreshTitleText();
|
||||||
updateSendWayControlsVisibility();
|
updateSendWayControlsVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -669,20 +707,6 @@ void SendFilesBox::captionResized() {
|
||||||
|
|
||||||
bool SendFilesBox::canAddFiles(not_null<const QMimeData*> data) const {
|
bool SendFilesBox::canAddFiles(not_null<const QMimeData*> data) const {
|
||||||
return (data->hasUrls() && CanAddUrls(data->urls())) || data->hasImage();
|
return (data->hasUrls() && CanAddUrls(data->urls())) || data->hasImage();
|
||||||
//const auto urls = data->hasUrls() ? data->urls() : QList<QUrl>();
|
|
||||||
//auto filesCount = CanAddUrls(urls) ? urls.size() : 0;
|
|
||||||
//if (!filesCount && data->hasImage()) {
|
|
||||||
// ++filesCount;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//if (_list.files.size() + filesCount > Ui::MaxAlbumItems()) { // #TODO files
|
|
||||||
// return false;
|
|
||||||
//} else if (_list.files.size() > 1 && !_albumPreview) {
|
|
||||||
// return false;
|
|
||||||
//} else if (!IsFirstAlbumItem(_list)) {
|
|
||||||
// return false;
|
|
||||||
//}
|
|
||||||
//return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SendFilesBox::addFiles(not_null<const QMimeData*> data) {
|
bool SendFilesBox::addFiles(not_null<const QMimeData*> data) {
|
||||||
|
@ -713,36 +737,54 @@ bool SendFilesBox::addFiles(not_null<const QMimeData*> data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SendFilesBox::addFiles(Ui::PreparedList list) {
|
bool SendFilesBox::addFiles(Ui::PreparedList list) {
|
||||||
const auto sumFiles = _list.files.size() + list.files.size();
|
|
||||||
const auto cutToAlbumSize = (sumFiles > Ui::MaxAlbumItems());
|
|
||||||
if (list.error != Ui::PreparedList::Error::None) {
|
if (list.error != Ui::PreparedList::Error::None) {
|
||||||
return false;
|
return false;
|
||||||
//} else if (!IsSingleItem(list) && !list.albumIsPossible) { // #TODO files
|
|
||||||
// return false;
|
|
||||||
//} else if (!IsFirstAlbumItem(list)) {
|
|
||||||
// return false;
|
|
||||||
//} else if (_list.files.size() > 1 && !_albumPreview) {
|
|
||||||
// return false;
|
|
||||||
//} else if (!IsFirstAlbumItem(_list)) {
|
|
||||||
// return false;
|
|
||||||
}
|
}
|
||||||
//applyAlbumOrder();
|
const auto count = int(_list.files.size());
|
||||||
//delete base::take(_preview);
|
_list.filesToProcess.insert(
|
||||||
//_albumPreview = nullptr;
|
_list.filesToProcess.end(),
|
||||||
return false;
|
std::make_move_iterator(list.files.begin()),
|
||||||
_list.mergeToEnd(std::move(list), cutToAlbumSize);
|
std::make_move_iterator(list.files.end()));
|
||||||
|
_list.filesToProcess.insert(
|
||||||
refreshAllAfterAlbumChanges();
|
_list.filesToProcess.end(),
|
||||||
|
std::make_move_iterator(list.filesToProcess.begin()),
|
||||||
|
std::make_move_iterator(list.filesToProcess.end()));
|
||||||
|
enqueueNextPrepare();
|
||||||
|
if (_list.files.size() > count) {
|
||||||
|
refreshAllAfterChanges(count);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendFilesBox::setupTitleText() {
|
void SendFilesBox::addPreparedAsyncFile(Ui::PreparedFile &&file) {
|
||||||
if (_list.files.size() > 1) {
|
Expects(file.information != nullptr);
|
||||||
const auto onlyImages = false;/* #TODO files (_compressConfirm != CompressConfirm::None)
|
|
||||||
&& (_albumVideosCount == 0);*/
|
_preparing = false;
|
||||||
_titleText = onlyImages
|
const auto count = int(_list.files.size());
|
||||||
? tr::lng_send_images_selected(tr::now, lt_count, _list.files.size())
|
addFile(std::move(file));
|
||||||
: tr::lng_send_files_selected(tr::now, lt_count, _list.files.size());
|
enqueueNextPrepare();
|
||||||
|
if (_list.files.size() > count) {
|
||||||
|
refreshAllAfterChanges(count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendFilesBox::addFile(Ui::PreparedFile &&file) {
|
||||||
|
_list.files.push_back(std::move(file));
|
||||||
|
if (_sendLimit == SendLimit::One && !_list.canBeSentInSlowmode()) {
|
||||||
|
_list.files.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendFilesBox::refreshTitleText() {
|
||||||
|
const auto count = int(_list.files.size());
|
||||||
|
if (count > 1) {
|
||||||
|
const auto imagesCount = ranges::count(
|
||||||
|
_list.files,
|
||||||
|
Ui::PreparedFile::AlbumType::Photo,
|
||||||
|
&Ui::PreparedFile::type);
|
||||||
|
_titleText = (imagesCount == count)
|
||||||
|
? tr::lng_send_images_selected(tr::now, lt_count, count)
|
||||||
|
: tr::lng_send_files_selected(tr::now, lt_count, count);
|
||||||
_titleHeight = st::boxTitleHeight;
|
_titleHeight = st::boxTitleHeight;
|
||||||
} else {
|
} else {
|
||||||
_titleText = QString();
|
_titleText = QString();
|
||||||
|
|
|
@ -114,7 +114,7 @@ private:
|
||||||
void initSendWay();
|
void initSendWay();
|
||||||
void initPreview();
|
void initPreview();
|
||||||
|
|
||||||
void setupControls();
|
void refreshControls();
|
||||||
void setupSendWayControls();
|
void setupSendWayControls();
|
||||||
void setupCaption();
|
void setupCaption();
|
||||||
void setupShadows();
|
void setupShadows();
|
||||||
|
@ -133,7 +133,7 @@ private:
|
||||||
void captionResized();
|
void captionResized();
|
||||||
|
|
||||||
void setupDragArea();
|
void setupDragArea();
|
||||||
void setupTitleText();
|
void refreshTitleText();
|
||||||
void updateBoxSize();
|
void updateBoxSize();
|
||||||
void updateControlsGeometry();
|
void updateControlsGeometry();
|
||||||
void updateCaptionPlaceholder();
|
void updateCaptionPlaceholder();
|
||||||
|
@ -143,9 +143,13 @@ private:
|
||||||
bool canAddFiles(not_null<const QMimeData*> data) const;
|
bool canAddFiles(not_null<const QMimeData*> data) const;
|
||||||
bool addFiles(not_null<const QMimeData*> data);
|
bool addFiles(not_null<const QMimeData*> data);
|
||||||
bool addFiles(Ui::PreparedList list);
|
bool addFiles(Ui::PreparedList list);
|
||||||
|
void addFile(Ui::PreparedFile &&file);
|
||||||
|
|
||||||
void openDialogToAddFileToAlbum();
|
void openDialogToAddFileToAlbum();
|
||||||
void refreshAllAfterAlbumChanges();
|
void refreshAllAfterChanges(int fromItem);
|
||||||
|
|
||||||
|
void enqueueNextPrepare();
|
||||||
|
void addPreparedAsyncFile(Ui::PreparedFile &&file);
|
||||||
|
|
||||||
const not_null<Window::SessionController*> _controller;
|
const not_null<Window::SessionController*> _controller;
|
||||||
const Api::SendType _sendType = Api::SendType();
|
const Api::SendType _sendType = Api::SendType();
|
||||||
|
@ -178,12 +182,12 @@ private:
|
||||||
rpl::variable<Ui::SendFilesWay> _sendWay = Ui::SendFilesWay();
|
rpl::variable<Ui::SendFilesWay> _sendWay = Ui::SendFilesWay();
|
||||||
|
|
||||||
rpl::variable<int> _footerHeight = 0;
|
rpl::variable<int> _footerHeight = 0;
|
||||||
rpl::event_stream<> _albumChanged;
|
|
||||||
rpl::lifetime _dimensionsLifetime;
|
rpl::lifetime _dimensionsLifetime;
|
||||||
|
|
||||||
object_ptr<Ui::ScrollArea> _scroll;
|
object_ptr<Ui::ScrollArea> _scroll;
|
||||||
QPointer<Ui::VerticalLayout> _inner;
|
QPointer<Ui::VerticalLayout> _inner;
|
||||||
std::vector<Block> _blocks;
|
std::vector<Block> _blocks;
|
||||||
|
bool _preparing = false;
|
||||||
|
|
||||||
int _lastScrollTop = 0;
|
int _lastScrollTop = 0;
|
||||||
|
|
||||||
|
|
|
@ -62,77 +62,20 @@ QSize PrepareShownDimensions(const QImage &preview) {
|
||||||
: result;
|
: result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PrepareDetailsIsWaiting(
|
|
||||||
QSemaphore &semaphore,
|
|
||||||
PreparedFile &file,
|
|
||||||
int previewWidth) {
|
|
||||||
crl::async([=, &semaphore, &file] {
|
|
||||||
const auto guard = gsl::finally([&] { semaphore.release(); });
|
|
||||||
if (!file.path.isEmpty()) {
|
|
||||||
file.mime = Core::MimeTypeForFile(QFileInfo(file.path)).name();
|
|
||||||
file.information = FileLoadTask::ReadMediaInformation(
|
|
||||||
file.path,
|
|
||||||
QByteArray(),
|
|
||||||
file.mime);
|
|
||||||
} else if (!file.content.isEmpty()) {
|
|
||||||
file.mime = Core::MimeTypeForData(file.content).name();
|
|
||||||
file.information = FileLoadTask::ReadMediaInformation(
|
|
||||||
QString(),
|
|
||||||
file.content,
|
|
||||||
file.mime);
|
|
||||||
} else {
|
|
||||||
Assert(file.information != nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
using Image = PreparedFileInformation::Image;
|
|
||||||
using Video = PreparedFileInformation::Video;
|
|
||||||
if (const auto image = std::get_if<Image>(
|
|
||||||
&file.information->media)) {
|
|
||||||
if (ValidPhotoForAlbum(*image, file.mime)) {
|
|
||||||
file.shownDimensions = PrepareShownDimensions(image->data);
|
|
||||||
file.preview = Images::prepareOpaque(image->data.scaledToWidth(
|
|
||||||
std::min(previewWidth, style::ConvertScale(image->data.width()))
|
|
||||||
* cIntRetinaFactor(),
|
|
||||||
Qt::SmoothTransformation));
|
|
||||||
Assert(!file.preview.isNull());
|
|
||||||
file.preview.setDevicePixelRatio(cRetinaFactor());
|
|
||||||
file.type = PreparedFile::AlbumType::Photo;
|
|
||||||
} else if (Core::IsMimeSticker(file.mime)) {
|
|
||||||
file.type = PreparedFile::AlbumType::None;
|
|
||||||
}
|
|
||||||
} else if (const auto video = std::get_if<Video>(
|
|
||||||
&file.information->media)) {
|
|
||||||
if (ValidVideoForAlbum(*video)) {
|
|
||||||
auto blurred = Images::prepareBlur(Images::prepareOpaque(video->thumbnail));
|
|
||||||
file.shownDimensions = PrepareShownDimensions(video->thumbnail);
|
|
||||||
file.preview = std::move(blurred).scaledToWidth(
|
|
||||||
previewWidth * cIntRetinaFactor(),
|
|
||||||
Qt::SmoothTransformation);
|
|
||||||
Assert(!file.preview.isNull());
|
|
||||||
file.preview.setDevicePixelRatio(cRetinaFactor());
|
|
||||||
file.type = PreparedFile::AlbumType::Video;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrepareDetailsInParallel(PreparedList &result, int previewWidth) {
|
void PrepareDetailsInParallel(PreparedList &result, int previewWidth) {
|
||||||
Expects(result.files.size() <= Ui::MaxAlbumItems());
|
Expects(result.files.size() <= Ui::MaxAlbumItems());
|
||||||
|
|
||||||
auto waiting = 0;
|
if (result.files.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
QSemaphore semaphore;
|
QSemaphore semaphore;
|
||||||
for (auto &file : result.files) {
|
for (auto &file : result.files) {
|
||||||
if (PrepareDetailsIsWaiting(
|
crl::async([=, &semaphore, &file] {
|
||||||
semaphore,
|
PrepareDetails(file, previewWidth);
|
||||||
file,
|
semaphore.release();
|
||||||
previewWidth)) {
|
});
|
||||||
++waiting;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (waiting > 0) {
|
|
||||||
semaphore.acquire(waiting);
|
|
||||||
}
|
}
|
||||||
|
semaphore.acquire(result.files.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -277,91 +220,152 @@ PreparedList PrepareMediaFromImage(
|
||||||
|
|
||||||
std::optional<PreparedList> PreparedFileFromFilesDialog(
|
std::optional<PreparedList> PreparedFileFromFilesDialog(
|
||||||
FileDialog::OpenResult &&result,
|
FileDialog::OpenResult &&result,
|
||||||
bool isAlbum,
|
Fn<bool(const Ui::PreparedList&)> checkResult,
|
||||||
Fn<void(tr::phrase<>)> errorCallback,
|
Fn<void(tr::phrase<>)> errorCallback,
|
||||||
int previewWidth) {
|
int previewWidth) {
|
||||||
if (result.paths.isEmpty() && result.remoteContent.isEmpty()) {
|
if (result.paths.isEmpty() && result.remoteContent.isEmpty()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.remoteContent.isEmpty()) {
|
auto list = result.remoteContent.isEmpty()
|
||||||
auto list = PrepareMediaFromImage(
|
? PrepareMediaList(result.paths, previewWidth)
|
||||||
|
: PrepareMediaFromImage(
|
||||||
QImage(),
|
QImage(),
|
||||||
std::move(result.remoteContent),
|
std::move(result.remoteContent),
|
||||||
previewWidth);
|
previewWidth);
|
||||||
|
if (list.error != PreparedList::Error::None) {
|
||||||
const auto mimeFile = list.files.front().mime;
|
errorCallback(tr::lng_send_media_invalid_files);
|
||||||
if (Core::IsMimeSticker(mimeFile)) {
|
return std::nullopt;
|
||||||
errorCallback(tr::lng_edit_media_invalid_file);
|
} else if (!checkResult(list)) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
} else {
|
||||||
|
|
||||||
if (isAlbum) {
|
|
||||||
const auto file = &list.files.front();
|
|
||||||
if (!Core::IsMimeAcceptedForAlbum(mimeFile)
|
|
||||||
|| file->type == PreparedFile::AlbumType::File
|
|
||||||
|| file->type == PreparedFile::AlbumType::File) {
|
|
||||||
errorCallback(tr::lng_edit_media_album_error);
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expects(list.files.size() == 1);
|
|
||||||
return list;
|
|
||||||
} else if (!result.paths.isEmpty()) {
|
|
||||||
const auto isSingleFile = (result.paths.size() == 1);
|
|
||||||
auto temp = PrepareMediaList(result.paths, previewWidth);
|
|
||||||
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 list;
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
//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::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) {
|
||||||
|
if (!file.path.isEmpty()) {
|
||||||
|
file.information = FileLoadTask::ReadMediaInformation(
|
||||||
|
file.path,
|
||||||
|
QByteArray(),
|
||||||
|
Core::MimeTypeForFile(QFileInfo(file.path)).name());
|
||||||
|
} else if (!file.content.isEmpty()) {
|
||||||
|
file.information = FileLoadTask::ReadMediaInformation(
|
||||||
|
QString(),
|
||||||
|
file.content,
|
||||||
|
Core::MimeTypeForData(file.content).name());
|
||||||
|
} else {
|
||||||
|
Assert(file.information != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
using Image = PreparedFileInformation::Image;
|
||||||
|
using Video = PreparedFileInformation::Video;
|
||||||
|
if (const auto image = std::get_if<Image>(
|
||||||
|
&file.information->media)) {
|
||||||
|
if (ValidPhotoForAlbum(*image, file.information->filemime)) {
|
||||||
|
file.shownDimensions = PrepareShownDimensions(image->data);
|
||||||
|
file.preview = Images::prepareOpaque(image->data.scaledToWidth(
|
||||||
|
std::min(previewWidth, style::ConvertScale(image->data.width()))
|
||||||
|
* cIntRetinaFactor(),
|
||||||
|
Qt::SmoothTransformation));
|
||||||
|
Assert(!file.preview.isNull());
|
||||||
|
file.preview.setDevicePixelRatio(cRetinaFactor());
|
||||||
|
file.type = PreparedFile::AlbumType::Photo;
|
||||||
|
} else if (Core::IsMimeSticker(file.information->filemime)) {
|
||||||
|
file.type = PreparedFile::AlbumType::None;
|
||||||
|
}
|
||||||
|
} else if (const auto video = std::get_if<Video>(
|
||||||
|
&file.information->media)) {
|
||||||
|
if (ValidVideoForAlbum(*video)) {
|
||||||
|
auto blurred = Images::prepareBlur(Images::prepareOpaque(video->thumbnail));
|
||||||
|
file.shownDimensions = PrepareShownDimensions(video->thumbnail);
|
||||||
|
file.preview = std::move(blurred).scaledToWidth(
|
||||||
|
previewWidth * cIntRetinaFactor(),
|
||||||
|
Qt::SmoothTransformation);
|
||||||
|
Assert(!file.preview.isNull());
|
||||||
|
file.preview.setDevicePixelRatio(cRetinaFactor());
|
||||||
|
file.type = PreparedFile::AlbumType::Video;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Storage
|
} // namespace Storage
|
||||||
|
|
|
@ -27,7 +27,7 @@ enum class MimeDataState {
|
||||||
|
|
||||||
std::optional<Ui::PreparedList> PreparedFileFromFilesDialog(
|
std::optional<Ui::PreparedList> PreparedFileFromFilesDialog(
|
||||||
FileDialog::OpenResult &&result,
|
FileDialog::OpenResult &&result,
|
||||||
bool isAlbum,
|
Fn<bool(const Ui::PreparedList&)> checkResult,
|
||||||
Fn<void(tr::phrase<>)> errorCallback,
|
Fn<void(tr::phrase<>)> errorCallback,
|
||||||
int previewWidth);
|
int previewWidth);
|
||||||
MimeDataState ComputeMimeDataState(const QMimeData *data);
|
MimeDataState ComputeMimeDataState(const QMimeData *data);
|
||||||
|
@ -38,5 +38,6 @@ Ui::PreparedList PrepareMediaFromImage(
|
||||||
QImage &&image,
|
QImage &&image,
|
||||||
QByteArray &&content,
|
QByteArray &&content,
|
||||||
int previewWidth);
|
int previewWidth);
|
||||||
|
void PrepareDetails(Ui::PreparedFile &file, int previewWidth);
|
||||||
|
|
||||||
} // namespace Storage
|
} // namespace Storage
|
||||||
|
|
|
@ -60,24 +60,29 @@ void PreparedList::mergeToEnd(PreparedList &&other, bool cutToAlbumSize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PreparedList::canBeSentInSlowmode() const {
|
bool PreparedList::canBeSentInSlowmode() const {
|
||||||
if (!filesToProcess.empty()) {
|
return canBeSentInSlowmodeWith(PreparedList());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PreparedList::canBeSentInSlowmodeWith(const PreparedList &other) const {
|
||||||
|
if (!filesToProcess.empty() || !other.filesToProcess.empty()) {
|
||||||
return false;
|
return false;
|
||||||
} else if (files.size() < 2) {
|
} else if (files.size() + other.files.size() < 2) {
|
||||||
return true;
|
return true;
|
||||||
} else if (files.size() > kMaxAlbumCount) {
|
} else if (files.size() + other.files.size() > kMaxAlbumCount) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto &&all = ranges::view::concat(files, other.files);
|
||||||
const auto hasNonGrouping = ranges::contains(
|
const auto hasNonGrouping = ranges::contains(
|
||||||
files,
|
all,
|
||||||
PreparedFile::AlbumType::None,
|
PreparedFile::AlbumType::None,
|
||||||
&PreparedFile::type);
|
&PreparedFile::type);
|
||||||
const auto hasFiles = ranges::contains(
|
const auto hasFiles = ranges::contains(
|
||||||
files,
|
all,
|
||||||
PreparedFile::AlbumType::File,
|
PreparedFile::AlbumType::File,
|
||||||
&PreparedFile::type);
|
&PreparedFile::type);
|
||||||
const auto hasVideos = ranges::contains(
|
const auto hasVideos = ranges::contains(
|
||||||
files,
|
all,
|
||||||
PreparedFile::AlbumType::Video,
|
PreparedFile::AlbumType::Video,
|
||||||
&PreparedFile::type);
|
&PreparedFile::type);
|
||||||
|
|
||||||
|
@ -92,7 +97,9 @@ bool PreparedList::canAddCaption(bool groupMediaInAlbums) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (files.size() == 1) {
|
if (files.size() == 1) {
|
||||||
const auto isSticker = Core::IsMimeSticker(files.front().mime)
|
Assert(files.front().information != nullptr);
|
||||||
|
const auto isSticker = Core::IsMimeSticker(
|
||||||
|
files.front().information->filemime)
|
||||||
|| files.front().path.endsWith(
|
|| files.front().path.endsWith(
|
||||||
qstr(".tgs"),
|
qstr(".tgs"),
|
||||||
Qt::CaseInsensitive);
|
Qt::CaseInsensitive);
|
||||||
|
|
|
@ -56,7 +56,6 @@ struct PreparedFile {
|
||||||
|
|
||||||
QString path;
|
QString path;
|
||||||
QByteArray content;
|
QByteArray content;
|
||||||
QString mime;
|
|
||||||
int size = 0;
|
int size = 0;
|
||||||
std::unique_ptr<Ui::PreparedFileInformation> information;
|
std::unique_ptr<Ui::PreparedFileInformation> information;
|
||||||
QImage preview;
|
QImage preview;
|
||||||
|
@ -75,8 +74,8 @@ struct PreparedList {
|
||||||
|
|
||||||
PreparedList() = default;
|
PreparedList() = default;
|
||||||
PreparedList(Error error, QString errorData)
|
PreparedList(Error error, QString errorData)
|
||||||
: error(error)
|
: error(error)
|
||||||
, errorData(errorData) {
|
, errorData(errorData) {
|
||||||
}
|
}
|
||||||
[[nodiscard]] static PreparedList Reordered(
|
[[nodiscard]] static PreparedList Reordered(
|
||||||
PreparedList &&list,
|
PreparedList &&list,
|
||||||
|
@ -85,6 +84,8 @@ struct PreparedList {
|
||||||
|
|
||||||
[[nodiscard]] bool canAddCaption(bool groupMediaInAlbums) const;
|
[[nodiscard]] bool canAddCaption(bool groupMediaInAlbums) const;
|
||||||
[[nodiscard]] bool canBeSentInSlowmode() const;
|
[[nodiscard]] bool canBeSentInSlowmode() const;
|
||||||
|
[[nodiscard]] bool canBeSentInSlowmodeWith(
|
||||||
|
const PreparedList &other) const;
|
||||||
|
|
||||||
Error error = Error::None;
|
Error error = Error::None;
|
||||||
QString errorData;
|
QString errorData;
|
||||||
|
|
Loading…
Add table
Reference in a new issue