Allow uploading chat wallpapers from file.

This commit is contained in:
John Preston 2023-04-19 10:42:01 +04:00
parent 9130735ed6
commit 0b25d19e3b
7 changed files with 224 additions and 79 deletions

View File

@ -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<Image>(
std::move(image)));
_controller->show(Box<BackgroundPreviewBox>(
_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);

View File

@ -39,6 +39,8 @@ private:
void removePaper(const Data::WallPaper &paper);
void resetForPeer();
void chooseFromFile();
const not_null<Window::SessionController*> _controller;
QPointer<Inner> _inner;

View File

@ -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<Data::UploadState>(
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());

View File

@ -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<QColor> _serviceBg;
object_ptr<Ui::Checkbox> _blur = { nullptr };
FullMsgId _uploadId;
float64 _uploadProgress = 0.;
rpl::lifetime _uploadLifetime;
};

View File

@ -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(

View File

@ -457,70 +457,6 @@ void ClearApplying() {
GlobalApplying = Applying();
}
SendMediaReady PrepareWallPaper(MTP::DcId dcId, const QImage &image) {
PreparedPhotoThumbs thumbnails;
QVector<MTPPhotoSize> 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<MTPDocumentAttribute>(
1,
MTP_documentAttributeFilename(MTP_string(filename)));
attributes.push_back(MTP_documentAttributeImageSize(
MTP_int(image.width()),
MTP_int(image.height())));
const auto id = base::RandomValue<DocumentId>();
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<MTPPhotoSize>(sizes),
MTPVector<MTPVideoSize>(),
MTP_int(dcId),
MTP_vector<MTPDocumentAttribute>(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<bool(QLatin1String name, QL
};
}
SendMediaReady PrepareWallPaper(MTP::DcId dcId, const QImage &image) {
PreparedPhotoThumbs thumbnails;
QVector<MTPPhotoSize> 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<MTPDocumentAttribute>(
1,
MTP_documentAttributeFilename(MTP_string(filename)));
attributes.push_back(MTP_documentAttributeImageSize(
MTP_int(image.width()),
MTP_int(image.height())));
const auto id = base::RandomValue<DocumentId>();
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<MTPPhotoSize>(sizes),
MTPVector<MTPVideoSize>(),
MTP_int(dcId),
MTP_vector<MTPDocumentAttribute>(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

View File

@ -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(