mirror of https://github.com/procxx/kepka.git
Check changes when saving theme.
This commit is contained in:
parent
cedb2d31af
commit
c92a9585e1
|
@ -1180,6 +1180,34 @@ void KeepApplied() {
|
||||||
Background()->keepApplied(saved.object, true);
|
Background()->keepApplied(saved.object, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void KeepFromEditor(
|
||||||
|
const QByteArray &originalContent,
|
||||||
|
const ParsedTheme &originalParsed,
|
||||||
|
const Data::CloudTheme &cloud,
|
||||||
|
const QByteArray &themeContent,
|
||||||
|
const ParsedTheme &themeParsed) {
|
||||||
|
ClearApplying();
|
||||||
|
const auto content = themeContent.isEmpty()
|
||||||
|
? originalContent
|
||||||
|
: themeContent;
|
||||||
|
auto saved = Saved();
|
||||||
|
auto &cache = saved.cache;
|
||||||
|
auto &object = saved.object;
|
||||||
|
cache.colors = style::main_palette::save();
|
||||||
|
cache.paletteChecksum = style::palette::Checksum();
|
||||||
|
cache.contentChecksum = hashCrc32(content.constData(), content.size());
|
||||||
|
cache.background = themeParsed.background;
|
||||||
|
cache.tiled = themeParsed.tiled;
|
||||||
|
object.cloud = cloud;
|
||||||
|
object.content = themeContent.isEmpty()
|
||||||
|
? originalContent
|
||||||
|
: themeContent;
|
||||||
|
object.pathAbsolute = object.pathRelative = CachedThemePath(
|
||||||
|
cloud.documentId);
|
||||||
|
Local::writeTheme(saved);
|
||||||
|
Background()->keepApplied(saved.object, true);
|
||||||
|
}
|
||||||
|
|
||||||
void Revert() {
|
void Revert() {
|
||||||
if (!AreTestingTheme()) {
|
if (!AreTestingTheme()) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -20,6 +20,8 @@ namespace Theme {
|
||||||
inline constexpr auto kThemeSchemeSizeLimit = 1024 * 1024;
|
inline constexpr auto kThemeSchemeSizeLimit = 1024 * 1024;
|
||||||
inline constexpr auto kThemeBackgroundSizeLimit = 4 * 1024 * 1024;
|
inline constexpr auto kThemeBackgroundSizeLimit = 4 * 1024 * 1024;
|
||||||
|
|
||||||
|
struct ParsedTheme;
|
||||||
|
|
||||||
[[nodiscard]] bool IsEmbeddedTheme(const QString &path);
|
[[nodiscard]] bool IsEmbeddedTheme(const QString &path);
|
||||||
|
|
||||||
struct Object {
|
struct Object {
|
||||||
|
@ -62,6 +64,12 @@ bool Apply(std::unique_ptr<Preview> preview);
|
||||||
void ApplyDefaultWithPath(const QString &themePath);
|
void ApplyDefaultWithPath(const QString &themePath);
|
||||||
bool ApplyEditedPalette(const QByteArray &content);
|
bool ApplyEditedPalette(const QByteArray &content);
|
||||||
void KeepApplied();
|
void KeepApplied();
|
||||||
|
void KeepFromEditor(
|
||||||
|
const QByteArray &originalContent,
|
||||||
|
const ParsedTheme &originalParsed,
|
||||||
|
const Data::CloudTheme &cloud,
|
||||||
|
const QByteArray &themeContent,
|
||||||
|
const ParsedTheme &themeParsed);
|
||||||
QString NightThemePath();
|
QString NightThemePath();
|
||||||
[[nodiscard]] bool IsNightMode();
|
[[nodiscard]] bool IsNightMode();
|
||||||
void SetNightModeValue(bool nightMode);
|
void SetNightModeValue(bool nightMode);
|
||||||
|
@ -191,6 +199,12 @@ private:
|
||||||
friend void ToggleNightMode();
|
friend void ToggleNightMode();
|
||||||
friend void ToggleNightMode(const QString &themePath);
|
friend void ToggleNightMode(const QString &themePath);
|
||||||
friend void KeepApplied();
|
friend void KeepApplied();
|
||||||
|
friend void KeepFromEditor(
|
||||||
|
const QByteArray &originalContent,
|
||||||
|
const ParsedTheme &originalParsed,
|
||||||
|
const Data::CloudTheme &cloud,
|
||||||
|
const QByteArray &themeContent,
|
||||||
|
const ParsedTheme &themeParsed);
|
||||||
friend bool IsNonDefaultBackground();
|
friend bool IsNonDefaultBackground();
|
||||||
|
|
||||||
Main::Session *_session = nullptr;
|
Main::Session *_session = nullptr;
|
||||||
|
|
|
@ -25,6 +25,13 @@ namespace Theme {
|
||||||
|
|
||||||
struct Colorizer;
|
struct Colorizer;
|
||||||
|
|
||||||
|
struct ParsedTheme {
|
||||||
|
QByteArray palette;
|
||||||
|
QByteArray background;
|
||||||
|
bool isPng = false;
|
||||||
|
bool tiled = false;
|
||||||
|
};
|
||||||
|
|
||||||
[[nodiscard]] QByteArray WriteCloudToText(const Data::CloudTheme &cloud);
|
[[nodiscard]] QByteArray WriteCloudToText(const Data::CloudTheme &cloud);
|
||||||
[[nodiscard]] Data::CloudTheme ReadCloudFromText(const QByteArray &text);
|
[[nodiscard]] Data::CloudTheme ReadCloudFromText(const QByteArray &text);
|
||||||
|
|
||||||
|
|
|
@ -56,37 +56,14 @@ enum class SaveErrorType {
|
||||||
Link,
|
Link,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ParsedTheme {
|
|
||||||
QByteArray palette;
|
|
||||||
QByteArray background;
|
|
||||||
bool isPng = false;
|
|
||||||
bool tiled = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PreparedBackground {
|
|
||||||
QByteArray content;
|
|
||||||
bool tile = false;
|
|
||||||
bool isPng = false;
|
|
||||||
bool changed = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <size_t Size>
|
|
||||||
QByteArray qba(const char(&string)[Size]) {
|
|
||||||
return QByteArray::fromRawData(string, Size - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray qba(QLatin1String string) {
|
|
||||||
return QByteArray::fromRawData(string.data(), string.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
class BackgroundSelector : public Ui::RpWidget {
|
class BackgroundSelector : public Ui::RpWidget {
|
||||||
public:
|
public:
|
||||||
BackgroundSelector(
|
BackgroundSelector(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
const QImage &background,
|
const QImage &background,
|
||||||
const PreparedBackground &data);
|
const ParsedTheme &parsed);
|
||||||
|
|
||||||
[[nodiscard]] PreparedBackground result() const;
|
[[nodiscard]] ParsedTheme result() const;
|
||||||
|
|
||||||
int resizeGetHeight(int newWidth) override;
|
int resizeGetHeight(int newWidth) override;
|
||||||
|
|
||||||
|
@ -101,19 +78,26 @@ private:
|
||||||
object_ptr<Ui::Checkbox> _tileBackground;
|
object_ptr<Ui::Checkbox> _tileBackground;
|
||||||
|
|
||||||
QImage _background;
|
QImage _background;
|
||||||
QByteArray _backgroundContent;
|
ParsedTheme _parsed;
|
||||||
bool _isPng = false;
|
|
||||||
bool _changed = false;
|
|
||||||
QString _imageText;
|
QString _imageText;
|
||||||
int _thumbnailSize = 0;
|
int _thumbnailSize = 0;
|
||||||
QPixmap _thumbnail;
|
QPixmap _thumbnail;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <size_t Size>
|
||||||
|
QByteArray qba(const char(&string)[Size]) {
|
||||||
|
return QByteArray::fromRawData(string, Size - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray qba(QLatin1String string) {
|
||||||
|
return QByteArray::fromRawData(string.data(), string.size());
|
||||||
|
}
|
||||||
|
|
||||||
BackgroundSelector::BackgroundSelector(
|
BackgroundSelector::BackgroundSelector(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
const QImage &background,
|
const QImage &background,
|
||||||
const PreparedBackground &data)
|
const ParsedTheme &parsed)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
, _chooseFromFile(
|
, _chooseFromFile(
|
||||||
this,
|
this,
|
||||||
|
@ -122,14 +106,14 @@ BackgroundSelector::BackgroundSelector(
|
||||||
, _tileBackground(
|
, _tileBackground(
|
||||||
this,
|
this,
|
||||||
tr::lng_settings_bg_tile(tr::now),
|
tr::lng_settings_bg_tile(tr::now),
|
||||||
data.tile,
|
parsed.tiled,
|
||||||
st::defaultBoxCheckbox)
|
st::defaultBoxCheckbox)
|
||||||
, _background(background)
|
, _background(background)
|
||||||
, _backgroundContent(data.content) {
|
, _parsed(parsed) {
|
||||||
_imageText = tr::lng_theme_editor_saved_to_jpg(
|
_imageText = tr::lng_theme_editor_saved_to_jpg(
|
||||||
tr::now,
|
tr::now,
|
||||||
lt_size,
|
lt_size,
|
||||||
formatSizeText(_backgroundContent.size()));
|
formatSizeText(_parsed.background.size()));
|
||||||
_chooseFromFile->setClickedCallback([=] { chooseBackgroundFromFile(); });
|
_chooseFromFile->setClickedCallback([=] { chooseBackgroundFromFile(); });
|
||||||
|
|
||||||
_thumbnailSize = st::boxTextFont->height
|
_thumbnailSize = st::boxTextFont->height
|
||||||
|
@ -201,16 +185,15 @@ void BackgroundSelector::chooseBackgroundFromFile() {
|
||||||
|| format == "jpg"
|
|| format == "jpg"
|
||||||
|| format == "png")) {
|
|| format == "png")) {
|
||||||
_background = image;
|
_background = image;
|
||||||
_backgroundContent = content;
|
_parsed.background = content;
|
||||||
_isPng = (format == "png");
|
_parsed.isPng = (format == "png");
|
||||||
_changed = true;
|
const auto phrase = _parsed.isPng
|
||||||
const auto phrase = _isPng
|
|
||||||
? tr::lng_theme_editor_read_from_png
|
? tr::lng_theme_editor_read_from_png
|
||||||
: tr::lng_theme_editor_read_from_jpg;
|
: tr::lng_theme_editor_read_from_jpg;
|
||||||
_imageText = phrase(
|
_imageText = phrase(
|
||||||
tr::now,
|
tr::now,
|
||||||
lt_size,
|
lt_size,
|
||||||
formatSizeText(_backgroundContent.size()));
|
formatSizeText(_parsed.background.size()));
|
||||||
_tileBackground->setChecked(false);
|
_tileBackground->setChecked(false);
|
||||||
updateThumbnail();
|
updateThumbnail();
|
||||||
}
|
}
|
||||||
|
@ -223,13 +206,18 @@ void BackgroundSelector::chooseBackgroundFromFile() {
|
||||||
crl::guard(this, callback));
|
crl::guard(this, callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
PreparedBackground BackgroundSelector::result() const {
|
ParsedTheme BackgroundSelector::result() const {
|
||||||
return {
|
auto result = _parsed;
|
||||||
_backgroundContent,
|
result.tiled = _tileBackground->checked();
|
||||||
_tileBackground->checked(),
|
return result;
|
||||||
_isPng,
|
}
|
||||||
_changed,
|
|
||||||
};
|
bool PaletteChanged(
|
||||||
|
const QByteArray &editorPalette,
|
||||||
|
const QByteArray &originalPalette,
|
||||||
|
const Data::CloudTheme &cloud) {
|
||||||
|
return originalPalette.isEmpty()
|
||||||
|
|| (editorPalette != WriteCloudToText(cloud) + originalPalette);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImportFromFile(
|
void ImportFromFile(
|
||||||
|
@ -260,32 +248,40 @@ void ImportFromFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] ParsedTheme ParseTheme(
|
[[nodiscard]] ParsedTheme ParseTheme(
|
||||||
const QByteArray &themeContent,
|
const Object &theme,
|
||||||
bool onlyPalette) {
|
bool onlyPalette = false) {
|
||||||
auto result = ParsedTheme();
|
auto raw = ParsedTheme();
|
||||||
result.palette = themeContent;
|
raw.palette = theme.content;
|
||||||
|
const auto result = [&] {
|
||||||
|
if (const auto colorizer = ColorizerForTheme(theme.pathAbsolute)) {
|
||||||
|
raw.palette = Editor::ColorizeInContent(
|
||||||
|
std::move(raw.palette),
|
||||||
|
colorizer);
|
||||||
|
}
|
||||||
|
return raw;
|
||||||
|
};
|
||||||
|
|
||||||
zlib::FileToRead file(themeContent);
|
zlib::FileToRead file(theme.content);
|
||||||
|
|
||||||
unz_global_info globalInfo = { 0 };
|
unz_global_info globalInfo = { 0 };
|
||||||
file.getGlobalInfo(&globalInfo);
|
file.getGlobalInfo(&globalInfo);
|
||||||
if (file.error() != UNZ_OK) {
|
if (file.error() != UNZ_OK) {
|
||||||
return result;
|
return result();
|
||||||
}
|
}
|
||||||
result.palette = file.readFileContent("colors.tdesktop-theme", zlib::kCaseInsensitive, kThemeSchemeSizeLimit);
|
raw.palette = file.readFileContent("colors.tdesktop-theme", zlib::kCaseInsensitive, kThemeSchemeSizeLimit);
|
||||||
if (file.error() == UNZ_END_OF_LIST_OF_FILE) {
|
if (file.error() == UNZ_END_OF_LIST_OF_FILE) {
|
||||||
file.clearError();
|
file.clearError();
|
||||||
result.palette = file.readFileContent("colors.tdesktop-palette", zlib::kCaseInsensitive, kThemeSchemeSizeLimit);
|
raw.palette = file.readFileContent("colors.tdesktop-palette", zlib::kCaseInsensitive, kThemeSchemeSizeLimit);
|
||||||
}
|
}
|
||||||
if (file.error() != UNZ_OK) {
|
if (file.error() != UNZ_OK) {
|
||||||
LOG(("Theme Error: could not read 'colors.tdesktop-theme' or 'colors.tdesktop-palette' in the theme file."));
|
LOG(("Theme Error: could not read 'colors.tdesktop-theme' or 'colors.tdesktop-palette' in the theme file."));
|
||||||
return ParsedTheme();
|
return ParsedTheme();
|
||||||
} else if (onlyPalette) {
|
} else if (onlyPalette) {
|
||||||
return result;
|
return result();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto fromFile = [&](const char *filename) {
|
const auto fromFile = [&](const char *filename) {
|
||||||
result.background = file.readFileContent(filename, zlib::kCaseInsensitive, kThemeBackgroundSizeLimit);
|
raw.background = file.readFileContent(filename, zlib::kCaseInsensitive, kThemeBackgroundSizeLimit);
|
||||||
if (file.error() == UNZ_OK) {
|
if (file.error() == UNZ_OK) {
|
||||||
return true;
|
return true;
|
||||||
} else if (file.error() == UNZ_END_OF_LIST_OF_FILE) {
|
} else if (file.error() == UNZ_END_OF_LIST_OF_FILE) {
|
||||||
|
@ -296,30 +292,29 @@ void ImportFromFile(
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!fromFile("background.jpg") || !result.background.isEmpty()) {
|
if (!fromFile("background.jpg") || !raw.background.isEmpty()) {
|
||||||
return result.background.isEmpty() ? ParsedTheme() : result;
|
return raw.background.isEmpty() ? ParsedTheme() : result();
|
||||||
}
|
}
|
||||||
result.isPng = true;
|
raw.isPng = true;
|
||||||
if (!fromFile("background.png") || !result.background.isEmpty()) {
|
if (!fromFile("background.png") || !raw.background.isEmpty()) {
|
||||||
return result.background.isEmpty() ? ParsedTheme() : result;
|
return raw.background.isEmpty() ? ParsedTheme() : result();
|
||||||
}
|
}
|
||||||
result.tiled = true;
|
raw.tiled = true;
|
||||||
if (!fromFile("tiled.png") || !result.background.isEmpty()) {
|
if (!fromFile("tiled.png") || !raw.background.isEmpty()) {
|
||||||
return result.background.isEmpty() ? ParsedTheme() : result;
|
return raw.background.isEmpty() ? ParsedTheme() : result();
|
||||||
}
|
}
|
||||||
result.isPng = false;
|
raw.isPng = false;
|
||||||
if (!fromFile("background.jpg") || !result.background.isEmpty()) {
|
if (!fromFile("background.jpg") || !raw.background.isEmpty()) {
|
||||||
return result.background.isEmpty() ? ParsedTheme() : result;
|
return raw.background.isEmpty() ? ParsedTheme() : result();
|
||||||
}
|
}
|
||||||
return result;
|
return result();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool CopyColorsToPalette(
|
[[nodiscard]] bool CopyColorsToPalette(
|
||||||
const QString &destination,
|
const QString &destination,
|
||||||
const QString &themePath,
|
const Object &theme,
|
||||||
const QByteArray &themeContent,
|
|
||||||
const Data::CloudTheme &cloud) {
|
const Data::CloudTheme &cloud) {
|
||||||
auto parsed = ParseTheme(themeContent, true);
|
auto parsed = ParseTheme(theme, true);
|
||||||
if (parsed.palette.isEmpty()) {
|
if (parsed.palette.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -330,11 +325,6 @@ void ImportFromFile(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto colorizer = ColorizerForTheme(themePath)) {
|
|
||||||
parsed.palette = Editor::ColorizeInContent(
|
|
||||||
std::move(parsed.palette),
|
|
||||||
colorizer);
|
|
||||||
}
|
|
||||||
const auto content = WriteCloudToText(cloud) + parsed.palette;
|
const auto content = WriteCloudToText(cloud) + parsed.palette;
|
||||||
if (f.write(content) != content.size()) {
|
if (f.write(content) != content.size()) {
|
||||||
LOG(("Theme Error: could not write palette to '%1'").arg(destination));
|
LOG(("Theme Error: could not write palette to '%1'").arg(destination));
|
||||||
|
@ -402,14 +392,12 @@ bool WriteDefaultPalette(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] QByteArray PrepareTheme(
|
[[nodiscard]] QByteArray PackTheme(const ParsedTheme &parsed) {
|
||||||
const QByteArray &palette,
|
|
||||||
const PreparedBackground &background) {
|
|
||||||
zlib::FileToWrite zip;
|
zlib::FileToWrite zip;
|
||||||
|
|
||||||
zip_fileinfo zfi = { { 0, 0, 0, 0, 0, 0 }, 0, 0, 0 };
|
zip_fileinfo zfi = { { 0, 0, 0, 0, 0, 0 }, 0, 0, 0 };
|
||||||
const auto back = std::string(background.tile ? "tiled" : "background")
|
const auto back = std::string(parsed.tiled ? "tiled" : "background")
|
||||||
+ (background.isPng ? ".png" : ".jpg");
|
+ (parsed.isPng ? ".png" : ".jpg");
|
||||||
zip.openNewFile(
|
zip.openNewFile(
|
||||||
back.c_str(),
|
back.c_str(),
|
||||||
&zfi,
|
&zfi,
|
||||||
|
@ -421,8 +409,8 @@ bool WriteDefaultPalette(
|
||||||
Z_DEFLATED,
|
Z_DEFLATED,
|
||||||
Z_DEFAULT_COMPRESSION);
|
Z_DEFAULT_COMPRESSION);
|
||||||
zip.writeInFile(
|
zip.writeInFile(
|
||||||
background.content.constData(),
|
parsed.background.constData(),
|
||||||
background.content.size());
|
parsed.background.size());
|
||||||
zip.closeFile();
|
zip.closeFile();
|
||||||
const auto scheme = "colors.tdesktop-theme";
|
const auto scheme = "colors.tdesktop-theme";
|
||||||
zip.openNewFile(
|
zip.openNewFile(
|
||||||
|
@ -435,7 +423,7 @@ bool WriteDefaultPalette(
|
||||||
nullptr,
|
nullptr,
|
||||||
Z_DEFLATED,
|
Z_DEFLATED,
|
||||||
Z_DEFAULT_COMPRESSION);
|
Z_DEFAULT_COMPRESSION);
|
||||||
zip.writeInFile(palette.constData(), palette.size());
|
zip.writeInFile(parsed.palette.constData(), parsed.palette.size());
|
||||||
zip.closeFile();
|
zip.closeFile();
|
||||||
zip.close();
|
zip.close();
|
||||||
|
|
||||||
|
@ -513,8 +501,9 @@ SendMediaReady PrepareThemeMedia(
|
||||||
|
|
||||||
Fn<void()> SavePreparedTheme(
|
Fn<void()> SavePreparedTheme(
|
||||||
not_null<Window::Controller*> window,
|
not_null<Window::Controller*> window,
|
||||||
const QByteArray &palette,
|
const ParsedTheme &parsed,
|
||||||
const PreparedBackground &background,
|
const QByteArray &originalContent,
|
||||||
|
const ParsedTheme &originalParsed,
|
||||||
const Data::CloudTheme &fields,
|
const Data::CloudTheme &fields,
|
||||||
Fn<void()> done,
|
Fn<void()> done,
|
||||||
Fn<void(SaveErrorType,QString)> fail) {
|
Fn<void(SaveErrorType,QString)> fail) {
|
||||||
|
@ -530,13 +519,6 @@ Fn<void()> SavePreparedTheme(
|
||||||
rpl::lifetime lifetime;
|
rpl::lifetime lifetime;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (fields.title.isEmpty()) {
|
|
||||||
fail(SaveErrorType::Name, {});
|
|
||||||
return nullptr;
|
|
||||||
} else if (!IsGoodSlug(fields.slug)) {
|
|
||||||
fail(SaveErrorType::Link, {});
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
const auto session = &window->account().session();
|
const auto session = &window->account().session();
|
||||||
const auto api = &session->api();
|
const auto api = &session->api();
|
||||||
const auto state = std::make_shared<State>();
|
const auto state = std::make_shared<State>();
|
||||||
|
@ -546,6 +528,9 @@ Fn<void()> SavePreparedTheme(
|
||||||
|
|
||||||
const auto creating = !fields.id
|
const auto creating = !fields.id
|
||||||
|| (fields.createdBy != session->userId());
|
|| (fields.createdBy != session->userId());
|
||||||
|
const auto changed = (parsed.background != originalParsed.background)
|
||||||
|
|| (parsed.tiled != originalParsed.tiled)
|
||||||
|
|| PaletteChanged(parsed.palette, originalParsed.palette, fields);
|
||||||
|
|
||||||
const auto finish = [=](const MTPTheme &result) {
|
const auto finish = [=](const MTPTheme &result) {
|
||||||
Background()->clearEditingTheme(ClearEditing::KeepChanges);
|
Background()->clearEditingTheme(ClearEditing::KeepChanges);
|
||||||
|
@ -559,18 +544,16 @@ Fn<void()> SavePreparedTheme(
|
||||||
LOG(("API Error: Unexpected themeDocumentNotModified."));
|
LOG(("API Error: Unexpected themeDocumentNotModified."));
|
||||||
return fields;
|
return fields;
|
||||||
});
|
});
|
||||||
if (cloud.documentId) {
|
if (cloud.documentId && !state->themeContent.isEmpty()) {
|
||||||
const auto document = session->data().document(cloud.documentId);
|
const auto document = session->data().document(cloud.documentId);
|
||||||
document->setDataAndCache(state->themeContent);
|
document->setDataAndCache(state->themeContent);
|
||||||
}
|
}
|
||||||
auto preview = PreviewFromFile(
|
KeepFromEditor(
|
||||||
|
originalContent,
|
||||||
|
originalParsed,
|
||||||
|
cloud,
|
||||||
state->themeContent,
|
state->themeContent,
|
||||||
QString(),
|
parsed);
|
||||||
cloud);
|
|
||||||
if (preview) {
|
|
||||||
Apply(std::move(preview));
|
|
||||||
KeepApplied();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto createTheme = [=](const MTPDocument &data) {
|
const auto createTheme = [=](const MTPDocument &data) {
|
||||||
|
@ -587,10 +570,13 @@ Fn<void()> SavePreparedTheme(
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto updateTheme = [=](const MTPDocument &data) {
|
const auto updateTheme = [=](const MTPDocument &data) {
|
||||||
|
using Flag = MTPaccount_UpdateTheme::Flag;
|
||||||
const auto document = session->data().processDocument(data);
|
const auto document = session->data().processDocument(data);
|
||||||
const auto flags = MTPaccount_UpdateTheme::Flag::f_title
|
const auto flags = Flag::f_title
|
||||||
| MTPaccount_UpdateTheme::Flag::f_slug
|
| Flag::f_slug
|
||||||
| MTPaccount_UpdateTheme::Flag::f_document;
|
| (data.type() == mtpc_documentEmpty
|
||||||
|
? Flag(0)
|
||||||
|
: Flag::f_document);
|
||||||
state->requestId = api->request(MTPaccount_UpdateTheme(
|
state->requestId = api->request(MTPaccount_UpdateTheme(
|
||||||
MTP_flags(flags),
|
MTP_flags(flags),
|
||||||
MTP_string(Data::CloudThemes::Format()),
|
MTP_string(Data::CloudThemes::Format()),
|
||||||
|
@ -639,9 +625,13 @@ Fn<void()> SavePreparedTheme(
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto save = [=] {
|
const auto save = [=] {
|
||||||
|
if (!creating && !changed) {
|
||||||
|
updateTheme(MTP_documentEmpty(MTP_long(fields.documentId)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
state->generating = true;
|
state->generating = true;
|
||||||
crl::async([=] {
|
crl::async([=] {
|
||||||
crl::on_main([=, ready = PrepareTheme(palette, background)]{
|
crl::on_main([=, ready = PackTheme(parsed)]{
|
||||||
if (!state->generating) {
|
if (!state->generating) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -683,6 +673,16 @@ Fn<void()> SavePreparedTheme(
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
bool PaletteChanged(
|
||||||
|
const QByteArray &editorPalette,
|
||||||
|
const Data::CloudTheme &cloud) {
|
||||||
|
auto object = Local::ReadThemeContent();
|
||||||
|
const auto real = object.content.isEmpty()
|
||||||
|
? GenerateDefaultPalette()
|
||||||
|
: ParseTheme(object, true).palette;
|
||||||
|
return PaletteChanged(editorPalette, real, cloud);
|
||||||
|
}
|
||||||
|
|
||||||
void StartEditor(
|
void StartEditor(
|
||||||
not_null<Window::Controller*> window,
|
not_null<Window::Controller*> window,
|
||||||
const Data::CloudTheme &cloud) {
|
const Data::CloudTheme &cloud) {
|
||||||
|
@ -690,11 +690,7 @@ void StartEditor(
|
||||||
auto object = Local::ReadThemeContent();
|
auto object = Local::ReadThemeContent();
|
||||||
const auto written = object.content.isEmpty()
|
const auto written = object.content.isEmpty()
|
||||||
? WriteDefaultPalette(path, cloud)
|
? WriteDefaultPalette(path, cloud)
|
||||||
: CopyColorsToPalette(
|
: CopyColorsToPalette(path, object, cloud);
|
||||||
path,
|
|
||||||
object.pathAbsolute,
|
|
||||||
object.content,
|
|
||||||
cloud);
|
|
||||||
if (!written) {
|
if (!written) {
|
||||||
window->show(Box<InformBox>(tr::lng_theme_editor_error(tr::now)));
|
window->show(Box<InformBox>(tr::lng_theme_editor_error(tr::now)));
|
||||||
return;
|
return;
|
||||||
|
@ -801,16 +797,30 @@ void SaveThemeBox(
|
||||||
const QByteArray &palette) {
|
const QByteArray &palette) {
|
||||||
Expects(window->account().sessionExists());
|
Expects(window->account().sessionExists());
|
||||||
|
|
||||||
//Local::ReadThemeContent()
|
const auto original = Local::ReadThemeContent();
|
||||||
const auto background = Background()->createCurrentImage();
|
const auto originalContent = original.content;
|
||||||
//if (Data::IsThemeWallPaper(Background()->paper())) {
|
|
||||||
|
|
||||||
//}
|
// We don't need default palette here, because in case of it we are
|
||||||
auto backgroundContent = QByteArray();
|
// not interested if the palette was changed, we'll save it anyway.
|
||||||
const auto tiled = Background()->tile();
|
const auto originalParsed = originalContent.isEmpty()
|
||||||
{
|
? ParsedTheme() // GenerateDefaultPalette()
|
||||||
QBuffer buffer(&backgroundContent);
|
: ParseTheme(original);
|
||||||
|
|
||||||
|
const auto background = Background()->createCurrentImage();
|
||||||
|
const auto backgroundIsTiled = Background()->tile();
|
||||||
|
const auto changed = !Data::IsThemeWallPaper(Background()->paper())
|
||||||
|
|| originalParsed.background.isEmpty();
|
||||||
|
|
||||||
|
auto parsed = ParsedTheme();
|
||||||
|
parsed.palette = palette;
|
||||||
|
parsed.isPng = false;
|
||||||
|
if (changed) {
|
||||||
|
QBuffer buffer(&parsed.background);
|
||||||
background.save(&buffer, "JPG", 87);
|
background.save(&buffer, "JPG", 87);
|
||||||
|
} else {
|
||||||
|
// Use existing background serialization.
|
||||||
|
parsed.background = originalParsed.background;
|
||||||
|
parsed.isPng = originalParsed.isPng;
|
||||||
}
|
}
|
||||||
|
|
||||||
box->setTitle(tr::lng_theme_editor_save_title(Ui::Text::WithEntities));
|
box->setTitle(tr::lng_theme_editor_save_title(Ui::Text::WithEntities));
|
||||||
|
@ -868,7 +878,7 @@ void SaveThemeBox(
|
||||||
object_ptr<BackgroundSelector>(
|
object_ptr<BackgroundSelector>(
|
||||||
box,
|
box,
|
||||||
background,
|
background,
|
||||||
PreparedBackground{ backgroundContent, tiled }),
|
parsed),
|
||||||
style::margins(
|
style::margins(
|
||||||
st::boxRowPadding.left(),
|
st::boxRowPadding.left(),
|
||||||
st::themesSmallSkip,
|
st::themesSmallSkip,
|
||||||
|
@ -886,8 +896,6 @@ void SaveThemeBox(
|
||||||
if (*saving) {
|
if (*saving) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
*saving = true;
|
|
||||||
box->showLoading(true);
|
|
||||||
const auto done = crl::guard(box, [=] {
|
const auto done = crl::guard(box, [=] {
|
||||||
box->closeBox();
|
box->closeBox();
|
||||||
window->showRightColumn(nullptr);
|
window->showRightColumn(nullptr);
|
||||||
|
@ -905,7 +913,7 @@ void SaveThemeBox(
|
||||||
Ui::Toast::Show(
|
Ui::Toast::Show(
|
||||||
tr::lng_create_channel_link_occupied(tr::now));
|
tr::lng_create_channel_link_occupied(tr::now));
|
||||||
type = SaveErrorType::Link;
|
type = SaveErrorType::Link;
|
||||||
} else {
|
} else if (!error.isEmpty()) {
|
||||||
Ui::Toast::Show(error);
|
Ui::Toast::Show(error);
|
||||||
}
|
}
|
||||||
if (type == SaveErrorType::Name) {
|
if (type == SaveErrorType::Name) {
|
||||||
|
@ -917,10 +925,21 @@ void SaveThemeBox(
|
||||||
auto fields = cloud;
|
auto fields = cloud;
|
||||||
fields.title = name->getLastText().trimmed();
|
fields.title = name->getLastText().trimmed();
|
||||||
fields.slug = link->getLastText().trimmed();
|
fields.slug = link->getLastText().trimmed();
|
||||||
|
if (fields.title.isEmpty()) {
|
||||||
|
fail(SaveErrorType::Name, QString());
|
||||||
|
return;
|
||||||
|
} else if (!IsGoodSlug(fields.slug)) {
|
||||||
|
fail(SaveErrorType::Link, QString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*saving = true;
|
||||||
|
box->showLoading(true);
|
||||||
*cancel = SavePreparedTheme(
|
*cancel = SavePreparedTheme(
|
||||||
window,
|
window,
|
||||||
palette,
|
|
||||||
back->result(),
|
back->result(),
|
||||||
|
originalContent,
|
||||||
|
originalParsed,
|
||||||
fields,
|
fields,
|
||||||
done,
|
done,
|
||||||
fail);
|
fail);
|
||||||
|
@ -929,15 +948,5 @@ void SaveThemeBox(
|
||||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PaletteChanged(
|
|
||||||
const QByteArray &editorPalette,
|
|
||||||
const Data::CloudTheme &cloud) {
|
|
||||||
auto object = Local::ReadThemeContent();
|
|
||||||
const auto real = object.content.isEmpty()
|
|
||||||
? GenerateDefaultPalette()
|
|
||||||
: ParseTheme(object.content, true).palette;
|
|
||||||
return (editorPalette != WriteCloudToText(cloud) + real);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Theme
|
} // namespace Theme
|
||||||
} // namespace Window
|
} // namespace Window
|
||||||
|
|
|
@ -906,12 +906,12 @@ void Generator::restoreTextPalette() {
|
||||||
_p->restoreTextPalette();
|
_p->restoreTextPalette();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] QString CachedThemePath(uint64 documentId) {
|
} // namespace
|
||||||
|
|
||||||
|
QString CachedThemePath(uint64 documentId) {
|
||||||
return QString::fromLatin1("special://cached-%1").arg(documentId);
|
return QString::fromLatin1("special://cached-%1").arg(documentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
std::unique_ptr<Preview> PreviewFromFile(
|
std::unique_ptr<Preview> PreviewFromFile(
|
||||||
const QByteArray &bytes,
|
const QByteArray &bytes,
|
||||||
const QString &filepath,
|
const QString &filepath,
|
||||||
|
|
|
@ -22,6 +22,8 @@ struct CurrentData {
|
||||||
bool backgroundTiled = false;
|
bool backgroundTiled = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] QString CachedThemePath(uint64 documentId);
|
||||||
|
|
||||||
std::unique_ptr<Preview> PreviewFromFile(
|
std::unique_ptr<Preview> PreviewFromFile(
|
||||||
const QByteArray &bytes,
|
const QByteArray &bytes,
|
||||||
const QString &filepath,
|
const QString &filepath,
|
||||||
|
|
Loading…
Reference in New Issue