mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
Replaced old upload pipeline in create_poll_box with extracted pipeline.
This commit is contained in:
@@ -188,6 +188,7 @@ void Polls::sendVotes(
|
||||
void Polls::addAnswer(
|
||||
FullMsgId itemId,
|
||||
const TextWithEntities &text,
|
||||
const PollMedia &media,
|
||||
Fn<void()> done,
|
||||
Fn<void(QString)> fail) {
|
||||
if (_pollAddAnswerRequestIds.contains(itemId)) {
|
||||
@@ -201,12 +202,16 @@ void Polls::addAnswer(
|
||||
_session,
|
||||
text.entities,
|
||||
Api::ConvertOption::SkipLocal);
|
||||
using Flag = MTPDinputPollAnswer::Flag;
|
||||
const auto flags = media
|
||||
? Flag::f_media
|
||||
: Flag();
|
||||
const auto answer = MTP_inputPollAnswer(
|
||||
MTP_flags(MTPDinputPollAnswer::Flags(0)),
|
||||
MTP_flags(flags),
|
||||
MTP_textWithEntities(
|
||||
MTP_string(text.text),
|
||||
sentEntities),
|
||||
MTPInputMedia());
|
||||
media ? PollMediaToMTP(media) : MTPInputMedia());
|
||||
const auto requestId = _api.request(MTPmessages_AddPollAnswer(
|
||||
item->history()->peer->input(),
|
||||
MTP_int(item->id),
|
||||
|
||||
@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
class ApiWrap;
|
||||
class HistoryItem;
|
||||
struct PollData;
|
||||
struct PollMedia;
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
@@ -38,6 +39,7 @@ public:
|
||||
void addAnswer(
|
||||
FullMsgId itemId,
|
||||
const TextWithEntities &text,
|
||||
const PollMedia &media,
|
||||
Fn<void()> done,
|
||||
Fn<void(QString)> fail);
|
||||
void close(not_null<HistoryItem*> item);
|
||||
|
||||
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/create_poll_box.h"
|
||||
|
||||
#include "poll/poll_media_upload.h"
|
||||
#include "base/call_delayed.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "boxes/premium_limits_box.h"
|
||||
@@ -95,387 +96,16 @@ constexpr auto kSolutionLimit = 200;
|
||||
constexpr auto kWarnSolutionLimit = 60;
|
||||
constexpr auto kErrorLimit = 99;
|
||||
|
||||
[[nodiscard]] 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();
|
||||
}
|
||||
using PollMediaState = PollMediaUpload::PollMediaState;
|
||||
using PollMediaButton = PollMediaUpload::PollMediaButton;
|
||||
using PollMediaUploader = PollMediaUpload::PollMediaUploader;
|
||||
|
||||
[[nodiscard]] 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());
|
||||
}
|
||||
|
||||
struct PollMediaState {
|
||||
PollMedia media;
|
||||
std::shared_ptr<Ui::DynamicImage> thumbnail;
|
||||
bool rounded = false;
|
||||
bool uploading = false;
|
||||
float64 progress = 0.;
|
||||
uint64 uploadDataId = 0;
|
||||
uint64 token = 0;
|
||||
Fn<void()> update;
|
||||
};
|
||||
|
||||
class LocalImageThumbnail final : public Ui::DynamicImage {
|
||||
public:
|
||||
explicit LocalImageThumbnail(QImage original)
|
||||
: _original(std::move(original)) {
|
||||
}
|
||||
|
||||
std::shared_ptr<Ui::DynamicImage> clone() override {
|
||||
return std::make_shared<LocalImageThumbnail>(_original);
|
||||
}
|
||||
|
||||
QImage image(int size) override {
|
||||
return _original;
|
||||
}
|
||||
|
||||
void subscribeToUpdates(Fn<void()> callback) override {
|
||||
}
|
||||
|
||||
private:
|
||||
QImage _original;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]] 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;
|
||||
}
|
||||
|
||||
class PollMediaButton final : public Ui::RippleButton {
|
||||
public:
|
||||
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();
|
||||
}
|
||||
|
||||
~PollMediaButton() override {
|
||||
if (_subscribed) {
|
||||
_subscribed->subscribeToUpdates(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override {
|
||||
auto p = Painter(this);
|
||||
paintRipple(p, _st.rippleAreaPosition);
|
||||
if (const auto image = _state->thumbnail
|
||||
? _state->thumbnail
|
||||
: currentAttachThumbnail()) {
|
||||
const auto target = _state->thumbnail
|
||||
? rippleRect()
|
||||
: iconRect();
|
||||
if (_state->thumbnail) {
|
||||
paintCover(
|
||||
p,
|
||||
target,
|
||||
image->image(std::max(target.width(), target.height())),
|
||||
_state->rounded);
|
||||
} else {
|
||||
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 onStateChanged(State was, StateChangeSource source) override {
|
||||
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 prepareRippleMask() const override {
|
||||
return Ui::RippleAnimation::EllipseMask(QSize(
|
||||
_st.rippleAreaSize,
|
||||
_st.rippleAreaSize));
|
||||
}
|
||||
|
||||
QPoint prepareRippleStartPosition() const override {
|
||||
auto result = mapFromGlobal(QCursor::pos())
|
||||
- _st.rippleAreaPosition;
|
||||
const auto rect = QRect(
|
||||
QPoint(),
|
||||
QSize(_st.rippleAreaSize, _st.rippleAreaSize));
|
||||
return rect.contains(result)
|
||||
? result
|
||||
: DisabledRippleStartPosition();
|
||||
}
|
||||
|
||||
private:
|
||||
void 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 radialAnimationCallback(crl::time now) {
|
||||
const auto updated = _radial.update(
|
||||
_state->progress,
|
||||
!_state->uploading,
|
||||
now);
|
||||
if (!anim::Disabled() || updated || _radial.animating()) {
|
||||
update(rippleRect());
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] QRect rippleRect() const {
|
||||
return QRect(
|
||||
_st.rippleAreaPosition,
|
||||
QSize(_st.rippleAreaSize, _st.rippleAreaSize));
|
||||
}
|
||||
|
||||
[[nodiscard]] QRect 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> currentAttachThumbnail() const {
|
||||
return (isOver() || isDown()) ? _attachOver : _attach;
|
||||
}
|
||||
|
||||
void 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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const style::IconButton &_st;
|
||||
const std::shared_ptr<PollMediaState> _state;
|
||||
const std::shared_ptr<Ui::DynamicImage> _attach;
|
||||
const std::shared_ptr<Ui::DynamicImage> _attachOver;
|
||||
std::shared_ptr<Ui::DynamicImage> _subscribed;
|
||||
Ui::RadialAnimation _radial;
|
||||
Ui::Animations::Simple _cancelShown;
|
||||
Ui::Animations::Simple _viewShown;
|
||||
|
||||
};
|
||||
|
||||
class PreparePollMediaTask final : public Task {
|
||||
public:
|
||||
PreparePollMediaTask(
|
||||
FileLoadTask::Args &&args,
|
||||
Fn<void(std::shared_ptr<FilePrepareResult>)> done)
|
||||
: _task(std::move(args))
|
||||
, _done(std::move(done)) {
|
||||
}
|
||||
|
||||
void process() override {
|
||||
_task.process({ .generateGoodThumbnail = false });
|
||||
}
|
||||
|
||||
void finish() override {
|
||||
_done(_task.peekResult());
|
||||
}
|
||||
|
||||
private:
|
||||
FileLoadTask _task;
|
||||
Fn<void(std::shared_ptr<FilePrepareResult>)> _done;
|
||||
|
||||
};
|
||||
using PollMediaUpload::FileListFromMimeData;
|
||||
using PollMediaUpload::GenerateDocumentFilePreview;
|
||||
using PollMediaUpload::LocalImageThumbnail;
|
||||
using PollMediaUpload::PreparePollMediaTask;
|
||||
using PollMediaUpload::UploadContext;
|
||||
using PollMediaUpload::ValidateFileDragData;
|
||||
|
||||
class Options {
|
||||
public:
|
||||
|
||||
@@ -122,6 +122,7 @@ void AddPollOptionWidget::triggerSubmit() {
|
||||
_session->api().polls().addAnswer(
|
||||
_itemId,
|
||||
{ text },
|
||||
PollMedia(),
|
||||
[=] { _submittedEvents.fire({}); },
|
||||
[=](QString error) {
|
||||
_field->setEnabled(true);
|
||||
|
||||
Reference in New Issue
Block a user