Files
AyuGramDesktop/Telegram/SourceFiles/poll/poll_media_upload.cpp
T

1044 lines
28 KiB
C++

/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "poll/poll_media_upload.h"
#include "api/api_sending.h"
#include "apiwrap.h"
#include "base/event_filter.h"
#include "core/file_utilities.h"
#include "core/mime_type.h"
#include "data/data_document.h"
#include "data/data_peer.h"
#include "data/data_photo.h"
#include "data/data_session.h"
#include "lang/lang_keys.h"
#include "layout/layout_document_generic_preview.h"
#include "main/main_session.h"
#include "platform/platform_file_utilities.h"
#include "storage/file_upload.h"
#include "storage/localimageloader.h"
#include "storage/storage_media_prepare.h"
#include "ui/chat/attach/attach_prepare.h"
#include "ui/dynamic_thumbnails.h"
#include "ui/effects/ripple_animation.h"
#include "ui/painter.h"
#include "ui/rect.h"
#include "ui/widgets/fields/input_field.h"
#include "styles/style_boxes.h"
#include "styles/style_overview.h"
#include "styles/style_polls.h"
#include <QtCore/QMimeData>
namespace PollMediaUpload {
LocalImageThumbnail::LocalImageThumbnail(QImage original)
: _original(std::move(original)) {
}
std::shared_ptr<Ui::DynamicImage> LocalImageThumbnail::clone() {
return std::make_shared<LocalImageThumbnail>(_original);
}
QImage LocalImageThumbnail::image(int size) {
return _original;
}
void LocalImageThumbnail::subscribeToUpdates(Fn<void()> callback) {
}
QImage GenerateDocumentFilePreview(
const QString &filename,
int size) {
const auto preview = Layout::DocumentGenericPreview::Create(filename);
const auto &color = preview.color;
const auto &ext = preview.ext;
const auto ratio = style::DevicePixelRatio();
auto result = QImage(
QSize(size, size) * ratio,
QImage::Format_ARGB32_Premultiplied);
result.fill(Qt::transparent);
result.setDevicePixelRatio(ratio);
auto p = QPainter(&result);
PainterHighQualityEnabler hq(p);
p.setPen(Qt::NoPen);
p.setBrush(color);
p.drawRoundedRect(
QRect(0, 0, size, size),
st::roundRadiusSmall,
st::roundRadiusSmall);
if (!ext.isEmpty()) {
const auto refSize = st::overviewFileLayout.fileThumbSize;
const auto fontSize = std::max(
size * st::overviewFileExtFont->f.pixelSize() / refSize,
8);
const auto font = style::font(
fontSize,
st::overviewFileExtFont->flags(),
st::overviewFileExtFont->family());
const auto padding = size * st::overviewFileExtPadding / refSize;
const auto maxw = size - padding * 2;
auto extStr = ext;
auto extw = font->width(extStr);
if (extw > maxw) {
extStr = font->elided(extStr, maxw, Qt::ElideMiddle);
extw = font->width(extStr);
}
p.setFont(font);
p.setPen(st::overviewFileExtFg);
p.drawText(
(size - extw) / 2,
(size - font->height) / 2 + font->ascent,
extStr);
}
p.end();
return result;
}
bool ValidateFileDragData(not_null<const QMimeData*> data) {
if (data->hasImage()) {
return true;
}
const auto urls = Core::ReadMimeUrls(data);
return (urls.size() == 1) && urls.front().isLocalFile();
}
QVector<MTPDocumentAttribute> ExtractAudioAttributes(
const Ui::PreparedFile &file) {
auto result = QVector<MTPDocumentAttribute>();
if (!file.information) {
return result;
}
const auto song = std::get_if<Ui::PreparedFileInformation::Song>(
&file.information->media);
if (!song) {
return result;
}
const auto seconds = song->duration / 1000;
using Flag = MTPDdocumentAttributeAudio::Flag;
result.push_back(MTP_documentAttributeAudio(
MTP_flags(Flag::f_title | Flag::f_performer),
MTP_int(seconds),
MTP_string(song->title),
MTP_string(song->performer),
MTPstring()));
return result;
}
Ui::PreparedList FileListFromMimeData(
not_null<const QMimeData*> data,
bool premium) {
using Error = Ui::PreparedList::Error;
const auto urls = Core::ReadMimeUrls(data);
if (!urls.isEmpty()) {
return Storage::PrepareMediaList(
urls.mid(0, 1),
st::sendMediaPreviewSize,
premium);
} else if (auto read = Core::ReadMimeImage(data)) {
return Storage::PrepareMediaFromImage(
std::move(read.image),
std::move(read.content),
st::sendMediaPreviewSize);
}
return Ui::PreparedList(Error::EmptyFile, QString());
}
PollMediaButton::PollMediaButton(
not_null<QWidget*> parent,
const style::IconButton &st,
std::shared_ptr<PollMediaState> state)
: Ui::RippleButton(parent, st.ripple)
, _st(st)
, _state(std::move(state))
, _attach(Ui::MakeIconThumbnail(_st.icon))
, _attachOver(_st.iconOver.empty()
? _attach
: Ui::MakeIconThumbnail(_st.iconOver))
, _radial([=](crl::time now) { radialAnimationCallback(now); }) {
const auto weak = QPointer<PollMediaButton>(this);
_state->update = [=] {
if (weak) {
weak->updateMediaSubscription();
weak->update();
}
};
resize(_st.width, _st.height);
setPointerCursor(true);
updateMediaSubscription();
}
void PollMediaButton::setIconColorOverride(
std::optional<QColor> colorOverride) {
_iconColorOverride = colorOverride;
update();
}
void PollMediaButton::setRippleColorOverride(
std::optional<QColor> colorOverride) {
_rippleColorOverride = colorOverride;
update();
}
PollMediaButton::~PollMediaButton() {
if (_subscribed) {
_subscribed->subscribeToUpdates(nullptr);
}
}
void PollMediaButton::paintEvent(QPaintEvent *e) {
auto p = Painter(this);
paintRipple(
p,
_st.rippleAreaPosition,
_rippleColorOverride ? &*_rippleColorOverride : nullptr);
if (_state->thumbnail) {
const auto target = rippleRect();
paintCover(
p,
target,
_state->thumbnail->image(
std::max(target.width(), target.height())),
_state->rounded);
} else if (_iconColorOverride) {
const auto &icon = (isOver() && !_st.iconOver.empty())
? _st.iconOver
: _st.icon;
auto position = _st.iconPosition;
if (position.x() < 0) {
position.setX((width() - icon.width()) / 2);
}
if (position.y() < 0) {
position.setY((height() - icon.height()) / 2);
}
icon.paint(p, position, width(), *_iconColorOverride);
} else if (const auto image = currentAttachThumbnail()) {
const auto target = iconRect();
p.drawImage(
target,
image->image(std::max(target.width(), target.height())));
}
if (_state->thumbnail && !_state->uploading) {
const auto viewOpacity = _viewShown.value(
(isOver() || isDown()) ? 1. : 0.);
if (viewOpacity > 0.) {
p.save();
p.setOpacity(viewOpacity);
auto path = QPainterPath();
path.addRoundedRect(
rippleRect(),
st::roundRadiusSmall,
st::roundRadiusSmall);
p.setClipPath(path);
p.fillRect(rippleRect(), st::songCoverOverlayFg);
st::pollAttachView.paintInCenter(p, rippleRect());
p.restore();
}
}
if (_state->uploading && !_radial.animating()) {
_radial.start(_state->progress);
}
if (_state->uploading || _radial.animating()) {
if (_state->thumbnail) {
p.save();
auto path = QPainterPath();
path.addRoundedRect(
rippleRect(),
st::roundRadiusSmall,
st::roundRadiusSmall);
p.setClipPath(path);
p.fillRect(rippleRect(), st::songCoverOverlayFg);
p.restore();
}
const auto cancelOpacity = _state->uploading
? _cancelShown.value(
(isOver() || isDown()) ? 1. : 0.)
: 0.;
const auto line = float64(st::lineWidth * 2);
const auto margin = float64(st::pollAttachProgressMargin);
const auto arc = QRectF(rippleRect()) - Margins(margin);
if (cancelOpacity > 0.) {
p.setOpacity(cancelOpacity);
st::pollAttachCancel.paintInCenter(p, rippleRect());
p.setOpacity(1.);
}
_radial.draw(p, arc, line, st::historyFileThumbRadialFg);
}
}
void PollMediaButton::onStateChanged(State was, StateChangeSource source) {
RippleButton::onStateChanged(was, source);
if (!_state->thumbnail) {
update();
}
const auto over = isOver() || isDown();
const auto wasOver = (was & StateFlag::Over)
|| (was & StateFlag::Down);
if (over != wasOver) {
if (_state->uploading) {
_cancelShown.start(
[=] { update(); },
over ? 0. : 1.,
over ? 1. : 0.,
st::universalDuration);
}
if (_state->thumbnail && !_state->uploading) {
_viewShown.start(
[=] { update(); },
over ? 0. : 1.,
over ? 1. : 0.,
st::universalDuration);
}
}
}
QImage PollMediaButton::prepareRippleMask() const {
return Ui::RippleAnimation::EllipseMask(QSize(
_st.rippleAreaSize,
_st.rippleAreaSize));
}
QPoint PollMediaButton::prepareRippleStartPosition() const {
auto result = mapFromGlobal(QCursor::pos())
- _st.rippleAreaPosition;
const auto rect = QRect(
QPoint(),
QSize(_st.rippleAreaSize, _st.rippleAreaSize));
return rect.contains(result)
? result
: DisabledRippleStartPosition();
}
void PollMediaButton::paintCover(
Painter &p,
QRect target,
QImage image,
bool rounded) const {
if (image.isNull() || target.isEmpty()) {
return;
}
const auto source = QRectF(0, 0, image.width(), image.height());
const auto kx = target.width() / source.width();
const auto ky = target.height() / source.height();
const auto scale = std::max(kx, ky);
const auto size = QSizeF(
source.width() * scale,
source.height() * scale);
const auto geometry = QRectF(
target.x() + (target.width() - size.width()) / 2.,
target.y() + (target.height() - size.height()) / 2.,
size.width(),
size.height());
p.save();
if (rounded) {
auto path = QPainterPath();
path.addRoundedRect(
target,
st::roundRadiusSmall,
st::roundRadiusSmall);
p.setClipPath(path);
}
p.drawImage(geometry, image, source);
p.restore();
}
void PollMediaButton::radialAnimationCallback(crl::time now) {
const auto updated = _radial.update(
_state->progress,
!_state->uploading,
now);
if (!anim::Disabled() || updated || _radial.animating()) {
update(rippleRect());
}
}
QRect PollMediaButton::rippleRect() const {
return QRect(
_st.rippleAreaPosition,
QSize(_st.rippleAreaSize, _st.rippleAreaSize));
}
QRect PollMediaButton::iconRect() const {
const auto over = isOver() || isDown();
const auto &icon = over && !_st.iconOver.empty()
? _st.iconOver
: _st.icon;
auto position = _st.iconPosition;
if (position.x() < 0) {
position.setX((width() - icon.width()) / 2);
}
if (position.y() < 0) {
position.setY((height() - icon.height()) / 2);
}
return QRect(position, QSize(icon.width(), icon.height()));
}
std::shared_ptr<Ui::DynamicImage>
PollMediaButton::currentAttachThumbnail() const {
return (isOver() || isDown()) ? _attachOver : _attach;
}
void PollMediaButton::updateMediaSubscription() {
if (_subscribed == _state->thumbnail) {
return;
}
if (_subscribed) {
_subscribed->subscribeToUpdates(nullptr);
}
_subscribed = _state->thumbnail;
if (!_subscribed) {
return;
}
const auto weak = QPointer<PollMediaButton>(this);
_subscribed->subscribeToUpdates([=] {
if (weak) {
weak->update();
}
});
}
PreparePollMediaTask::PreparePollMediaTask(
FileLoadTask::Args &&args,
Fn<void(std::shared_ptr<FilePrepareResult>)> done)
: _task(std::move(args))
, _done(std::move(done)) {
}
void PreparePollMediaTask::process() {
_task.process({ .generateGoodThumbnail = false });
}
void PreparePollMediaTask::finish() {
_done(_task.peekResult());
}
PollMediaUploader::PollMediaUploader(Args &&args)
: _session(args.session)
, _peer(args.peer)
, _showError(std::move(args.showError))
, _prepareQueue(std::make_unique<TaskQueue>()) {
subscribeToUploader();
}
PollMediaUploader::~PollMediaUploader() = default;
void PollMediaUploader::subscribeToUploader() {
auto &uploader = _session->uploader();
uploader.photoReady(
) | rpl::on_next([=](const Storage::UploadedMedia &data) {
const auto context = _uploads.take(data.fullId);
if (!context) {
return;
}
const auto media = context->media.lock();
if (!media || (media->token != context->token)) {
return;
}
applyUploaded(media, context->token, data.fullId, data.info.file);
}, _lifetime);
uploader.photoProgress(
) | rpl::on_next([=](const FullMsgId &id) {
const auto i = _uploads.find(id);
if (i == _uploads.end()) {
return;
}
const auto &context = i->second;
const auto media = context.media.lock();
if (!media
|| (media->token != context.token)
|| !media->uploadDataId) {
return;
}
media->progress = _session->data().photo(
media->uploadDataId)->progress();
updateMedia(media);
}, _lifetime);
uploader.photoFailed(
) | rpl::on_next([=](const FullMsgId &id) {
const auto context = _uploads.take(id);
if (!context) {
return;
}
const auto media = context->media.lock();
if (!media || (media->token != context->token)) {
return;
}
setMedia(media, PollMedia(), nullptr, false);
_showError(tr::lng_attach_failed(tr::now));
}, _lifetime);
uploader.documentReady(
) | rpl::on_next([=](const Storage::UploadedMedia &data) {
const auto context = _uploads.take(data.fullId);
if (!context) {
return;
}
const auto media = context->media.lock();
if (!media || (media->token != context->token)) {
return;
}
applyUploadedDocument(
media,
context->token,
data.fullId,
data.info,
*context);
}, _lifetime);
uploader.documentProgress(
) | rpl::on_next([=](const FullMsgId &id) {
const auto i = _uploads.find(id);
if (i == _uploads.end()) {
return;
}
const auto &context = i->second;
const auto media = context.media.lock();
if (!media
|| (media->token != context.token)
|| !media->uploadDataId) {
return;
}
media->progress = _session->data().document(
media->uploadDataId)->progress();
updateMedia(media);
}, _lifetime);
uploader.documentFailed(
) | rpl::on_next([=](const FullMsgId &id) {
const auto context = _uploads.take(id);
if (!context) {
return;
}
const auto media = context->media.lock();
if (!media || (media->token != context->token)) {
return;
}
setMedia(media, PollMedia(), nullptr, false);
_showError(tr::lng_attach_failed(tr::now));
}, _lifetime);
}
void PollMediaUploader::updateMedia(
const std::shared_ptr<PollMediaState> &media) {
if (media->update) {
media->update();
}
}
void PollMediaUploader::setMedia(
const std::shared_ptr<PollMediaState> &media,
PollMedia value,
std::shared_ptr<Ui::DynamicImage> thumbnail,
bool rounded) {
const auto wasUploading = media->uploading;
media->token++;
media->media = value;
media->thumbnail = std::move(thumbnail);
media->rounded = rounded;
media->progress = (media->uploading && media->media)
? 1.
: 0.;
media->uploadDataId = 0;
media->uploading = false;
if (wasUploading && value) {
media->uploadedAt = crl::now();
} else {
media->uploadedAt = 0;
media->reupload = nullptr;
}
updateMedia(media);
}
auto PollMediaUploader::parseUploaded(
const MTPMessageMedia &result,
FullMsgId fullId) -> UploadedMedia {
auto parsed = UploadedMedia();
auto &owner = _session->data();
result.match([&](const MTPDmessageMediaPhoto &media) {
if (const auto photo = media.vphoto()) {
photo->match([&](const MTPDphoto &) {
parsed.input.photo = owner.processPhoto(*photo);
parsed.thumbnail = Ui::MakePhotoThumbnail(
parsed.input.photo,
fullId);
}, [](const auto &) {
});
}
}, [&](const MTPDmessageMediaDocument &media) {
if (const auto document = media.vdocument()) {
document->match([&](const MTPDdocument &) {
parsed.input.document = owner.processDocument(
*document);
parsed.thumbnail
= Ui::MakeDocumentFilePreviewThumbnail(
parsed.input.document,
fullId);
}, [](const auto &) {
});
}
}, [](const auto &) {
});
return parsed;
}
void PollMediaUploader::applyUploaded(
const std::shared_ptr<PollMediaState> &media,
uint64 token,
FullMsgId fullId,
const MTPInputFile &file) {
const auto uploaded = MTP_inputMediaUploadedPhoto(
MTP_flags(0),
file,
MTP_vector<MTPInputDocument>(QVector<MTPInputDocument>()),
MTPint(),
MTPInputDocument());
_session->api().request(MTPmessages_UploadMedia(
MTP_flags(0),
MTPstring(),
_peer->input(),
uploaded
)).done([=](const MTPMessageMedia &result) {
if (media->token != token) {
return;
}
auto parsed = parseUploaded(result, fullId);
if (!parsed.input) {
setMedia(media, PollMedia(), nullptr, false);
_showError(tr::lng_attach_failed(tr::now));
return;
}
setMedia(
media,
parsed.input,
media->thumbnail
? media->thumbnail
: std::move(parsed.thumbnail),
true);
}).fail([=](const MTP::Error &) {
if (media->token != token) {
return;
}
setMedia(media, PollMedia(), nullptr, false);
_showError(tr::lng_attach_failed(tr::now));
}).send();
}
void PollMediaUploader::applyUploadedDocument(
const std::shared_ptr<PollMediaState> &media,
uint64 token,
FullMsgId fullId,
const Api::RemoteFileInfo &info,
const UploadContext &context) {
using Flag = MTPDinputMediaUploadedDocument::Flag;
const auto flags = (context.forceFile ? Flag::f_force_file : Flag())
| (info.thumb ? Flag::f_thumb : Flag());
auto attributes = !context.attributes.isEmpty()
? context.attributes
: QVector<MTPDocumentAttribute>{
MTP_documentAttributeFilename(
MTP_string(context.filename)),
};
const auto uploaded = MTP_inputMediaUploadedDocument(
MTP_flags(flags),
info.file,
info.thumb.value_or(MTPInputFile()),
MTP_string(context.filemime),
MTP_vector<MTPDocumentAttribute>(std::move(attributes)),
MTP_vector<MTPInputDocument>(),
MTPInputPhoto(),
MTP_int(0),
MTP_int(0));
_session->api().request(MTPmessages_UploadMedia(
MTP_flags(0),
MTPstring(),
_peer->input(),
uploaded
)).done([=](const MTPMessageMedia &result) {
if (media->token != token) {
return;
}
auto parsed = parseUploaded(result, fullId);
if (!parsed.input) {
setMedia(media, PollMedia(), nullptr, false);
_showError(tr::lng_attach_failed(tr::now));
return;
}
const auto isVideo = parsed.input.document
&& parsed.input.document->isVideoFile();
setMedia(
media,
parsed.input,
isVideo
? (media->thumbnail
? media->thumbnail
: std::move(parsed.thumbnail))
: std::move(parsed.thumbnail),
isVideo);
}).fail([=](const MTP::Error &) {
if (media->token != token) {
return;
}
setMedia(media, PollMedia(), nullptr, false);
_showError(tr::lng_attach_failed(tr::now));
}).send();
}
void PollMediaUploader::startPhotoUpload(
std::shared_ptr<PollMediaState> media,
Ui::PreparedFile file) {
const auto token = ++media->token;
media->media = PollMedia();
media->thumbnail = std::make_shared<LocalImageThumbnail>(
std::move(file.preview));
media->rounded = true;
media->uploading = true;
media->progress = 0.;
media->uploadDataId = 0;
updateMedia(media);
_prepareQueue->addTask(std::make_unique<PreparePollMediaTask>(
FileLoadTask::Args{
.session = _session,
.filepath = file.path,
.content = file.content,
.information = std::move(file.information),
.videoCover = nullptr,
.type = SendMediaType::Photo,
.to = FileLoadTo(
_peer->id,
Api::SendOptions(),
FullReplyTo(),
MsgId()),
.caption = TextWithTags(),
.spoiler = false,
.album = nullptr,
.forceFile = false,
.idOverride = 0,
.displayName = file.displayName,
},
[=](std::shared_ptr<FilePrepareResult> prepared) {
if ((media->token != token)
|| !prepared
|| (prepared->type != SendMediaType::Photo)) {
if (media->token == token) {
setMedia(media, PollMedia(), nullptr, false);
_showError(tr::lng_attach_failed(tr::now));
}
return;
}
const auto uploadId = FullMsgId(
_peer->id,
_session->data().nextLocalMessageId());
_uploads.emplace(uploadId, UploadContext{
.media = media,
.token = token,
});
media->uploadDataId = prepared->id;
_session->uploader().upload(uploadId, prepared);
}));
}
void PollMediaUploader::startDocumentUpload(
std::shared_ptr<PollMediaState> media,
Ui::PreparedFile file) {
const auto displayName = file.displayName.isEmpty()
? QFileInfo(file.path).fileName()
: file.displayName;
auto audioAttributes = ExtractAudioAttributes(file);
const auto isAudio = !audioAttributes.isEmpty();
const auto token = ++media->token;
media->media = PollMedia();
media->thumbnail = std::make_shared<LocalImageThumbnail>(
GenerateDocumentFilePreview(
displayName,
st::pollAttach.rippleAreaSize));
media->rounded = false;
media->uploading = true;
media->progress = 0.;
media->uploadDataId = 0;
updateMedia(media);
_prepareQueue->addTask(std::make_unique<PreparePollMediaTask>(
FileLoadTask::Args{
.session = _session,
.filepath = file.path,
.content = file.content,
.information = std::move(file.information),
.videoCover = nullptr,
.type = SendMediaType::File,
.to = FileLoadTo(
_peer->id,
Api::SendOptions(),
FullReplyTo(),
MsgId()),
.caption = TextWithTags(),
.spoiler = false,
.album = nullptr,
.forceFile = !isAudio,
.idOverride = 0,
.displayName = displayName,
},
[=, attributes = std::move(audioAttributes)](
std::shared_ptr<FilePrepareResult> prepared) {
if ((media->token != token) || !prepared) {
if (media->token == token) {
setMedia(media, PollMedia(), nullptr, false);
_showError(tr::lng_attach_failed(tr::now));
}
return;
}
const auto uploadId = FullMsgId(
_peer->id,
_session->data().nextLocalMessageId());
_uploads.emplace(uploadId, UploadContext{
.media = media,
.token = token,
.filename = prepared->filename,
.filemime = prepared->filemime,
.attributes = attributes,
.forceFile = !isAudio,
});
media->uploadDataId = prepared->id;
_session->uploader().upload(uploadId, prepared);
}));
}
void PollMediaUploader::startVideoUpload(
std::shared_ptr<PollMediaState> media,
Ui::PreparedFile file) {
const auto token = ++media->token;
media->media = PollMedia();
media->thumbnail = std::make_shared<LocalImageThumbnail>(
std::move(file.preview));
media->rounded = true;
media->uploading = true;
media->progress = 0.;
media->uploadDataId = 0;
updateMedia(media);
_prepareQueue->addTask(std::make_unique<PreparePollMediaTask>(
FileLoadTask::Args{
.session = _session,
.filepath = file.path,
.content = file.content,
.information = std::move(file.information),
.videoCover = nullptr,
.type = SendMediaType::File,
.to = FileLoadTo(
_peer->id,
Api::SendOptions(),
FullReplyTo(),
MsgId()),
.caption = TextWithTags(),
.spoiler = false,
.album = nullptr,
.forceFile = false,
.idOverride = 0,
.displayName = file.displayName,
},
[=](std::shared_ptr<FilePrepareResult> prepared) {
if ((media->token != token) || !prepared) {
if (media->token == token) {
setMedia(media, PollMedia(), nullptr, false);
_showError(tr::lng_attach_failed(tr::now));
}
return;
}
auto attributes = QVector<MTPDocumentAttribute>();
prepared->document.match([&](const MTPDdocument &data) {
attributes = data.vattributes().v;
}, [](const auto &) {
});
const auto uploadId = FullMsgId(
_peer->id,
_session->data().nextLocalMessageId());
_uploads.emplace(uploadId, UploadContext{
.media = media,
.token = token,
.filename = prepared->filename,
.filemime = prepared->filemime,
.attributes = std::move(attributes),
.forceFile = false,
});
media->uploadDataId = prepared->id;
_session->uploader().upload(uploadId, prepared);
}));
}
bool PollMediaUploader::applyPreparedPhotoList(
std::shared_ptr<PollMediaState> media,
Ui::PreparedList &&list) {
if (list.error != Ui::PreparedList::Error::None
|| (list.files.size() != 1)
|| (list.files.front().type != Ui::PreparedFile::Type::Photo)) {
return false;
}
startPhotoUpload(media, std::move(list.files.front()));
return true;
}
void PollMediaUploader::clearMedia(std::shared_ptr<PollMediaState> media) {
auto toCancel = std::vector<FullMsgId>();
for (auto i = _uploads.begin(); i != _uploads.end();) {
if (i->second.media.lock() == media) {
toCancel.push_back(i->first);
i = _uploads.erase(i);
} else {
++i;
}
}
for (const auto &id : toCancel) {
_session->uploader().cancel(id);
}
setMedia(media, PollMedia(), nullptr, false);
}
void PollMediaUploader::choosePhotoOrVideo(
not_null<QWidget*> parent,
std::shared_ptr<PollMediaState> media) {
const auto weak = QPointer<QWidget>(parent.get());
const auto callback = crl::guard(parent.get(), [=](
FileDialog::OpenResult &&result) {
const auto checkResult = [&](const Ui::PreparedList &list) {
using namespace Ui;
if (list.files.size() != 1) {
return false;
}
const auto type = list.files.front().type;
return (type == PreparedFile::Type::Photo)
|| (type == PreparedFile::Type::Video);
};
const auto showError = [=](tr::phrase<> text) {
_showError(text(tr::now));
};
auto list = Storage::PreparedFileFromFilesDialog(
std::move(result),
checkResult,
showError,
st::sendMediaPreviewSize,
_session->premium());
if (!list) {
return;
}
auto &file = list->files.front();
if (file.type == Ui::PreparedFile::Type::Photo) {
applyPreparedPhotoList(media, std::move(*list));
} else {
startVideoUpload(media, std::move(file));
}
});
FileDialog::GetOpenPath(
QPointer<QWidget>(parent.get()),
tr::lng_attach_photo_or_video(tr::now),
FileDialog::PhotoVideoFilesFilter(),
callback);
}
void PollMediaUploader::chooseDocument(
not_null<QWidget*> parent,
std::shared_ptr<PollMediaState> media) {
const auto callback = crl::guard(parent.get(), [=](
FileDialog::OpenResult &&result) {
if (result.paths.isEmpty()) {
return;
}
auto list = Storage::PrepareMediaList(
result.paths.mid(0, 1),
st::sendMediaPreviewSize,
_session->premium());
if (list.error != Ui::PreparedList::Error::None
|| list.files.empty()) {
return;
}
startDocumentUpload(
media,
std::move(list.files.front()));
});
FileDialog::GetOpenPath(
QPointer<QWidget>(parent.get()),
tr::lng_attach_file(tr::now),
FileDialog::AllFilesFilter(),
callback);
}
void PollMediaUploader::installDropToWidget(
not_null<QWidget*> widget,
std::shared_ptr<PollMediaState> media,
bool allowDocuments) {
widget->setAcceptDrops(true);
base::install_event_filter(widget, [=](not_null<QEvent*> event) {
const auto type = event->type();
if (type != QEvent::DragEnter
&& type != QEvent::DragMove
&& type != QEvent::Drop) {
return base::EventFilterResult::Continue;
}
const auto drop = static_cast<QDropEvent*>(event.get());
const auto data = drop->mimeData();
if (!data || !ValidateFileDragData(data)) {
return base::EventFilterResult::Continue;
}
if (type == QEvent::Drop) {
auto list = FileListFromMimeData(data, _session->premium());
if (list.error != Ui::PreparedList::Error::None
|| list.files.empty()) {
return base::EventFilterResult::Continue;
}
auto &file = list.files.front();
if (file.type == Ui::PreparedFile::Type::Photo) {
startPhotoUpload(media, std::move(file));
} else if (file.type == Ui::PreparedFile::Type::Video) {
startVideoUpload(media, std::move(file));
} else if (allowDocuments) {
startDocumentUpload(media, std::move(file));
} else {
return base::EventFilterResult::Continue;
}
}
drop->acceptProposedAction();
return base::EventFilterResult::Cancel;
});
}
void PollMediaUploader::installDropToField(
not_null<Ui::InputField*> field,
std::shared_ptr<PollMediaState> media,
bool allowDocuments) {
field->setMimeDataHook([=](
not_null<const QMimeData*> data,
Ui::InputField::MimeAction action) {
if (action == Ui::InputField::MimeAction::Check) {
return ValidateFileDragData(data);
} else if (action == Ui::InputField::MimeAction::Insert) {
auto list = FileListFromMimeData(data, _session->premium());
if (list.error != Ui::PreparedList::Error::None
|| list.files.empty()) {
return false;
}
auto &file = list.files.front();
if (file.type == Ui::PreparedFile::Type::Photo) {
startPhotoUpload(media, std::move(file));
return true;
} else if (file.type == Ui::PreparedFile::Type::Video) {
startVideoUpload(media, std::move(file));
return true;
} else if (allowDocuments) {
startDocumentUpload(media, std::move(file));
return true;
}
return false;
}
Unexpected("Polls: action in MimeData hook.");
});
}
} // namespace PollMediaUpload