From 0b25d19e3bf3ac1c8a08a4ce6093195eb32b4722 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 19 Apr 2023 10:42:01 +0400 Subject: [PATCH] Allow uploading chat wallpapers from file. --- Telegram/SourceFiles/boxes/background_box.cpp | 55 ++++++++ Telegram/SourceFiles/boxes/background_box.h | 2 + .../boxes/background_preview_box.cpp | 102 ++++++++++++-- .../boxes/background_preview_box.h | 6 + .../media/history_view_theme_document.cpp | 5 +- .../window/themes/window_theme.cpp | 128 +++++++++--------- .../SourceFiles/window/themes/window_theme.h | 5 + 7 files changed, 224 insertions(+), 79 deletions(-) diff --git a/Telegram/SourceFiles/boxes/background_box.cpp b/Telegram/SourceFiles/boxes/background_box.cpp index 0aefa881f..40407334a 100644 --- a/Telegram/SourceFiles/boxes/background_box.cpp +++ b/Telegram/SourceFiles/boxes/background_box.cpp @@ -10,11 +10,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "ui/effects/round_checkbox.h" #include "ui/image/image.h" +#include "ui/chat/attach/attach_extensions.h" #include "ui/chat/chat_theme.h" #include "ui/ui_utility.h" #include "main/main_session.h" #include "apiwrap.h" #include "mtproto/sender.h" +#include "core/file_utilities.h" #include "data/data_peer.h" #include "data/data_session.h" #include "data/data_file_origin.h" @@ -174,6 +176,10 @@ void BackgroundBox::prepare() { st::infoIconMediaPhoto, st::infoSharedMediaButtonIconPosition); + button->setClickedCallback([=] { + chooseFromFile(); + }); + Settings::AddSkip(container); Settings::AddDivider(container); @@ -196,6 +202,55 @@ void BackgroundBox::prepare() { }, _inner->lifetime()); } +void BackgroundBox::chooseFromFile() { + const auto filterStart = _forPeer + ? u"Image files (*"_q + : u"Theme files (*.tdesktop-theme *.tdesktop-palette *"_q; + auto filters = QStringList( + filterStart + + Ui::ImageExtensions().join(u" *"_q) + + u")"_q); + filters.push_back(FileDialog::AllFilesFilter()); + const auto callback = [=](const FileDialog::OpenResult &result) { + if (result.paths.isEmpty() && result.remoteContent.isEmpty()) { + return; + } + + if (!_forPeer && !result.paths.isEmpty()) { + const auto filePath = result.paths.front(); + const auto hasExtension = [&](QLatin1String extension) { + return filePath.endsWith(extension, Qt::CaseInsensitive); + }; + if (hasExtension(qstr(".tdesktop-theme")) + || hasExtension(qstr(".tdesktop-palette"))) { + Window::Theme::Apply(filePath); + return; + } + } + + auto image = Images::Read({ + .path = result.paths.isEmpty() ? QString() : result.paths.front(), + .content = result.remoteContent, + .forceOpaque = true, + }).image; + if (image.isNull() || image.width() <= 0 || image.height() <= 0) { + return; + } + auto local = Data::CustomWallPaper(); + local.setLocalImageAsThumbnail(std::make_shared( + std::move(image))); + _controller->show(Box( + _controller, + local, + BackgroundPreviewArgs{ _forPeer })); + }; + FileDialog::GetOpenPath( + this, + tr::lng_choose_image(tr::now), + filters.join(u";;"_q), + crl::guard(this, callback)); +} + bool BackgroundBox::hasDefaultForPeer() const { Expects(_forPeer != nullptr); diff --git a/Telegram/SourceFiles/boxes/background_box.h b/Telegram/SourceFiles/boxes/background_box.h index 0fc28c44f..4e0bc3727 100644 --- a/Telegram/SourceFiles/boxes/background_box.h +++ b/Telegram/SourceFiles/boxes/background_box.h @@ -39,6 +39,8 @@ private: void removePaper(const Data::WallPaper &paper); void resetForPeer(); + void chooseFromFile(); + const not_null _controller; QPointer _inner; diff --git a/Telegram/SourceFiles/boxes/background_preview_box.cpp b/Telegram/SourceFiles/boxes/background_preview_box.cpp index d35d1844b..de9e75dd5 100644 --- a/Telegram/SourceFiles/boxes/background_preview_box.cpp +++ b/Telegram/SourceFiles/boxes/background_preview_box.cpp @@ -35,6 +35,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/background_preview_box.h" #include "window/window_session_controller.h" #include "settings/settings_common.h" +#include "storage/file_upload.h" +#include "storage/localimageloader.h" #include "styles/style_chat.h" #include "styles/style_layers.h" #include "styles/style_boxes.h" @@ -275,18 +277,82 @@ void BackgroundPreviewBox::createBlurCheckbox() { } void BackgroundPreviewBox::apply() { - const auto weak = Ui::MakeWeak(this); if (_forPeer) { applyForPeer(); } else { applyForEveryone(); } - if (weak) { - closeBox(); - } } -void BackgroundPreviewBox::applyForPeer() { +void BackgroundPreviewBox::uploadForPeer() { + Expects(_forPeer != nullptr); + + if (_uploadId) { + return; + } + + const auto session = &_controller->session(); + const auto ready = Window::Theme::PrepareWallPaper( + session->mainDcId(), + _paper.localThumbnail()->original()); + const auto documentId = ready.id; + _uploadId = FullMsgId( + session->userPeerId(), + session->data().nextLocalMessageId()); + session->uploader().uploadMedia(_uploadId, ready); + if (_uploadLifetime) { + return; + } + + const auto document = session->data().document(documentId); + document->uploadingData = std::make_unique( + document->size); + + session->uploader().documentProgress( + ) | rpl::start_with_next([=](const FullMsgId &fullId) { + if (fullId != _uploadId) { + return; + } + _uploadProgress = document->uploading() + ? ((document->uploadingData->offset * 100) + / document->uploadingData->size) + : 0.; + update(radialRect()); + }, _uploadLifetime); + + session->uploader().documentReady( + ) | rpl::start_with_next([=](const Storage::UploadedMedia &data) { + if (data.fullId != _uploadId) { + return; + } + _uploadProgress = 1.; + _uploadLifetime.destroy(); + update(radialRect()); + session->api().request(MTPaccount_UploadWallPaper( + MTP_flags(MTPaccount_UploadWallPaper::Flag::f_for_chat), + data.info.file, + MTP_string("image/jpeg"), + _paper.mtpSettings() + )).done([=](const MTPWallPaper &result) { + result.match([&](const MTPDwallPaper &data) { + session->data().documentConvert( + session->data().document(documentId), + data.vdocument()); + }, [&](const MTPDwallPaperNoFile &data) { + LOG(("API Error: " + "Got wallPaperNoFile after account.UploadWallPaper.")); + }); + if (const auto paper = Data::WallPaper::Create(session, result)) { + setExistingForPeer(*paper); + } + }).send(); + }, _uploadLifetime); + + _uploadProgress = 0.; + _radial.start(_uploadProgress); +} + +void BackgroundPreviewBox::setExistingForPeer(const Data::WallPaper &paper) { Expects(_forPeer != nullptr); const auto api = &_controller->session().api(); @@ -296,17 +362,27 @@ void BackgroundPreviewBox::applyForPeer() { | (_fromMessageId ? Flag() : Flag::f_wallpaper) | Flag::f_settings), _forPeer->input, - _paper.mtpInput(&_controller->session()), - _paper.mtpSettings(), + paper.mtpInput(&_controller->session()), + paper.mtpSettings(), MTP_int(_fromMessageId.msg) )).done([=](const MTPUpdates &result) { api->applyUpdates(result); }).send(); - _forPeer->setWallPaper(_paper); + _forPeer->setWallPaper(paper); _controller->finishChatThemeEdit(_forPeer); } +void BackgroundPreviewBox::applyForPeer() { + Expects(_forPeer != nullptr); + + if (Data::IsCustomWallPaper(_paper)) { + uploadForPeer(); + } else { + setExistingForPeer(_paper); + } +} + void BackgroundPreviewBox::applyForEveryone() { const auto install = (_paper.id() != Window::Theme::Background()->id()) && Data::IsCloudWallPaper(_paper); @@ -317,6 +393,7 @@ void BackgroundPreviewBox::applyForEveryone() { _paper.mtpSettings() )).send(); } + closeBox(); } void BackgroundPreviewBox::share() { @@ -441,14 +518,11 @@ void BackgroundPreviewBox::paintTexts(Painter &p, crl::time ms) { } void BackgroundPreviewBox::radialAnimationCallback(crl::time now) { - Expects(_paper.document() != nullptr); - const auto document = _paper.document(); const auto wasAnimating = _radial.animating(); - const auto updated = _radial.update( - _media->progress(), - !document->loading(), - now); + const auto updated = _uploadId + ? _radial.update(_uploadProgress, !_uploadLifetime, now) + : _radial.update(_media->progress(), !document->loading(), now); if ((wasAnimating || _radial.animating()) && (!anim::Disabled() || updated)) { update(radialRect()); diff --git a/Telegram/SourceFiles/boxes/background_preview_box.h b/Telegram/SourceFiles/boxes/background_preview_box.h index 708361d7a..ebd1b6269 100644 --- a/Telegram/SourceFiles/boxes/background_preview_box.h +++ b/Telegram/SourceFiles/boxes/background_preview_box.h @@ -61,6 +61,8 @@ private: void apply(); void applyForPeer(); void applyForEveryone(); + void uploadForPeer(); + void setExistingForPeer(const Data::WallPaper &paper); void share(); void radialAnimationCallback(crl::time now); QRect radialRect() const; @@ -96,4 +98,8 @@ private: std::optional _serviceBg; object_ptr _blur = { nullptr }; + FullMsgId _uploadId; + float64 _uploadProgress = 0.; + rpl::lifetime _uploadLifetime; + }; diff --git a/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp index fd59d20ae..5e4a48623 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp @@ -329,9 +329,12 @@ void ThemeDocument::prepareThumbnailFrom( tw = th = 1; } const auto ratio = style::DevicePixelRatio(); + const auto resizeTo = _serviceWidth + ? QSize(tw, th).scaled(_pixw, _pixh, Qt::KeepAspectRatioByExpanding) + : QSize(_pixw, (_pixw * th) / tw); original = Images::Prepare( std::move(original), - QSize(_pixw, (_pixw * th) / tw) * ratio, + resizeTo * ratio, { .options = options, .outer = { _pixw, _pixh } }); if (isPattern) { original = Ui::PreparePatternImage( diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index 286781b8c..1aae8805b 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -457,70 +457,6 @@ void ClearApplying() { GlobalApplying = Applying(); } -SendMediaReady PrepareWallPaper(MTP::DcId dcId, const QImage &image) { - PreparedPhotoThumbs thumbnails; - QVector sizes; - - QByteArray jpeg; - QBuffer jpegBuffer(&jpeg); - image.save(&jpegBuffer, "JPG", 87); - - const auto scaled = [&](int size) { - return image.scaled( - size, - size, - Qt::KeepAspectRatio, - Qt::SmoothTransformation); - }; - const auto push = [&](const char *type, QImage &&image) { - sizes.push_back(MTP_photoSize( - MTP_string(type), - MTP_int(image.width()), - MTP_int(image.height()), MTP_int(0))); - thumbnails.emplace( - type[0], - PreparedPhotoThumb{ .image = std::move(image) }); - }; - push("s", scaled(320)); - - const auto filename = u"wallpaper.jpg"_q; - auto attributes = QVector( - 1, - MTP_documentAttributeFilename(MTP_string(filename))); - attributes.push_back(MTP_documentAttributeImageSize( - MTP_int(image.width()), - MTP_int(image.height()))); - const auto id = base::RandomValue(); - const auto document = MTP_document( - MTP_flags(0), - MTP_long(id), - MTP_long(0), - MTP_bytes(), - MTP_int(base::unixtime::now()), - MTP_string("image/jpeg"), - MTP_long(jpeg.size()), - MTP_vector(sizes), - MTPVector(), - MTP_int(dcId), - MTP_vector(attributes)); - - return SendMediaReady( - SendMediaType::ThemeFile, - QString(), // filepath - filename, - jpeg.size(), - jpeg, - id, - 0, - QString(), - PeerId(), - MTP_photoEmpty(MTP_long(0)), - thumbnails, - document, - QByteArray(), - 0); -} - void ClearEditingPalette() { QFile(EditingPalettePath()).remove(); } @@ -1565,5 +1501,69 @@ bool ReadPaletteValues(const QByteArray &content, Fn sizes; + + QByteArray jpeg; + QBuffer jpegBuffer(&jpeg); + image.save(&jpegBuffer, "JPG", 87); + + const auto scaled = [&](int size) { + return image.scaled( + size, + size, + Qt::KeepAspectRatio, + Qt::SmoothTransformation); + }; + const auto push = [&](const char *type, QImage &&image) { + sizes.push_back(MTP_photoSize( + MTP_string(type), + MTP_int(image.width()), + MTP_int(image.height()), MTP_int(0))); + thumbnails.emplace( + type[0], + PreparedPhotoThumb{ .image = std::move(image) }); + }; + push("s", scaled(320)); + + const auto filename = u"wallpaper.jpg"_q; + auto attributes = QVector( + 1, + MTP_documentAttributeFilename(MTP_string(filename))); + attributes.push_back(MTP_documentAttributeImageSize( + MTP_int(image.width()), + MTP_int(image.height()))); + const auto id = base::RandomValue(); + const auto document = MTP_document( + MTP_flags(0), + MTP_long(id), + MTP_long(0), + MTP_bytes(), + MTP_int(base::unixtime::now()), + MTP_string("image/jpeg"), + MTP_long(jpeg.size()), + MTP_vector(sizes), + MTPVector(), + MTP_int(dcId), + MTP_vector(attributes)); + + return SendMediaReady( + SendMediaType::ThemeFile, + QString(), // filepath + filename, + jpeg.size(), + jpeg, + id, + 0, + QString(), + PeerId(), + MTP_photoEmpty(MTP_long(0)), + thumbnails, + document, + QByteArray(), + 0); +} + } // namespace Theme } // namespace Window diff --git a/Telegram/SourceFiles/window/themes/window_theme.h b/Telegram/SourceFiles/window/themes/window_theme.h index f8458c2ac..32f43977c 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.h +++ b/Telegram/SourceFiles/window/themes/window_theme.h @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/style/style_core_palette.h" class QFileSystemWatcher; +struct SendMediaReady; namespace style { struct colorizer; @@ -296,6 +297,10 @@ private: }; +[[nodiscard]] SendMediaReady PrepareWallPaper( + MTP::DcId dcId, + const QImage &image); + [[nodiscard]] ChatBackground *Background(); bool ReadPaletteValues(