From edbdbef2eb360535443ef6713e8b63a2774d40ba Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 23 Mar 2026 23:16:40 +0300 Subject: [PATCH] Replaced old upload pipeline in create_poll_box with extracted pipeline. --- Telegram/SourceFiles/api/api_polls.cpp | 9 +- Telegram/SourceFiles/api/api_polls.h | 2 + .../SourceFiles/boxes/create_poll_box.cpp | 390 +----------------- .../view/history_view_add_poll_option.cpp | 1 + 4 files changed, 20 insertions(+), 382 deletions(-) diff --git a/Telegram/SourceFiles/api/api_polls.cpp b/Telegram/SourceFiles/api/api_polls.cpp index 794e86a18d..7ed110d0b4 100644 --- a/Telegram/SourceFiles/api/api_polls.cpp +++ b/Telegram/SourceFiles/api/api_polls.cpp @@ -188,6 +188,7 @@ void Polls::sendVotes( void Polls::addAnswer( FullMsgId itemId, const TextWithEntities &text, + const PollMedia &media, Fn done, Fn 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), diff --git a/Telegram/SourceFiles/api/api_polls.h b/Telegram/SourceFiles/api/api_polls.h index 57d8568d4a..0268547344 100644 --- a/Telegram/SourceFiles/api/api_polls.h +++ b/Telegram/SourceFiles/api/api_polls.h @@ -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 done, Fn fail); void close(not_null item); diff --git a/Telegram/SourceFiles/boxes/create_poll_box.cpp b/Telegram/SourceFiles/boxes/create_poll_box.cpp index 0c1219ceb1..f066fce823 100644 --- a/Telegram/SourceFiles/boxes/create_poll_box.cpp +++ b/Telegram/SourceFiles/boxes/create_poll_box.cpp @@ -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 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 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 thumbnail; - bool rounded = false; - bool uploading = false; - float64 progress = 0.; - uint64 uploadDataId = 0; - uint64 token = 0; - Fn update; -}; - -class LocalImageThumbnail final : public Ui::DynamicImage { -public: - explicit LocalImageThumbnail(QImage original) - : _original(std::move(original)) { - } - - std::shared_ptr clone() override { - return std::make_shared(_original); - } - - QImage image(int size) override { - return _original; - } - - void subscribeToUpdates(Fn 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 parent, - const style::IconButton &st, - std::shared_ptr 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(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 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(this); - _subscribed->subscribeToUpdates([=] { - if (weak) { - weak->update(); - } - }); - } - - const style::IconButton &_st; - const std::shared_ptr _state; - const std::shared_ptr _attach; - const std::shared_ptr _attachOver; - std::shared_ptr _subscribed; - Ui::RadialAnimation _radial; - Ui::Animations::Simple _cancelShown; - Ui::Animations::Simple _viewShown; - -}; - -class PreparePollMediaTask final : public Task { -public: - PreparePollMediaTask( - FileLoadTask::Args &&args, - Fn)> 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)> _done; - -}; +using PollMediaUpload::FileListFromMimeData; +using PollMediaUpload::GenerateDocumentFilePreview; +using PollMediaUpload::LocalImageThumbnail; +using PollMediaUpload::PreparePollMediaTask; +using PollMediaUpload::UploadContext; +using PollMediaUpload::ValidateFileDragData; class Options { public: diff --git a/Telegram/SourceFiles/history/view/history_view_add_poll_option.cpp b/Telegram/SourceFiles/history/view/history_view_add_poll_option.cpp index 21ea0c4fad..d7cd1e1f00 100644 --- a/Telegram/SourceFiles/history/view/history_view_add_poll_option.cpp +++ b/Telegram/SourceFiles/history/view/history_view_add_poll_option.cpp @@ -122,6 +122,7 @@ void AddPollOptionWidget::triggerSubmit() { _session->api().polls().addAnswer( _itemId, { text }, + PollMedia(), [=] { _submittedEvents.fire({}); }, [=](QString error) { _field->setEnabled(true);