diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp
index a852c8b31..dab7d9ad5 100644
--- a/Telegram/SourceFiles/core/application.cpp
+++ b/Telegram/SourceFiles/core/application.cpp
@@ -284,6 +284,14 @@ void Application::showDocument(not_null<DocumentData*> document, HistoryItem *it
 	}
 }
 
+void Application::showTheme(
+		not_null<DocumentData*> document,
+		const Data::CloudTheme &cloud) {
+	_mediaView->showTheme(document, cloud);
+	_mediaView->activateWindow();
+	_mediaView->setFocus();
+}
+
 PeerData *Application::ui_getPeerForMouseAction() {
 	if (_mediaView && !_mediaView->isHidden()) {
 		return _mediaView->ui_getPeerForMouseAction();
diff --git a/Telegram/SourceFiles/core/application.h b/Telegram/SourceFiles/core/application.h
index 65dd47e5a..adc7f139e 100644
--- a/Telegram/SourceFiles/core/application.h
+++ b/Telegram/SourceFiles/core/application.h
@@ -68,6 +68,10 @@ class Translator;
 class CloudManager;
 } // namespace Lang
 
+namespace Data {
+struct CloudTheme;
+} // namespace Data
+
 namespace Core {
 
 class Launcher;
@@ -103,6 +107,9 @@ public:
 	void showPhoto(not_null<PhotoData*> photo, HistoryItem *item);
 	void showPhoto(not_null<PhotoData*> photo, not_null<PeerData*> item);
 	void showDocument(not_null<DocumentData*> document, HistoryItem *item);
+	void showTheme(
+		not_null<DocumentData*> document,
+		const Data::CloudTheme &cloud);
 	PeerData *ui_getPeerForMouseAction();
 
 	QPoint getPointForCallPanelCenter() const;
diff --git a/Telegram/SourceFiles/data/data_cloud_themes.cpp b/Telegram/SourceFiles/data/data_cloud_themes.cpp
index 51c15854a..c049d39d3 100644
--- a/Telegram/SourceFiles/data/data_cloud_themes.cpp
+++ b/Telegram/SourceFiles/data/data_cloud_themes.cpp
@@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_file_origin.h"
 #include "main/main_session.h"
 #include "boxes/confirm_box.h"
+#include "core/application.h" // Core::App().showTheme.
 #include "lang/lang_keys.h"
 #include "apiwrap.h"
 #include "mainwindow.h"
@@ -147,26 +148,7 @@ void CloudThemes::resolve(
 		MTP_inputThemeSlug(MTP_string(slug)),
 		MTP_long(0)
 	)).done([=](const MTPTheme &result) {
-		result.match([&](const MTPDtheme &data) {
-			const auto cloud = CloudTheme::Parse(_session, data);
-			if (cloud.documentId) {
-				const auto document = _session->data().document(
-					cloud.documentId);
-				DocumentOpenClickHandler::Open(
-					Data::FileOrigin(),
-					document,
-					_session->data().message(clickFromMessageId));
-			} else if (cloud.createdBy == _session->userId()) {
-				Ui::show(Box(
-					Window::Theme::CreateForExistingBox,
-					&App::wnd()->controller(),
-					cloud));
-			} else {
-				Ui::show(Box<InformBox>(
-					tr::lng_theme_no_desktop(tr::now)));
-			}
-		}, [&](const MTPDthemeDocumentNotModified &data) {
-		});
+		showPreview(result);
 	}).fail([=](const RPCError &error) {
 		if (error.type() == qstr("THEME_FORMAT_INVALID")) {
 			Ui::show(Box<InformBox>(
@@ -175,30 +157,80 @@ void CloudThemes::resolve(
 	}).send();
 }
 
+void CloudThemes::showPreview(const MTPTheme &data) {
+	data.match([&](const MTPDtheme &data) {
+		showPreview(CloudTheme::Parse(_session, data));
+	}, [&](const MTPDthemeDocumentNotModified &data) {
+		LOG(("API Error: Unexpected themeDocumentNotModified."));
+	});
+}
+
+void CloudThemes::showPreview(const CloudTheme &cloud) {
+	if (const auto documentId = cloud.documentId) {
+		previewFromDocument(cloud, _session->data().document(documentId));
+	} else if (cloud.createdBy == _session->userId()) {
+		Ui::show(Box(
+			Window::Theme::CreateForExistingBox,
+			&App::wnd()->controller(),
+			cloud));
+	} else {
+		Ui::show(Box<InformBox>(
+			tr::lng_theme_no_desktop(tr::now)));
+	}
+}
+
 void CloudThemes::updateFromDocument(
 		const CloudTheme &cloud,
 		not_null<DocumentData*> document) {
-	if (_updatingFrom) {
-		_updatingFrom->cancel();
-	} else {
+	loadDocumentAndInvoke(_updatingFrom, document, [=] {
+		auto preview = Window::Theme::PreviewFromFile(
+			document->data(),
+			document->location().name(),
+			cloud);
+		if (preview) {
+			Window::Theme::Apply(std::move(preview));
+		}
+	});
+}
+
+void CloudThemes::previewFromDocument(
+		const CloudTheme &cloud,
+		not_null<DocumentData*> document) {
+	loadDocumentAndInvoke(_previewFrom, document, [=] {
+		Core::App().showTheme(document, cloud);
+	});
+}
+
+void CloudThemes::loadDocumentAndInvoke(
+		LoadingDocument &value,
+		not_null<DocumentData*> document,
+		Fn<void()> callback) {
+	const auto alreadyWaiting = (value.document != nullptr);
+	if (alreadyWaiting) {
+		value.document->cancel();
+	}
+	value.document = document;
+	value.document->save(Data::FileOrigin(), QString()); // #TODO themes
+	value.callback = std::move(callback);
+	if (document->loaded()) {
+		invokeForLoaded(value);
+		return;
+	}
+	if (!alreadyWaiting) {
 		base::ObservableViewer(
 			_session->downloaderTaskFinished()
 		) | rpl::filter([=] {
-			return _updatingFrom->loaded();
-		}) | rpl::start_with_next([=] {
-			_updatingFromLifetime.destroy();
-			auto preview = Window::Theme::PreviewFromFile(
-				document->data(),
-				document->location().name(),
-				cloud);
-			if (preview) {
-				Window::Theme::Apply(std::move(preview));
-			}
-		}, _updatingFromLifetime);
+			return document->loaded();
+		}) | rpl::start_with_next([=, &value] {
+			invokeForLoaded(value);
+		}, value.subscription);
 	}
+}
 
-	_updatingFrom = document;
-	_updatingFrom->save(Data::FileOrigin(), QString()); // #TODO themes
+void CloudThemes::invokeForLoaded(LoadingDocument &value) {
+	const auto onstack = std::move(value.callback);
+	value = LoadingDocument();
+	onstack();
 }
 
 void CloudThemes::scheduleReload() {
diff --git a/Telegram/SourceFiles/data/data_cloud_themes.h b/Telegram/SourceFiles/data/data_cloud_themes.h
index 2536ed4bb..a2fccfdcf 100644
--- a/Telegram/SourceFiles/data/data_cloud_themes.h
+++ b/Telegram/SourceFiles/data/data_cloud_themes.h
@@ -44,8 +44,16 @@ public:
 	void applyUpdate(const MTPTheme &theme);
 
 	void resolve(const QString &slug, const FullMsgId &clickFromMessageId);
+	void showPreview(const MTPTheme &data);
+	void showPreview(const CloudTheme &cloud);
 
 private:
+	struct LoadingDocument {
+		DocumentData *document = nullptr;
+		rpl::lifetime subscription;
+		Fn<void()> callback;
+	};
+
 	void parseThemes(const QVector<MTPTheme> &list);
 
 	void install();
@@ -56,6 +64,14 @@ private:
 	void updateFromDocument(
 		const CloudTheme &cloud,
 		not_null<DocumentData*> document);
+	void previewFromDocument(
+		const CloudTheme &cloud,
+		not_null<DocumentData*> document);
+	void loadDocumentAndInvoke(
+		LoadingDocument &value,
+		not_null<DocumentData*> document,
+		Fn<void()> callback);
+	void invokeForLoaded(LoadingDocument &value);
 
 	const not_null<Main::Session*> _session;
 	int32 _hash = 0;
@@ -65,8 +81,8 @@ private:
 	rpl::event_stream<> _updates;
 
 	base::Timer _reloadCurrentTimer;
-	DocumentData *_updatingFrom = nullptr;
-	rpl::lifetime _updatingFromLifetime;
+	LoadingDocument _updatingFrom;
+	LoadingDocument _previewFrom;
 	uint64 _installedDayThemeId = 0;
 	uint64 _installedNightThemeId = 0;
 
diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp
index e0422f646..06f3f7c2c 100644
--- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp
+++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp
@@ -1744,6 +1744,19 @@ void OverlayWidget::showPhoto(not_null<PhotoData*> photo, not_null<PeerData*> co
 }
 
 void OverlayWidget::showDocument(not_null<DocumentData*> document, HistoryItem *context) {
+	showDocument(document, context, Data::CloudTheme());
+}
+
+void OverlayWidget::showTheme(
+		not_null<DocumentData*> document,
+		const Data::CloudTheme &cloud) {
+	showDocument(document, nullptr, cloud);
+}
+
+void OverlayWidget::showDocument(
+		not_null<DocumentData*> document,
+		HistoryItem *context,
+		const Data::CloudTheme &cloud) {
 	if (context) {
 		setContext(context);
 	} else {
@@ -1754,7 +1767,7 @@ void OverlayWidget::showDocument(not_null<DocumentData*> document, HistoryItem *
 	_photo = nullptr;
 
 	_streamingStartPaused = false;
-	displayDocument(document, context);
+	displayDocument(document, context, cloud);
 	preloadData(0);
 	activateControls();
 }
@@ -1813,7 +1826,10 @@ void OverlayWidget::redisplayContent() {
 }
 
 // Empty messages shown as docs: doc can be nullptr.
-void OverlayWidget::displayDocument(DocumentData *doc, HistoryItem *item) {
+void OverlayWidget::displayDocument(
+		DocumentData *doc,
+		HistoryItem *item,
+		const Data::CloudTheme &cloud) {
 	if (isHidden()) {
 		moveToScreen();
 	}
@@ -1822,6 +1838,7 @@ void OverlayWidget::displayDocument(DocumentData *doc, HistoryItem *item) {
 	clearStreaming();
 	destroyThemePreview();
 	_doc = doc;
+	_themeCloudData = cloud;
 	_photo = nullptr;
 	_radial.stop();
 
@@ -2223,7 +2240,7 @@ void OverlayWidget::initThemePreview() {
 	const auto cloud = (i != end(cloudList)) ? *i : Data::CloudTheme();
 	const auto isTrusted = (cloud.documentId != 0);
 	const auto fields = [&] {
-		auto result = cloud;
+		auto result = _themeCloudData.id ? _themeCloudData : cloud;
 		if (!result.documentId) {
 			result.documentId = _doc->id;
 		}
diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h
index a418c8565..62ff4289e 100644
--- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h
+++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h
@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_shared_media.h"
 #include "data/data_user_photos.h"
 #include "data/data_web_page.h"
+#include "data/data_cloud_themes.h" // Data::CloudTheme.
 #include "media/view/media_view_playback_controls.h"
 
 namespace Ui {
@@ -70,7 +71,12 @@ public:
 
 	void showPhoto(not_null<PhotoData*> photo, HistoryItem *context);
 	void showPhoto(not_null<PhotoData*> photo, not_null<PeerData*> context);
-	void showDocument(not_null<DocumentData*> document, HistoryItem *context);
+	void showDocument(
+		not_null<DocumentData*> document,
+		HistoryItem *context);
+	void showTheme(
+		not_null<DocumentData*> document,
+		const Data::CloudTheme &cloud);
 
 	void leaveToChildEvent(QEvent *e, QWidget *child) override { // e -- from enterEvent() of child TWidget
 		updateOverState(OverNone);
@@ -229,8 +235,15 @@ private:
 	void resizeCenteredControls();
 	void resizeContentByScreenSize();
 
+	void showDocument(
+		not_null<DocumentData*> document,
+		HistoryItem *context,
+		const Data::CloudTheme &cloud);
 	void displayPhoto(not_null<PhotoData*> photo, HistoryItem *item);
-	void displayDocument(DocumentData *document, HistoryItem *item);
+	void displayDocument(
+		DocumentData *document,
+		HistoryItem *item,
+		const Data::CloudTheme &cloud = Data::CloudTheme());
 	void displayFinished();
 	void redisplayContent();
 	void findCurrent();
@@ -455,6 +468,7 @@ private:
 	std::unique_ptr<Window::Theme::Preview> _themePreview;
 	object_ptr<Ui::RoundButton> _themeApply = { nullptr };
 	object_ptr<Ui::RoundButton> _themeCancel = { nullptr };
+	Data::CloudTheme _themeCloudData;
 
 	bool _wasRepainted = false;
 
diff --git a/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp b/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp
index ec2a7c8a5..f27031045 100644
--- a/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp
+++ b/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp
@@ -481,19 +481,7 @@ void CloudList::insert(int index, const Data::CloudTheme &theme) {
 			|| i->waiting) {
 			return;
 		}
-		const auto documentId = i->theme.documentId;
-		if (!documentId) {
-			if (amCreator(i->theme)) {
-				_window->window().show(
-					Box(CreateForExistingBox, &_window->window(), i->theme));
-			}
-			return;
-		}
-		const auto document = _window->session().data().document(documentId);
-		DocumentOpenClickHandler::Open(
-			Data::FileOrigin(),
-			document,
-			nullptr);
+		_window->session().data().cloudThemes().showPreview(i->theme);
 	});
 	auto &element = *_elements.insert(
 		begin(_elements) + index,