2019-09-03 15:24:51 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
|
|
|
the official desktop application for the Telegram messaging service.
|
|
|
|
|
|
|
|
For license and copyright information please follow this link:
|
|
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
|
|
*/
|
|
|
|
#include "data/data_cloud_themes.h"
|
|
|
|
|
2019-09-06 13:35:26 +00:00
|
|
|
#include "window/themes/window_theme.h"
|
|
|
|
#include "window/themes/window_theme_preview.h"
|
2019-09-06 14:24:22 +00:00
|
|
|
#include "window/themes/window_theme_editor_box.h"
|
|
|
|
#include "window/window_controller.h"
|
2019-09-03 15:24:51 +00:00
|
|
|
#include "data/data_session.h"
|
2019-09-03 18:04:38 +00:00
|
|
|
#include "data/data_document.h"
|
2019-09-06 13:35:26 +00:00
|
|
|
#include "data/data_file_origin.h"
|
2020-04-09 12:27:53 +00:00
|
|
|
#include "data/data_document_media.h"
|
2019-09-03 15:24:51 +00:00
|
|
|
#include "main/main_session.h"
|
2019-09-06 14:24:22 +00:00
|
|
|
#include "boxes/confirm_box.h"
|
2021-06-15 20:30:08 +00:00
|
|
|
#include "media/view/media_view_open_common.h"
|
2019-09-06 14:24:22 +00:00
|
|
|
#include "lang/lang_keys.h"
|
2019-09-03 15:24:51 +00:00
|
|
|
#include "apiwrap.h"
|
|
|
|
|
|
|
|
namespace Data {
|
2019-09-06 13:35:26 +00:00
|
|
|
namespace {
|
|
|
|
|
2019-09-06 15:54:50 +00:00
|
|
|
constexpr auto kFirstReloadTimeout = 10 * crl::time(1000);
|
2019-09-06 13:35:26 +00:00
|
|
|
constexpr auto kReloadTimeout = 3600 * crl::time(1000);
|
|
|
|
|
|
|
|
} // namespace
|
2019-09-03 15:24:51 +00:00
|
|
|
|
2019-09-03 18:04:38 +00:00
|
|
|
CloudTheme CloudTheme::Parse(
|
|
|
|
not_null<Main::Session*> session,
|
|
|
|
const MTPDtheme &data) {
|
|
|
|
const auto document = data.vdocument();
|
|
|
|
return {
|
|
|
|
data.vid().v,
|
|
|
|
data.vaccess_hash().v,
|
|
|
|
qs(data.vslug()),
|
|
|
|
qs(data.vtitle()),
|
|
|
|
(document
|
|
|
|
? session->data().processDocument(*document)->id
|
|
|
|
: DocumentId(0)),
|
2019-09-09 13:13:16 +00:00
|
|
|
data.is_creator() ? session->userId() : UserId(0),
|
|
|
|
data.vinstalls_count().v
|
2019-09-03 18:04:38 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-09-03 15:24:51 +00:00
|
|
|
QString CloudThemes::Format() {
|
|
|
|
static const auto kResult = QString::fromLatin1("tdesktop");
|
|
|
|
return kResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
CloudThemes::CloudThemes(not_null<Main::Session*> session)
|
2019-09-06 13:35:26 +00:00
|
|
|
: _session(session)
|
|
|
|
, _reloadCurrentTimer([=] { reloadCurrent(); }) {
|
|
|
|
setupReload();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CloudThemes::setupReload() {
|
|
|
|
using namespace Window::Theme;
|
|
|
|
|
|
|
|
if (needReload()) {
|
|
|
|
_reloadCurrentTimer.callOnce(kFirstReloadTimeout);
|
|
|
|
}
|
|
|
|
base::ObservableViewer(
|
|
|
|
*Background()
|
|
|
|
) | rpl::filter([](const BackgroundUpdate &update) {
|
|
|
|
return (update.type == BackgroundUpdate::Type::ApplyingTheme);
|
|
|
|
}) | rpl::map([=] {
|
|
|
|
return needReload();
|
|
|
|
}) | rpl::start_with_next([=](bool need) {
|
2019-09-06 13:52:16 +00:00
|
|
|
install();
|
2019-09-06 13:35:26 +00:00
|
|
|
if (need) {
|
|
|
|
scheduleReload();
|
|
|
|
} else {
|
|
|
|
_reloadCurrentTimer.cancel();
|
|
|
|
}
|
|
|
|
}, _lifetime);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CloudThemes::needReload() const {
|
|
|
|
const auto &fields = Window::Theme::Background()->themeObject().cloud;
|
|
|
|
return fields.id && fields.documentId;
|
|
|
|
}
|
|
|
|
|
2019-09-06 13:52:16 +00:00
|
|
|
void CloudThemes::install() {
|
|
|
|
using namespace Window::Theme;
|
|
|
|
|
|
|
|
const auto &fields = Background()->themeObject().cloud;
|
|
|
|
auto &themeId = IsNightMode()
|
|
|
|
? _installedNightThemeId
|
|
|
|
: _installedDayThemeId;
|
|
|
|
const auto cloudId = fields.documentId ? fields.id : uint64(0);
|
|
|
|
if (themeId == cloudId) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
themeId = cloudId;
|
|
|
|
using Flag = MTPaccount_InstallTheme::Flag;
|
|
|
|
const auto flags = (IsNightMode() ? Flag::f_dark : Flag(0))
|
|
|
|
| Flag::f_format
|
|
|
|
| (themeId ? Flag::f_theme : Flag(0));
|
|
|
|
_session->api().request(MTPaccount_InstallTheme(
|
|
|
|
MTP_flags(flags),
|
|
|
|
MTP_string(Format()),
|
|
|
|
MTP_inputTheme(MTP_long(cloudId), MTP_long(fields.accessHash))
|
|
|
|
)).send();
|
|
|
|
}
|
|
|
|
|
2019-09-06 13:35:26 +00:00
|
|
|
void CloudThemes::reloadCurrent() {
|
|
|
|
if (!needReload()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const auto &fields = Window::Theme::Background()->themeObject().cloud;
|
|
|
|
_session->api().request(MTPaccount_GetTheme(
|
|
|
|
MTP_string(Format()),
|
|
|
|
MTP_inputTheme(MTP_long(fields.id), MTP_long(fields.accessHash)),
|
|
|
|
MTP_long(fields.documentId)
|
|
|
|
)).done([=](const MTPTheme &result) {
|
|
|
|
applyUpdate(result);
|
2021-03-12 12:48:00 +00:00
|
|
|
}).fail([=](const MTP::Error &error) {
|
2019-09-06 13:35:26 +00:00
|
|
|
_reloadCurrentTimer.callOnce(kReloadTimeout);
|
|
|
|
}).send();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CloudThemes::applyUpdate(const MTPTheme &theme) {
|
|
|
|
theme.match([&](const MTPDtheme &data) {
|
|
|
|
const auto cloud = CloudTheme::Parse(_session, data);
|
|
|
|
const auto &object = Window::Theme::Background()->themeObject();
|
|
|
|
if ((cloud.id != object.cloud.id)
|
2019-09-06 14:24:22 +00:00
|
|
|
|| (cloud.documentId == object.cloud.documentId)
|
|
|
|
|| !cloud.documentId) {
|
2019-09-06 13:35:26 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-09-09 09:13:07 +00:00
|
|
|
applyFromDocument(cloud);
|
2019-09-06 13:35:26 +00:00
|
|
|
});
|
|
|
|
scheduleReload();
|
|
|
|
}
|
|
|
|
|
2019-09-06 14:24:22 +00:00
|
|
|
void CloudThemes::resolve(
|
2021-06-15 20:30:08 +00:00
|
|
|
not_null<Window::Controller*> controller,
|
2019-09-06 14:24:22 +00:00
|
|
|
const QString &slug,
|
|
|
|
const FullMsgId &clickFromMessageId) {
|
|
|
|
_session->api().request(_resolveRequestId).cancel();
|
|
|
|
_resolveRequestId = _session->api().request(MTPaccount_GetTheme(
|
|
|
|
MTP_string(Format()),
|
|
|
|
MTP_inputThemeSlug(MTP_string(slug)),
|
|
|
|
MTP_long(0)
|
|
|
|
)).done([=](const MTPTheme &result) {
|
2021-06-15 20:30:08 +00:00
|
|
|
showPreview(controller, result);
|
2021-03-12 12:48:00 +00:00
|
|
|
}).fail([=](const MTP::Error &error) {
|
2019-09-06 14:24:22 +00:00
|
|
|
if (error.type() == qstr("THEME_FORMAT_INVALID")) {
|
2021-06-15 20:30:08 +00:00
|
|
|
controller->show(Box<InformBox>(
|
2019-09-06 14:24:22 +00:00
|
|
|
tr::lng_theme_no_desktop(tr::now)));
|
|
|
|
}
|
|
|
|
}).send();
|
|
|
|
}
|
|
|
|
|
2021-06-15 20:30:08 +00:00
|
|
|
void CloudThemes::showPreview(
|
|
|
|
not_null<Window::Controller*> controller,
|
|
|
|
const MTPTheme &data) {
|
2019-09-06 15:31:01 +00:00
|
|
|
data.match([&](const MTPDtheme &data) {
|
2021-06-15 20:30:08 +00:00
|
|
|
showPreview(controller, CloudTheme::Parse(_session, data));
|
2019-09-06 15:31:01 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-06-15 20:30:08 +00:00
|
|
|
void CloudThemes::showPreview(
|
|
|
|
not_null<Window::Controller*> controller,
|
|
|
|
const CloudTheme &cloud) {
|
2019-09-06 15:31:01 +00:00
|
|
|
if (const auto documentId = cloud.documentId) {
|
2021-06-15 20:30:08 +00:00
|
|
|
previewFromDocument(controller, cloud);
|
2019-09-06 15:31:01 +00:00
|
|
|
} else if (cloud.createdBy == _session->userId()) {
|
2021-06-15 20:30:08 +00:00
|
|
|
controller->show(Box(
|
2019-09-06 15:31:01 +00:00
|
|
|
Window::Theme::CreateForExistingBox,
|
2021-06-15 20:30:08 +00:00
|
|
|
controller,
|
2019-09-06 15:31:01 +00:00
|
|
|
cloud));
|
|
|
|
} else {
|
2021-06-15 20:30:08 +00:00
|
|
|
controller->show(Box<InformBox>(
|
2019-09-06 15:31:01 +00:00
|
|
|
tr::lng_theme_no_desktop(tr::now)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-09 09:13:07 +00:00
|
|
|
void CloudThemes::applyFromDocument(const CloudTheme &cloud) {
|
|
|
|
const auto document = _session->data().document(cloud.documentId);
|
2020-04-09 12:27:53 +00:00
|
|
|
loadDocumentAndInvoke(_updatingFrom, cloud, document, [=](
|
|
|
|
std::shared_ptr<Data::DocumentMedia> media) {
|
|
|
|
const auto document = media->owner();
|
2019-09-06 15:31:01 +00:00
|
|
|
auto preview = Window::Theme::PreviewFromFile(
|
2020-04-09 12:27:53 +00:00
|
|
|
media->bytes(),
|
2019-09-06 15:31:01 +00:00
|
|
|
document->location().name(),
|
|
|
|
cloud);
|
|
|
|
if (preview) {
|
|
|
|
Window::Theme::Apply(std::move(preview));
|
2019-09-06 15:54:50 +00:00
|
|
|
Window::Theme::KeepApplied();
|
2019-09-06 15:31:01 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-06-15 20:30:08 +00:00
|
|
|
void CloudThemes::previewFromDocument(
|
|
|
|
not_null<Window::Controller*> controller,
|
|
|
|
const CloudTheme &cloud) {
|
|
|
|
const auto sessionController = controller->sessionController();
|
|
|
|
if (!sessionController) {
|
|
|
|
return;
|
|
|
|
}
|
2019-09-09 09:13:07 +00:00
|
|
|
const auto document = _session->data().document(cloud.documentId);
|
2020-04-09 12:27:53 +00:00
|
|
|
loadDocumentAndInvoke(_previewFrom, cloud, document, [=](
|
|
|
|
std::shared_ptr<Data::DocumentMedia> media) {
|
|
|
|
const auto document = media->owner();
|
2021-06-15 20:30:08 +00:00
|
|
|
using Open = Media::View::OpenRequest;
|
|
|
|
controller->openInMediaView(Open(sessionController, document, cloud));
|
2019-09-06 15:31:01 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void CloudThemes::loadDocumentAndInvoke(
|
|
|
|
LoadingDocument &value,
|
2019-09-09 08:51:07 +00:00
|
|
|
const CloudTheme &cloud,
|
2019-09-06 15:31:01 +00:00
|
|
|
not_null<DocumentData*> document,
|
2020-04-09 12:27:53 +00:00
|
|
|
Fn<void(std::shared_ptr<Data::DocumentMedia>)> callback) {
|
2019-09-06 15:31:01 +00:00
|
|
|
const auto alreadyWaiting = (value.document != nullptr);
|
|
|
|
if (alreadyWaiting) {
|
|
|
|
value.document->cancel();
|
|
|
|
}
|
|
|
|
value.document = document;
|
2020-04-09 12:27:53 +00:00
|
|
|
value.documentMedia = document->createMediaView();
|
2019-09-09 08:51:07 +00:00
|
|
|
value.document->save(
|
|
|
|
Data::FileOriginTheme(cloud.id, cloud.accessHash),
|
|
|
|
QString());
|
2019-09-06 15:31:01 +00:00
|
|
|
value.callback = std::move(callback);
|
2020-04-09 14:02:09 +00:00
|
|
|
if (value.documentMedia->loaded()) {
|
2019-09-06 15:31:01 +00:00
|
|
|
invokeForLoaded(value);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!alreadyWaiting) {
|
2020-07-01 08:03:34 +00:00
|
|
|
_session->downloaderTaskFinished(
|
2020-04-09 14:02:09 +00:00
|
|
|
) | rpl::filter([=, &value] {
|
|
|
|
return value.documentMedia->loaded();
|
2019-09-06 15:31:01 +00:00
|
|
|
}) | rpl::start_with_next([=, &value] {
|
|
|
|
invokeForLoaded(value);
|
|
|
|
}, value.subscription);
|
2019-09-06 13:35:26 +00:00
|
|
|
}
|
2019-09-06 15:31:01 +00:00
|
|
|
}
|
2019-09-06 13:35:26 +00:00
|
|
|
|
2019-09-06 15:31:01 +00:00
|
|
|
void CloudThemes::invokeForLoaded(LoadingDocument &value) {
|
|
|
|
const auto onstack = std::move(value.callback);
|
2020-04-09 12:27:53 +00:00
|
|
|
auto media = std::move(value.documentMedia);
|
2019-09-06 15:31:01 +00:00
|
|
|
value = LoadingDocument();
|
2020-04-09 12:27:53 +00:00
|
|
|
onstack(std::move(media));
|
2019-09-06 13:35:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CloudThemes::scheduleReload() {
|
|
|
|
if (needReload()) {
|
|
|
|
_reloadCurrentTimer.callOnce(kReloadTimeout);
|
|
|
|
} else {
|
|
|
|
_reloadCurrentTimer.cancel();
|
|
|
|
}
|
2019-09-03 15:24:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CloudThemes::refresh() {
|
2019-09-06 14:24:22 +00:00
|
|
|
if (_refreshRquestId) {
|
2019-09-03 15:24:51 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-09-06 14:24:22 +00:00
|
|
|
_refreshRquestId = _session->api().request(MTPaccount_GetThemes(
|
2019-09-03 15:24:51 +00:00
|
|
|
MTP_string(Format()),
|
|
|
|
MTP_int(_hash)
|
|
|
|
)).done([=](const MTPaccount_Themes &result) {
|
2019-09-06 14:24:22 +00:00
|
|
|
_refreshRquestId = 0;
|
2019-09-03 15:24:51 +00:00
|
|
|
result.match([&](const MTPDaccount_themes &data) {
|
|
|
|
_hash = data.vhash().v;
|
|
|
|
parseThemes(data.vthemes().v);
|
|
|
|
_updates.fire({});
|
|
|
|
}, [](const MTPDaccount_themesNotModified &) {
|
|
|
|
});
|
2021-03-12 12:48:00 +00:00
|
|
|
}).fail([=](const MTP::Error &error) {
|
2019-09-06 14:24:22 +00:00
|
|
|
_refreshRquestId = 0;
|
2019-09-03 15:24:51 +00:00
|
|
|
}).send();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CloudThemes::parseThemes(const QVector<MTPTheme> &list) {
|
|
|
|
_list.clear();
|
|
|
|
_list.reserve(list.size());
|
|
|
|
for (const auto &theme : list) {
|
|
|
|
theme.match([&](const MTPDtheme &data) {
|
2019-09-03 18:04:38 +00:00
|
|
|
_list.push_back(CloudTheme::Parse(_session, data));
|
2019-09-03 15:24:51 +00:00
|
|
|
});
|
|
|
|
}
|
2019-09-09 06:59:57 +00:00
|
|
|
checkCurrentTheme();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CloudThemes::checkCurrentTheme() {
|
|
|
|
const auto &object = Window::Theme::Background()->themeObject();
|
|
|
|
if (!object.cloud.id || !object.cloud.documentId) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const auto i = ranges::find(_list, object.cloud.id, &CloudTheme::id);
|
|
|
|
if (i == end(_list)) {
|
|
|
|
install();
|
|
|
|
}
|
2019-09-03 15:24:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
rpl::producer<> CloudThemes::updated() const {
|
|
|
|
return _updates.events();
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::vector<CloudTheme> &CloudThemes::list() const {
|
|
|
|
return _list;
|
|
|
|
}
|
|
|
|
|
2019-09-08 18:01:45 +00:00
|
|
|
void CloudThemes::savedFromEditor(const CloudTheme &theme) {
|
2019-09-06 11:33:51 +00:00
|
|
|
const auto i = ranges::find(_list, theme.id, &CloudTheme::id);
|
|
|
|
if (i != end(_list)) {
|
|
|
|
*i = theme;
|
|
|
|
_updates.fire({});
|
2019-09-08 18:01:45 +00:00
|
|
|
} else {
|
|
|
|
_list.insert(begin(_list), theme);
|
|
|
|
_updates.fire({});
|
2019-09-06 11:33:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-08 17:05:26 +00:00
|
|
|
void CloudThemes::remove(uint64 cloudThemeId) {
|
|
|
|
const auto i = ranges::find(_list, cloudThemeId, &CloudTheme::id);
|
|
|
|
if (i == end(_list)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_session->api().request(MTPaccount_SaveTheme(
|
|
|
|
MTP_inputTheme(
|
|
|
|
MTP_long(i->id),
|
|
|
|
MTP_long(i->accessHash)),
|
|
|
|
MTP_bool(true)
|
|
|
|
)).send();
|
|
|
|
_list.erase(i);
|
|
|
|
_updates.fire({});
|
|
|
|
}
|
|
|
|
|
2019-09-03 15:24:51 +00:00
|
|
|
} // namespace Data
|