mirror of https://github.com/procxx/kepka.git
Save themes, apply and close editor on save.
This commit is contained in:
parent
910f16312c
commit
9c86f0e0a5
|
@ -78,4 +78,12 @@ const std::vector<CloudTheme> &CloudThemes::list() const {
|
||||||
return _list;
|
return _list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CloudThemes::apply(const CloudTheme &theme) {
|
||||||
|
const auto i = ranges::find(_list, theme.id, &CloudTheme::id);
|
||||||
|
if (i != end(_list)) {
|
||||||
|
*i = theme;
|
||||||
|
_updates.fire({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
@ -35,6 +35,7 @@ public:
|
||||||
void refresh();
|
void refresh();
|
||||||
[[nodiscard]] rpl::producer<> updated() const;
|
[[nodiscard]] rpl::producer<> updated() const;
|
||||||
[[nodiscard]] const std::vector<CloudTheme> &list() const;
|
[[nodiscard]] const std::vector<CloudTheme> &list() const;
|
||||||
|
void apply(const CloudTheme &data);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void parseThemes(const QVector<MTPTheme> &list);
|
void parseThemes(const QVector<MTPTheme> &list);
|
||||||
|
|
|
@ -568,6 +568,17 @@ void DocumentData::validateLottieSticker() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DocumentData::setDataAndCache(const QByteArray &data) {
|
||||||
|
setData(data);
|
||||||
|
if (saveToCache() && data.size() <= Storage::kMaxFileInMemory) {
|
||||||
|
session().data().cache().put(
|
||||||
|
cacheKey(),
|
||||||
|
Storage::Cache::Database::TaggedValue(
|
||||||
|
base::duplicate(data),
|
||||||
|
cacheTag()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool DocumentData::checkWallPaperProperties() {
|
bool DocumentData::checkWallPaperProperties() {
|
||||||
if (type == WallPaperDocument) {
|
if (type == WallPaperDocument) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -166,6 +166,7 @@ public:
|
||||||
void setData(const QByteArray &data) {
|
void setData(const QByteArray &data) {
|
||||||
_data = data;
|
_data = data;
|
||||||
}
|
}
|
||||||
|
void setDataAndCache(const QByteArray &data);
|
||||||
bool checkWallPaperProperties();
|
bool checkWallPaperProperties();
|
||||||
[[nodiscard]] bool isWallPaper() const;
|
[[nodiscard]] bool isWallPaper() const;
|
||||||
[[nodiscard]] bool isPatternWallPaper() const;
|
[[nodiscard]] bool isPatternWallPaper() const;
|
||||||
|
|
|
@ -154,10 +154,6 @@ QPixmap PrepareStaticImage(const QString &path) {
|
||||||
return App::pixmapFromImageInPlace(std::move(image));
|
return App::pixmapFromImageInPlace(std::move(image));
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] QString CachedThemePath(uint64 documentId) {
|
|
||||||
return QString::fromLatin1("special://cached-%1").arg(documentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
struct OverlayWidget::SharedMedia {
|
struct OverlayWidget::SharedMedia {
|
||||||
|
@ -2226,15 +2222,19 @@ void OverlayWidget::initThemePreview() {
|
||||||
&Data::CloudTheme::documentId);
|
&Data::CloudTheme::documentId);
|
||||||
const auto cloud = (i != end(cloudList)) ? *i : Data::CloudTheme();
|
const auto cloud = (i != end(cloudList)) ? *i : Data::CloudTheme();
|
||||||
const auto isTrusted = (cloud.documentId != 0);
|
const auto isTrusted = (cloud.documentId != 0);
|
||||||
|
const auto fields = [&] {
|
||||||
|
auto result = cloud;
|
||||||
|
if (!result.documentId) {
|
||||||
|
result.documentId = _doc->id;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}();
|
||||||
|
|
||||||
const auto realPath = _doc->location().name();
|
const auto path = _doc->location().name();
|
||||||
const auto path = realPath.isEmpty()
|
|
||||||
? CachedThemePath(_doc->id)
|
|
||||||
: realPath;
|
|
||||||
const auto id = _themePreviewId = rand_value<uint64>();
|
const auto id = _themePreviewId = rand_value<uint64>();
|
||||||
const auto weak = make_weak(this);
|
const auto weak = make_weak(this);
|
||||||
crl::async([=, data = std::move(current)]() mutable {
|
crl::async([=, data = std::move(current)]() mutable {
|
||||||
auto preview = GeneratePreview(bytes, path, cloud, std::move(data));
|
auto preview = GeneratePreview(bytes, path, fields, std::move(data));
|
||||||
crl::on_main(weak, [=, result = std::move(preview)]() mutable {
|
crl::on_main(weak, [=, result = std::move(preview)]() mutable {
|
||||||
if (id != _themePreviewId) {
|
if (id != _themePreviewId) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -162,18 +162,10 @@ void Uploader::uploadMedia(
|
||||||
media.document,
|
media.document,
|
||||||
base::duplicate(media.photoThumbs.front().second));
|
base::duplicate(media.photoThumbs.front().second));
|
||||||
if (!media.data.isEmpty()) {
|
if (!media.data.isEmpty()) {
|
||||||
document->setData(media.data);
|
document->setDataAndCache(media.data);
|
||||||
if (media.type == SendMediaType::ThemeFile) {
|
if (media.type == SendMediaType::ThemeFile) {
|
||||||
document->checkWallPaperProperties();
|
document->checkWallPaperProperties();
|
||||||
}
|
}
|
||||||
if (document->saveToCache()
|
|
||||||
&& media.data.size() <= Storage::kMaxFileInMemory) {
|
|
||||||
Auth().data().cache().put(
|
|
||||||
document->cacheKey(),
|
|
||||||
Storage::Cache::Database::TaggedValue(
|
|
||||||
base::duplicate(media.data),
|
|
||||||
document->cacheTag()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!media.file.isEmpty()) {
|
if (!media.file.isEmpty()) {
|
||||||
document->setLocation(FileLocation(media.file));
|
document->setLocation(FileLocation(media.file));
|
||||||
|
@ -206,18 +198,10 @@ void Uploader::upload(
|
||||||
std::move(file->goodThumbnail),
|
std::move(file->goodThumbnail),
|
||||||
std::move(file->goodThumbnailBytes));
|
std::move(file->goodThumbnailBytes));
|
||||||
if (!file->content.isEmpty()) {
|
if (!file->content.isEmpty()) {
|
||||||
document->setData(file->content);
|
document->setDataAndCache(file->content);
|
||||||
if (file->type == SendMediaType::ThemeFile) {
|
if (file->type == SendMediaType::ThemeFile) {
|
||||||
document->checkWallPaperProperties();
|
document->checkWallPaperProperties();
|
||||||
}
|
}
|
||||||
if (document->saveToCache()
|
|
||||||
&& file->content.size() <= Storage::kMaxFileInMemory) {
|
|
||||||
Auth().data().cache().put(
|
|
||||||
document->cacheKey(),
|
|
||||||
Storage::Cache::Database::TaggedValue(
|
|
||||||
base::duplicate(file->content),
|
|
||||||
document->cacheTag()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!file->filepath.isEmpty()) {
|
if (!file->filepath.isEmpty()) {
|
||||||
document->setLocation(FileLocation(file->filepath));
|
document->setLocation(FileLocation(file->filepath));
|
||||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "window/themes/window_theme.h"
|
#include "window/themes/window_theme.h"
|
||||||
#include "window/themes/window_theme_editor.h"
|
#include "window/themes/window_theme_editor.h"
|
||||||
|
#include "window/themes/window_theme_preview.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
#include "boxes/confirm_box.h"
|
#include "boxes/confirm_box.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
|
@ -409,7 +410,7 @@ SendMediaReady PrepareThemeMedia(
|
||||||
//push("s", scaled(320));
|
//push("s", scaled(320));
|
||||||
|
|
||||||
const auto filename = File::NameFromUserString(name)
|
const auto filename = File::NameFromUserString(name)
|
||||||
+ qsl(".tdesktop-theme"); // #TODO themes
|
+ qsl(".tdesktop-theme");
|
||||||
auto attributes = QVector<MTPDocumentAttribute>(
|
auto attributes = QVector<MTPDocumentAttribute>(
|
||||||
1,
|
1,
|
||||||
MTP_documentAttributeFilename(MTP_string(filename)));
|
MTP_documentAttributeFilename(MTP_string(filename)));
|
||||||
|
@ -447,22 +448,25 @@ Fn<void()> SavePreparedTheme(
|
||||||
not_null<Window::Controller*> window,
|
not_null<Window::Controller*> window,
|
||||||
const QByteArray &palette,
|
const QByteArray &palette,
|
||||||
const PreparedBackground &background,
|
const PreparedBackground &background,
|
||||||
const QString &name,
|
const Data::CloudTheme &fields,
|
||||||
const QString &link,
|
|
||||||
Fn<void()> done,
|
Fn<void()> done,
|
||||||
Fn<void(SaveErrorType,QString)> fail) {
|
Fn<void(SaveErrorType,QString)> fail) {
|
||||||
|
Expects(window->account().sessionExists());
|
||||||
|
|
||||||
using Storage::UploadedDocument;
|
using Storage::UploadedDocument;
|
||||||
struct State {
|
struct State {
|
||||||
FullMsgId id;
|
FullMsgId id;
|
||||||
bool generating = false;
|
bool generating = false;
|
||||||
mtpRequestId requestId = 0;
|
mtpRequestId requestId = 0;
|
||||||
|
QByteArray themeContent;
|
||||||
|
QString filename;
|
||||||
rpl::lifetime lifetime;
|
rpl::lifetime lifetime;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (name.isEmpty()) {
|
if (fields.title.isEmpty()) {
|
||||||
fail(SaveErrorType::Name, {});
|
fail(SaveErrorType::Name, {});
|
||||||
return nullptr;
|
return nullptr;
|
||||||
} else if (!IsGoodSlug(link)) {
|
} else if (!IsGoodSlug(fields.slug)) {
|
||||||
fail(SaveErrorType::Link, {});
|
fail(SaveErrorType::Link, {});
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -473,14 +477,62 @@ Fn<void()> SavePreparedTheme(
|
||||||
0,
|
0,
|
||||||
session->data().nextLocalMessageId());
|
session->data().nextLocalMessageId());
|
||||||
|
|
||||||
|
const auto creating = !fields.id
|
||||||
|
|| (fields.createdBy != session->userId());
|
||||||
|
|
||||||
|
const auto finish = [=](const MTPTheme &result) {
|
||||||
|
done();
|
||||||
|
|
||||||
|
const auto cloud = result.match([&](const MTPDtheme &data) {
|
||||||
|
const auto result = Data::CloudTheme::Parse(session, data);
|
||||||
|
session->data().cloudThemes().apply(result);
|
||||||
|
return result;
|
||||||
|
}, [&](const MTPDthemeDocumentNotModified &data) {
|
||||||
|
LOG(("API Error: Unexpected themeDocumentNotModified."));
|
||||||
|
return fields;
|
||||||
|
});
|
||||||
|
if (cloud.documentId) {
|
||||||
|
const auto document = session->data().document(cloud.documentId);
|
||||||
|
document->setDataAndCache(state->themeContent);
|
||||||
|
}
|
||||||
|
auto preview = PreviewFromFile(
|
||||||
|
state->themeContent,
|
||||||
|
QString(),
|
||||||
|
cloud);
|
||||||
|
if (preview) {
|
||||||
|
Apply(std::move(preview));
|
||||||
|
KeepApplied();
|
||||||
|
}
|
||||||
|
Background()->setEditingTheme(std::nullopt);
|
||||||
|
};
|
||||||
|
|
||||||
const auto createTheme = [=](const MTPDocument &data) {
|
const auto createTheme = [=](const MTPDocument &data) {
|
||||||
const auto document = session->data().processDocument(data);
|
const auto document = session->data().processDocument(data);
|
||||||
state->requestId = api->request(MTPaccount_CreateTheme(
|
state->requestId = api->request(MTPaccount_CreateTheme(
|
||||||
MTP_string(link),
|
MTP_string(fields.slug),
|
||||||
MTP_string(name),
|
MTP_string(fields.title),
|
||||||
document->mtpInput()
|
document->mtpInput()
|
||||||
)).done([=](const MTPTheme &result) {
|
)).done([=](const MTPTheme &result) {
|
||||||
done();
|
finish(result);
|
||||||
|
}).fail([=](const RPCError &error) {
|
||||||
|
fail(SaveErrorType::Other, error.type());
|
||||||
|
}).send();
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto updateTheme = [=](const MTPDocument &data) {
|
||||||
|
const auto document = session->data().processDocument(data);
|
||||||
|
const auto flags = MTPaccount_UpdateTheme::Flag::f_title
|
||||||
|
| MTPaccount_UpdateTheme::Flag::f_slug
|
||||||
|
| MTPaccount_UpdateTheme::Flag::f_document;
|
||||||
|
state->requestId = api->request(MTPaccount_UpdateTheme(
|
||||||
|
MTP_flags(flags),
|
||||||
|
MTP_string(Data::CloudThemes::Format()),
|
||||||
|
MTP_inputTheme(MTP_long(fields.id), MTP_long(fields.accessHash)),
|
||||||
|
MTP_string(fields.slug),
|
||||||
|
MTP_string(fields.title),
|
||||||
|
document->mtpInput()
|
||||||
|
)).done([=](const MTPTheme &result) {
|
||||||
|
finish(result);
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
fail(SaveErrorType::Other, error.type());
|
fail(SaveErrorType::Other, error.type());
|
||||||
}).send();
|
}).send();
|
||||||
|
@ -491,16 +543,24 @@ Fn<void()> SavePreparedTheme(
|
||||||
MTP_flags(0),
|
MTP_flags(0),
|
||||||
data.file,
|
data.file,
|
||||||
MTPInputFile(), // thumb
|
MTPInputFile(), // thumb
|
||||||
MTP_string(name + ".tdesktop-theme"), // #TODO themes
|
MTP_string(state->filename),
|
||||||
MTP_string("application/x-tgtheme-tdesktop")
|
MTP_string("application/x-tgtheme-tdesktop")
|
||||||
)).done([=](const MTPDocument &result) {
|
)).done([=](const MTPDocument &result) {
|
||||||
createTheme(result);
|
if (creating) {
|
||||||
|
createTheme(result);
|
||||||
|
} else {
|
||||||
|
updateTheme(result);
|
||||||
|
}
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
fail(SaveErrorType::Other, error.type());
|
fail(SaveErrorType::Other, error.type());
|
||||||
}).send();
|
}).send();
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto uploadFile = [=](const QByteArray &theme) {
|
const auto uploadFile = [=](const QByteArray &theme) {
|
||||||
|
const auto media = PrepareThemeMedia(fields.title, theme);
|
||||||
|
state->filename = media.filename;
|
||||||
|
state->themeContent = theme;
|
||||||
|
|
||||||
session->uploader().documentReady(
|
session->uploader().documentReady(
|
||||||
) | rpl::filter([=](const UploadedDocument &data) {
|
) | rpl::filter([=](const UploadedDocument &data) {
|
||||||
return data.fullId == state->id;
|
return data.fullId == state->id;
|
||||||
|
@ -508,9 +568,7 @@ Fn<void()> SavePreparedTheme(
|
||||||
uploadTheme(data);
|
uploadTheme(data);
|
||||||
}, state->lifetime);
|
}, state->lifetime);
|
||||||
|
|
||||||
session->uploader().uploadMedia(
|
session->uploader().uploadMedia(state->id, media);
|
||||||
state->id,
|
|
||||||
PrepareThemeMedia(name, theme));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
state->generating = true;
|
state->generating = true;
|
||||||
|
@ -621,6 +679,7 @@ void SaveTheme(
|
||||||
using Data::CloudTheme;
|
using Data::CloudTheme;
|
||||||
|
|
||||||
const auto save = [=](const CloudTheme &fields) {
|
const auto save = [=](const CloudTheme &fields) {
|
||||||
|
unlock();
|
||||||
window->show(Box(SaveThemeBox, window, fields, palette));
|
window->show(Box(SaveThemeBox, window, fields, palette));
|
||||||
};
|
};
|
||||||
if (cloud.id) {
|
if (cloud.id) {
|
||||||
|
@ -629,7 +688,6 @@ void SaveTheme(
|
||||||
MTP_inputTheme(MTP_long(cloud.id), MTP_long(cloud.accessHash)),
|
MTP_inputTheme(MTP_long(cloud.id), MTP_long(cloud.accessHash)),
|
||||||
MTP_long(0)
|
MTP_long(0)
|
||||||
)).done([=](const MTPTheme &result) {
|
)).done([=](const MTPTheme &result) {
|
||||||
unlock();
|
|
||||||
result.match([&](const MTPDtheme &data) {
|
result.match([&](const MTPDtheme &data) {
|
||||||
save(CloudTheme::Parse(&window->account().session(), data));
|
save(CloudTheme::Parse(&window->account().session(), data));
|
||||||
}, [&](const MTPDthemeDocumentNotModified &data) {
|
}, [&](const MTPDthemeDocumentNotModified &data) {
|
||||||
|
@ -637,7 +695,6 @@ void SaveTheme(
|
||||||
save(CloudTheme());
|
save(CloudTheme());
|
||||||
});
|
});
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
unlock();
|
|
||||||
save(CloudTheme());
|
save(CloudTheme());
|
||||||
}).send();
|
}).send();
|
||||||
} else {
|
} else {
|
||||||
|
@ -751,12 +808,14 @@ void SaveThemeBox(
|
||||||
link->showError();
|
link->showError();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
auto fields = cloud;
|
||||||
|
fields.title = name->getLastText().trimmed();
|
||||||
|
fields.slug = link->getLastText().trimmed();
|
||||||
*cancel = SavePreparedTheme(
|
*cancel = SavePreparedTheme(
|
||||||
window,
|
window,
|
||||||
palette,
|
palette,
|
||||||
back->result(),
|
back->result(),
|
||||||
name->getLastText().trimmed(),
|
fields,
|
||||||
link->getLastText().trimmed(),
|
|
||||||
done,
|
done,
|
||||||
fail);
|
fail);
|
||||||
});
|
});
|
||||||
|
|
|
@ -906,23 +906,25 @@ void Generator::restoreTextPalette() {
|
||||||
_p->restoreTextPalette();
|
_p->restoreTextPalette();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] QString CachedThemePath(uint64 documentId) {
|
||||||
|
return QString::fromLatin1("special://cached-%1").arg(documentId);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::unique_ptr<Preview> PreviewFromFile(
|
std::unique_ptr<Preview> PreviewFromFile(
|
||||||
const QByteArray &bytes,
|
const QByteArray &bytes,
|
||||||
const QString &filepath,
|
const QString &filepath,
|
||||||
const Data::CloudTheme &cloud) {
|
const Data::CloudTheme &cloud) {
|
||||||
Expects(!filepath.isEmpty()); // Use
|
|
||||||
|
|
||||||
auto result = std::make_unique<Preview>();
|
auto result = std::make_unique<Preview>();
|
||||||
auto &object = result->object;
|
auto &object = result->object;
|
||||||
object.cloud = cloud;
|
object.cloud = cloud;
|
||||||
object.pathRelative = filepath.isEmpty()
|
|
||||||
? QString()
|
|
||||||
: QDir().relativeFilePath(filepath);
|
|
||||||
object.pathAbsolute = filepath.isEmpty()
|
object.pathAbsolute = filepath.isEmpty()
|
||||||
? QString()
|
? CachedThemePath(cloud.documentId)
|
||||||
: QFileInfo(filepath).absoluteFilePath();
|
: QFileInfo(filepath).absoluteFilePath();
|
||||||
|
object.pathRelative = filepath.isEmpty()
|
||||||
|
? object.pathAbsolute
|
||||||
|
: QDir().relativeFilePath(filepath);
|
||||||
if (bytes.isEmpty()) {
|
if (bytes.isEmpty()) {
|
||||||
if (!LoadFromFile(filepath, &result->instance, &object.content)) {
|
if (!LoadFromFile(filepath, &result->instance, &object.content)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -447,9 +447,12 @@ bool CloudList::insertTillLimit(
|
||||||
}) | ranges::view::take(insertCount);
|
}) | ranges::view::take(insertCount);
|
||||||
|
|
||||||
for (const auto &theme : insertElements) {
|
for (const auto &theme : insertElements) {
|
||||||
auto &index = isGood(theme) ? positionForGood : positionForBad;
|
const auto good = isGood(theme);
|
||||||
insert(index, theme);
|
insert(good ? positionForGood : positionForBad, theme);
|
||||||
++index;
|
if (good) {
|
||||||
|
++positionForGood;
|
||||||
|
}
|
||||||
|
++positionForBad;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue