diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index 4db06b26f..29d434847 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -531,6 +531,10 @@ PRIVATE
     data/business/data_business_info.h
     data/business/data_shortcut_messages.cpp
     data/business/data_shortcut_messages.h
+    data/components/scheduled_messages.cpp
+    data/components/scheduled_messages.h
+    data/components/sponsored_messages.cpp
+    data/components/sponsored_messages.h
     data/notify/data_notify_settings.cpp
     data/notify/data_notify_settings.h
     data/notify/data_peer_notify_settings.cpp
@@ -648,14 +652,10 @@ PRIVATE
     data/data_send_action.h
     data/data_session.cpp
     data/data_session.h
-    data/data_scheduled_messages.cpp
-    data/data_scheduled_messages.h
     data/data_shared_media.cpp
     data/data_shared_media.h
     data/data_sparse_ids.cpp
     data/data_sparse_ids.h
-    data/data_sponsored_messages.cpp
-    data/data_sponsored_messages.h
     data/data_statistics.h
     data/data_stories.cpp
     data/data_stories.h
@@ -1530,8 +1530,6 @@ PRIVATE
     ui/image/image_location.h
     ui/image/image_location_factory.cpp
     ui/image/image_location_factory.h
-    ui/widgets/level_meter.cpp
-    ui/widgets/level_meter.h
     ui/countryinput.cpp
     ui/countryinput.h
     ui/dynamic_thumbnails.cpp
@@ -1547,6 +1545,8 @@ PRIVATE
     ui/search_field_controller.h
     ui/text/format_song_document_name.cpp
     ui/text/format_song_document_name.h
+    ui/widgets/label_with_custom_emoji.cpp
+    ui/widgets/label_with_custom_emoji.h
     ui/unread_badge.cpp
     ui/unread_badge.h
     window/main_window.cpp
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 00e0bcf7c..2c2009edd 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -309,7 +309,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_edit_limit_reached#one" = "You've reached the message text limit. Please make the text shorter by {count} character.";
 "lng_edit_limit_reached#other" = "You've reached the message text limit. Please make the text shorter by {count} characters.";
 "lng_edit_message" = "Edit message";
-"lng_edit_message_text" = "New message text...";
+"lng_edit_message_text" = "Caption";
 "lng_deleted" = "Deleted Account";
 "lng_deleted_message" = "Deleted message";
 "lng_deleted_story" = "Deleted story";
@@ -734,6 +734,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_settings_sensitive_disable_filtering" = "Disable filtering";
 "lng_settings_sensitive_about" = "Display sensitive media in public channels on all your Telegram devices.";
 "lng_settings_security_bots" = "Bots and websites";
+"lng_settings_file_confirmations" = "File open confirmations";
+"lng_settings_edit_extensions" = "Extensions whitelist";
+"lng_settings_edit_extensions_about" = "Open files with the following extensions without additional confirmation.";
+"lng_settings_edit_ip_confirm" = "IP reveal warning";
+"lng_settings_edit_ip_confirm_about" = "Show confirmation when opening files that may reveal your IP address.";
 "lng_settings_clear_payment_info" = "Clear Payment and Shipping Info";
 "lng_settings_logged_in" = "Connected websites";
 "lng_settings_logged_in_title" = "Logged in with Telegram";
@@ -4480,10 +4485,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_translate_settings_about" = "The 'Translate' button will appear when you open a context menu on a text message.";
 "lng_translate_settings_one" = "Please choose at least one language so that it can be used as the \"Translate to\" language.";
 
-"lng_launch_exe_warning" = "This file has a {extension} extension.\nAre you sure you want to run it?";
+"lng_launch_exe_warning" = "This file has {extension} extension.\nAre you sure you want to run it?";
+"lng_launch_other_warning" = "This file has {extension} extension.\nAre you sure you want to open it?";
 "lng_launch_svg_warning" = "Opening this file can potentially expose your IP address to its sender. Continue?";
 "lng_launch_exe_sure" = "Run";
+"lng_launch_other_sure" = "Open";
 "lng_launch_exe_dont_ask" = "Don't ask me again";
+"lng_launch_dont_ask" = "Remember for this file type";
+"lng_launch_dont_ask_settings" = "You can later edit trusted file types in Settings > Privacy and Security > File open confirmations.";
 
 "lng_polls_anonymous" = "Anonymous Poll";
 "lng_polls_public" = "Poll";
diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml
index 3488163e5..734a63bc4 100644
--- a/Telegram/Resources/uwp/AppX/AppxManifest.xml
+++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml
@@ -10,7 +10,7 @@
   <Identity Name="TelegramMessengerLLP.TelegramDesktop"
     ProcessorArchitecture="ARCHITECTURE"
     Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
-    Version="4.16.6.0" />
+    Version="4.16.8.0" />
   <Properties>
     <DisplayName>Telegram Desktop</DisplayName>
     <PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc
index 07be79162..481b83cfb 100644
--- a/Telegram/Resources/winrc/Telegram.rc
+++ b/Telegram/Resources/winrc/Telegram.rc
@@ -44,8 +44,8 @@ IDI_ICON1               ICON                    "..\\art\\icon256.ico"
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,16,6,0
- PRODUCTVERSION 4,16,6,0
+ FILEVERSION 4,16,8,0
+ PRODUCTVERSION 4,16,8,0
  FILEFLAGSMASK 0x3fL
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -62,10 +62,10 @@ BEGIN
         BEGIN
             VALUE "CompanyName", "Radolyn Labs"
             VALUE "FileDescription", "AyuGram Desktop"
-            VALUE "FileVersion", "4.16.6.0"
+            VALUE "FileVersion", "4.16.8.0"
             VALUE "LegalCopyright", "Copyright (C) 2014-2024"
             VALUE "ProductName", "AyuGram Desktop"
-            VALUE "ProductVersion", "4.16.6.0"
+            VALUE "ProductVersion", "4.16.8.0"
         END
     END
     BLOCK "VarFileInfo"
diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc
index 88221f475..208fdf399 100644
--- a/Telegram/Resources/winrc/Updater.rc
+++ b/Telegram/Resources/winrc/Updater.rc
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,16,6,0
- PRODUCTVERSION 4,16,6,0
+ FILEVERSION 4,16,8,0
+ PRODUCTVERSION 4,16,8,0
  FILEFLAGSMASK 0x3fL
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -53,10 +53,10 @@ BEGIN
         BEGIN
             VALUE "CompanyName", "Radolyn Labs"
             VALUE "FileDescription", "AyuGram Desktop Updater"
-            VALUE "FileVersion", "4.16.6.0"
+            VALUE "FileVersion", "4.16.8.0"
             VALUE "LegalCopyright", "Copyright (C) 2014-2024"
             VALUE "ProductName", "AyuGram Desktop"
-            VALUE "ProductVersion", "4.16.6.0"
+            VALUE "ProductVersion", "4.16.8.0"
         END
     END
     BLOCK "VarFileInfo"
diff --git a/Telegram/SourceFiles/api/api_bot.cpp b/Telegram/SourceFiles/api/api_bot.cpp
index 706b7a7cb..0284ae719 100644
--- a/Telegram/SourceFiles/api/api_bot.cpp
+++ b/Telegram/SourceFiles/api/api_bot.cpp
@@ -236,14 +236,14 @@ void SendBotCallbackDataWithPassword(
 				} else {
 					return;
 				}
-				const auto box = std::make_shared<QPointer<PasscodeBox>>();
 				auto fields = PasscodeBox::CloudFields::From(state);
 				fields.customTitle = tr::lng_bots_password_confirm_title();
 				fields.customDescription
 					= tr::lng_bots_password_confirm_description(tr::now);
 				fields.customSubmitButton = tr::lng_passcode_submit();
 				fields.customCheckCallback = [=](
-						const Core::CloudPasswordResult &result) {
+						const Core::CloudPasswordResult &result,
+						QPointer<PasscodeBox> box) {
 					if (const auto button = getButton()) {
 						if (button->requestId) {
 							return;
@@ -257,18 +257,17 @@ void SendBotCallbackDataWithPassword(
 							return;
 						}
 						SendBotCallbackData(strongController, item, row, column, result, [=] {
-							if (*box) {
-								(*box)->closeBox();
+							if (box) {
+								box->closeBox();
 							}
 						}, [=](const QString &error) {
-							if (*box) {
-								(*box)->handleCustomCheckError(error);
+							if (box) {
+								box->handleCustomCheckError(error);
 							}
 						});
 					}
 				};
 				auto object = Box<PasscodeBox>(session, fields);
-				*box = Ui::MakeWeak(object.data());
 				show->showBox(std::move(object), Ui::LayerOption::CloseOther);
 			}, *lifetime);
 		}
diff --git a/Telegram/SourceFiles/api/api_chat_participants.cpp b/Telegram/SourceFiles/api/api_chat_participants.cpp
index 6761d2fff..745e0b883 100644
--- a/Telegram/SourceFiles/api/api_chat_participants.cpp
+++ b/Telegram/SourceFiles/api/api_chat_participants.cpp
@@ -112,8 +112,8 @@ void ApplyLastList(
 	channel->mgInfo->lastAdmins.clear();
 	channel->mgInfo->lastRestricted.clear();
 	channel->mgInfo->lastParticipants.clear();
-	channel->mgInfo->lastParticipantsStatus =
-		MegagroupInfo::LastParticipantsUpToDate
+	channel->mgInfo->lastParticipantsStatus
+		= MegagroupInfo::LastParticipantsUpToDate
 			| MegagroupInfo::LastParticipantsOnceReceived;
 
 	auto botStatus = channel->mgInfo->botStatus;
diff --git a/Telegram/SourceFiles/api/api_earn.cpp b/Telegram/SourceFiles/api/api_earn.cpp
index e8f38e06b..d6425ef69 100644
--- a/Telegram/SourceFiles/api/api_earn.cpp
+++ b/Telegram/SourceFiles/api/api_earn.cpp
@@ -58,25 +58,33 @@ void HandleWithdrawalButton(
 			state->loading = false;
 
 			auto fields = PasscodeBox::CloudFields::From(pass);
-			fields.customTitle =
-				tr::lng_channel_earn_balance_password_title();
-			fields.customDescription =
-				tr::lng_channel_earn_balance_password_description(tr::now);
+			fields.customTitle
+				= tr::lng_channel_earn_balance_password_title();
+			fields.customDescription
+				= tr::lng_channel_earn_balance_password_description(tr::now);
 			fields.customSubmitButton = tr::lng_passcode_submit();
 			fields.customCheckCallback = crl::guard(button, [=](
-					const Core::CloudPasswordResult &result) {
+					const Core::CloudPasswordResult &result,
+					QPointer<PasscodeBox> box) {
+				const auto done = [=](const QString &result) {
+					if (!result.isEmpty()) {
+						UrlClickHandler::Open(result);
+						if (box) {
+							box->closeBox();
+						}
+					}
+				};
+				const auto fail = [=](const QString &error) {
+					show->showToast(error);
+				};
 				session->api().request(
 					MTPstats_GetBroadcastRevenueWithdrawalUrl(
 						channel->inputChannel,
 						result.result
 				)).done([=](const MTPstats_BroadcastRevenueWithdrawalUrl &r) {
-					const auto url = qs(r.data().vurl());
-
-					if (!url.isEmpty()) {
-						UrlClickHandler::Open(url);
-					}
+					done(qs(r.data().vurl()));
 				}).fail([=](const MTP::Error &error) {
-					show->showToast(error.type());
+					fail(error.type());
 				}).send();
 			});
 			show->show(Box<PasscodeBox>(session, fields));
diff --git a/Telegram/SourceFiles/api/api_editing.cpp b/Telegram/SourceFiles/api/api_editing.cpp
index 84f0cbfff..f91a73579 100644
--- a/Telegram/SourceFiles/api/api_editing.cpp
+++ b/Telegram/SourceFiles/api/api_editing.cpp
@@ -12,12 +12,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "api/api_text_entities.h"
 #include "ui/boxes/confirm_box.h"
 #include "data/business/data_shortcut_messages.h"
+#include "data/components/scheduled_messages.h"
 #include "data/data_histories.h"
-#include "data/data_scheduled_messages.h"
 #include "data/data_session.h"
 #include "data/data_web_page.h"
 #include "history/history.h"
-#include "history/history_item.h"
 #include "lang/lang_keys.h"
 #include "main/main_session.h"
 #include "mtproto/mtproto_response.h"
@@ -29,20 +28,20 @@ namespace {
 using namespace rpl::details;
 
 template <typename T>
-constexpr auto WithId =
-	is_callable_plain_v<T, Fn<void()>, mtpRequestId>;
+constexpr auto WithId
+	= is_callable_plain_v<T, Fn<void()>, mtpRequestId>;
 template <typename T>
-constexpr auto WithoutId =
-	is_callable_plain_v<T, Fn<void()>>;
+constexpr auto WithoutId
+	= is_callable_plain_v<T, Fn<void()>>;
 template <typename T>
-constexpr auto WithoutCallback =
-	is_callable_plain_v<T>;
+constexpr auto WithoutCallback
+	= is_callable_plain_v<T>;
 template <typename T>
-constexpr auto ErrorWithId =
-	is_callable_plain_v<T, QString, mtpRequestId>;
+constexpr auto ErrorWithId
+	= is_callable_plain_v<T, QString, mtpRequestId>;
 template <typename T>
-constexpr auto ErrorWithoutId =
-	is_callable_plain_v<T, QString>;
+constexpr auto ErrorWithoutId
+	= is_callable_plain_v<T, QString>;
 
 template <typename DoneCallback, typename FailCallback>
 mtpRequestId EditMessage(
@@ -95,7 +94,7 @@ mtpRequestId EditMessage(
 		: emptyFlag);
 
 	const auto id = item->isScheduled()
-		? session->data().scheduledMessages().lookupId(item)
+		? session->scheduledMessages().lookupId(item)
 		: item->isBusinessShortcut()
 		? session->data().shortcutMessages().lookupId(item)
 		: item->id;
diff --git a/Telegram/SourceFiles/api/api_statistics.cpp b/Telegram/SourceFiles/api/api_statistics.cpp
index 9bc31c483..7139c5ebb 100644
--- a/Telegram/SourceFiles/api/api_statistics.cpp
+++ b/Telegram/SourceFiles/api/api_statistics.cpp
@@ -860,6 +860,7 @@ void EarnStatistics::requestHistory(
 			.token = Data::EarnHistorySlice::OffsetToken(nextToken),
 		});
 	}).fail([=] {
+		done({});
 		_requestId = 0;
 	}).send();
 }
diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp
index b5deb99ae..97b2f7529 100644
--- a/Telegram/SourceFiles/api/api_updates.cpp
+++ b/Telegram/SourceFiles/api/api_updates.cpp
@@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "mtproto/mtproto_config.h"
 #include "mtproto/mtproto_dc_options.h"
 #include "data/business/data_shortcut_messages.h"
+#include "data/components/scheduled_messages.h"
 #include "data/notify/data_notify_settings.h"
 #include "data/stickers/data_stickers.h"
 #include "data/data_saved_messages.h"
@@ -37,7 +38,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_histories.h"
 #include "data/data_folder.h"
 #include "data/data_forum.h"
-#include "data/data_scheduled_messages.h"
 #include "data/data_send_action.h"
 #include "data/data_stories.h"
 #include "data/data_message_reactions.h"
@@ -98,7 +98,7 @@ void ProcessScheduledMessageWithElapsedTime(
 		// Note that when a message is scheduled until online
 		// while the recipient is already online, the server sends
 		// an ordinary new message with skipped "from_scheduled" flag.
-		session->data().scheduledMessages().checkEntitiesAndUpdate(data);
+		session->scheduledMessages().checkEntitiesAndUpdate(data);
 	}
 }
 
@@ -1472,7 +1472,9 @@ void Updates::applyUpdates(
 			if (const auto id = owner.messageIdByRandomId(randomId)) {
 				const auto local = owner.message(id);
 				if (local && local->isScheduled()) {
-					owner.scheduledMessages().sendNowSimpleMessage(d, local);
+					session().scheduledMessages().sendNowSimpleMessage(
+						d,
+						local);
 				}
 			}
 			const auto wasAlready = (lookupMessage() != nullptr);
@@ -1569,7 +1571,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
 			auto &owner = session().data();
 			if (const auto local = owner.message(id)) {
 				if (local->isScheduled()) {
-					session().data().scheduledMessages().apply(d, local);
+					session().scheduledMessages().apply(d, local);
 				} else if (local->isBusinessShortcut()) {
 					session().data().shortcutMessages().apply(d, local);
 				} else {
@@ -1779,12 +1781,12 @@ void Updates::feedUpdate(const MTPUpdate &update) {
 
 	case mtpc_updateNewScheduledMessage: {
 		const auto &d = update.c_updateNewScheduledMessage();
-		session().data().scheduledMessages().apply(d);
+		session().scheduledMessages().apply(d);
 	} break;
 
 	case mtpc_updateDeleteScheduledMessages: {
 		const auto &d = update.c_updateDeleteScheduledMessages();
-		session().data().scheduledMessages().apply(d);
+		session().scheduledMessages().apply(d);
 	} break;
 
 	case mtpc_updateQuickReplies: {
diff --git a/Telegram/SourceFiles/api/api_user_privacy.cpp b/Telegram/SourceFiles/api/api_user_privacy.cpp
index b27580aaa..d0c17fa6b 100644
--- a/Telegram/SourceFiles/api/api_user_privacy.cpp
+++ b/Telegram/SourceFiles/api/api_user_privacy.cpp
@@ -307,8 +307,8 @@ void UserPrivacy::reload(Key key) {
 }
 
 void UserPrivacy::pushPrivacy(Key key, const TLRules &rules) {
-	const auto &saved = (_privacyValues[key] =
-		TLToRules(rules, _session->data()));
+	const auto &saved
+		= (_privacyValues[key] = TLToRules(rules, _session->data()));
 	const auto i = _privacyChanges.find(key);
 	if (i != end(_privacyChanges)) {
 		i->second.fire_copy(saved);
diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index ab030fb57..057b7629f 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -35,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "api/api_user_names.h"
 #include "api/api_websites.h"
 #include "data/business/data_shortcut_messages.h"
+#include "data/components/scheduled_messages.h"
 #include "data/notify/data_notify_settings.h"
 #include "data/data_changes.h"
 #include "data/data_web_page.h"
@@ -43,7 +44,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_forum.h"
 #include "data/data_saved_sublist.h"
 #include "data/data_search_controller.h"
-#include "data/data_scheduled_messages.h"
 #include "data/data_session.h"
 #include "data/data_channel.h"
 #include "data/data_chat.h"
@@ -559,7 +559,7 @@ void ApiWrap::sendMessageFail(
 			}
 		}
 	} else if (error == u"SCHEDULE_STATUS_PRIVATE"_q) {
-		auto &scheduled = _session->data().scheduledMessages();
+		auto &scheduled = _session->scheduledMessages();
 		Assert(peer->isUser());
 		if (const auto item = scheduled.lookupItem(peer->id, itemId.msg)) {
 			scheduled.removeSending(item);
@@ -1573,8 +1573,8 @@ void ApiWrap::saveStickerSets(
 				writeRecent = true;
 			}
 
-			const auto isAttached =
-				(removedSetId == Data::Stickers::CloudRecentAttachedSetId);
+			const auto isAttached
+				= (removedSetId == Data::Stickers::CloudRecentAttachedSetId);
 			const auto flags = isAttached
 				? MTPmessages_ClearRecentStickers::Flag::f_attached
 				: MTPmessages_ClearRecentStickers::Flags(0);
@@ -2480,8 +2480,8 @@ void ApiWrap::refreshFileReference(
 					_session->data().peer(storyId.peer)->input,
 					MTP_vector<MTPint>(1, MTP_int(storyId.story))));
 			} else if (item->isScheduled()) {
-				const auto &scheduled = _session->data().scheduledMessages();
-				const auto realId = scheduled.lookupId(item);
+				const auto realId = _session->scheduledMessages().lookupId(
+					item);
 				request(MTPmessages_GetScheduledMessages(
 					item->history()->peer->input,
 					MTP_vector<MTPint>(1, MTP_int(realId))));
@@ -2527,8 +2527,8 @@ void ApiWrap::refreshFileReference(
 	}, [&](Data::FileOriginPeerPhoto data) {
 		fail();
 	}, [&](Data::FileOriginStickerSet data) {
-		const auto isRecentAttached =
-			(data.setId == Data::Stickers::CloudRecentAttachedSetId);
+		const auto isRecentAttached
+			= (data.setId == Data::Stickers::CloudRecentAttachedSetId);
 		if (data.setId == Data::Stickers::CloudRecentSetId
 			|| data.setId == Data::Stickers::RecentSetId
 			|| isRecentAttached) {
diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp
index ba35e8d98..45d4b1e99 100644
--- a/Telegram/SourceFiles/boxes/add_contact_box.cpp
+++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp
@@ -605,8 +605,8 @@ void GroupInfoBox::prepare() {
 		_navigation->session().api().selfDestruct().reload();
 
 		const auto top = addTopButton(st::infoTopBarMenu);
-		const auto menu =
-			top->lifetime().make_state<base::unique_qptr<Ui::PopupMenu>>();
+		const auto menu
+			= top->lifetime().make_state<base::unique_qptr<Ui::PopupMenu>>();
 		top->setClickedCallback([=] {
 			*menu = base::make_unique_q<Ui::PopupMenu>(
 				top,
@@ -1306,8 +1306,8 @@ void SetupChannelBox::handleChange() {
 				&& (ch < 'a' || ch > 'z')
 				&& (ch < '0' || ch > '9')
 				&& ch != '_') {
-				const auto badSymbols =
-					tr::lng_create_channel_link_bad_symbols(tr::now);
+				const auto badSymbols
+					= tr::lng_create_channel_link_bad_symbols(tr::now);
 				if (_errorText != badSymbols) {
 					_errorText = badSymbols;
 					update();
@@ -1317,8 +1317,8 @@ void SetupChannelBox::handleChange() {
 			}
 		}
 		if (name.size() < Ui::EditPeer::kMinUsernameLength) {
-			const auto tooShort =
-				tr::lng_create_channel_link_too_short(tr::now);
+			const auto tooShort
+				= tr::lng_create_channel_link_too_short(tr::now);
 			if (_errorText != tooShort) {
 				_errorText = tooShort;
 				update();
diff --git a/Telegram/SourceFiles/boxes/delete_messages_box.cpp b/Telegram/SourceFiles/boxes/delete_messages_box.cpp
index 0a939ace6..1def53870 100644
--- a/Telegram/SourceFiles/boxes/delete_messages_box.cpp
+++ b/Telegram/SourceFiles/boxes/delete_messages_box.cpp
@@ -232,8 +232,8 @@ void DeleteMessagesBox::prepare() {
 			if (hasScheduledMessages()) {
 			} else if (auto revoke = revokeText(peer)) {
 				const auto &settings = Core::App().settings();
-				const auto revokeByDefault =
-					!settings.rememberedDeleteMessageOnlyForYou();
+				const auto revokeByDefault
+					= !settings.rememberedDeleteMessageOnlyForYou();
 				_revoke.create(
 					this,
 					revoke->checkbox,
diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.cpp b/Telegram/SourceFiles/boxes/gift_premium_box.cpp
index b107af7c9..10ad9409d 100644
--- a/Telegram/SourceFiles/boxes/gift_premium_box.cpp
+++ b/Telegram/SourceFiles/boxes/gift_premium_box.cpp
@@ -16,7 +16,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "boxes/peers/prepare_short_info_box.h"
 #include "boxes/peers/replace_boost_box.h" // BoostsForGift.
 #include "boxes/premium_preview_box.h" // ShowPremiumPreviewBox.
-#include "core/ui_integration.h" // Core::MarkedTextContext.
 #include "data/data_boosts.h"
 #include "data/data_changes.h"
 #include "data/data_channel.h"
@@ -48,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/toast/toast.h"
 #include "ui/widgets/checkbox.h"
 #include "ui/widgets/gradient_round_button.h"
+#include "ui/widgets/label_with_custom_emoji.h"
 #include "ui/wrap/padding_wrap.h"
 #include "ui/wrap/slide_wrap.h"
 #include "ui/wrap/table_layout.h"
@@ -319,21 +319,20 @@ void GiftBox(
 			std::move(titleLabel)),
 		st::premiumGiftTitlePadding);
 
-	auto textLabel = object_ptr<Ui::FlatLabel>(box, st::premiumPreviewAbout);
-	tr::lng_premium_gift_about(
-		lt_user,
-		user->session().changes().peerFlagsValue(
-			user,
-			Data::PeerUpdate::Flag::Name
-		) | rpl::map([=] { return TextWithEntities{ user->firstName }; }),
-		Ui::Text::RichLangValue
-	) | rpl::map(
-		BoostsForGiftText({ user })
-	) | rpl::start_with_next([
-			raw = textLabel.data(),
-			session = &user->session()](const TextWithEntities &t) {
-		raw->setMarkedText(t, Core::MarkedTextContext{ .session = session });
-	}, textLabel->lifetime());
+	auto textLabel = Ui::CreateLabelWithCustomEmoji(
+		box,
+		tr::lng_premium_gift_about(
+			lt_user,
+			user->session().changes().peerFlagsValue(
+				user,
+				Data::PeerUpdate::Flag::Name
+			) | rpl::map([=] { return TextWithEntities{ user->firstName }; }),
+			Ui::Text::RichLangValue
+		) | rpl::map(
+			BoostsForGiftText({ user })
+		),
+		{ .session = &user->session() },
+		st::premiumPreviewAbout);
 	textLabel->setTextColorOverride(stTitle.textFg->c);
 	textLabel->resizeToWidth(available);
 	box->addRow(
@@ -536,14 +535,12 @@ void GiftsBox(
 		const auto label = box->addRow(
 			object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
 				box,
-				object_ptr<Ui::FlatLabel>(box, st::premiumPreviewAbout)),
+				Ui::CreateLabelWithCustomEmoji(
+					box,
+					std::move(text),
+					{ .session = session },
+					st::premiumPreviewAbout)),
 			padding)->entity();
-		std::move(
-			text
-		) | rpl::start_with_next([=](const TextWithEntities &t) {
-			using namespace Core;
-			label->setMarkedText(t, MarkedTextContext{ .session = session });
-		}, label->lifetime());
 		label->setTextColorOverride(stTitle.textFg->c);
 		label->resizeToWidth(available);
 	}
diff --git a/Telegram/SourceFiles/boxes/passcode_box.cpp b/Telegram/SourceFiles/boxes/passcode_box.cpp
index e320b434f..840e4872d 100644
--- a/Telegram/SourceFiles/boxes/passcode_box.cpp
+++ b/Telegram/SourceFiles/boxes/passcode_box.cpp
@@ -740,7 +740,7 @@ void PasscodeBox::submitOnlyCheckCloudPassword(const QString &oldPassword) {
 void PasscodeBox::sendOnlyCheckCloudPassword(const QString &oldPassword) {
 	checkPassword(oldPassword, [=](const Core::CloudPasswordResult &check) {
 		if (const auto onstack = _cloudFields.customCheckCallback) {
-			onstack(check);
+			onstack(check, Ui::MakeWeak(this));
 		} else {
 			Assert(_cloudFields.turningOff);
 			sendClearCloudPassword(check);
diff --git a/Telegram/SourceFiles/boxes/passcode_box.h b/Telegram/SourceFiles/boxes/passcode_box.h
index 684da859a..b651cda64 100644
--- a/Telegram/SourceFiles/boxes/passcode_box.h
+++ b/Telegram/SourceFiles/boxes/passcode_box.h
@@ -51,7 +51,10 @@ public:
 		TimeId pendingResetDate = 0;
 
 		// Check cloud password for some action.
-		Fn<void(const Core::CloudPasswordResult &)> customCheckCallback;
+		using CustomCheck = Fn<void(
+			const Core::CloudPasswordResult &,
+			QPointer<PasscodeBox>)>;
+		CustomCheck customCheckCallback;
 		rpl::producer<QString> customTitle;
 		std::optional<QString> customDescription;
 		rpl::producer<QString> customSubmitButton;
diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp
index c1c086ad1..39389d1d5 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp
@@ -598,19 +598,17 @@ void EditAdminBox::requestTransferPassword(not_null<ChannelData*> channel) {
 	) | rpl::take(
 		1
 	) | rpl::start_with_next([=](const Core::CloudPasswordState &state) {
-		const auto box = std::make_shared<QPointer<PasscodeBox>>();
 		auto fields = PasscodeBox::CloudFields::From(state);
 		fields.customTitle = tr::lng_rights_transfer_password_title();
 		fields.customDescription
 			= tr::lng_rights_transfer_password_description(tr::now);
 		fields.customSubmitButton = tr::lng_passcode_submit();
 		fields.customCheckCallback = crl::guard(this, [=](
-				const Core::CloudPasswordResult &result) {
-			sendTransferRequestFrom(*box, channel, result);
+				const Core::CloudPasswordResult &result,
+				QPointer<PasscodeBox> box) {
+			sendTransferRequestFrom(box, channel, result);
 		});
-		*box = getDelegate()->show(Box<PasscodeBox>(
-			&channel->session(),
-			fields));
+		getDelegate()->show(Box<PasscodeBox>(&channel->session(), fields));
 	}, lifetime());
 }
 
diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_history_visibility_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_history_visibility_box.cpp
index 9086dfce8..8883ad0a5 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_history_visibility_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_history_visibility_box.cpp
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "lang/lang_keys.h"
 #include "ui/layers/generic_box.h"
+#include "ui/widgets/buttons.h"
 #include "ui/widgets/checkbox.h"
 #include "ui/widgets/labels.h"
 #include "styles/style_layers.h"
@@ -23,6 +24,17 @@ void EditPeerHistoryVisibilityBox(
 		Ui::RadioenumGroup<HistoryVisibility>
 	>(historyVisibilitySavedValue);
 
+	const auto addButton = [=](
+			not_null<Ui::RpWidget*> inner,
+			HistoryVisibility v) {
+		const auto button = Ui::CreateChild<Ui::AbstractButton>(inner.get());
+		inner->sizeValue(
+		) | rpl::start_with_next([=](const QSize &s) {
+			button->resize(s);
+		}, button->lifetime());
+		button->setClickedCallback([=] { historyVisibility->setValue(v); });
+	};
+
 	box->setTitle(tr::lng_manage_history_visibility_title());
 	box->addButton(tr::lng_settings_save(), [=] {
 		savedCallback(historyVisibility->current());
@@ -31,32 +43,36 @@ void EditPeerHistoryVisibilityBox(
 	box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
 
 	box->addSkip(st::editPeerHistoryVisibilityTopSkip);
-	box->addRow(object_ptr<Ui::Radioenum<HistoryVisibility>>(
+	const auto visible = box->addRow(object_ptr<Ui::VerticalLayout>(box));
+	visible->add(object_ptr<Ui::Radioenum<HistoryVisibility>>(
 		box,
 		historyVisibility,
 		HistoryVisibility::Visible,
 		tr::lng_manage_history_visibility_shown(tr::now),
 		st::defaultBoxCheckbox));
-	box->addRow(
+	visible->add(
 		object_ptr<Ui::FlatLabel>(
 			box,
 			tr::lng_manage_history_visibility_shown_about(),
 			st::editPeerPrivacyLabel),
-		st::editPeerPreHistoryLabelMargins + st::boxRowPadding);
+		st::editPeerPreHistoryLabelMargins);
+	addButton(visible, HistoryVisibility::Visible);
 
 	box->addSkip(st::editPeerHistoryVisibilityTopSkip);
-	box->addRow(object_ptr<Ui::Radioenum<HistoryVisibility>>(
+	const auto hidden = box->addRow(object_ptr<Ui::VerticalLayout>(box));
+	hidden->add(object_ptr<Ui::Radioenum<HistoryVisibility>>(
 		box,
 		historyVisibility,
 		HistoryVisibility::Hidden,
 		tr::lng_manage_history_visibility_hidden(tr::now),
 		st::defaultBoxCheckbox));
-	box->addRow(
+	hidden->add(
 		object_ptr<Ui::FlatLabel>(
 			box,
 			(isLegacy
 				? tr::lng_manage_history_visibility_hidden_legacy
 				: tr::lng_manage_history_visibility_hidden_about)(),
 			st::editPeerPrivacyLabel),
-		st::editPeerPreHistoryLabelMargins + st::boxRowPadding);
+		st::editPeerPreHistoryLabelMargins);
+	addButton(hidden, HistoryVisibility::Hidden);
 }
diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp
index 31faabec1..a1ee68248 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp
@@ -983,8 +983,8 @@ void Controller::fillHistoryVisibilityButton() {
 		: HistoryVisibility::Visible;
 	_channelHasLocationOriginalValue = channel && channel->hasLocation();
 
-	const auto updateHistoryVisibility =
-		std::make_shared<rpl::event_stream<HistoryVisibility>>();
+	const auto updateHistoryVisibility
+		= std::make_shared<rpl::event_stream<HistoryVisibility>>();
 
 	const auto boxCallback = crl::guard(this, [=](HistoryVisibility checked) {
 		updateHistoryVisibility->fire(std::move(checked));
@@ -1698,8 +1698,8 @@ void Controller::saveUsernamesOrder() {
 			channel->setUsernames(ranges::views::all(
 				newUsernames
 			) | ranges::views::transform([&](QString username) {
-				const auto editable =
-					(channel->editableUsername() == username);
+				const auto editable
+					= (channel->editableUsername() == username);
 				return Data::Username{
 					.username = std::move(username),
 					.active = true,
diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp
index 7b80e2652..8f67b6ba4 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp
@@ -227,9 +227,9 @@ QImage QrExact(const Qr::Data &data, int pixel, QColor color) {
 			p.drawImage(
 				skip,
 				skip,
-				Intro::details::TelegramLogoImage().scaled(
-					logoSize * style::DevicePixelRatio(),
-					logoSize * style::DevicePixelRatio(),
+				Window::LogoNoMargin().scaled(
+					logoSize,
+					logoSize,
 					Qt::IgnoreAspectRatio,
 					Qt::SmoothTransformation));
 		}
diff --git a/Telegram/SourceFiles/boxes/premium_preview_box.cpp b/Telegram/SourceFiles/boxes/premium_preview_box.cpp
index 67e402da5..da1ab9064 100644
--- a/Telegram/SourceFiles/boxes/premium_preview_box.cpp
+++ b/Telegram/SourceFiles/boxes/premium_preview_box.cpp
@@ -1240,8 +1240,8 @@ void DecorateListPromoBox(
 		box->setStyle(st::premiumPreviewDoubledLimitsBox);
 		box->widthValue(
 		) | rpl::start_with_next([=](int width) {
-			const auto &padding =
-				st::premiumPreviewDoubledLimitsBox.buttonPadding;
+			const auto &padding
+				= st::premiumPreviewDoubledLimitsBox.buttonPadding;
 			button->resizeToWidth(width
 				- padding.left()
 				- padding.right());
diff --git a/Telegram/SourceFiles/boxes/ringtones_box.cpp b/Telegram/SourceFiles/boxes/ringtones_box.cpp
index d8097222e..4c299c162 100644
--- a/Telegram/SourceFiles/boxes/ringtones_box.cpp
+++ b/Telegram/SourceFiles/boxes/ringtones_box.cpp
@@ -90,7 +90,7 @@ QString ExtractRingtoneName(not_null<DocumentData*> document) {
 	}
 	const auto name = document->filename();
 	if (!name.isEmpty()) {
-		const auto extension = Data::FileExtension(name);
+		const auto extension = Core::FileExtension(name);
 		if (extension.isEmpty()) {
 			return name;
 		} else if (name.size() > extension.size() + 1) {
diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp
index 9e30ebded..09efc7dfe 100644
--- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp
+++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp
@@ -611,8 +611,8 @@ void StickerSetBox::updateButtons() {
 
 			if (!_inner->shortName().isEmpty()) {
 				const auto top = addTopButton(st::infoTopBarMenu);
-				const auto menu =
-					std::make_shared<base::unique_qptr<Ui::PopupMenu>>();
+				const auto menu
+					= std::make_shared<base::unique_qptr<Ui::PopupMenu>>();
 				top->setClickedCallback([=] {
 					*menu = base::make_unique_q<Ui::PopupMenu>(
 						top,
@@ -656,8 +656,8 @@ void StickerSetBox::updateButtons() {
 						_show->showBox(std::move(box));
 					}
 				};
-				const auto menu =
-					std::make_shared<base::unique_qptr<Ui::PopupMenu>>();
+				const auto menu
+					= std::make_shared<base::unique_qptr<Ui::PopupMenu>>();
 				top->setClickedCallback([=] {
 					*menu = base::make_unique_q<Ui::PopupMenu>(
 						top,
diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp
index c3cbe68d6..d30c49a04 100644
--- a/Telegram/SourceFiles/calls/calls_call.cpp
+++ b/Telegram/SourceFiles/calls/calls_call.cpp
@@ -945,8 +945,8 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
 	tgcalls::Descriptor descriptor = {
 		.version = versionString,
 		.config = tgcalls::Config{
-			.initializationTimeout =
-				serverConfig.callConnectTimeoutMs / 1000.,
+			.initializationTimeout
+				= serverConfig.callConnectTimeoutMs / 1000.,
 			.receiveTimeout = serverConfig.callPacketTimeoutMs / 1000.,
 			.dataSaving = tgcalls::DataSaving::Never,
 			.enableP2P = call.is_p2p_allowed(),
diff --git a/Telegram/SourceFiles/calls/calls_call.h b/Telegram/SourceFiles/calls/calls_call.h
index 55f48237a..0a99c8067 100644
--- a/Telegram/SourceFiles/calls/calls_call.h
+++ b/Telegram/SourceFiles/calls/calls_call.h
@@ -277,11 +277,11 @@ private:
 	MTP::Sender _api;
 	Type _type = Type::Outgoing;
 	rpl::variable<State> _state = State::Starting;
-	rpl::variable<RemoteAudioState> _remoteAudioState =
-		RemoteAudioState::Active;
+	rpl::variable<RemoteAudioState> _remoteAudioState
+		= RemoteAudioState::Active;
 	rpl::variable<Webrtc::VideoState> _remoteVideoState;
-	rpl::variable<RemoteBatteryState> _remoteBatteryState =
-		RemoteBatteryState::Normal;
+	rpl::variable<RemoteBatteryState> _remoteBatteryState
+		= RemoteBatteryState::Normal;
 	rpl::event_stream<Error> _errors;
 	FinishType _finishAfterRequestingCall = FinishType::None;
 	bool _answerAfterDhConfigReceived = false;
diff --git a/Telegram/SourceFiles/calls/calls_panel.h b/Telegram/SourceFiles/calls/calls_panel.h
index f6c8666d8..11c6dceae 100644
--- a/Telegram/SourceFiles/calls/calls_panel.h
+++ b/Telegram/SourceFiles/calls/calls_panel.h
@@ -162,8 +162,8 @@ private:
 	object_ptr<Ui::FlatLabel> _status;
 	object_ptr<Ui::RpWidget> _fingerprint = { nullptr };
 	object_ptr<Ui::PaddingWrap<Ui::FlatLabel>> _remoteAudioMute = { nullptr };
-	object_ptr<Ui::PaddingWrap<Ui::FlatLabel>> _remoteLowBattery =
-		{ nullptr };
+	object_ptr<Ui::PaddingWrap<Ui::FlatLabel>> _remoteLowBattery
+		= { nullptr };
 	std::unique_ptr<Userpic> _userpic;
 	std::unique_ptr<VideoBubble> _outgoingVideoBubble;
 	QPixmap _bottomShadow;
diff --git a/Telegram/SourceFiles/calls/group/calls_volume_item.cpp b/Telegram/SourceFiles/calls/group/calls_volume_item.cpp
index 5942a97e7..d34a51060 100644
--- a/Telegram/SourceFiles/calls/group/calls_volume_item.cpp
+++ b/Telegram/SourceFiles/calls/group/calls_volume_item.cpp
@@ -25,8 +25,8 @@ const auto kSpeakerThreshold = std::vector<float>{
 	50.0f / kMaxVolumePercent,
 	150.0f / kMaxVolumePercent };
 
-constexpr auto kVolumeStickedValues =
-	std::array<std::pair<float64, float64>, 7>{{
+constexpr auto kVolumeStickedValues
+	= std::array<std::pair<float64, float64>, 7>{{
 		{ 25. / kMaxVolumePercent, 2. / kMaxVolumePercent },
 		{ 50. / kMaxVolumePercent, 2. / kMaxVolumePercent },
 		{ 75. / kMaxVolumePercent, 2. / kMaxVolumePercent },
@@ -93,8 +93,8 @@ MenuVolumeItem::MenuVolumeItem(
 		const auto volume = _localMuted
 			? 0
 			: base::SafeRound(_slider->value() * kMaxVolumePercent);
-		const auto muteProgress =
-			_crossLineAnimation.value((!volume) ? 1. : 0.);
+		const auto muteProgress
+			= _crossLineAnimation.value((!volume) ? 1. : 0.);
 
 		const auto selected = isSelected();
 		p.fillRect(clip, selected ? st.itemBgOver : st.itemBg);
@@ -174,8 +174,8 @@ MenuVolumeItem::MenuVolumeItem(
 			return;
 		}
 		if (_waitingForUpdateVolume) {
-			const auto localVolume =
-				base::SafeRound(_slider->value() * _maxVolume);
+			const auto localVolume
+				= base::SafeRound(_slider->value() * _maxVolume);
 			if ((localVolume != newVolume)
 				&& (_cloudVolume == newVolume)) {
 				_changeVolumeRequests.fire(int(localVolume));
diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp
index e82ab9b9f..9bc6e88f2 100644
--- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp
+++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp
@@ -437,8 +437,8 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
 
 		auto filterNotPassedByUsername = [this](UserData *user) -> bool {
 			if (PrimaryUsername(user).startsWith(_filter, Qt::CaseInsensitive)) {
-				const auto exactUsername =
-					(PrimaryUsername(user).size() == _filter.size());
+				const auto exactUsername
+					= (PrimaryUsername(user).size() == _filter.size());
 				return exactUsername;
 			}
 			return true;
@@ -446,8 +446,9 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
 		auto filterNotPassedByName = [&](UserData *user) -> bool {
 			for (const auto &nameWord : user->nameWords()) {
 				if (nameWord.startsWith(_filter, Qt::CaseInsensitive)) {
-					const auto exactUsername =
-						(PrimaryUsername(user).compare(_filter, Qt::CaseInsensitive) == 0);
+					const auto exactUsername = PrimaryUsername(user).compare(
+						_filter,
+						Qt::CaseInsensitive) == 0;
 					return exactUsername;
 				}
 			}
diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp
index 9e3997a42..6f82c252e 100644
--- a/Telegram/SourceFiles/core/application.cpp
+++ b/Telegram/SourceFiles/core/application.cpp
@@ -933,8 +933,8 @@ void Application::handleAppDeactivated() {
 }
 
 rpl::producer<bool> Application::appDeactivatedValue() const {
-	const auto &app =
-		static_cast<QGuiApplication*>(QCoreApplication::instance());
+	const auto &app
+		= static_cast<QGuiApplication*>(QCoreApplication::instance());
 	return rpl::single(
 		app->applicationState()
 	) | rpl::then(
diff --git a/Telegram/SourceFiles/core/core_cloud_password.cpp b/Telegram/SourceFiles/core/core_cloud_password.cpp
index 3a65f24d5..a835112dc 100644
--- a/Telegram/SourceFiles/core/core_cloud_password.cpp
+++ b/Telegram/SourceFiles/core/core_cloud_password.cpp
@@ -313,8 +313,8 @@ CloudPasswordState ParseCloudPasswordState(
 		ParseCloudPasswordAlgo(data.vnew_algo()));
 	result.mtp.newSecureSecret = ValidateNewSecureSecretAlgo(
 		ParseSecureSecretAlgo(data.vnew_secure_algo()));
-	result.unconfirmedPattern =
-		qs(data.vemail_unconfirmed_pattern().value_or_empty());
+	result.unconfirmedPattern = qs(
+		data.vemail_unconfirmed_pattern().value_or_empty());
 	result.pendingResetDate = data.vpending_reset_date().value_or_empty();
 
 	result.outdatedClient = [&] {
diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp
index bbff44551..c8297e668 100644
--- a/Telegram/SourceFiles/core/core_settings.cpp
+++ b/Telegram/SourceFiles/core/core_settings.cpp
@@ -160,6 +160,10 @@ QByteArray Settings::serialize() const {
 	const auto &recentEmojiPreloadData = _recentEmojiPreload.empty()
 		? recentEmojiPreloadGenerated
 		: _recentEmojiPreload;
+	const auto noWarningExtensions = QStringList(
+		begin(_noWarningExtensions),
+		end(_noWarningExtensions)
+	).join(' ');
 
 	auto size = Serialize::bytearraySize(themesAccentColors)
 		+ sizeof(qint32) * 5
@@ -216,7 +220,8 @@ QByteArray Settings::serialize() const {
 		+ Serialize::stringSize(_captureDeviceId.current())
 		+ Serialize::stringSize(_callPlaybackDeviceId.current())
 		+ Serialize::stringSize(_callCaptureDeviceId.current())
-		+ Serialize::bytearraySize(ivPosition);
+		+ Serialize::bytearraySize(ivPosition)
+		+ Serialize::stringSize(noWarningExtensions);
 
 	auto result = QByteArray();
 	result.reserve(size);
@@ -256,7 +261,7 @@ QByteArray Settings::serialize() const {
 			<< qint32(_sendSubmitWay)
 			<< qint32(_includeMutedCounter ? 1 : 0)
 			<< qint32(_countUnreadMessages ? 1 : 0)
-			<< qint32(_exeLaunchWarning ? 1 : 0)
+			<< qint32(1) // legacy exe launch warning
 			<< qint32(_notifyAboutPinned.current() ? 1 : 0)
 			<< qint32(_loopAnimatedStickers ? 1 : 0)
 			<< qint32(_largeEmoji.current() ? 1 : 0)
@@ -361,7 +366,8 @@ QByteArray Settings::serialize() const {
 			<< _captureDeviceId.current()
 			<< _callPlaybackDeviceId.current()
 			<< _callCaptureDeviceId.current()
-			<< ivPosition;
+			<< ivPosition
+			<< noWarningExtensions;
 	}
 
 	Ensures(result.size() == size);
@@ -412,7 +418,8 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
 	qint32 sendSubmitWay = static_cast<qint32>(_sendSubmitWay);
 	qint32 includeMutedCounter = _includeMutedCounter ? 1 : 0;
 	qint32 countUnreadMessages = _countUnreadMessages ? 1 : 0;
-	qint32 exeLaunchWarning = _exeLaunchWarning ? 1 : 0;
+	std::optional<QString> noWarningExtensions;
+	qint32 legacyExeLaunchWarning = 1;
 	qint32 notifyAboutPinned = _notifyAboutPinned.current() ? 1 : 0;
 	qint32 loopAnimatedStickers = _loopAnimatedStickers ? 1 : 0;
 	qint32 largeEmoji = _largeEmoji.current() ? 1 : 0;
@@ -519,7 +526,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
 			>> sendSubmitWay
 			>> includeMutedCounter
 			>> countUnreadMessages
-			>> exeLaunchWarning
+			>> legacyExeLaunchWarning
 			>> notifyAboutPinned
 			>> loopAnimatedStickers
 			>> largeEmoji
@@ -761,6 +768,10 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
 	if (!stream.atEnd()) {
 		stream >> ivPosition;
 	}
+	if (!stream.atEnd()) {
+		noWarningExtensions = QString();
+		stream >> *noWarningExtensions;
+	}
 	if (stream.status() != QDataStream::Ok) {
 		LOG(("App Error: "
 			"Bad data for Core::Settings::constructFromSerialized()"));
@@ -824,7 +835,12 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
 	}
 	_includeMutedCounter = (includeMutedCounter == 1);
 	_countUnreadMessages = (countUnreadMessages == 1);
-	_exeLaunchWarning = (exeLaunchWarning == 1);
+	if (noWarningExtensions) {
+		const auto list = noWarningExtensions->mid(0, 10240)
+			.split(' ', Qt::SkipEmptyParts)
+			.mid(0, 1024);
+		_noWarningExtensions = base::flat_set<QString>(list.begin(), list.end());
+	}
 	_ipRevealWarning = (ipRevealWarning == 1);
 	_notifyAboutPinned = (notifyAboutPinned == 1);
 	_loopAnimatedStickers = (loopAnimatedStickers == 1);
@@ -1290,7 +1306,7 @@ void Settings::resetOnLastLogout() {
 	//_sendSubmitWay = Ui::InputSubmitSettings::Enter;
 	_soundOverrides = {};
 
-	_exeLaunchWarning = true;
+	_noWarningExtensions.clear();
 	_ipRevealWarning = true;
 	_loopAnimatedStickers = true;
 	_largeEmoji = true;
diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h
index bc40e39e0..e1ff616ec 100644
--- a/Telegram/SourceFiles/core/core_settings.h
+++ b/Telegram/SourceFiles/core/core_settings.h
@@ -404,11 +404,12 @@ public:
 	}
 	[[nodiscard]] QString getSoundPath(const QString &key) const;
 
-	[[nodiscard]] bool exeLaunchWarning() const {
-		return _exeLaunchWarning;
+	[[nodiscard]] auto noWarningExtensions() const
+	-> const base::flat_set<QString> & {
+		return _noWarningExtensions;
 	}
-	void setExeLaunchWarning(bool warning) {
-		_exeLaunchWarning = warning;
+	void setNoWarningExtensions(base::flat_set<QString> extensions) {
+		_noWarningExtensions = std::move(extensions);
 	}
 	[[nodiscard]] bool ipRevealWarning() const {
 		return _ipRevealWarning;
@@ -939,7 +940,7 @@ private:
 	Ui::SendFilesWay _sendFilesWay = Ui::SendFilesWay();
 	Ui::InputSubmitSettings _sendSubmitWay = Ui::InputSubmitSettings();
 	base::flat_map<QString, QString> _soundOverrides;
-	bool _exeLaunchWarning = true;
+	base::flat_set<QString> _noWarningExtensions;
 	bool _ipRevealWarning = true;
 	bool _loopAnimatedStickers = true;
 	rpl::variable<bool> _largeEmoji = true;
@@ -990,8 +991,8 @@ private:
 #else // Q_OS_MAC
 	bool _hardwareAcceleratedVideo = false;
 #endif // Q_OS_MAC
-	HistoryView::DoubleClickQuickAction _chatQuickAction =
-		HistoryView::DoubleClickQuickAction();
+	HistoryView::DoubleClickQuickAction _chatQuickAction
+		= HistoryView::DoubleClickQuickAction();
 	bool _translateButtonEnabled = false;
 	rpl::variable<bool> _translateChatEnabled = true;
 	rpl::variable<int> _translateToRaw = 0;
diff --git a/Telegram/SourceFiles/core/mime_type.cpp b/Telegram/SourceFiles/core/mime_type.cpp
index 8ba1b6494..7467145cf 100644
--- a/Telegram/SourceFiles/core/mime_type.cpp
+++ b/Telegram/SourceFiles/core/mime_type.cpp
@@ -37,6 +37,12 @@ namespace {
 		&& data->hasImage();
 }
 
+[[nodiscard]] base::flat_set<QString> SplitExtensions(
+		const QString &joined) {
+	const auto list = joined.split(' ');
+	return base::flat_set<QString>(list.begin(), list.end());
+}
+
 } // namespace
 
 MimeType::MimeType(const QMimeType &type) : _typeStruct(type) {
@@ -162,22 +168,9 @@ bool IsMimeAcceptedForPhotoVideoAlbum(const QString &mime) {
 }
 
 bool FileIsImage(const QString &name, const QString &mime) {
-	QString lowermime = mime.toLower(), namelower = name.toLower();
-	if (lowermime.startsWith(u"image/"_q)) {
-		return true;
-	} else if (namelower.endsWith(u".bmp"_q)
-		|| namelower.endsWith(u".jpg"_q)
-		|| namelower.endsWith(u".jpeg"_q)
-		|| namelower.endsWith(u".gif"_q)
-		|| namelower.endsWith(u".webp"_q)
-		|| namelower.endsWith(u".tga"_q)
-		|| namelower.endsWith(u".tiff"_q)
-		|| namelower.endsWith(u".tif"_q)
-		|| namelower.endsWith(u".psd"_q)
-		|| namelower.endsWith(u".png"_q)) {
-		return true;
-	}
-	return false;
+	return name.isEmpty()
+		? mime.toLower().startsWith(u"image/"_q)
+		: (DetectNameType(name) == NameType::Image);
 }
 
 std::shared_ptr<QMimeData> ShareMimeMediaData(
@@ -194,10 +187,10 @@ std::shared_ptr<QMimeData> ShareMimeMediaData(
 		result->setData(u"application/x-td-use-jpeg"_q, "1");
 		result->setData(u"image/jpeg"_q, original->data(u"image/jpeg"_q));
 	}
-	if (auto list = Core::ReadMimeUrls(original); !list.isEmpty()) {
+	if (auto list = ReadMimeUrls(original); !list.isEmpty()) {
 		result->setUrls(std::move(list));
 	}
-	result->setText(Core::ReadMimeText(original));
+	result->setText(ReadMimeText(original));
 	return result;
 }
 
@@ -240,4 +233,116 @@ bool CanSendFiles(not_null<const QMimeData*> data) {
 	return false;
 }
 
+QString FileExtension(const QString &filepath) {
+	const auto reversed = ranges::views::reverse(filepath);
+	const auto last = ranges::find_first_of(reversed, ".\\/");
+	if (last == reversed.end() || *last != '.') {
+		return QString();
+	}
+	return QString(last.base(), last - reversed.begin());
+}
+
+NameType DetectNameType(const QString &filepath) {
+	static const auto kImage = SplitExtensions(u"\
+afdesign ai avif bmp dng gif heic icns ico jfif jpeg jpg jpg-large nef png \
+png-large psd raw sketch svg tga tif tiff webp"_q);
+	static const auto kVideo = SplitExtensions(u"\
+3g2 3gp 3gpp aep avi flv h264 m4s m4v mkv mov mp4 mpeg mpg ogv srt tgs tgv \
+vob webm wmv"_q);
+	static const auto kAudio = SplitExtensions(u"\
+aac ac3 aif amr caf cda cue flac m4a m4b mid midi mp3 ogg opus wav wma"_q);
+	static const auto kDocument = SplitExtensions(u"\
+pdf doc docx ppt pptx pps ppsx xls xlsx txt rtf odt ods odp csv text log tl \
+tex xspf xml djvu diag ps ost kml pub epub mobi cbr cbz fb2 prc ris pem p7b \
+m3u m3u8 wpd wpl htm html xhtml key"_q);
+	static const auto kArchive = SplitExtensions(u"\
+7z arj bz2 gz rar tar xz z zip zst"_q);
+	static const auto kThemeFile = SplitExtensions(u"\
+tdesktop-theme tdesktop-palette tgios-theme attheme"_q);
+	static const auto kOtherBenign = SplitExtensions(u"\
+c cc cpp cxx h m mm swift cs ts class java css ninja cmake patch diff plist \
+gyp gitignore strings asoundrc torrent csr json xaml md keylayout sql \
+sln xib mk \
+\
+dmg img iso vcd \
+\
+pdb eot ics ips ipa core mem pcap ovpn part pcapng dmp pkpass dat zxp crash \
+file bak gbr plain dlc fon fnt otf ttc ttf gpx db rss cur \
+\
+tdesktop-endpoints"_q);
+
+	static const auto kExecutable = SplitExtensions(
+#ifdef Q_OS_WIN
+		u"\
+ad ade adp ahk app application appref-ms asp aspx asx bas bat bin cab cdxml \
+cer cfg cgi chi chm cmd cnt com conf cpl crt csh der diagcab dll drv eml \
+exe fon fxp gadget grp hlp hpj hta htt inf ini ins inx isp isu its jar jnlp \
+job js jse jsp key ksh lexe library-ms lnk local lua mad maf mag mam \
+manifest maq mar mas mat mau mav maw mcf mda mdb mde mdt mdw mdz mht mhtml \
+mjs mmc mof msc msg msh msh1 msh2 msh1xml msh2xml mshxml msi msp mst ops \
+osd paf pcd phar php php3 php4 php5 php7 phps php-s pht phtml pif pl plg pm \
+pod prf prg ps1 ps2 ps1xml ps2xml psc1 psc2 psd1 psm1 pssc pst py py3 pyc \
+pyd pyi pyo pyw pyzw pyz rb reg rgs scf scr sct search-ms settingcontent-ms \
+sh shb shs slk sys swf t tmp u3p url vb vbe vbp vbs vbscript vdx vsmacros \
+vsd vsdm vsdx vss vssm vssx vst vstm vstx vsw vsx vtx website wlua ws wsc \
+wsf wsh xbap xll xlsm xnk xs"_q
+#elif defined Q_OS_MAC // Q_OS_MAC
+		u"\
+applescript action app bin command csh osx workflow terminal url caction \
+mpkg pkg scpt scptd xhtm xhtml webarchive"_q
+#else // Q_OS_WIN || Q_OS_MAC
+		u"bin csh deb desktop ksh out pet pkg pup rpm run sh shar slp zsh"_q
+#endif // !Q_OS_WIN && !Q_OS_MAC
+	);
+
+	const auto extension = FileExtension(filepath).toLower();
+	if (kExecutable.contains(extension)) {
+		return NameType::Executable;
+	} else if (kImage.contains(extension)) {
+		return NameType::Image;
+	} else if (kVideo.contains(extension)) {
+		return NameType::Video;
+	} else if (kAudio.contains(extension)) {
+		return NameType::Audio;
+	} else if (kDocument.contains(extension)) {
+		return NameType::Document;
+	} else if (kArchive.contains(extension)) {
+		return NameType::Archive;
+	} else if (kThemeFile.contains(extension)) {
+		return NameType::ThemeFile;
+	} else if (kOtherBenign.contains(extension)) {
+		return NameType::OtherBenign;
+	}
+	return NameType::Unknown;
+}
+
+bool NameTypeAllowsThumbnail(NameType type) {
+	return type == NameType::Image
+		|| type == NameType::Video
+		|| type == NameType::Audio
+		|| type == NameType::Document
+		|| type == NameType::ThemeFile;
+}
+
+bool IsIpRevealingPath(const QString &filepath) {
+	static const auto kExtensions = [] {
+		const auto joined = u"htm html svg m4v m3u8 xhtml"_q;
+		const auto list = joined.split(' ');
+		return base::flat_set<QString>(list.begin(), list.end());
+	}();
+	static const auto kMimeTypes = [] {
+		const auto joined = u"text/html image/svg+xml"_q;
+		const auto list = joined.split(' ');
+		return base::flat_set<QString>(list.begin(), list.end());
+	}();
+
+	return ranges::binary_search(
+		kExtensions,
+		FileExtension(filepath).toLower()
+	) || ranges::binary_search(
+		kMimeTypes,
+		QMimeDatabase().mimeTypeForFile(QFileInfo(filepath)).name()
+	);
+}
+
 } // namespace Core
diff --git a/Telegram/SourceFiles/core/mime_type.h b/Telegram/SourceFiles/core/mime_type.h
index 3271adafe..ebf4db64b 100644
--- a/Telegram/SourceFiles/core/mime_type.h
+++ b/Telegram/SourceFiles/core/mime_type.h
@@ -69,4 +69,21 @@ struct MimeImageData {
 [[nodiscard]] QList<QUrl> ReadMimeUrls(not_null<const QMimeData*> data);
 [[nodiscard]] bool CanSendFiles(not_null<const QMimeData*> data);
 
+enum class NameType : uchar {
+	Unknown,
+	Executable,
+	Image,
+	Video,
+	Audio,
+	Document,
+	Archive,
+	ThemeFile,
+	OtherBenign,
+};
+
+[[nodiscard]] QString FileExtension(const QString &filepath);
+[[nodiscard]] NameType DetectNameType(const QString &filepath);
+[[nodiscard]] bool NameTypeAllowsThumbnail(NameType type);
+[[nodiscard]] bool IsIpRevealingPath(const QString &filepath);
+
 } // namespace Core
diff --git a/Telegram/SourceFiles/core/sandbox.cpp b/Telegram/SourceFiles/core/sandbox.cpp
index 6feacdb5b..3daf91784 100644
--- a/Telegram/SourceFiles/core/sandbox.cpp
+++ b/Telegram/SourceFiles/core/sandbox.cpp
@@ -82,7 +82,6 @@ bool Sandbox::QuitOnStartRequested = false;
 Sandbox::Sandbox(int &argc, char **argv)
 : QApplication(argc, argv)
 , _mainThreadId(QThread::currentThreadId()) {
-	setQuitOnLastWindowClosed(false);
 }
 
 int Sandbox::start() {
diff --git a/Telegram/SourceFiles/core/sandbox.h b/Telegram/SourceFiles/core/sandbox.h
index 4c15c2828..dfb1fe4a6 100644
--- a/Telegram/SourceFiles/core/sandbox.h
+++ b/Telegram/SourceFiles/core/sandbox.h
@@ -107,6 +107,7 @@ private:
 	void readClients();
 	void removeClients();
 
+	QEventLoopLocker _eventLoopLocker;
 	const Qt::HANDLE _mainThreadId = nullptr;
 	int _eventNestingLevel = 0;
 	int _loopNestingLevel = 0;
diff --git a/Telegram/SourceFiles/core/shortcuts.cpp b/Telegram/SourceFiles/core/shortcuts.cpp
index 08d8cc59b..43ccc4f8a 100644
--- a/Telegram/SourceFiles/core/shortcuts.cpp
+++ b/Telegram/SourceFiles/core/shortcuts.cpp
@@ -230,8 +230,24 @@ void WriteDefaultCustomFile() {
 	const auto path = CustomFilePath();
 	auto input = QFile(":/misc/default_shortcuts-custom.json");
 	auto output = QFile(path);
-	if (input.open(QIODevice::ReadOnly) && output.open(QIODevice::WriteOnly)) {
+	if (input.open(QIODevice::ReadOnly)
+		&& output.open(QIODevice::WriteOnly)) {
+#ifdef Q_OS_MAC
+		auto text = qs(input.readAll());
+		const auto note = R"(
+// Note:
+// On Apple platforms, reference to "ctrl" corresponds to the Command keys )"
+			+ QByteArray()
+			+ R"(on the Macintosh keyboard.
+// On Apple platforms, reference to "meta" corresponds to the Control keys.
+
+[
+)";
+		text.replace(u"\n\n["_q, QString(note));
+		output.write(text.toUtf8());
+#else
 		output.write(input.readAll());
+#endif // !Q_OS_MAC
 	}
 }
 
diff --git a/Telegram/SourceFiles/core/ui_integration.cpp b/Telegram/SourceFiles/core/ui_integration.cpp
index 5f0c200a7..75a4d9e8a 100644
--- a/Telegram/SourceFiles/core/ui_integration.cpp
+++ b/Telegram/SourceFiles/core/ui_integration.cpp
@@ -13,9 +13,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "core/application.h"
 #include "core/sandbox.h"
 #include "core/click_handler_types.h"
+#include "data/components/sponsored_messages.h"
 #include "data/stickers/data_custom_emoji.h"
 #include "data/data_session.h"
-#include "data/data_sponsored_messages.h"
 #include "iv/iv_instance.h"
 #include "ui/text/text_custom_emoji.h"
 #include "ui/basic_click_handlers.h"
@@ -303,7 +303,7 @@ bool UiIntegration::allowClickHandlerActivation(
 		const ClickContext &context) {
 	const auto my = context.other.value<ClickHandlerContext>();
 	if (const auto window = my.sessionWindow.get()) {
-		window->session().data().sponsoredMessages().clicked(my.itemId);
+		window->session().sponsoredMessages().clicked(my.itemId);
 	}
 	return true;
 }
diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h
index ffbc3b76c..4230495a6 100644
--- a/Telegram/SourceFiles/core/version.h
+++ b/Telegram/SourceFiles/core/version.h
@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D666}"_cs;
 constexpr auto AppNameOld = "AyuGram for Windows"_cs;
 constexpr auto AppName = "AyuGram Desktop"_cs;
 constexpr auto AppFile = "AyuGram"_cs;
-constexpr auto AppVersion = 4016006;
-constexpr auto AppVersionStr = "4.16.6";
+constexpr auto AppVersion = 4016008;
+constexpr auto AppVersionStr = "4.16.8";
 constexpr auto AppBetaVersion = false;
 constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
diff --git a/Telegram/SourceFiles/data/data_scheduled_messages.cpp b/Telegram/SourceFiles/data/components/scheduled_messages.cpp
similarity index 98%
rename from Telegram/SourceFiles/data/data_scheduled_messages.cpp
rename to Telegram/SourceFiles/data/components/scheduled_messages.cpp
index ffe4ab436..92e0da880 100644
--- a/Telegram/SourceFiles/data/data_scheduled_messages.cpp
+++ b/Telegram/SourceFiles/data/components/scheduled_messages.cpp
@@ -5,7 +5,7 @@ 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_scheduled_messages.h"
+#include "data/components/scheduled_messages.h"
 
 #include "base/unixtime.h"
 #include "data/data_forum_topic.h"
@@ -101,10 +101,10 @@ bool IsScheduledMsgId(MsgId id) {
 	return (id > ServerMaxMsgId) && (id < ScheduledMaxMsgId);
 }
 
-ScheduledMessages::ScheduledMessages(not_null<Session*> owner)
-: _session(&owner->session())
+ScheduledMessages::ScheduledMessages(not_null<Main::Session*> session)
+: _session(session)
 , _clearTimer([=] { clearOldRequests(); }) {
-	owner->itemRemoved(
+	_session->data().itemRemoved(
 	) | rpl::filter([](not_null<const HistoryItem*> item) {
 		return item->isScheduled();
 	}) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
@@ -113,9 +113,16 @@ ScheduledMessages::ScheduledMessages(not_null<Session*> owner)
 }
 
 ScheduledMessages::~ScheduledMessages() {
-	for (const auto &request : _requests) {
+	Expects(_data.empty());
+	Expects(_requests.empty());
+}
+
+void ScheduledMessages::clear() {
+	_lifetime.destroy();
+	for (const auto &request : base::take(_requests)) {
 		_session->api().request(request.second.requestId).cancel();
 	}
+	base::take(_data);
 }
 
 void ScheduledMessages::clearOldRequests() {
diff --git a/Telegram/SourceFiles/data/data_scheduled_messages.h b/Telegram/SourceFiles/data/components/scheduled_messages.h
similarity index 97%
rename from Telegram/SourceFiles/data/data_scheduled_messages.h
rename to Telegram/SourceFiles/data/components/scheduled_messages.h
index 5daacd099..90d5b0415 100644
--- a/Telegram/SourceFiles/data/data_scheduled_messages.h
+++ b/Telegram/SourceFiles/data/components/scheduled_messages.h
@@ -18,14 +18,13 @@ class Session;
 
 namespace Data {
 
-class Session;
 struct MessagesSlice;
 
 [[nodiscard]] bool IsScheduledMsgId(MsgId id);
 
 class ScheduledMessages final {
 public:
-	explicit ScheduledMessages(not_null<Session*> owner);
+	explicit ScheduledMessages(not_null<Main::Session*> session);
 	ScheduledMessages(const ScheduledMessages &other) = delete;
 	ScheduledMessages &operator=(const ScheduledMessages &other) = delete;
 	~ScheduledMessages();
@@ -56,6 +55,8 @@ public:
 	[[nodiscard]] Data::MessagesSlice list(
 		not_null<const Data::ForumTopic*> topic) const;
 
+	void clear();
+
 private:
 	using OwnedItem = std::unique_ptr<HistoryItem, HistoryItem::Destroyer>;
 	struct List {
diff --git a/Telegram/SourceFiles/data/data_sponsored_messages.cpp b/Telegram/SourceFiles/data/components/sponsored_messages.cpp
similarity index 99%
rename from Telegram/SourceFiles/data/data_sponsored_messages.cpp
rename to Telegram/SourceFiles/data/components/sponsored_messages.cpp
index c22cce33a..23d4e1e45 100644
--- a/Telegram/SourceFiles/data/data_sponsored_messages.cpp
+++ b/Telegram/SourceFiles/data/components/sponsored_messages.cpp
@@ -5,7 +5,7 @@ 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_sponsored_messages.h"
+#include "data/components/sponsored_messages.h"
 
 #include "api/api_text_entities.h"
 #include "apiwrap.h"
@@ -34,8 +34,8 @@ constexpr auto kRequestTimeLimit = 5 * 60 * crl::time(1000);
 
 } // namespace
 
-SponsoredMessages::SponsoredMessages(not_null<Session*> owner)
-: _session(&owner->session())
+SponsoredMessages::SponsoredMessages(not_null<Main::Session*> session)
+: _session(session)
 , _clearTimer([=] { clearOldRequests(); }) {
 }
 
diff --git a/Telegram/SourceFiles/data/data_sponsored_messages.h b/Telegram/SourceFiles/data/components/sponsored_messages.h
similarity index 98%
rename from Telegram/SourceFiles/data/data_sponsored_messages.h
rename to Telegram/SourceFiles/data/components/sponsored_messages.h
index cad8a6134..022ccd19e 100644
--- a/Telegram/SourceFiles/data/data_sponsored_messages.h
+++ b/Telegram/SourceFiles/data/components/sponsored_messages.h
@@ -20,8 +20,6 @@ class Session;
 
 namespace Data {
 
-class Session;
-
 struct SponsoredReportResult final {
 	using Id = QByteArray;
 	struct Option final {
@@ -89,7 +87,7 @@ public:
 		bool canReport = false;
 	};
 	using RandomId = QByteArray;
-	explicit SponsoredMessages(not_null<Session*> owner);
+	explicit SponsoredMessages(not_null<Main::Session*> session);
 	SponsoredMessages(const SponsoredMessages &other) = delete;
 	SponsoredMessages &operator=(const SponsoredMessages &other) = delete;
 	~SponsoredMessages();
diff --git a/Telegram/SourceFiles/data/data_cloud_file.cpp b/Telegram/SourceFiles/data/data_cloud_file.cpp
index bee90358a..526dd8904 100644
--- a/Telegram/SourceFiles/data/data_cloud_file.cpp
+++ b/Telegram/SourceFiles/data/data_cloud_file.cpp
@@ -22,6 +22,14 @@ CloudFile::~CloudFile() {
 	base::take(loader);
 }
 
+void CloudFile::clear() {
+	location = {};
+	base::take(loader);
+	byteSize = 0;
+	progressivePartSize = 0;
+	flags = {};
+}
+
 CloudImage::CloudImage() = default;
 
 CloudImage::CloudImage(
diff --git a/Telegram/SourceFiles/data/data_cloud_file.h b/Telegram/SourceFiles/data/data_cloud_file.h
index e71cbe462..b44a35c7d 100644
--- a/Telegram/SourceFiles/data/data_cloud_file.h
+++ b/Telegram/SourceFiles/data/data_cloud_file.h
@@ -37,6 +37,8 @@ struct CloudFile final {
 
 	~CloudFile();
 
+	void clear();
+
 	ImageLocation location;
 	std::unique_ptr<FileLoader> loader;
 	int byteSize = 0;
diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp
index afe91b8a1..9c25a7861 100644
--- a/Telegram/SourceFiles/data/data_document.cpp
+++ b/Telegram/SourceFiles/data/data_document.cpp
@@ -478,6 +478,31 @@ void DocumentData::setattributes(
 		_additional = nullptr;
 	}
 
+	if (!_filename.isEmpty()) {
+		using Type = Core::NameType;
+		if (type == VideoDocument
+			|| type == AnimatedDocument
+			|| type == RoundVideoDocument
+			|| isAnimation()) {
+			if (!enforceNameType(Type::Video)) {
+				type = FileDocument;
+				_additional = nullptr;
+			}
+		}
+		if (type == SongDocument || type == VoiceDocument || isAudioFile()) {
+			if (!enforceNameType(Type::Audio)) {
+				type = FileDocument;
+				_additional = nullptr;
+			}
+		}
+		if (!Core::NameTypeAllowsThumbnail(_nameType)) {
+			_inlineThumbnailBytes = {};
+			_flags &= ~Flag::InlineThumbnailIsPath;
+			_thumbnail.clear();
+			_videoThumbnail.clear();
+		}
+	}
+
 	if (isAudioFile()
 		|| isAnimation()
 		|| isVoiceMessage()
@@ -530,6 +555,10 @@ void DocumentData::updateThumbnails(
 		const ImageWithLocation &thumbnail,
 		const ImageWithLocation &videoThumbnail,
 		bool isPremiumSticker) {
+	if (!_filename.isEmpty()
+		&& !Core::NameTypeAllowsThumbnail(Core::DetectNameType(_filename))) {
+		return;
+	}
 	if (!inlineThumbnail.bytes.isEmpty()
 		&& _inlineThumbnailBytes.isEmpty()) {
 		_inlineThumbnailBytes = inlineThumbnail.bytes;
@@ -919,6 +948,25 @@ void DocumentData::setFileName(const QString &remoteFileName) {
 	for (const auto &ch : controls) {
 		_filename = std::move(_filename).replace(ch, "_");
 	}
+	_nameType = Core::DetectNameType(_filename);
+}
+
+bool DocumentData::enforceNameType(Core::NameType nameType) {
+	if (_nameType == nameType) {
+		return true;
+	}
+	const auto base = _filename.isEmpty() ? u"file"_q : _filename;
+	const auto mime = Core::MimeTypeForName(mimeString());
+	const auto patterns = mime.globPatterns();
+	for (const auto &pattern : mime.globPatterns()) {
+		const auto now = base + QString(pattern).replace('*', QString());
+		if (Core::DetectNameType(now) == nameType) {
+			_filename = now;
+			_nameType = nameType;
+			return true;
+		}
+	}
+	return false;
 }
 
 void DocumentData::setLoadedInMediaCacheLocation() {
@@ -1460,6 +1508,10 @@ QString DocumentData::filename() const {
 	return _filename;
 }
 
+Core::NameType DocumentData::nameType() const {
+	return _nameType;
+}
+
 QString DocumentData::mimeString() const {
 	return _mimeString;
 }
@@ -1527,7 +1579,10 @@ bool DocumentData::isVideoMessage() const {
 bool DocumentData::isAnimation() const {
 	return (type == AnimatedDocument)
 		|| isVideoMessage()
-		|| (hasMimeType(u"image/gif"_q)
+		|| ((_filename.isEmpty()
+			|| _nameType == Core::NameType::Image
+			|| _nameType == Core::NameType::Video)
+			&& hasMimeType(u"image/gif"_q)
 			&& !(_flags & Flag::StreamingPlaybackFailed));
 }
 
@@ -1537,9 +1592,11 @@ bool DocumentData::isGifv() const {
 }
 
 bool DocumentData::isTheme() const {
-	return hasMimeType(u"application/x-tgtheme-tdesktop"_q)
-		|| _filename.endsWith(u".tdesktop-theme"_q, Qt::CaseInsensitive)
-		|| _filename.endsWith(u".tdesktop-palette"_q, Qt::CaseInsensitive);
+	return _filename.endsWith(u".tdesktop-theme"_q, Qt::CaseInsensitive)
+		|| _filename.endsWith(u".tdesktop-palette"_q, Qt::CaseInsensitive)
+		|| (hasMimeType(u"application/x-tgtheme-tdesktop"_q)
+			&& (_filename.isEmpty()
+				|| _nameType == Core::NameType::ThemeFile));
 }
 
 bool DocumentData::isSong() const {
@@ -1562,6 +1619,10 @@ bool DocumentData::isAudioFile() const {
 			return true;
 		}
 		return false;
+	} else if (!_filename.isEmpty()
+		&& _nameType != Core::NameType::Audio
+		&& _nameType != Core::NameType::Video) {
+		return false;
 	}
 	const auto left = _mimeString.mid(prefix.size());
 	const auto types = { u"x-wav"_q, u"wav"_q, u"mp4"_q };
diff --git a/Telegram/SourceFiles/data/data_document.h b/Telegram/SourceFiles/data/data_document.h
index 82fcd9ba3..93297550c 100644
--- a/Telegram/SourceFiles/data/data_document.h
+++ b/Telegram/SourceFiles/data/data_document.h
@@ -20,6 +20,10 @@ namespace Images {
 class Source;
 } // namespace Images
 
+namespace Core {
+enum class NameType : uchar;
+} // namespace Core
+
 namespace Storage {
 namespace Cache {
 struct Key;
@@ -255,6 +259,7 @@ public:
 	void collectLocalData(not_null<DocumentData*> local);
 
 	[[nodiscard]] QString filename() const;
+	[[nodiscard]] Core::NameType nameType() const;
 	[[nodiscard]] QString mimeString() const;
 	[[nodiscard]] bool hasMimeType(const QString &mime) const;
 	void setMimeString(const QString &mime);
@@ -340,6 +345,7 @@ private:
 	void setMaybeSupportsStreaming(bool supports);
 	void setLoadedInMediaCacheLocation();
 	void setFileName(const QString &remoteFileName);
+	bool enforceNameType(Core::NameType nameType);
 
 	void finishLoad();
 	void handleLoaderUpdates();
@@ -373,6 +379,7 @@ private:
 	std::unique_ptr<DocumentAdditionalData> _additional;
 	mutable Flags _flags = kStreamingSupportedUnknown;
 	GoodThumbnailState _goodThumbnailState = GoodThumbnailState();
+	Core::NameType _nameType = Core::NameType();
 	std::unique_ptr<FileLoader> _loader;
 
 };
diff --git a/Telegram/SourceFiles/data/data_document_media.cpp b/Telegram/SourceFiles/data/data_document_media.cpp
index a7783605c..9cb579812 100644
--- a/Telegram/SourceFiles/data/data_document_media.cpp
+++ b/Telegram/SourceFiles/data/data_document_media.cpp
@@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "window/themes/window_theme_preview.h"
 #include "core/core_settings.h"
 #include "core/application.h"
+#include "core/mime_type.h"
 #include "storage/file_download.h"
 #include "ui/chat/attach/attach_prepare.h"
 
@@ -295,10 +296,12 @@ void DocumentMedia::automaticLoad(
 		// No automatic download in this case.
 		return;
 	}
+	const auto indata = _owner->filename();
 	const auto filename = toCache
 		? QString()
 		: DocumentFileNameForSave(_owner);
-	const auto shouldLoadFromCloud = !Data::IsExecutableName(filename)
+	const auto shouldLoadFromCloud = (indata.isEmpty()
+		|| Core::DetectNameType(indata) != Core::NameType::Executable)
 		&& (item
 			? Data::AutoDownload::Should(
 				_owner->session().settings().autoDownload(),
diff --git a/Telegram/SourceFiles/data/data_document_resolver.cpp b/Telegram/SourceFiles/data/data_document_resolver.cpp
index 8ab502564..575d695cf 100644
--- a/Telegram/SourceFiles/data/data_document_resolver.cpp
+++ b/Telegram/SourceFiles/data/data_document_resolver.cpp
@@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/chat/chat_theme.h"
 #include "ui/text/text_utilities.h"
 #include "ui/widgets/checkbox.h"
+#include "ui/wrap/slide_wrap.h"
 #include "window/window_session_controller.h"
 #include "styles/style_layers.h"
 
@@ -46,11 +47,12 @@ base::options::toggle OptionExternalVideoPlayer({
 void ConfirmDontWarnBox(
 		not_null<Ui::GenericBox*> box,
 		rpl::producer<TextWithEntities> &&text,
+		rpl::producer<QString> &&check,
 		rpl::producer<QString> &&confirm,
 		Fn<void(bool)> callback) {
 	auto checkbox = object_ptr<Ui::Checkbox>(
 		box.get(),
-		tr::lng_launch_exe_dont_ask(),
+		std::move(check),
 		false,
 		st::defaultBoxCheckbox);
 	const auto weak = Ui::MakeWeak(checkbox.data());
@@ -67,29 +69,43 @@ void ConfirmDontWarnBox(
 	auto padding = st::boxPadding;
 	padding.setTop(padding.bottom());
 	box->addRow(std::move(checkbox), std::move(padding));
+	box->addRow(object_ptr<Ui::SlideWrap<Ui::FlatLabel>>(
+		box,
+		object_ptr<Ui::FlatLabel>(
+			box,
+			tr::lng_launch_dont_ask_settings(),
+			st::boxLabel)
+	))->toggleOn(weak->checkedValue());
 }
 
 void LaunchWithWarning(
 		// not_null<Window::Controller*> controller,
 		const QString &name,
 		HistoryItem *item) {
-	const auto isExecutable = Data::IsExecutableName(name);
-	const auto isIpReveal = Data::IsIpRevealingName(name);
+	const auto nameType = Core::DetectNameType(name);
+	const auto isIpReveal = (nameType != Core::NameType::Executable)
+		&& Core::IsIpRevealingPath(name);
+	const auto extension = Core::FileExtension(name).toLower();
+
 	auto &app = Core::App();
+	auto &settings = app.settings();
 	const auto warn = [&] {
 		if (item && item->history()->peer->isVerified()) {
 			return false;
 		}
-		return (isExecutable && app.settings().exeLaunchWarning())
-			|| (isIpReveal && app.settings().ipRevealWarning());
+		return (isIpReveal && settings.ipRevealWarning())
+			|| ((nameType == Core::NameType::Executable
+				|| nameType == Core::NameType::Unknown)
+				&& !settings.noWarningExtensions().contains(extension));
 	}();
-	const auto extension = '.' + Data::FileExtension(name);
-	if (Platform::IsWindows() && extension == u"."_q) {
+	if (extension.isEmpty()) {
 		// If you launch a file without extension, like "test", in case
 		// there is an executable file with the same name in this folder,
 		// like "test.bat", the executable file will be launched.
 		//
 		// Now we always force an Open With dialog box for such files.
+		//
+		// Let's force it for all platforms for files without extension.
 		crl::on_main([=] {
 			Platform::File::UnsafeShowOpenWith(name);
 		});
@@ -98,27 +114,38 @@ void LaunchWithWarning(
 		File::Launch(name);
 		return;
 	}
-	const auto callback = [=, &app](bool checked) {
+	const auto callback = [=, &app, &settings](bool checked) {
 		if (checked) {
-			if (isExecutable) {
-				app.settings().setExeLaunchWarning(false);
-			} else if (isIpReveal) {
-				app.settings().setIpRevealWarning(false);
+			if (isIpReveal) {
+				settings.setIpRevealWarning(false);
+			} else {
+				auto copy = settings.noWarningExtensions();
+				copy.emplace(extension);
+				settings.setNoWarningExtensions(std::move(copy));
 			}
 			app.saveSettingsDelayed();
 		}
 		File::Launch(name);
 	};
-	auto text = isExecutable
-		? tr::lng_launch_exe_warning(
-			lt_extension,
-			rpl::single(Ui::Text::Bold(extension)),
-			Ui::Text::WithEntities)
-		: tr::lng_launch_svg_warning(Ui::Text::WithEntities);
+	auto text = isIpReveal
+		? tr::lng_launch_svg_warning(Ui::Text::WithEntities)
+		: ((nameType == Core::NameType::Executable)
+			? tr::lng_launch_exe_warning
+			: tr::lng_launch_other_warning)(
+				lt_extension,
+				rpl::single(Ui::Text::Bold('.' + extension)),
+				Ui::Text::WithEntities);
+	auto check = (isIpReveal
+		? tr::lng_launch_exe_dont_ask
+		: tr::lng_launch_dont_ask)();
+	auto confirm = ((nameType == Core::NameType::Executable)
+		? tr::lng_launch_exe_sure
+		: tr::lng_launch_other_sure)();
 	Ui::show(Box(
 		ConfirmDontWarnBox,
 		std::move(text),
-		(isExecutable ? tr::lng_launch_exe_sure : tr::lng_continue)(),
+		std::move(check),
+		std::move(confirm),
 		callback));
 }
 
@@ -126,91 +153,6 @@ void LaunchWithWarning(
 
 const char kOptionExternalVideoPlayer[] = "external-video-player";
 
-QString FileExtension(const QString &filepath) {
-	const auto reversed = ranges::views::reverse(filepath);
-	const auto last = ranges::find_first_of(reversed, ".\\/");
-	if (last == reversed.end() || *last != '.') {
-		return QString();
-	}
-	return QString(last.base(), last - reversed.begin());
-}
-
-#if 0
-bool IsValidMediaFile(const QString &filepath) {
-	static const auto kExtensions = [] {
-		const auto list = qsl("\
-16svx 2sf 3g2 3gp 8svx aac aaf aif aifc aiff amr amv ape asf ast au aup \
-avchd avi brstm bwf cam cdda cust dat divx drc dsh dsf dts dtshd dtsma \
-dvr-ms dwd evo f4a f4b f4p f4v fla flac flr flv gif gifv gsf gsm gym iff \
-ifo it jam la ly m1v m2p m2ts m2v m4a m4p m4v mcf mid mk3d mka mks mkv mng \
-mov mp1 mp2 mp3 mp4 minipsf mod mpc mpe mpeg mpg mpv mscz mt2 mus mxf mxl \
-niff nsf nsv off ofr ofs ogg ogv opus ots pac ps psf psf2 psflib ptb qsf \
-qt ra raw rka rm rmj rmvb roq s3m shn sib sid smi smp sol spc spx ssf svi \
-swa swf tak ts tta txm usf vgm vob voc vox vqf wav webm wma wmv wrap wtv \
-wv xm xml ym yuv").split(' ');
-		return base::flat_set<QString>(list.begin(), list.end());
-	}();
-
-	return ranges::binary_search(
-		kExtensions,
-		FileExtension(filepath).toLower());
-}
-#endif
-
-bool IsExecutableName(const QString &filepath) {
-	static const auto kExtensions = [] {
-		const auto joined =
-#ifdef Q_OS_WIN
-			u"\
-ad ade adp app application appref-ms asp asx bas bat bin cab cdxml cer cfg \
-chi chm cmd cnt com cpl crt csh der diagcab dll drv eml exe fon fxp gadget \
-grp hlp hpj hta htt inf ini ins inx isp isu its jar jnlp job js jse key ksh \
-lnk local lua mad maf mag mam manifest maq mar mas mat mau mav maw mcf mda \
-mdb mde mdt mdw mdz mht mhtml mjs mmc mof msc msg msh msh1 msh2 msh1xml \
-msh2xml mshxml msi msp mst ops osd paf pcd phar php php3 php4 php5 php7 phps \
-php-s pht phtml pif pl plg pm pod prf prg ps1 ps2 ps1xml ps2xml psc1 psc2 \
-psd1 psm1 pssc pst py py3 pyc pyd pyi pyo pyw pywz pyz rb reg rgs scf scr \
-sct search-ms settingcontent-ms sh shb shs slk sys t tmp u3p url vb vbe vbp \
-vbs vbscript vdx vsmacros vsd vsdm vsdx vss vssm vssx vst vstm vstx vsw vsx \
-vtx website ws wsc wsf wsh xbap xll xnk xs"_q;
-#elif defined Q_OS_MAC // Q_OS_MAC
-			u"\
-applescript action app bin command csh osx workflow terminal url caction \
-mpkg pkg scpt scptd xhtm webarchive"_q;
-#else // Q_OS_WIN || Q_OS_MAC
-			u"bin csh deb desktop ksh out pet pkg pup rpm run sh shar \
-slp zsh"_q;
-#endif // !Q_OS_WIN && !Q_OS_MAC
-		const auto list = joined.split(' ');
-		return base::flat_set<QString>(list.begin(), list.end());
-	}();
-
-	return ranges::binary_search(
-		kExtensions,
-		FileExtension(filepath).toLower());
-}
-
-bool IsIpRevealingName(const QString &filepath) {
-	static const auto kExtensions = [] {
-		const auto joined = u"htm html svg m4v m3u8"_q;
-		const auto list = joined.split(' ');
-		return base::flat_set<QString>(list.begin(), list.end());
-	}();
-	static const auto kMimeTypes = [] {
-		const auto joined = u"text/html image/svg+xml"_q;
-		const auto list = joined.split(' ');
-		return base::flat_set<QString>(list.begin(), list.end());
-	}();
-
-	return ranges::binary_search(
-		kExtensions,
-		FileExtension(filepath).toLower()
-	) || ranges::binary_search(
-		kMimeTypes,
-		QMimeDatabase().mimeTypeForFile(QFileInfo(filepath)).name()
-	);
-}
-
 base::binary_guard ReadBackgroundImageAsync(
 		not_null<Data::DocumentMedia*> media,
 		FnMut<QImage(QImage)> postprocess,
diff --git a/Telegram/SourceFiles/data/data_document_resolver.h b/Telegram/SourceFiles/data/data_document_resolver.h
index 9931da3e8..4988297aa 100644
--- a/Telegram/SourceFiles/data/data_document_resolver.h
+++ b/Telegram/SourceFiles/data/data_document_resolver.h
@@ -22,10 +22,6 @@ class DocumentMedia;
 
 extern const char kOptionExternalVideoPlayer[];
 
-[[nodiscard]] QString FileExtension(const QString &filepath);
-// [[nodiscard]] bool IsValidMediaFile(const QString &filepath);
-[[nodiscard]] bool IsExecutableName(const QString &filepath);
-[[nodiscard]] bool IsIpRevealingName(const QString &filepath);
 base::binary_guard ReadBackgroundImageAsync(
 	not_null<Data::DocumentMedia*> media,
 	FnMut<QImage(QImage)> postprocess,
diff --git a/Telegram/SourceFiles/data/data_histories.cpp b/Telegram/SourceFiles/data/data_histories.cpp
index 8f599ff03..e9c16c32e 100644
--- a/Telegram/SourceFiles/data/data_histories.cpp
+++ b/Telegram/SourceFiles/data/data_histories.cpp
@@ -9,13 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "api/api_text_entities.h"
 #include "data/business/data_shortcut_messages.h"
+#include "data/components/scheduled_messages.h"
 #include "data/data_session.h"
 #include "data/data_channel.h"
 #include "data/data_chat.h"
 #include "data/data_folder.h"
 #include "data/data_forum.h"
 #include "data/data_forum_topic.h"
-#include "data/data_scheduled_messages.h"
 #include "data/data_user.h"
 #include "base/unixtime.h"
 #include "base/random.h"
@@ -846,11 +846,12 @@ void Histories::deleteMessages(const MessageIdsList &ids, bool revoke) {
 			if (item->isScheduled()) {
 				const auto wasOnServer = !item->isSending()
 					&& !item->hasFailed();
+				auto &scheduled = _owner->session().scheduledMessages();
 				if (wasOnServer) {
-					scheduledIdsByPeer[history->peer].push_back(MTP_int(
-						_owner->scheduledMessages().lookupId(item)));
+					scheduledIdsByPeer[history->peer].push_back(
+						MTP_int(scheduled.lookupId(item)));
 				} else {
-					_owner->scheduledMessages().removeSending(item);
+					scheduled.removeSending(item);
 				}
 				continue;
 			} else if (item->isBusinessShortcut()) {
diff --git a/Telegram/SourceFiles/data/data_poll.cpp b/Telegram/SourceFiles/data/data_poll.cpp
index f781f95bc..800f3ee32 100644
--- a/Telegram/SourceFiles/data/data_poll.cpp
+++ b/Telegram/SourceFiles/data/data_poll.cpp
@@ -121,8 +121,8 @@ bool PollData::applyResults(const MTPPollResults &results) {
 	return results.match([&](const MTPDpollResults &results) {
 		_lastResultsUpdate = crl::now();
 
-		const auto newTotalVoters =
-			results.vtotal_voters().value_or(totalVoters);
+		const auto newTotalVoters
+			= results.vtotal_voters().value_or(totalVoters);
 		auto changed = (newTotalVoters != totalVoters);
 		if (const auto list = results.vresults()) {
 			for (const auto &result : list->v) {
diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp
index e21d11a65..0301d74a2 100644
--- a/Telegram/SourceFiles/data/data_session.cpp
+++ b/Telegram/SourceFiles/data/data_session.cpp
@@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/business/data_business_chatbots.h"
 #include "data/business/data_business_info.h"
 #include "data/business/data_shortcut_messages.h"
+#include "data/components/scheduled_messages.h"
 #include "data/stickers/data_stickers.h"
 #include "data/notify/data_notify_settings.h"
 #include "data/data_bot_app.h"
@@ -56,9 +57,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_poll.h"
 #include "data/data_replies_list.h"
 #include "data/data_chat_filters.h"
-#include "data/data_scheduled_messages.h"
 #include "data/data_send_action.h"
-#include "data/data_sponsored_messages.h"
 #include "data/data_message_reactions.h"
 #include "data/data_emoji_statuses.h"
 #include "data/data_forum_icons.h"
@@ -278,9 +277,7 @@ Session::Session(not_null<Main::Session*> session)
 , _savedMessages(std::make_unique<SavedMessages>(this))
 , _chatbots(std::make_unique<Chatbots>(this))
 , _businessInfo(std::make_unique<BusinessInfo>(this))
-, _scheduledMessages(std::make_unique<ScheduledMessages>(this))
-, _shortcutMessages(std::make_unique<ShortcutMessages>(this))
-, _sponsoredMessages(std::make_unique<SponsoredMessages>(this)) {
+, _shortcutMessages(std::make_unique<ShortcutMessages>(this)) {
 	_cache->open(_session->local().cacheKey());
 	_bigFileCache->open(_session->local().cacheBigFileKey());
 
@@ -407,9 +404,8 @@ void Session::clear() {
 	_sendActionManager->clear();
 
 	_histories->unloadAll();
-	_scheduledMessages = nullptr;
 	_shortcutMessages = nullptr;
-	_sponsoredMessages = nullptr;
+	_session->scheduledMessages().clear();
 	_dependentMessages.clear();
 	base::take(_messages);
 	base::take(_nonChannelMessages);
diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h
index 0f7df77f4..fa58d4847 100644
--- a/Telegram/SourceFiles/data/data_session.h
+++ b/Telegram/SourceFiles/data/data_session.h
@@ -47,10 +47,8 @@ namespace Data {
 class Folder;
 class LocationPoint;
 class WallPaper;
-class ScheduledMessages;
 class ShortcutMessages;
 class SendActionManager;
-class SponsoredMessages;
 class Reactions;
 class EmojiStatuses;
 class ForumIcons;
@@ -104,9 +102,6 @@ public:
 	[[nodiscard]] ChatFilters &chatsFilters() const {
 		return *_chatsFilters;
 	}
-	[[nodiscard]] ScheduledMessages &scheduledMessages() const {
-		return *_scheduledMessages;
-	}
 	[[nodiscard]] ShortcutMessages &shortcutMessages() const {
 		return *_shortcutMessages;
 	}
@@ -128,9 +123,6 @@ public:
 	[[nodiscard]] Stickers &stickers() const {
 		return *_stickers;
 	}
-	[[nodiscard]] SponsoredMessages &sponsoredMessages() const {
-		return *_sponsoredMessages;
-	}
 	[[nodiscard]] Reactions &reactions() const {
 		return *_reactions;
 	}
@@ -1084,9 +1076,7 @@ private:
 	const std::unique_ptr<SavedMessages> _savedMessages;
 	const std::unique_ptr<Chatbots> _chatbots;
 	const std::unique_ptr<BusinessInfo> _businessInfo;
-	std::unique_ptr<ScheduledMessages> _scheduledMessages;
 	std::unique_ptr<ShortcutMessages> _shortcutMessages;
-	std::unique_ptr<SponsoredMessages> _sponsoredMessages;
 
 	MsgId _nonHistoryEntryId = ShortcutMaxMsgId;
 
diff --git a/Telegram/SourceFiles/data/data_shared_media.cpp b/Telegram/SourceFiles/data/data_shared_media.cpp
index 8033fba9c..6834190bf 100644
--- a/Telegram/SourceFiles/data/data_shared_media.cpp
+++ b/Telegram/SourceFiles/data/data_shared_media.cpp
@@ -13,10 +13,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "storage/storage_facade.h"
 #include "history/history.h"
 #include "history/history_item.h"
+#include "data/components/scheduled_messages.h"
 #include "data/data_document.h"
 #include "data/data_media_types.h"
 #include "data/data_photo.h"
-#include "data/data_scheduled_messages.h"
 #include "data/data_session.h"
 #include "core/crash_reports.h"
 
@@ -193,9 +193,9 @@ rpl::producer<SparseIdsMergedSlice> SharedScheduledMediaViewer(
 	const auto history = session->data().history(key.mergedKey.peerId);
 
 	return rpl::single(rpl::empty) | rpl::then(
-		session->data().scheduledMessages().updates(history)
+		session->scheduledMessages().updates(history)
 	) | rpl::map([=] {
-		const auto list = session->data().scheduledMessages().list(history);
+		const auto list = session->scheduledMessages().list(history);
 
 		auto items = ranges::views::all(
 			list.ids
diff --git a/Telegram/SourceFiles/data/data_statistics_chart.h b/Telegram/SourceFiles/data/data_statistics_chart.h
index 5a29ebef7..fba664d34 100644
--- a/Telegram/SourceFiles/data/data_statistics_chart.h
+++ b/Telegram/SourceFiles/data/data_statistics_chart.h
@@ -30,14 +30,15 @@ struct StatisticalChart {
 	[[nodiscard]] int findIndex(int left, int right, float64 v) const;
 
 	struct Line final {
-		std::vector<int> y;
+		std::vector<Statistic::ChartValue> y;
 
 		Statistic::SegmentTree segmentTree;
 		int id = 0;
 		QString idString;
 		QString name;
-		int maxValue = 0;
-		int minValue = std::numeric_limits<int>::max();
+		Statistic::ChartValue maxValue = 0;
+		Statistic::ChartValue minValue
+			= std::numeric_limits<Statistic::ChartValue>::max();
 		QString colorKey;
 		QColor color;
 		QColor colorDark;
@@ -55,8 +56,9 @@ struct StatisticalChart {
 		float64 max = 0.;
 	} defaultZoomXIndex;
 
-	int maxValue = 0;
-	int minValue = std::numeric_limits<int>::max();
+	Statistic::ChartValue maxValue = 0;
+	Statistic::ChartValue minValue
+		= std::numeric_limits<Statistic::ChartValue>::max();
 
 	float64 oneDayPercentage = 0.;
 
diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
index 789226009..55839da4d 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
+++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
@@ -775,8 +775,8 @@ void Widget::updateScrollUpPosition() {
 	_scrollToTop->moveToRight(
 		st::historyToDownPosition.x(),
 		_scroll->height() - top);
-	const auto shouldBeHidden =
-		!_scrollToTopIsShown && !_scrollToTopShown.animating();
+	const auto shouldBeHidden
+		= !_scrollToTopIsShown && !_scrollToTopShown.animating();
 	if (shouldBeHidden != _scrollToTop->isHidden()) {
 		_scrollToTop->setVisible(!shouldBeHidden);
 	}
diff --git a/Telegram/SourceFiles/editor/photo_editor_content.cpp b/Telegram/SourceFiles/editor/photo_editor_content.cpp
index 065b2a1a6..1a379b9b3 100644
--- a/Telegram/SourceFiles/editor/photo_editor_content.cpp
+++ b/Telegram/SourceFiles/editor/photo_editor_content.cpp
@@ -48,8 +48,8 @@ PhotoEditorContent::PhotoEditorContent(
 			return;
 		}
 		const auto imageSizeF = [&] {
-			const auto rotatedSize =
-				FlipSizeByRotation(size, mods.angle);
+			const auto rotatedSize
+				= FlipSizeByRotation(size, mods.angle);
 			const auto m = _crop->cropMargins();
 			const auto sizeForCrop = rotatedSize
 				- QSize(m.left() + m.right(), m.top() + m.bottom());
diff --git a/Telegram/SourceFiles/export/export_api_wrap.cpp b/Telegram/SourceFiles/export/export_api_wrap.cpp
index 6471feb82..734f347df 100644
--- a/Telegram/SourceFiles/export/export_api_wrap.cpp
+++ b/Telegram/SourceFiles/export/export_api_wrap.cpp
@@ -541,8 +541,8 @@ void ApiWrap::requestDialogsCount() {
 	Expects(_startProcess != nullptr);
 
 	if (_settings->onlySinglePeer()) {
-		_startProcess->info.dialogsCount =
-			(_settings->singlePeer.type() == mtpc_inputPeerChannel
+		_startProcess->info.dialogsCount
+			= (_settings->singlePeer.type() == mtpc_inputPeerChannel
 				? 1
 				: _splits.size());
 		sendNextStartRequest();
diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp
index 5899117be..53e0926e0 100644
--- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp
+++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp
@@ -738,8 +738,8 @@ void GenerateItems(
 	using LogPromote = MTPDchannelAdminLogEventActionParticipantToggleAdmin;
 	using LogSticker = MTPDchannelAdminLogEventActionChangeStickerSet;
 	using LogEmoji = MTPDchannelAdminLogEventActionChangeEmojiStickerSet;
-	using LogPreHistory =
-		MTPDchannelAdminLogEventActionTogglePreHistoryHidden;
+	using LogPreHistory
+		= MTPDchannelAdminLogEventActionTogglePreHistoryHidden;
 	using LogPermissions = MTPDchannelAdminLogEventActionDefaultBannedRights;
 	using LogPoll = MTPDchannelAdminLogEventActionStopPoll;
 	using LogDiscussion = MTPDchannelAdminLogEventActionChangeLinkedChat;
@@ -749,19 +749,19 @@ void GenerateItems(
 	using LogDiscardCall = MTPDchannelAdminLogEventActionDiscardGroupCall;
 	using LogMute = MTPDchannelAdminLogEventActionParticipantMute;
 	using LogUnmute = MTPDchannelAdminLogEventActionParticipantUnmute;
-	using LogCallSetting =
-		MTPDchannelAdminLogEventActionToggleGroupCallSetting;
-	using LogJoinByInvite =
-		MTPDchannelAdminLogEventActionParticipantJoinByInvite;
-	using LogInviteDelete =
-		MTPDchannelAdminLogEventActionExportedInviteDelete;
-	using LogInviteRevoke =
-		MTPDchannelAdminLogEventActionExportedInviteRevoke;
+	using LogCallSetting
+		= MTPDchannelAdminLogEventActionToggleGroupCallSetting;
+	using LogJoinByInvite
+		= MTPDchannelAdminLogEventActionParticipantJoinByInvite;
+	using LogInviteDelete
+		= MTPDchannelAdminLogEventActionExportedInviteDelete;
+	using LogInviteRevoke
+		= MTPDchannelAdminLogEventActionExportedInviteRevoke;
 	using LogInviteEdit = MTPDchannelAdminLogEventActionExportedInviteEdit;
 	using LogVolume = MTPDchannelAdminLogEventActionParticipantVolume;
 	using LogTTL = MTPDchannelAdminLogEventActionChangeHistoryTTL;
-	using LogJoinByRequest =
-		MTPDchannelAdminLogEventActionParticipantJoinByRequest;
+	using LogJoinByRequest
+		= MTPDchannelAdminLogEventActionParticipantJoinByRequest;
 	using LogNoForwards = MTPDchannelAdminLogEventActionToggleNoForwards;
 	using LogSendMessage = MTPDchannelAdminLogEventActionSendMessage;
 	using LogChangeAvailableReactions = MTPDchannelAdminLogEventActionChangeAvailableReactions;
diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp
index bdfa6d1d0..6eb9d43bb 100644
--- a/Telegram/SourceFiles/history/history.cpp
+++ b/Telegram/SourceFiles/history/history.cpp
@@ -19,6 +19,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "history/history_unread_things.h"
 #include "dialogs/ui/dialogs_layout.h"
 #include "data/business/data_shortcut_messages.h"
+#include "data/components/scheduled_messages.h"
+#include "data/components/sponsored_messages.h"
 #include "data/notify/data_notify_settings.h"
 #include "data/stickers/data_stickers.h"
 #include "data/data_drafts.h"
@@ -28,8 +30,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_channel_admins.h"
 #include "data/data_changes.h"
 #include "data/data_chat_filters.h"
-#include "data/data_scheduled_messages.h"
-#include "data/data_sponsored_messages.h"
 #include "data/data_send_action.h"
 #include "data/data_folder.h"
 #include "data/data_forum.h"
@@ -598,7 +598,7 @@ not_null<HistoryItem*> History::addNewItem(
 		not_null<HistoryItem*> item,
 		bool unread) {
 	if (item->isScheduled()) {
-		owner().scheduledMessages().appendSending(item);
+		session().scheduledMessages().appendSending(item);
 		return item;
 	} else if (item->isBusinessShortcut()) {
 		owner().shortcutMessages().appendSending(item);
diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h
index 29620dc2e..4a4ad8ddc 100644
--- a/Telegram/SourceFiles/history/history.h
+++ b/Telegram/SourceFiles/history/history.h
@@ -25,10 +25,6 @@ struct HistoryMessageMarkupData;
 class HistoryMainElementDelegateMixin;
 struct LanguageId;
 
-namespace Main {
-class Session;
-} // namespace Main
-
 namespace Data {
 struct Draft;
 class Session;
diff --git a/Telegram/SourceFiles/history/history_drag_area.cpp b/Telegram/SourceFiles/history/history_drag_area.cpp
index 39ba5fe3c..4f99ec778 100644
--- a/Telegram/SourceFiles/history/history_drag_area.cpp
+++ b/Telegram/SourceFiles/history/history_drag_area.cpp
@@ -52,8 +52,8 @@ DragArea::Areas DragArea::SetupDragAreaToContainer(
 	auto &lifetime = container->lifetime();
 	container->setAcceptDrops(true);
 
-	const auto attachDragDocument =
-		Ui::CreateChild<DragArea>(container.get());
+	const auto attachDragDocument
+		= Ui::CreateChild<DragArea>(container.get());
 	const auto attachDragPhoto = Ui::CreateChild<DragArea>(container.get());
 
 	attachDragDocument->hide();
@@ -62,8 +62,8 @@ DragArea::Areas DragArea::SetupDragAreaToContainer(
 	attachDragDocument->raise();
 	attachDragPhoto->raise();
 
-	const auto attachDragState =
-		lifetime.make_state<DragState>(DragState::None);
+	const auto attachDragState
+		= lifetime.make_state<DragState>(DragState::None);
 
 	const auto width = [=] {
 		return container->width();
diff --git a/Telegram/SourceFiles/history/history_drag_area.h b/Telegram/SourceFiles/history/history_drag_area.h
index 64e581d58..c9c075c5a 100644
--- a/Telegram/SourceFiles/history/history_drag_area.h
+++ b/Telegram/SourceFiles/history/history_drag_area.h
@@ -23,8 +23,8 @@ public:
 		DragArea *photo;
 	};
 
-	using CallbackComputeState =
-		Fn<Storage::MimeDataState(const QMimeData *data)>;
+	using CallbackComputeState
+		= Fn<Storage::MimeDataState(const QMimeData *data)>;
 
 	static Areas SetupDragAreaToContainer(
 		not_null<Ui::RpWidget*> container,
diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp
index 91dd7401a..4a91f933e 100644
--- a/Telegram/SourceFiles/history/history_inner_widget.cpp
+++ b/Telegram/SourceFiles/history/history_inner_widget.cpp
@@ -68,6 +68,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "api/api_who_reacted.h"
 #include "api/api_views.h"
 #include "lang/lang_keys.h"
+#include "data/components/sponsored_messages.h"
 #include "data/data_session.h"
 #include "data/data_document.h"
 #include "data/data_channel.h"
@@ -79,7 +80,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_file_click_handler.h"
 #include "data/data_histories.h"
 #include "data/data_changes.h"
-#include "data/data_sponsored_messages.h"
 #include "dialogs/ui/dialogs_video_userpic.h"
 #include "styles/style_chat.h"
 #include "styles/style_menu_icons.h"
@@ -123,7 +123,7 @@ void FillSponsoredMessagesMenu(
 		not_null<Window::SessionController*> controller,
 		FullMsgId itemId,
 		not_null<Ui::PopupMenu*> menu) {
-	const auto &data = controller->session().data().sponsoredMessages();
+	const auto &data = controller->session().sponsoredMessages();
 	const auto info = data.lookupDetails(itemId).info;
 	const auto show = controller->uiShow();
 	if (!info.empty()) {
@@ -984,7 +984,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
 			: yShown(top + height / 2);
 		if (markShown) {
 			if (isSponsored) {
-				session().data().sponsoredMessages().view(item->fullId());
+				session().sponsoredMessages().view(item->fullId());
 			} else if (isUnread) {
 				readTill = item;
 			}
diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp
index 7d5c966eb..4a523dc0d 100644
--- a/Telegram/SourceFiles/history/history_item.cpp
+++ b/Telegram/SourceFiles/history/history_item.cpp
@@ -40,11 +40,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "base/timer_rpl.h"
 #include "api/api_text_entities.h"
 #include "api/api_updates.h"
+#include "data/components/scheduled_messages.h"
+#include "data/components/sponsored_messages.h"
 #include "data/notify/data_notify_settings.h"
 #include "data/data_bot_app.h"
 #include "data/data_saved_messages.h"
 #include "data/data_saved_sublist.h"
-#include "data/data_scheduled_messages.h"
 #include "data/data_changes.h"
 #include "data/data_session.h"
 #include "data/data_message_reactions.h"
@@ -57,7 +58,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_user.h"
 #include "data/data_group_call.h" // Data::GroupCall::id().
 #include "data/data_poll.h" // PollData::publicVotes.
-#include "data/data_sponsored_messages.h"
 #include "data/data_stories.h"
 #include "data/data_web_page.h"
 #include "chat_helpers/stickers_gift_box_pack.h"
diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp
index aeef37557..22b513730 100644
--- a/Telegram/SourceFiles/history/history_item_components.cpp
+++ b/Telegram/SourceFiles/history/history_item_components.cpp
@@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "media/audio/media_audio.h"
 #include "media/player/media_player_instance.h"
 #include "data/business/data_shortcut_messages.h"
+#include "data/components/scheduled_messages.h"
 #include "data/stickers/data_custom_emoji.h"
 #include "data/data_channel.h"
 #include "data/data_media_types.h"
@@ -42,7 +43,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_document.h"
 #include "data/data_web_page.h"
 #include "data/data_file_click_handler.h"
-#include "data/data_scheduled_messages.h"
 #include "data/data_session.h"
 #include "data/data_stories.h"
 #include "main/main_session.h"
@@ -301,7 +301,7 @@ ReplyFields ReplyFieldsFromMTP(
 		const auto owner = &item->history()->owner();
 		if (const auto id = data.vreply_to_msg_id().value_or_empty()) {
 			result.messageId = data.is_reply_to_scheduled()
-				? owner->scheduledMessages().localMessageId(id)
+				? owner->session().scheduledMessages().localMessageId(id)
 				: item->shortcutId()
 				? owner->shortcutMessages().localMessageId(id)
 				: id;
diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index ae2ba9f8a..6bf793a89 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -54,6 +54,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "base/unixtime.h"
 #include "base/call_delayed.h"
 #include "data/business/data_shortcut_messages.h"
+#include "data/components/scheduled_messages.h"
+#include "data/components/sponsored_messages.h"
 #include "data/notify/data_notify_settings.h"
 #include "data/data_changes.h"
 #include "data/data_drafts.h"
@@ -68,8 +70,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_forum_topic.h"
 #include "data/data_user.h"
 #include "data/data_chat_filters.h"
-#include "data/data_scheduled_messages.h"
-#include "data/data_sponsored_messages.h"
 #include "data/data_file_origin.h"
 #include "data/data_histories.h"
 #include "data/data_group_call.h"
@@ -319,7 +319,7 @@ HistoryWidget::HistoryWidget(
 	) | rpl::start_with_next([=] {
 		if (_history
 			&& _history->loadedAtBottom()
-			&& session().data().sponsoredMessages().append(_history)) {
+			&& session().sponsoredMessages().append(_history)) {
 			_scroll->contentAdded();
 		}
 	}, lifetime());
@@ -2241,7 +2241,7 @@ void HistoryWidget::showHistory(
 			return;
 		} else {
 			_sponsoredMessagesStateKnown = false;
-			session().data().sponsoredMessages().clearItems(_history);
+			session().sponsoredMessages().clearItems(_history);
 			session().data().hideShownSpoilers();
 			_composeSearch = nullptr;
 		}
@@ -2491,20 +2491,18 @@ void HistoryWidget::showHistory(
 				if (history != _history) {
 					return;
 				}
-				auto &sponsored = session().data().sponsoredMessages();
 				using State = Data::SponsoredMessages::State;
-				const auto state = sponsored.state(_history);
+				const auto state = session().sponsoredMessages().state(
+					_history);
 				_sponsoredMessagesStateKnown = (state != State::None);
 				if (state == State::AppendToEnd) {
 					_scroll->setTrackingContent(
-						sponsored.canHaveFor(_history));
+						session().sponsoredMessages().canHaveFor(_history));
 				} else if (state == State::InjectToMiddle) {
 					injectSponsoredMessages();
 				}
 			});
-			session().data().sponsoredMessages().request(
-				_history,
-				checkState);
+			session().sponsoredMessages().request(_history, checkState);
 			checkState();
 		}
 	} else {
@@ -2608,7 +2606,7 @@ void HistoryWidget::setupPreview() {
 }
 
 void HistoryWidget::injectSponsoredMessages() const {
-	session().data().sponsoredMessages().inject(
+	session().sponsoredMessages().inject(
 		_history,
 		_showAtMsgId,
 		_scroll->height() * 2,
@@ -2799,9 +2797,9 @@ void HistoryWidget::setupScheduledToggle() {
 	controller()->activeChatValue(
 	) | rpl::map([=](Dialogs::Key key) -> rpl::producer<> {
 		if (const auto history = key.history()) {
-			return session().data().scheduledMessages().updates(history);
+			return session().scheduledMessages().updates(history);
 		} else if (const auto topic = key.topic()) {
-			return session().data().scheduledMessages().updates(
+			return session().scheduledMessages().updates(
 				topic->owningHistory());
 		}
 		return rpl::never<rpl::empty_value>();
@@ -2816,7 +2814,7 @@ void HistoryWidget::setupScheduledToggle() {
 void HistoryWidget::refreshScheduledToggle() {
 	const auto has = _history
 		&& _canSendMessages
-		&& (session().data().scheduledMessages().count(_history) > 0);
+		&& (session().scheduledMessages().count(_history) > 0);
 	if (!_scheduled && has) {
 		_scheduled.create(this, st::historyScheduledToggle);
 		_scheduled->show();
@@ -3659,7 +3657,7 @@ void HistoryWidget::loadMessagesDown() {
 	auto from = loadMigrated ? _migrated : _history;
 	if (from->loadedAtBottom()) {
 		if (_sponsoredMessagesStateKnown) {
-			session().data().sponsoredMessages().request(_history, nullptr);
+			session().sponsoredMessages().request(_history, nullptr);
 		}
 		return;
 	}
diff --git a/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp b/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp
index 3b223b166..92a1d329c 100644
--- a/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp
+++ b/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp
@@ -377,8 +377,8 @@ TTLButton::TTLButton(
 			return (r.left() + r.width() > parentWidget()->width());
 		}) | rpl::distinct_until_changed(
 		) | rpl::start_with_next([=](bool toHide) {
-			const auto isFirstTooltip =
-				!Core::App().settings().ttlVoiceClickTooltipHidden();
+			const auto isFirstTooltip
+				= !Core::App().settings().ttlVoiceClickTooltipHidden();
 			if (isFirstTooltip || (!isFirstTooltip && toHide)) {
 				_tooltip->toggleAnimated(!toHide);
 			}
diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp
index de58b7ac9..742034bbe 100644
--- a/Telegram/SourceFiles/history/view/history_view_element.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_element.cpp
@@ -39,10 +39,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/text/text_utilities.h"
 #include "ui/item_text_options.h"
 #include "ui/painter.h"
+#include "data/components/sponsored_messages.h"
 #include "data/data_session.h"
 #include "data/data_forum.h"
 #include "data/data_forum_topic.h"
-#include "data/data_sponsored_messages.h"
 #include "data/data_message_reactions.h"
 #include "data/data_user.h"
 #include "lang/lang_keys.h"
@@ -1125,7 +1125,7 @@ ClickHandlerPtr Element::fromLink() const {
 			}
 			const auto my = context.other.value<ClickHandlerContext>();
 			if (const auto window = ContextOrSessionWindow(my, session)) {
-				auto &sponsored = session->data().sponsoredMessages();
+				auto &sponsored = session->sponsoredMessages();
 				const auto itemId = my.itemId ? my.itemId : item->fullId();
 				const auto details = sponsored.lookupDetails(itemId);
 				if (!details.externalLink.isEmpty()) {
diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
index ac95a45a3..03b5466e3 100644
--- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
@@ -56,8 +56,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "boxes/premium_preview_box.h"
 #include "boxes/peers/edit_participant_box.h"
 #include "core/crash_reports.h"
+#include "data/components/sponsored_messages.h"
 #include "data/data_session.h"
-#include "data/data_sponsored_messages.h"
 #include "data/data_changes.h"
 #include "data/data_folder.h"
 #include "data/data_media_types.h"
@@ -2138,8 +2138,7 @@ void ListWidget::paintEvent(QPaintEvent *e) {
 			: yShown(top + height / 2);
 		if (markShown) {
 			if (isSponsored) {
-				session->data().sponsoredMessages().view(
-					item->fullId());
+				session->sponsoredMessages().view(item->fullId());
 			} else if (isUnread) {
 				readTill = item;
 			}
@@ -3898,8 +3897,8 @@ bool ListWidget::lastMessageEditRequestNotify() const {
 	if (it == end(list)) {
 		return false;
 	} else {
-		const auto item =
-			session().data().groups().findItemToEdit((*it)->data()).get();
+		const auto item
+			= session().data().groups().findItemToEdit((*it)->data()).get();
 		editMessageRequestNotify(item->fullId());
 		return true;
 	}
diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp
index 06706372a..4bba62d73 100644
--- a/Telegram/SourceFiles/history/view/history_view_message.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_message.cpp
@@ -27,12 +27,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/round_rect.h"
 #include "ui/text/text_utilities.h"
 #include "ui/power_saving.h"
+#include "data/components/sponsored_messages.h"
 #include "data/data_session.h"
 #include "data/data_user.h"
 #include "data/data_channel.h"
 #include "data/data_forum_topic.h"
 #include "data/data_message_reactions.h"
-#include "data/data_sponsored_messages.h"
 #include "lang/lang_keys.h"
 #include "mainwidget.h"
 #include "main/main_session.h"
@@ -440,8 +440,9 @@ Message::Message(
 		}
 	}
 	if (data->isSponsored()) {
-		const auto &messages = data->history()->owner().sponsoredMessages();
-		const auto details = messages.lookupDetails(data->fullId());
+		const auto &session = data->history()->session();
+		const auto details = session.sponsoredMessages().lookupDetails(
+			data->fullId());
 		if (details.canReport) {
 			_rightAction = std::make_unique<RightAction>();
 			_rightAction->second = std::make_unique<SecondRightAction>();
@@ -1867,27 +1868,27 @@ void Message::clickHandlerPressedChanged(
 	Element::clickHandlerPressedChanged(handler, pressed);
 	if (!handler) {
 		return;
-	} else if (_rightAction) {
-		if (_rightAction->second && (handler == _rightAction->second->link)) {
-			const auto rightSize = rightActionSize();
-			Assert(rightSize != std::nullopt);
-			if (pressed) {
-				if (!_rightAction->second->ripple) {
-					// Create a ripple.
-					_rightAction->second->ripple =
-						std::make_unique<Ui::RippleAnimation>(
-							st::defaultRippleAnimation,
-							Ui::RippleAnimation::RoundRectMask(
-								Size(rightSize->width()),
-								rightSize->width() / 2),
-							[=] { repaint(); });
-				}
-				_rightAction->second->ripple->add(_rightAction->lastPoint);
-			} else if (_rightAction->second->ripple) {
-				_rightAction->second->ripple->lastStop();
+	} else if (_rightAction && (handler == _rightAction->link)) {
+		toggleRightActionRipple(pressed);
+	} else if (_rightAction
+		&& _rightAction->second
+		&& (handler == _rightAction->second->link)) {
+		const auto rightSize = rightActionSize();
+		Assert(rightSize != std::nullopt);
+		if (pressed) {
+			if (!_rightAction->second->ripple) {
+				// Create a ripple.
+				_rightAction->second->ripple
+					= std::make_unique<Ui::RippleAnimation>(
+						st::defaultRippleAnimation,
+						Ui::RippleAnimation::RoundRectMask(
+							Size(rightSize->width()),
+							rightSize->width() / 2),
+						[=] { repaint(); });
 			}
-		} else if (handler == _rightAction->link) {
-			toggleRightActionRipple(pressed);
+			_rightAction->second->ripple->add(_rightAction->lastPoint);
+		} else if (_rightAction->second->ripple) {
+			_rightAction->second->ripple->lastStop();
 		}
 	} else if (_comments && (handler == _comments->link)) {
 		toggleCommentsButtonRipple(pressed);
diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp
index 91183100a..ac2fa2efb 100644
--- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp
@@ -52,6 +52,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "core/mime_type.h"
 #include "main/main_session.h"
 #include "main/main_session_settings.h"
+#include "data/components/scheduled_messages.h"
 #include "data/data_session.h"
 #include "data/data_user.h"
 #include "data/data_chat.h"
@@ -63,7 +64,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_changes.h"
 #include "data/data_shared_media.h"
 #include "data/data_send_action.h"
-#include "data/data_scheduled_messages.h"
 #include "data/data_premium_limits.h"
 #include "storage/storage_media_prepare.h"
 #include "storage/storage_account.h"
@@ -234,10 +234,9 @@ RepliesWidget::RepliesWidget(
 		.stickerOrEmojiChosen = controller->stickerOrEmojiChosen(),
 		.scheduledToggleValue = _topic
 			? rpl::single(rpl::empty_value()) | rpl::then(
-				session().data().scheduledMessages().updates(
-					_topic->owningHistory())
+				session().scheduledMessages().updates(_topic->owningHistory())
 			) | rpl::map([=] {
-				return session().data().scheduledMessages().hasFor(_topic);
+				return session().scheduledMessages().hasFor(_topic);
 			}) | rpl::type_erased()
 			: rpl::single(false),
 	}))
diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp
index df1a88f8c..632132a13 100644
--- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp
@@ -33,11 +33,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "core/mime_type.h"
 #include "chat_helpers/tabbed_selector.h"
 #include "main/main_session.h"
+#include "data/components/scheduled_messages.h"
 #include "data/data_forum.h"
 #include "data/data_forum_topic.h"
 #include "data/data_session.h"
 #include "data/data_changes.h"
-#include "data/data_scheduled_messages.h"
 #include "data/data_user.h"
 #include "data/data_message_reactions.h"
 #include "data/data_peer_values.h"
@@ -62,7 +62,7 @@ namespace HistoryView {
 ScheduledMemento::ScheduledMemento(not_null<History*> history)
 : _history(history)
 , _forumTopic(nullptr) {
-	const auto list = _history->owner().scheduledMessages().list(_history);
+	const auto list = _history->session().scheduledMessages().list(_history);
 	if (!list.ids.empty()) {
 		_list.setScrollTopState({ .item = { .fullId = list.ids.front() } });
 	}
@@ -71,7 +71,8 @@ ScheduledMemento::ScheduledMemento(not_null<History*> history)
 ScheduledMemento::ScheduledMemento(not_null<Data::ForumTopic*> forumTopic)
 : _history(forumTopic->owningHistory())
 , _forumTopic(forumTopic) {
-	const auto list = _history->owner().scheduledMessages().list(_forumTopic);
+	const auto list = _history->session().scheduledMessages().list(
+		_forumTopic);
 	if (!list.ids.empty()) {
 		_list.setScrollTopState({ .item = { .fullId = list.ids.front() } });
 	}
@@ -1201,13 +1202,13 @@ rpl::producer<Data::MessagesSlice> ScheduledWidget::listSource(
 		Data::MessagePosition aroundId,
 		int limitBefore,
 		int limitAfter) {
-	const auto data = &controller()->session().data();
+	const auto session = &controller()->session();
 	return rpl::single(rpl::empty) | rpl::then(
-		data->scheduledMessages().updates(_history)
+		session->scheduledMessages().updates(_history)
 	) | rpl::map([=] {
 		return _forumTopic
-			? data->scheduledMessages().list(_forumTopic)
-			: data->scheduledMessages().list(_history);
+			? session->scheduledMessages().list(_forumTopic)
+			: session->scheduledMessages().list(_history);
 	}) | rpl::after_next([=](const Data::MessagesSlice &slice) {
 		highlightSingleNewMessage(slice);
 	});
diff --git a/Telegram/SourceFiles/history/view/history_view_send_action.cpp b/Telegram/SourceFiles/history/view/history_view_send_action.cpp
index 5380ccfb5..460941e6d 100644
--- a/Telegram/SourceFiles/history/view/history_view_send_action.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_send_action.cpp
@@ -150,8 +150,8 @@ bool SendActionPainter::paint(
 		const auto extraAnimationWidth = _animationLeft
 			? animationWidth * 2
 			: 0;
-		const auto left =
-			(availableWidth < _animationLeft + extraAnimationWidth)
+		const auto left
+			= (availableWidth < _animationLeft + extraAnimationWidth)
 				? 0
 				: _animationLeft;
 		_sendActionAnimation.paint(
diff --git a/Telegram/SourceFiles/history/view/history_view_sponsored_click_handler.cpp b/Telegram/SourceFiles/history/view/history_view_sponsored_click_handler.cpp
index 65b030476..7a6d7b685 100644
--- a/Telegram/SourceFiles/history/view/history_view_sponsored_click_handler.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_sponsored_click_handler.cpp
@@ -10,8 +10,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "api/api_chat_invite.h"
 #include "core/click_handler_types.h"
 #include "core/file_utilities.h"
+#include "data/components/sponsored_messages.h"
 #include "data/data_session.h"
-#include "data/data_sponsored_messages.h"
 #include "main/main_session.h"
 #include "window/window_session_controller.h"
 
@@ -37,8 +37,8 @@ ClickHandlerPtr SponsoredLink(const QString &externalLink) {
 			if (!controller) {
 				return;
 			}
-			const auto &data = controller->session().data();
-			const auto details = data.sponsoredMessages().lookupDetails(
+			const auto &session = controller->session();
+			const auto details = session.sponsoredMessages().lookupDetails(
 				my.itemId);
 			if (!details.externalLink.isEmpty()) {
 				File::OpenUrl(details.externalLink);
diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp
index d312489f4..bb5ebf46e 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp
+++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp
@@ -387,12 +387,9 @@ void Document::createComponents(bool caption) {
 		mask |= HistoryDocumentVoice::Bit();
 	} else {
 		mask |= HistoryDocumentNamed::Bit();
-		if (_data->hasThumbnail()) {
-			if (!_data->isSong()
-				&& !Data::IsExecutableName(_data->filename())) {
-				_data->loadThumbnail(_realParent->fullId());
-				mask |= HistoryDocumentThumbed::Bit();
-			}
+		if (_data->hasThumbnail() && !_data->isSong()) {
+			_data->loadThumbnail(_realParent->fullId());
+			mask |= HistoryDocumentThumbed::Bit();
 		}
 	}
 	if (caption) {
diff --git a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp
index 8967e74ac..ef3589ad3 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp
+++ b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp
@@ -13,11 +13,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "iv/iv_instance.h"
 #include "core/click_handler_types.h"
 #include "core/ui_integration.h"
+#include "data/components/sponsored_messages.h"
 #include "data/stickers/data_custom_emoji.h"
 #include "data/data_file_click_handler.h"
 #include "data/data_photo_media.h"
 #include "data/data_session.h"
-#include "data/data_sponsored_messages.h"
 #include "data/data_web_page.h"
 #include "history/history.h"
 #include "history/history_item_components.h"
@@ -231,8 +231,8 @@ WebPage::WebPage(
 	if (!(flags & MediaWebPageFlag::Sponsored)) {
 		return std::nullopt;
 	}
-	const auto &data = _parent->data()->history()->owner();
-	const auto details = data.sponsoredMessages().lookupDetails(
+	const auto &session = _parent->data()->history()->session();
+	const auto details = session.sponsoredMessages().lookupDetails(
 		_parent->data()->fullId());
 	auto result = std::make_optional<SponsoredData>();
 	result->buttonText = details.buttonText;
@@ -500,8 +500,8 @@ QSize WebPage::countOptimalSize() {
 		minHeight = resizeGetHeight(maxWidth);
 	}
 	if (_sponsoredData && _sponsoredData->canReport) {
-		_sponsoredData->widthBeforeHint =
-			st::webPageTitleStyle.font->width(siteName);
+		_sponsoredData->widthBeforeHint
+			= st::webPageTitleStyle.font->width(siteName);
 		const auto &font = st::webPageSponsoredHintFont;
 		_sponsoredData->hintSize = QSize(
 			font->width(tr::lng_sponsored_message_revenue_button(tr::now))
diff --git a/Telegram/SourceFiles/info/channel_statistics/earn/earn_format.cpp b/Telegram/SourceFiles/info/channel_statistics/earn/earn_format.cpp
index ea470af93..4f87fa505 100644
--- a/Telegram/SourceFiles/info/channel_statistics/earn/earn_format.cpp
+++ b/Telegram/SourceFiles/info/channel_statistics/earn/earn_format.cpp
@@ -48,8 +48,11 @@ QString MinorPart(EarnInt value) {
 
 QString ToUsd(EarnInt value, float64 rate) {
 	constexpr auto kApproximately = QChar(0x2248);
-	const auto multiplier = EarnInt(rate * Data::kEarnMultiplier);
-	const auto result = (value * multiplier) / Data::kEarnMultiplier;
+
+	const auto result = value
+		/ float64(Data::kEarnMultiplier)
+		* rate
+		* Data::kEarnMultiplier;
 	return QString(kApproximately)
 		+ QChar('$')
 		+ MajorPart(result)
diff --git a/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.cpp b/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.cpp
index 80e377e1e..ad1155b89 100644
--- a/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.cpp
+++ b/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.cpp
@@ -13,7 +13,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "boxes/peers/edit_peer_color_box.h" // AddLevelBadge.
 #include "chat_helpers/stickers_emoji_pack.h"
 #include "core/application.h"
-#include "core/ui_integration.h" // Core::MarkedTextContext.
 #include "data/data_channel.h"
 #include "data/data_premium_limits.h"
 #include "data/data_session.h"
@@ -31,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "main/main_session.h"
 #include "statistics/chart_widget.h"
 #include "ui/basic_click_handlers.h"
+#include "ui/widgets/label_with_custom_emoji.h"
 #include "ui/boxes/boost_box.h"
 #include "ui/controls/userpic_button.h"
 #include "ui/effects/animation_value_f.h"
@@ -334,23 +334,21 @@ void InnerWidget::fill() {
 			st::channelEarnLearnArrowMargins,
 			false));
 	const auto addAboutWithLearn = [&](const tr::phrase<lngtag_link> &text) {
-		auto label = object_ptr<Ui::FlatLabel>(
+		auto label = Ui::CreateLabelWithCustomEmoji(
 			container,
-			st::boxDividerLabel);
-		const auto raw = label.data();
-		text(
-			lt_link,
-			tr::lng_channel_earn_about_link(
-				lt_emoji,
-				rpl::single(arrow),
+			text(
+				lt_link,
+				tr::lng_channel_earn_about_link(
+					lt_emoji,
+					rpl::single(arrow),
+					Ui::Text::RichLangValue
+				) | rpl::map([](TextWithEntities text) {
+					return Ui::Text::Link(std::move(text), 1);
+				}),
 				Ui::Text::RichLangValue
-			) | rpl::map([](TextWithEntities text) {
-				return Ui::Text::Link(std::move(text), 1);
-			}),
-			Ui::Text::RichLangValue
-		) | rpl::start_with_next([=](const TextWithEntities &text) {
-			raw->setMarkedText(text, makeContext(raw));
-		}, label->lifetime());
+			),
+			{ .session = session },
+			st::boxDividerLabel);
 		label->setLink(1, std::make_shared<LambdaClickHandler>([=] {
 			_show->showBox(Box([=](not_null<Ui::GenericBox*> box) {
 				box->setNoContentMargin(true);
@@ -454,17 +452,16 @@ void InnerWidget::fill() {
 					const auto l = box->addRow(
 						object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
 							content,
-							object_ptr<Ui::FlatLabel>(
+							Ui::CreateLabelWithCustomEmoji(
 								content,
+								tr::lng_channel_earn_learn_coin_title(
+									lt_emoji,
+									rpl::single(
+										Ui::Text::Link(bigCurrencyIcon, 1)),
+									Ui::Text::RichLangValue
+								),
+								{ .session = session },
 								st::boxTitle)))->entity();
-					tr::lng_channel_earn_learn_coin_title(
-						lt_emoji,
-						rpl::single(
-							Ui::Text::Link(bigCurrencyIcon, 1)),
-						Ui::Text::RichLangValue
-					) | rpl::start_with_next([=](TextWithEntities t) {
-						l->setMarkedText(std::move(t), makeContext(l));
-					}, l->lifetime());
 					const auto diamonds = l->lifetime().make_state<int>(0);
 					l->setLink(1, std::make_shared<LambdaClickHandler>([=] {
 						const auto count = (*diamonds);
@@ -480,25 +477,23 @@ void InnerWidget::fill() {
 				Ui::AddSkip(content);
 				{
 					const auto label = box->addRow(
-						object_ptr<Ui::FlatLabel>(
+						Ui::CreateLabelWithCustomEmoji(
 							content,
+							tr::lng_channel_earn_learn_coin_about(
+								lt_link,
+								tr::lng_channel_earn_about_link(
+									lt_emoji,
+									rpl::single(arrow),
+									Ui::Text::RichLangValue
+								) | rpl::map([](TextWithEntities text) {
+									return Ui::Text::Link(std::move(text), 1);
+								}),
+								Ui::Text::RichLangValue
+							),
+							{ .session = session },
 							st::channelEarnLearnDescription));
-					tr::lng_channel_earn_learn_coin_about(
-						lt_link,
-						tr::lng_channel_earn_about_link(
-							lt_emoji,
-							rpl::single(arrow),
-							Ui::Text::RichLangValue
-						) | rpl::map([](TextWithEntities text) {
-							return Ui::Text::Link(std::move(text), 1);
-						}),
-						Ui::Text::RichLangValue
-					) | rpl::start_with_next([=, l = label](
-							TextWithEntities t) {
-						l->setMarkedText(std::move(t), makeContext(l));
-						l->resizeToWidth(box->width()
-							- rect::m::sum::h(st::boxRowPadding));
-					}, label->lifetime());
+					label->resizeToWidth(box->width()
+						- rect::m::sum::h(st::boxRowPadding));
 					label->setLink(
 						1,
 						LearnMoreCurrencyLink(
diff --git a/Telegram/SourceFiles/info/info_top_bar.cpp b/Telegram/SourceFiles/info/info_top_bar.cpp
index d09652a85..6a10bde23 100644
--- a/Telegram/SourceFiles/info/info_top_bar.cpp
+++ b/Telegram/SourceFiles/info/info_top_bar.cpp
@@ -693,13 +693,13 @@ void TopBar::createSelectionControls() {
 		_selectionActionRequests,
 		_cancelSelection->lifetime());
 	_delete->entity()->setVisible(_canDelete);
-	const auto archive =
-	_toggleStoryPin = wrap(Ui::CreateChild<Ui::FadeWrap<Ui::IconButton>>(
-		this,
-		object_ptr<Ui::IconButton>(
+	const auto archive = _toggleStoryPin = wrap(
+		Ui::CreateChild<Ui::FadeWrap<Ui::IconButton>>(
 			this,
-			_storiesArchive ? _st.storiesSave : _st.storiesArchive),
-		st::infoTopBarScale));
+			object_ptr<Ui::IconButton>(
+				this,
+				_storiesArchive ? _st.storiesSave : _st.storiesArchive),
+			st::infoTopBarScale));
 	registerToggleControlCallback(
 		_toggleStoryPin.data(),
 		[this] { return selectionMode() && _canToggleStoryPin; });
diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp
index 7cc90d0c6..1ebb1d315 100644
--- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp
+++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp
@@ -814,8 +814,8 @@ void ListWidget::paintEvent(QPaintEvent *e) {
 	}
 
 	if (_dateBadge->goodType && clip.intersects(_dateBadge->rect)) {
-		const auto scrollDateOpacity =
-			_dateBadge->opacity.value(_dateBadge->shown ? 1. : 0.);
+		const auto scrollDateOpacity
+			= _dateBadge->opacity.value(_dateBadge->shown ? 1. : 0.);
 		if (scrollDateOpacity > 0.) {
 			p.setOpacity(scrollDateOpacity);
 			if (_dateBadge->corners.p[0].isNull()) {
diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_inner_widget.cpp b/Telegram/SourceFiles/info/statistics/info_statistics_inner_widget.cpp
index 6f4e45fb2..6e5803114 100644
--- a/Telegram/SourceFiles/info/statistics/info_statistics_inner_widget.cpp
+++ b/Telegram/SourceFiles/info/statistics/info_statistics_inner_widget.cpp
@@ -349,12 +349,14 @@ void FillOverview(
 		const auto diffText = diffAbs > kTooMuchDiff
 			? Lang::FormatCountToShort(std::abs(diff)).string
 			: QString::number(diffAbs);
+		const auto percentage = std::abs(v.growthRatePercentage);
+		const auto precision = (percentage == int(percentage)) ? 0 : 1;
 		return {
 			(diff < 0 ? st::menuIconAttentionColor : st::settingsIconBg2)->c,
 			QString("%1%2 (%3%)")
 				.arg((diff < 0) ? QChar(0x2212) : QChar(0x002B))
 				.arg(diffText)
-				.arg(std::abs(std::round(v.growthRatePercentage * 10.) / 10.))
+				.arg(QString::number(percentage, 'f', precision))
 		};
 	};
 
diff --git a/Telegram/SourceFiles/main/main_session.cpp b/Telegram/SourceFiles/main/main_session.cpp
index 6705f9554..fcadbefe0 100644
--- a/Telegram/SourceFiles/main/main_session.cpp
+++ b/Telegram/SourceFiles/main/main_session.cpp
@@ -28,6 +28,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "storage/file_upload.h"
 #include "storage/storage_account.h"
 #include "storage/storage_facade.h"
+#include "data/components/scheduled_messages.h"
+#include "data/components/sponsored_messages.h"
 #include "data/data_session.h"
 #include "data/data_changes.h"
 #include "data/data_user.h"
@@ -100,6 +102,8 @@ Session::Session(
 , _giftBoxStickersPacks(std::make_unique<Stickers::GiftBoxPack>(this))
 , _sendAsPeers(std::make_unique<SendAsPeers>(this))
 , _attachWebView(std::make_unique<InlineBots::AttachWebView>(this))
+, _scheduledMessages(std::make_unique<Data::ScheduledMessages>(this))
+, _sponsoredMessages(std::make_unique<Data::SponsoredMessages>(this))
 , _supportHelper(Support::Helper::Create(this))
 , _saveSettingsTimer([=] { saveSettings(); }) {
 	Expects(_settings != nullptr);
diff --git a/Telegram/SourceFiles/main/main_session.h b/Telegram/SourceFiles/main/main_session.h
index d8c74e087..4249f888f 100644
--- a/Telegram/SourceFiles/main/main_session.h
+++ b/Telegram/SourceFiles/main/main_session.h
@@ -31,6 +31,8 @@ class Templates;
 namespace Data {
 class Session;
 class Changes;
+class ScheduledMessages;
+class SponsoredMessages;
 } // namespace Data
 
 namespace Storage {
@@ -104,6 +106,12 @@ public:
 	[[nodiscard]] Data::Changes &changes() const {
 		return *_changes;
 	}
+	[[nodiscard]] Data::SponsoredMessages &sponsoredMessages() const {
+		return *_sponsoredMessages;
+	}
+	[[nodiscard]] Data::ScheduledMessages &scheduledMessages() const {
+		return *_scheduledMessages;
+	}
 	[[nodiscard]] Api::Updates &updates() const {
 		return *_updates;
 	}
@@ -224,6 +232,8 @@ private:
 	const std::unique_ptr<Stickers::GiftBoxPack> _giftBoxStickersPacks;
 	const std::unique_ptr<SendAsPeers> _sendAsPeers;
 	const std::unique_ptr<InlineBots::AttachWebView> _attachWebView;
+	const std::unique_ptr<Data::ScheduledMessages> _scheduledMessages;
+	const std::unique_ptr<Data::SponsoredMessages> _sponsoredMessages;
 
 	const std::unique_ptr<Support::Helper> _supportHelper;
 
diff --git a/Telegram/SourceFiles/main/main_session_settings.cpp b/Telegram/SourceFiles/main/main_session_settings.cpp
index 239bc7cf6..7806dea9f 100644
--- a/Telegram/SourceFiles/main/main_session_settings.cpp
+++ b/Telegram/SourceFiles/main/main_session_settings.cpp
@@ -140,7 +140,7 @@ void SessionSettings::addFromSerialized(const QByteArray &serialized) {
 	qint32 supportChatsTimeSlice = _supportChatsTimeSlice.current();
 	qint32 appIncludeMutedCounter = app.includeMutedCounter() ? 1 : 0;
 	qint32 appCountUnreadMessages = app.countUnreadMessages() ? 1 : 0;
-	qint32 appExeLaunchWarning = app.exeLaunchWarning() ? 1 : 0;
+	qint32 legacyAppExeLaunchWarning = 1;
 	QByteArray autoDownload;
 	qint32 supportAllSearchResults = _supportAllSearchResults.current() ? 1 : 0;
 	qint32 archiveCollapsed = _archiveCollapsed.current() ? 1 : 0;
@@ -262,7 +262,7 @@ void SessionSettings::addFromSerialized(const QByteArray &serialized) {
 			stream >> appCountUnreadMessages;
 		}
 		if (!stream.atEnd()) {
-			stream >> appExeLaunchWarning;
+			stream >> legacyAppExeLaunchWarning;
 		}
 	}
 	if (!stream.atEnd()) {
@@ -509,7 +509,6 @@ void SessionSettings::addFromSerialized(const QByteArray &serialized) {
 		}
 		app.setIncludeMutedCounter(appIncludeMutedCounter == 1);
 		app.setCountUnreadMessages(appCountUnreadMessages == 1);
-		app.setExeLaunchWarning(appExeLaunchWarning == 1);
 		app.setNotifyAboutPinned(appNotifyAboutPinned == 1);
 		app.setLoopAnimatedStickers(appLoopAnimatedStickers == 1);
 		app.setLargeEmoji(appLargeEmoji == 1);
diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp
index 80390d239..7fdaf7202 100644
--- a/Telegram/SourceFiles/mainwidget.cpp
+++ b/Telegram/SourceFiles/mainwidget.cpp
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "api/api_updates.h"
 #include "api/api_views.h"
+#include "data/components/scheduled_messages.h"
 #include "data/data_document_media.h"
 #include "data/data_document_resolver.h"
 #include "data/data_forum_topic.h"
@@ -22,7 +23,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_chat.h"
 #include "data/data_user.h"
 #include "data/data_chat_filters.h"
-#include "data/data_scheduled_messages.h"
 #include "data/data_file_origin.h"
 #include "data/data_histories.h"
 #include "data/stickers/data_stickers.h"
@@ -811,6 +811,13 @@ void MainWidget::createPlayer() {
 		});
 		_player->entity()->setShowItemCallback([=](
 				not_null<const HistoryItem*> item) {
+			const auto peer = item->history()->peer;
+			if (const auto window = Core::App().windowFor(peer)) {
+				if (const auto controller = window->sessionController()) {
+					controller->showMessage(item);
+					return;
+				}
+			}
 			_controller->showMessage(item);
 		});
 
diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp
index 5e8724b37..93d6d3060 100644
--- a/Telegram/SourceFiles/mainwindow.cpp
+++ b/Telegram/SourceFiles/mainwindow.cpp
@@ -77,8 +77,8 @@ base::options::toggle AutoScrollInactiveChat({
 
 } // namespace
 
-const char kOptionAutoScrollInactiveChat[] =
-	"auto-scroll-inactive-chat";
+const char kOptionAutoScrollInactiveChat[]
+	= "auto-scroll-inactive-chat";
 
 MainWindow::MainWindow(not_null<Window::Controller*> controller)
 : Platform::MainWindow(controller) {
diff --git a/Telegram/SourceFiles/media/player/media_player_dropdown.cpp b/Telegram/SourceFiles/media/player/media_player_dropdown.cpp
index 618a0becb..e52728aba 100644
--- a/Telegram/SourceFiles/media/player/media_player_dropdown.cpp
+++ b/Telegram/SourceFiles/media/player/media_player_dropdown.cpp
@@ -35,8 +35,8 @@ constexpr auto kSpeedDebounceTimeout = crl::time(600);
 	return base::SafeRound(speed * 10) / 10.;
 }
 
-constexpr auto kSpeedStickedValues =
-	std::array<std::pair<float64, float64>, 7>{{
+constexpr auto kSpeedStickedValues
+	= std::array<std::pair<float64, float64>, 7>{{
 		{ 0.8, 0.05 },
 		{ 1.0, 0.05 },
 		{ 1.2, 0.05 },
diff --git a/Telegram/SourceFiles/media/system_media_controls_manager.cpp b/Telegram/SourceFiles/media/system_media_controls_manager.cpp
index d16685860..91f016aa5 100644
--- a/Telegram/SourceFiles/media/system_media_controls_manager.cpp
+++ b/Telegram/SourceFiles/media/system_media_controls_manager.cpp
@@ -44,8 +44,8 @@ bool SystemMediaControlsManager::Supported() {
 SystemMediaControlsManager::SystemMediaControlsManager()
 : _controls(std::make_unique<base::Platform::SystemMediaControls>()) {
 
-	using PlaybackStatus =
-		base::Platform::SystemMediaControls::PlaybackStatus;
+	using PlaybackStatus
+		= base::Platform::SystemMediaControls::PlaybackStatus;
 	using Command = base::Platform::SystemMediaControls::Command;
 
 	_controls->setApplicationName(AppName.utf16());
diff --git a/Telegram/SourceFiles/menu/menu_sponsored.cpp b/Telegram/SourceFiles/menu/menu_sponsored.cpp
index bfa8427f1..e917c3f07 100644
--- a/Telegram/SourceFiles/menu/menu_sponsored.cpp
+++ b/Telegram/SourceFiles/menu/menu_sponsored.cpp
@@ -9,10 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "boxes/premium_preview_box.h"
 #include "chat_helpers/compose/compose_show.h"
-#include "core/ui_integration.h" // Core::MarkedTextContext.
+#include "data/components/sponsored_messages.h"
 #include "data/data_premium_limits.h"
 #include "data/data_session.h"
-#include "data/data_sponsored_messages.h"
 #include "data/stickers/data_custom_emoji.h"
 #include "history/history.h"
 #include "lang/lang_keys.h"
@@ -25,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/vertical_list.h"
 #include "ui/widgets/buttons.h"
 #include "ui/widgets/popup_menu.h"
+#include "ui/widgets/label_with_custom_emoji.h"
 #include "styles/style_channel_earn.h"
 #include "styles/style_chat.h"
 #include "styles/style_layers.h"
@@ -169,31 +169,23 @@ void AboutBox(
 				st::topicButtonArrow,
 				st::channelEarnLearnArrowMargins,
 				false));
-		const auto label = box->addRow(
-			object_ptr<Ui::FlatLabel>(
+		const auto available = box->width()
+			- rect::m::sum::h(st::boxRowPadding);
+		box->addRow(
+			Ui::CreateLabelWithCustomEmoji(
 				content,
-				st::channelEarnLearnDescription));
-		tr::lng_sponsored_revenued_footer_description(
-			lt_link,
-			tr::lng_channel_earn_about_link(
-				lt_emoji,
-				rpl::single(arrow),
-				Ui::Text::RichLangValue
-			) | rpl::map([=](TextWithEntities text) {
-				return Ui::Text::Link(std::move(text), kUrl.utf16());
-			}),
-			Ui::Text::RichLangValue
-		) | rpl::start_with_next([=, l = label](
-				TextWithEntities t) {
-			l->setMarkedText(
-				std::move(t),
-				Core::MarkedTextContext{
-					.session = session,
-					.customEmojiRepaint = [=] { l->update(); },
-				});
-			l->resizeToWidth(box->width()
-				- rect::m::sum::h(st::boxRowPadding));
-		}, label->lifetime());
+				tr::lng_sponsored_revenued_footer_description(
+					lt_link,
+					tr::lng_channel_earn_about_link(
+						lt_emoji,
+						rpl::single(arrow),
+						Ui::Text::RichLangValue
+					) | rpl::map([=](TextWithEntities text) {
+						return Ui::Text::Link(std::move(text), kUrl.utf16());
+					}),
+					Ui::Text::RichLangValue),
+				{ .session = session },
+				st::channelEarnLearnDescription))->resizeToWidth(available);
 	}
 	Ui::AddSkip(content);
 	Ui::AddSkip(content);
@@ -217,7 +209,7 @@ void ShowReportSponsoredBox(
 		std::shared_ptr<ChatHelpers::Show> show,
 		not_null<HistoryItem*> item) {
 	const auto peer = item->history()->peer;
-	auto &sponsoredMessages = peer->session().data().sponsoredMessages();
+	auto &sponsoredMessages = peer->session().sponsoredMessages();
 	const auto fullId = item->fullId();
 	const auto report = sponsoredMessages.createReportCallback(fullId);
 	const auto guideLink = Ui::Text::Link(
diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp
index 69fc9f292..b742a6436 100644
--- a/Telegram/SourceFiles/overview/overview_layout.cpp
+++ b/Telegram/SourceFiles/overview/overview_layout.cpp
@@ -1514,9 +1514,7 @@ bool Document::iconAnimated() const {
 }
 
 bool Document::withThumb() const {
-	return !songLayout()
-		&& _data->hasThumbnail()
-		&& !Data::IsExecutableName(_data->filename());
+	return !songLayout() && _data->hasThumbnail();
 }
 
 bool Document::updateStatusText() {
diff --git a/Telegram/SourceFiles/passport/passport_form_controller.cpp b/Telegram/SourceFiles/passport/passport_form_controller.cpp
index 980888ffb..b0ad0fcdf 100644
--- a/Telegram/SourceFiles/passport/passport_form_controller.cpp
+++ b/Telegram/SourceFiles/passport/passport_form_controller.cpp
@@ -2686,8 +2686,8 @@ bool FormController::applyPassword(const MTPDaccount_password &result) {
 	settings.notEmptyPassport = result.is_has_secure_values();
 	settings.request = Core::ParseCloudPasswordCheckRequest(result);
 	settings.unknownAlgo = result.vcurrent_algo() && !settings.request;
-	settings.unconfirmedPattern =
-		qs(result.vemail_unconfirmed_pattern().value_or_empty());
+	settings.unconfirmedPattern = qs(
+		result.vemail_unconfirmed_pattern().value_or_empty());
 	settings.newAlgo = Core::ValidateNewCloudPasswordAlgo(
 		Core::ParseCloudPasswordAlgo(result.vnew_algo()));
 	settings.newSecureAlgo = Core::ValidateNewSecureSecretAlgo(
diff --git a/Telegram/SourceFiles/passport/passport_panel_controller.h b/Telegram/SourceFiles/passport/passport_panel_controller.h
index 94cd418c8..5c3d296b4 100644
--- a/Telegram/SourceFiles/passport/passport_panel_controller.h
+++ b/Telegram/SourceFiles/passport/passport_panel_controller.h
@@ -26,8 +26,8 @@ struct EditContactScheme;
 
 enum class ReadScanError;
 
-using preferredLangCallback =
-	Fn<rpl::producer<EditDocumentCountry>(const QString &)>;
+using preferredLangCallback
+	= Fn<rpl::producer<EditDocumentCountry>(const QString &)>;
 EditDocumentScheme GetDocumentScheme(
 	Scope::Type type,
 	std::optional<Value::Type> scansType,
diff --git a/Telegram/SourceFiles/payments/payments_checkout_process.cpp b/Telegram/SourceFiles/payments/payments_checkout_process.cpp
index b9e1b631d..ad7299664 100644
--- a/Telegram/SourceFiles/payments/payments_checkout_process.cpp
+++ b/Telegram/SourceFiles/payments/payments_checkout_process.cpp
@@ -766,12 +766,12 @@ void CheckoutProcess::requestPassword() {
 			(index < list.size()) ? list[index].title : QString());
 		fields.customSubmitButton = tr::lng_payments_password_submit();
 		fields.customCheckCallback = [=](
-				const Core::CloudPasswordResult &result) {
+				const Core::CloudPasswordResult &result,
+				QPointer<PasscodeBox> box) {
+			_enterPasswordBox = box;
 			_form->submit(result);
 		};
-		auto owned = Box<PasscodeBox>(_session, fields);
-		_enterPasswordBox = owned.data();
-		_panel->showBox(std::move(owned));
+		_panel->showBox(Box<PasscodeBox>(_session, fields));
 	});
 }
 
diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
index ae7fb1e99..9a0d91c4d 100644
--- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
+++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
@@ -99,42 +99,6 @@ void XCBSkipTaskbar(QWindow *window, bool skip) {
 			| XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY,
 		reinterpret_cast<const char*>(&xev));
 }
-
-void XCBSetDesktopFileName(QWindow *window) {
-	const auto connection = base::Platform::XCB::GetConnectionFromQt();
-	if (!connection) {
-		return;
-	}
-
-	const auto utf8Atom = base::Platform::XCB::GetAtom(
-		connection,
-		"UTF8_STRING");
-
-	if (!utf8Atom.has_value()) {
-		return;
-	}
-
-	const auto filenameAtoms = {
-		base::Platform::XCB::GetAtom(connection, "_GTK_APPLICATION_ID"),
-		base::Platform::XCB::GetAtom(connection, "_KDE_NET_WM_DESKTOP_FILE"),
-	};
-
-	const auto filename = QGuiApplication::desktopFileName().toUtf8();
-
-	for (const auto atom : filenameAtoms) {
-		if (atom.has_value()) {
-			xcb_change_property(
-				connection,
-				XCB_PROP_MODE_REPLACE,
-				window->winId(),
-				*atom,
-				*utf8Atom,
-				8,
-				filename.size(),
-				filename.data());
-		}
-	}
-}
 #endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
 
 void SkipTaskbar(QWindow *window, bool skip) {
@@ -206,10 +170,6 @@ void MainWindow::initHook() {
 		}
 		return base::EventFilterResult::Continue;
 	});
-
-#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
-	XCBSetDesktopFileName(windowHandle());
-#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
 }
 
 void MainWindow::workmodeUpdated(Core::Settings::WorkMode mode) {
diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp
index 0ea48a7de..747e4c2fa 100644
--- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp
+++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp
@@ -315,19 +315,19 @@ bool NotificationData::init(
 		_actions.push_back("inline-reply");
 		_actions.push_back(tr::lng_notification_reply(tr::now).toStdString());
 
-		_notificationRepliedSignalId =
-			_interface.signal_notification_replied().connect([=](
-					XdgNotifications::Notifications,
-					uint id,
-					std::string text) {
-				Core::Sandbox::Instance().customEnterFromEventLoop([&] {
-					if (id == _notificationId) {
-						_manager->notificationReplied(
-							_id,
-							{ QString::fromStdString(text), {} });
-					}
+		_notificationRepliedSignalId
+			= _interface.signal_notification_replied().connect([=](
+						XdgNotifications::Notifications,
+						uint id,
+						std::string text) {
+					Core::Sandbox::Instance().customEnterFromEventLoop([&] {
+						if (id == _notificationId) {
+							_manager->notificationReplied(
+								_id,
+								{ QString::fromStdString(text), {} });
+						}
+					});
 				});
-			});
 	}
 
 	_actionInvokedSignalId = _interface.signal_action_invoked().connect([=](
diff --git a/Telegram/SourceFiles/platform/mac/touchbar/items/mac_pinned_chats_item.mm b/Telegram/SourceFiles/platform/mac/touchbar/items/mac_pinned_chats_item.mm
index 477075719..ca4f88dad 100644
--- a/Telegram/SourceFiles/platform/mac/touchbar/items/mac_pinned_chats_item.mm
+++ b/Telegram/SourceFiles/platform/mac/touchbar/items/mac_pinned_chats_item.mm
@@ -551,13 +551,11 @@ NSRect PeerRectByIndex(int index) {
 	const auto processOnline = [=](const auto &pin) {
 		// TODO: this should be replaced
 		// with the global application timer for online statuses.
-		const auto onlineChanges =
-			peerChangedLifetime->make_state<rpl::event_stream<PeerData*>>();
+		const auto onlineChanges
+			= peerChangedLifetime->make_state<rpl::event_stream<PeerData*>>();
 		const auto peer = pin->peer;
-		const auto onlineTimer =
-			peerChangedLifetime->make_state<base::Timer>([=] {
-				onlineChanges->fire_copy({ peer });
-			});
+		const auto onlineTimer = peerChangedLifetime->make_state<base::Timer>(
+			[=] { onlineChanges->fire_copy({ peer }); });
 
 		const auto callTimer = [=](const auto &pin) {
 			onlineTimer->cancel();
diff --git a/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_manager.mm b/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_manager.mm
index f9c24a5a5..5f314e4b5 100644
--- a/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_manager.mm
+++ b/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_manager.mm
@@ -135,8 +135,8 @@ const auto kAudioItemIdentifier = @"touchbarAudio";
 
 	if (isEqual(kMainItemIdentifier)) {
 		auto *item = [[GroupTouchBarItem alloc] initWithIdentifier:itemId];
-		item.groupTouchBar =
-			[[[TouchBarMain alloc]
+		item.groupTouchBar
+			= [[[TouchBarMain alloc]
 				init:_controller
 				touchBarSwitches:_touchBarSwitches.events()] autorelease];
 		rpl::combine(
diff --git a/Telegram/SourceFiles/settings/settings_business.cpp b/Telegram/SourceFiles/settings/settings_business.cpp
index feee8e87c..163716819 100644
--- a/Telegram/SourceFiles/settings/settings_business.cpp
+++ b/Telegram/SourceFiles/settings/settings_business.cpp
@@ -605,8 +605,8 @@ QPointer<Ui::RpWidget> Business::createPinnedToBottom(
 	});
 	{
 		const auto callback = [=](int value) {
-			const auto options =
-				_controller->session().api().premium().subscriptionOptions();
+			auto &api = _controller->session().api();
+			const auto options = api.premium().subscriptionOptions();
 			if (options.empty()) {
 				return;
 			}
diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp
index a7eeeec80..902cae550 100644
--- a/Telegram/SourceFiles/settings/settings_premium.cpp
+++ b/Telegram/SourceFiles/settings/settings_premium.cpp
@@ -1221,8 +1221,8 @@ QPointer<Ui::RpWidget> Premium::createPinnedToBottom(
 #endif
 	{
 		const auto callback = [=](int value) {
-			const auto options =
-				_controller->session().api().premium().subscriptionOptions();
+			auto &api = _controller->session().api();
+			const auto options = api.premium().subscriptionOptions();
 			if (options.empty()) {
 				return;
 			}
diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.cpp b/Telegram/SourceFiles/settings/settings_privacy_security.cpp
index 045457819..4849719ff 100644
--- a/Telegram/SourceFiles/settings/settings_privacy_security.cpp
+++ b/Telegram/SourceFiles/settings/settings_privacy_security.cpp
@@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/toast/toast.h"
 #include "ui/wrap/slide_wrap.h"
 #include "ui/wrap/fade_wrap.h"
+#include "ui/widgets/fields/input_field.h"
 #include "ui/widgets/shadow.h"
 #include "ui/widgets/checkbox.h"
 #include "ui/vertical_list.h"
@@ -114,6 +115,56 @@ void AddPremiumStar(
 	}, badge->lifetime());
 }
 
+void OpenFileConfirmationsBox(not_null<Ui::GenericBox*> box) {
+	box->setTitle(tr::lng_settings_file_confirmations());
+
+	const auto settings = &Core::App().settings();
+	const auto &list = settings->noWarningExtensions();
+	const auto text = QStringList(begin(list), end(list)).join(' ');
+	const auto layout = box->verticalLayout();
+	const auto extensions = box->addRow(
+		object_ptr<Ui::InputField>(
+			box,
+			st::defaultInputField,
+			Ui::InputField::Mode::MultiLine,
+			tr::lng_settings_edit_extensions(),
+			TextWithTags{ text }),
+		st::boxRowPadding + QMargins(0, 0, 0, st::settingsPrivacySkip));
+	Ui::AddDividerText(layout, tr::lng_settings_edit_extensions_about());
+	Ui::AddSkip(layout);
+	const auto ip = layout->add(object_ptr<Ui::SettingsButton>(
+		box,
+		tr::lng_settings_edit_ip_confirm(),
+		st::settingsButtonNoIcon
+	))->toggleOn(rpl::single(settings->ipRevealWarning()));
+	Ui::AddSkip(layout);
+	Ui::AddDividerText(layout, tr::lng_settings_edit_ip_confirm_about());
+
+	box->setFocusCallback([=] {
+		extensions->setFocusFast();
+	});
+
+	box->addButton(tr::lng_settings_save(), [=] {
+		const auto extensionsList = extensions->getLastText()
+			.mid(0, 10240)
+			.split(' ', Qt::SkipEmptyParts)
+			.mid(0, 1024);
+		auto extensions = base::flat_set<QString>(
+			extensionsList.begin(),
+			extensionsList.end());
+		const auto ipRevealWarning = ip->toggled();
+		if (extensions != settings->noWarningExtensions()
+			|| ipRevealWarning != settings->ipRevealWarning()) {
+			settings->setNoWarningExtensions(std::move(extensions));
+			settings->setIpRevealWarning(ipRevealWarning);
+			Core::App().saveSettingsDelayed();
+		}
+		box->closeBox();
+
+	});
+	box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
+}
+
 QString PrivacyBase(Privacy::Key key, const Privacy::Rule &rule) {
 	using Key = Privacy::Key;
 	using Option = Privacy::Option;
@@ -645,6 +696,30 @@ void SetupBotsAndWebsites(
 	});
 
 	Ui::AddSkip(container);
+	Ui::AddDivider(container);
+}
+
+void SetupConfirmationExtensions(
+		not_null<Window::SessionController*> controller,
+		not_null<Ui::VerticalLayout*> container) {
+	if (Core::App().settings().noWarningExtensions().empty()
+		&& Core::App().settings().ipRevealWarning()) {
+		return;
+	}
+
+	Ui::AddSkip(container);
+	Ui::AddSubsectionTitle(container, tr::lng_settings_file_confirmations());
+
+	container->add(object_ptr<Button>(
+		container,
+		tr::lng_settings_edit_extensions(),
+		st::settingsButtonNoIcon
+	))->addClickHandler([=] {
+		controller->show(Box(OpenFileConfirmationsBox));
+	});
+
+	Ui::AddSkip(container);
+	Ui::AddDividerText(container, tr::lng_settings_edit_extensions_about());
 }
 
 void SetupBlockedList(
@@ -996,8 +1071,8 @@ void PrivacySecurity::setupContent(
 	AddDivider(content);
 #endif // !OS_MAC_STORE && !OS_WIN_STORE
 	SetupArchiveAndMute(controller, content);
+	SetupConfirmationExtensions(controller, content);
 	SetupBotsAndWebsites(controller, content);
-	AddDivider(content);
 	SetupSelfDestruction(controller, content, trigger());
 
 	Ui::ResizeFitChild(this, content);
diff --git a/Telegram/SourceFiles/statistics/chart_rulers_data.cpp b/Telegram/SourceFiles/statistics/chart_rulers_data.cpp
index 1b4e9c33a..4f9471c8c 100644
--- a/Telegram/SourceFiles/statistics/chart_rulers_data.cpp
+++ b/Telegram/SourceFiles/statistics/chart_rulers_data.cpp
@@ -12,17 +12,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 namespace Statistic {
 namespace {
 
-constexpr auto kMinLines = int(2);
-constexpr auto kMaxLines = int(6);
+constexpr auto kMinLines = ChartValue(2);
+constexpr auto kMaxLines = ChartValue(6);
 constexpr auto kStep = 5.;
 
-[[nodiscard]] int Round(int maxValue) {
-	const auto k = int(maxValue / kStep);
+[[nodiscard]] ChartValue Round(ChartValue maxValue) {
+	const auto k = ChartValue(maxValue / kStep);
 	return (k % 10 == 0) ? maxValue : ((maxValue / 10 + 1) * 10);
 }
 
-[[nodiscard]] QString Format(int absoluteValue) {
-	constexpr auto kTooMuch = int(10'000);
+[[nodiscard]] QString Format(ChartValue absoluteValue) {
+	constexpr auto kTooMuch = ChartValue(10'000);
 	return (absoluteValue >= kTooMuch)
 		? Lang::FormatCountToShort(absoluteValue).string
 		: QString::number(absoluteValue);
@@ -31,8 +31,8 @@ constexpr auto kStep = 5.;
 } // namespace
 
 ChartRulersData::ChartRulersData(
-		int newMaxHeight,
-		int newMinHeight,
+		ChartValue newMaxHeight,
+		ChartValue newMinHeight,
 		bool useMinHeight,
 		float64 rightRatio,
 		Fn<QString(float64)> leftCustomCaption,
@@ -42,11 +42,13 @@ ChartRulersData::ChartRulersData(
 			? Round(newMaxHeight)
 			: newMaxHeight;
 
-		const auto step = std::max(1, int(std::ceil(v / kStep)));
+		const auto step = std::max(
+			ChartValue(1),
+			ChartValue(std::ceil(v / kStep)));
 
 		auto n = kMaxLines;
 		if (v < kMaxLines) {
-			n = std::max(2, v + 1);
+			n = std::max(2, int(v + 1));
 		} else if (v / 2 < kMaxLines) {
 			n = v / 2 + 1;
 			if (v % 2 != 0) {
@@ -87,11 +89,11 @@ ChartRulersData::ChartRulersData(
 		}
 
 		lines.resize(n);
-		const auto diffAbsoluteValue = int((n - 1) * step);
+		const auto diffAbsoluteValue = ChartValue((n - 1) * step);
 		const auto skipFloatValues = (step / rightRatio) < 1;
 		for (auto i = 0; i < n; i++) {
 			auto &line = lines[i];
-			const auto value = int(i * step);
+			const auto value = ChartValue(i * step);
 			line.absoluteValue = newMinHeight + value;
 			line.relativeValue = 1. - value / float64(diffAbsoluteValue);
 			line.caption = leftCustomCaption
@@ -103,7 +105,7 @@ ChartRulersData::ChartRulersData(
 					? rightCustomCaption(line.absoluteValue)
 					: (!skipFloatValues)
 					? Format(v)
-					: ((v - int(v)) < 0.01)
+					: ((v - ChartValue(v)) < 0.01)
 					? Format(v)
 					: QString();
 			}
@@ -112,8 +114,8 @@ ChartRulersData::ChartRulersData(
 }
 
 void ChartRulersData::computeRelative(
-		int newMaxHeight,
-		int newMinHeight) {
+		ChartValue newMaxHeight,
+		ChartValue newMinHeight) {
 	for (auto &line : lines) {
 		line.relativeValue = 1.
 			- ((line.absoluteValue - newMinHeight)
@@ -121,10 +123,10 @@ void ChartRulersData::computeRelative(
 	}
 }
 
-int ChartRulersData::LookupHeight(int maxValue) {
+ChartValue ChartRulersData::LookupHeight(ChartValue maxValue) {
 	const auto v = (maxValue > 100) ? Round(maxValue) : maxValue;
 
-	const auto step = int(std::ceil(v / kStep));
+	const auto step = ChartValue(std::ceil(v / kStep));
 	return step * kStep;
 }
 
diff --git a/Telegram/SourceFiles/statistics/chart_rulers_data.h b/Telegram/SourceFiles/statistics/chart_rulers_data.h
index 4b5d63202..fef960c35 100644
--- a/Telegram/SourceFiles/statistics/chart_rulers_data.h
+++ b/Telegram/SourceFiles/statistics/chart_rulers_data.h
@@ -7,23 +7,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #pragma once
 
+#include "statistics/statistics_types.h"
+
 namespace Statistic {
 
 struct ChartRulersData final {
 public:
 	ChartRulersData(
-		int newMaxHeight,
-		int newMinHeight,
+		ChartValue newMaxHeight,
+		ChartValue newMinHeight,
 		bool useMinHeight,
 		float64 rightRatio,
 		Fn<QString(float64)> leftCustomCaption = nullptr,
 		Fn<QString(float64)> rightCustomCaption = nullptr);
 
 	void computeRelative(
-		int newMaxHeight,
-		int newMinHeight);
+		ChartValue newMaxHeight,
+		ChartValue newMinHeight);
 
-	[[nodiscard]] static int LookupHeight(int maxValue);
+	[[nodiscard]] static ChartValue LookupHeight(ChartValue maxValue);
 
 	struct Line final {
 		float64 absoluteValue = 0.;
diff --git a/Telegram/SourceFiles/statistics/chart_widget.cpp b/Telegram/SourceFiles/statistics/chart_widget.cpp
index 93d225c70..823481e6b 100644
--- a/Telegram/SourceFiles/statistics/chart_widget.cpp
+++ b/Telegram/SourceFiles/statistics/chart_widget.cpp
@@ -1157,7 +1157,7 @@ void ChartWidget::setupDetails() {
 		return;
 	}
 	const auto maxAbsoluteValue = [&] {
-		auto maxValue = 0;
+		auto maxValue = ChartValue(0);
 		for (const auto &l : _chartData.lines) {
 			maxValue = std::max(l.maxValue, maxValue);
 		}
diff --git a/Telegram/SourceFiles/statistics/segment_tree.cpp b/Telegram/SourceFiles/statistics/segment_tree.cpp
index f19f8b8d6..51c2082ed 100644
--- a/Telegram/SourceFiles/statistics/segment_tree.cpp
+++ b/Telegram/SourceFiles/statistics/segment_tree.cpp
@@ -14,7 +14,7 @@ constexpr auto kMinArraySize = size_t(30);
 
 } // namespace
 
-SegmentTree::SegmentTree(std::vector<int> array)
+SegmentTree::SegmentTree(std::vector<ChartValue> array)
 : _array(std::move(array)) {
 	if (_array.size() < kMinArraySize) {
 		return;
@@ -28,7 +28,7 @@ SegmentTree::SegmentTree(std::vector<int> array)
 	build(1, 0, _array.size());
 }
 
-void SegmentTree::build(int v, int from, int size) {
+void SegmentTree::build(ChartValue v, int from, int size) {
 	_heap[v].from = from;
 	_heap[v].to = (from + size - 1);
 
@@ -48,9 +48,9 @@ void SegmentTree::build(int v, int from, int size) {
 	}
 }
 
-int SegmentTree::rMaxQ(int from, int to) {
+ChartValue SegmentTree::rMaxQ(int from, int to) {
 	if (_array.size() < kMinArraySize) {
-		auto max = 0;
+		auto max = ChartValue(0);
 		from = std::max(from, 0);
 		to = std::min(to, int(_array.size() - 1));
 		for (auto i = from; i <= to; i++) {
@@ -61,7 +61,7 @@ int SegmentTree::rMaxQ(int from, int to) {
 	return rMaxQ(1, from, to);
 }
 
-int SegmentTree::rMaxQ(int v, int from, int to) {
+ChartValue SegmentTree::rMaxQ(ChartValue v, int from, int to) {
 	const auto &n = _heap[v];
 	// If you did a range update that contained this node,
 	// you can infer the Min value without going down the tree.
@@ -84,9 +84,9 @@ int SegmentTree::rMaxQ(int v, int from, int to) {
 	return 0;
 }
 
-int SegmentTree::rMinQ(int from, int to) {
+ChartValue SegmentTree::rMinQ(int from, int to) {
 	if (_array.size() < kMinArraySize) {
-		auto min = std::numeric_limits<int>::max();
+		auto min = std::numeric_limits<ChartValue>::max();
 		from = std::max(from, 0);
 		to = std::min(to, int(_array.size() - 1));
 		for (auto i = from; i <= to; i++) {
@@ -97,7 +97,7 @@ int SegmentTree::rMinQ(int from, int to) {
 	return rMinQ(1, from, to);
 }
 
-int SegmentTree::rMinQ(int v, int from, int to) {
+ChartValue SegmentTree::rMinQ(ChartValue v, int from, int to) {
 	const auto &n = _heap[v];
 	// If you did a range update that contained this node,
 	// you can infer the Min value without going down the tree.
@@ -117,10 +117,10 @@ int SegmentTree::rMinQ(int v, int from, int to) {
 		return std::min(leftMin, rightMin);
 	}
 
-	return std::numeric_limits<int>::max();
+	return std::numeric_limits<ChartValue>::max();
 }
 
-void SegmentTree::propagate(int v) {
+void SegmentTree::propagate(ChartValue v) {
 	auto &n = _heap[v];
 
 	if (n.pendingVal) {
@@ -131,7 +131,7 @@ void SegmentTree::propagate(int v) {
 	}
 }
 
-void SegmentTree::change(SegmentTree::Node &n, int value) {
+void SegmentTree::change(SegmentTree::Node &n, ChartValue value) {
 	n.pendingVal = { value, true };
 	n.sum = n.size() * value;
 	n.max = value;
diff --git a/Telegram/SourceFiles/statistics/segment_tree.h b/Telegram/SourceFiles/statistics/segment_tree.h
index e67adc76a..f83b16dd4 100644
--- a/Telegram/SourceFiles/statistics/segment_tree.h
+++ b/Telegram/SourceFiles/statistics/segment_tree.h
@@ -7,12 +7,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #pragma once
 
+#include "statistics/statistics_types.h"
+
 namespace Statistic {
 
 class SegmentTree final {
 public:
 	SegmentTree() = default;
-	SegmentTree(std::vector<int> array);
+	SegmentTree(std::vector<ChartValue> array);
 
 	[[nodiscard]] bool empty() const {
 		return _array.empty();
@@ -21,20 +23,20 @@ public:
 		return !empty();
 	}
 
-	[[nodiscard]] int rMaxQ(int from, int to);
-	[[nodiscard]] int rMinQ(int from, int to);
+	[[nodiscard]] ChartValue rMaxQ(int from, int to);
+	[[nodiscard]] ChartValue rMinQ(int from, int to);
 
 private:
 	struct Node final {
-		int sum = 0;
-		int max = 0;
-		int min = 0;
+		ChartValue sum = 0;
+		ChartValue max = 0;
+		ChartValue min = 0;
 
 		struct PendingVal {
 			[[nodiscard]] explicit operator bool() const {
 				return available;
 			}
-			int value = 0;
+			ChartValue value = 0;
 			bool available = false;
 		};
 		PendingVal pendingVal;
@@ -47,12 +49,12 @@ private:
 		}
 	};
 
-	void build(int v, int from, int size);
-	void propagate(int v);
-	void change(Node &n, int value);
+	void build(ChartValue v, int from, int size);
+	void propagate(ChartValue v);
+	void change(Node &n, ChartValue value);
 
-	[[nodiscard]] int rMaxQ(int v, int from, int to);
-	[[nodiscard]] int rMinQ(int v, int from, int to);
+	[[nodiscard]] ChartValue rMaxQ(ChartValue v, int from, int to);
+	[[nodiscard]] ChartValue rMinQ(ChartValue v, int from, int to);
 
 	[[nodiscard]] bool contains(int from1, int to1, int from2, int to2) const;
 	[[nodiscard]] bool intersects(
@@ -61,7 +63,7 @@ private:
 		int from2,
 		int to2) const;
 
-	std::vector<int> _array;
+	std::vector<ChartValue> _array;
 	std::vector<Node> _heap;
 
 };
diff --git a/Telegram/SourceFiles/statistics/statistics_data_deserialize.cpp b/Telegram/SourceFiles/statistics/statistics_data_deserialize.cpp
index ac649cf13..7f47a396a 100644
--- a/Telegram/SourceFiles/statistics/statistics_data_deserialize.cpp
+++ b/Telegram/SourceFiles/statistics/statistics_data_deserialize.cpp
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "base/debug_log.h"
 #include "data/data_statistics_chart.h"
+#include "statistics/statistics_types.h"
 
 #include <QtCore/QJsonArray>
 #include <QtCore/QJsonDocument>
@@ -61,7 +62,7 @@ Data::StatisticalChart StatisticalChartFromJSON(const QByteArray &json) {
 			line.isHiddenOnStart = ranges::contains(hiddenLines, columnId);
 			line.y.resize(length);
 			for (auto i = 0; i < length; i++) {
-				const auto value = int(base::SafeRound(
+				const auto value = ChartValue(base::SafeRound(
 					array.at(i + 1).toDouble()));
 				line.y[i] = value;
 				if (value > line.maxValue) {
diff --git a/Telegram/SourceFiles/statistics/statistics_types.h b/Telegram/SourceFiles/statistics/statistics_types.h
new file mode 100644
index 000000000..5d04d3442
--- /dev/null
+++ b/Telegram/SourceFiles/statistics/statistics_types.h
@@ -0,0 +1,14 @@
+/*
+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
+*/
+#pragma once
+
+namespace Statistic {
+
+using ChartValue = int64;
+
+} // namespace Statistic
diff --git a/Telegram/SourceFiles/statistics/view/abstract_chart_view.cpp b/Telegram/SourceFiles/statistics/view/abstract_chart_view.cpp
index d2c8e6bae..4b5f2929a 100644
--- a/Telegram/SourceFiles/statistics/view/abstract_chart_view.cpp
+++ b/Telegram/SourceFiles/statistics/view/abstract_chart_view.cpp
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "data/data_statistics_chart.h"
 #include "statistics/chart_lines_filter_controller.h"
+#include "statistics/statistics_types.h"
 
 namespace Statistic {
 
@@ -71,11 +72,11 @@ AbstractChartView::HeightLimits DefaultHeightLimits(
 		const std::shared_ptr<LinesFilterController> &linesFilter,
 		Data::StatisticalChart &chartData,
 		Limits xIndices) {
-	auto minValue = std::numeric_limits<int>::max();
-	auto maxValue = 0;
+	auto minValue = std::numeric_limits<ChartValue>::max();
+	auto maxValue = ChartValue(0);
 
-	auto minValueFull = std::numeric_limits<int>::max();
-	auto maxValueFull = 0;
+	auto minValueFull = std::numeric_limits<ChartValue>::max();
+	auto maxValueFull = ChartValue(0);
 	for (auto &l : chartData.lines) {
 		if (!linesFilter->isEnabled(l.id)) {
 			continue;
@@ -83,11 +84,11 @@ AbstractChartView::HeightLimits DefaultHeightLimits(
 		const auto r = ratios.ratio(l.id);
 		const auto lineMax = l.segmentTree.rMaxQ(xIndices.min, xIndices.max);
 		const auto lineMin = l.segmentTree.rMinQ(xIndices.min, xIndices.max);
-		maxValue = std::max(int(lineMax * r), maxValue);
-		minValue = std::min(int(lineMin * r), minValue);
+		maxValue = std::max(ChartValue(lineMax * r), maxValue);
+		minValue = std::min(ChartValue(lineMin * r), minValue);
 
-		maxValueFull = std::max(int(l.maxValue * r), maxValueFull);
-		minValueFull = std::min(int(l.minValue * r), minValueFull);
+		maxValueFull = std::max(ChartValue(l.maxValue * r), maxValueFull);
+		minValueFull = std::min(ChartValue(l.minValue * r), minValueFull);
 	}
 	if (maxValue == minValue) {
 		maxValue = chartData.maxValue;
diff --git a/Telegram/SourceFiles/statistics/view/bar_chart_view.cpp b/Telegram/SourceFiles/statistics/view/bar_chart_view.cpp
index 8beeaa509..f796e0294 100644
--- a/Telegram/SourceFiles/statistics/view/bar_chart_view.cpp
+++ b/Telegram/SourceFiles/statistics/view/bar_chart_view.cpp
@@ -256,9 +256,9 @@ AbstractChartView::HeightLimits BarChartView::heightLimits(
 	if (_cachedHeightLimits.ySum.empty()) {
 		_cachedHeightLimits.ySum.reserve(chartData.x.size());
 
-		auto maxValueFull = 0;
+		auto maxValueFull = ChartValue(0);
 		for (auto i = 0; i < chartData.x.size(); i++) {
-			auto sum = 0;
+			auto sum = ChartValue(0);
 			for (const auto &line : chartData.lines) {
 				if (linesFilterController()->isEnabled(line.id)) {
 					sum += line.y[i];
@@ -276,7 +276,7 @@ AbstractChartView::HeightLimits BarChartView::heightLimits(
 		_cachedHeightLimits.ySumSegmentTree.rMaxQ(
 			xIndices.min,
 			xIndices.max),
-		1);
+		ChartValue(1));
 	return {
 		.full = _cachedHeightLimits.full,
 		.ranged = { 0., float64(max) },
diff --git a/Telegram/SourceFiles/statistics/view/bar_chart_view.h b/Telegram/SourceFiles/statistics/view/bar_chart_view.h
index 44c1a7a6e..3105e0dae 100644
--- a/Telegram/SourceFiles/statistics/view/bar_chart_view.h
+++ b/Telegram/SourceFiles/statistics/view/bar_chart_view.h
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "statistics/segment_tree.h"
 #include "statistics/statistics_common.h"
+#include "statistics/statistics_types.h"
 #include "statistics/view/abstract_chart_view.h"
 #include "ui/effects/animation_value.h"
 
@@ -48,7 +49,7 @@ private:
 
 	struct {
 		Limits full;
-		std::vector<int> ySum;
+		std::vector<ChartValue> ySum;
 		SegmentTree ySumSegmentTree;
 	} _cachedHeightLimits;
 
diff --git a/Telegram/SourceFiles/statistics/view/stack_linear_chart_common.cpp b/Telegram/SourceFiles/statistics/view/stack_linear_chart_common.cpp
index a2ca3ff01..b7727bd71 100644
--- a/Telegram/SourceFiles/statistics/view/stack_linear_chart_common.cpp
+++ b/Telegram/SourceFiles/statistics/view/stack_linear_chart_common.cpp
@@ -81,7 +81,7 @@ PiePartData PiePartsPercentageByIndices(
 	sums.reserve(chartData.lines.size());
 	auto totalSum = 0.;
 	for (const auto &line : chartData.lines) {
-		auto sum = 0;
+		auto sum = ChartValue(0);
 		for (auto i = xIndices.min; i <= xIndices.max; i++) {
 			sum += line.y[i];
 		}
diff --git a/Telegram/SourceFiles/statistics/view/stack_linear_chart_view.cpp b/Telegram/SourceFiles/statistics/view/stack_linear_chart_view.cpp
index dc3e00d04..a4e4a7c87 100644
--- a/Telegram/SourceFiles/statistics/view/stack_linear_chart_view.cpp
+++ b/Telegram/SourceFiles/statistics/view/stack_linear_chart_view.cpp
@@ -165,8 +165,8 @@ void StackLinearChartView::prepareZoom(
 		_transition.zoomedOutXIndices = c.xIndices;
 		_transition.zoomedOutXPercentage = c.xPercentageLimits;
 	} else if (step == TransitionStep::PrepareToZoomIn) {
-		const auto &[zoomedStart, zoomedEnd] =
-			_transition.zoomedOutXIndices;
+		const auto &[zoomedStart, zoomedEnd]
+			= _transition.zoomedOutXIndices;
 		_transition.lines = std::vector<Transition::TransitionLine>(
 			c.chartData.lines.size(),
 			Transition::TransitionLine());
@@ -624,7 +624,7 @@ void StackLinearChartView::paintZoomed(QPainter &p, const PaintContext &c) {
 
 	if (selectedLineIndex >= 0) {
 		const auto &line = c.chartData.lines[selectedLineIndex];
-		auto sum = 0;
+		auto sum = ChartValue(0);
 		for (auto i = zoomedStart; i <= zoomedEnd; i++) {
 			sum += line.y[i];
 		}
@@ -669,8 +669,8 @@ void StackLinearChartView::paintZoomedFooter(
 			0);
 
 		const auto next = std::clamp(i + offset, zoomedStart, zoomedEnd);
-		const auto xPointPercentage =
-			(xPercentage[next] - xPercentage[zoomedStart])
+		const auto xPointPercentage
+			= (xPercentage[next] - xPercentage[zoomedStart])
 				/ (xPercentage[zoomedEnd] - xPercentage[zoomedStart]);
 		const auto xPoint = leftStart + width * xPointPercentage;
 
diff --git a/Telegram/SourceFiles/storage/storage_account.cpp b/Telegram/SourceFiles/storage/storage_account.cpp
index ca04135e8..e3c2e69c4 100644
--- a/Telegram/SourceFiles/storage/storage_account.cpp
+++ b/Telegram/SourceFiles/storage/storage_account.cpp
@@ -1991,8 +1991,8 @@ void Account::readStickerSets(
 			if (datesCount != scnt) {
 				return failed();
 			}
-			const auto fillDates =
-				((set->id == Data::Stickers::CloudRecentSetId)
+			const auto fillDates
+				= ((set->id == Data::Stickers::CloudRecentSetId)
 					|| (set->id == Data::Stickers::CloudRecentAttachedSetId))
 				&& (set->stickers.size() == datesCount);
 			if (fillDates) {
diff --git a/Telegram/SourceFiles/ui/boxes/choose_date_time.cpp b/Telegram/SourceFiles/ui/boxes/choose_date_time.cpp
index e4d8092a5..8651b5b01 100644
--- a/Telegram/SourceFiles/ui/boxes/choose_date_time.cpp
+++ b/Telegram/SourceFiles/ui/boxes/choose_date_time.cpp
@@ -146,8 +146,8 @@ ChooseDateTimeBoxDescriptor ChooseDateTimeBox(
 			width);
 	}, content->lifetime());
 
-	const auto calendar =
-		content->lifetime().make_state<QPointer<CalendarBox>>();
+	const auto calendar
+		= content->lifetime().make_state<QPointer<CalendarBox>>();
 	const auto calendarStyle = args.style.calendarStyle;
 	state->day->focusedChanges(
 	) | rpl::start_with_next([=](bool focused) {
diff --git a/Telegram/SourceFiles/ui/chat/chat_theme.cpp b/Telegram/SourceFiles/ui/chat/chat_theme.cpp
index 1022f8e56..4979e5c4a 100644
--- a/Telegram/SourceFiles/ui/chat/chat_theme.cpp
+++ b/Telegram/SourceFiles/ui/chat/chat_theme.cpp
@@ -909,12 +909,17 @@ QImage PrepareImageForTiled(const QImage &prepared) {
 		const QString &path,
 		const QByteArray &content,
 		bool gzipSvg) {
-	return Images::Read({
+	auto result = Images::Read({
 		.path = path,
 		.content = content,
 		.maxSize = QSize(kMaxSize, kMaxSize),
 		.gzipSvg = gzipSvg,
 	}).image;
+	if (result.isNull()) {
+		result = QImage(1, 1, QImage::Format_ARGB32_Premultiplied);
+		result.fill(Qt::black);
+	}
+	return result;
 }
 
 QImage GenerateBackgroundImage(
diff --git a/Telegram/SourceFiles/ui/controls/call_mute_button.cpp b/Telegram/SourceFiles/ui/controls/call_mute_button.cpp
index 2c57087c0..f9d1024e5 100644
--- a/Telegram/SourceFiles/ui/controls/call_mute_button.cpp
+++ b/Telegram/SourceFiles/ui/controls/call_mute_button.cpp
@@ -414,8 +414,8 @@ void BlobsWidget::init(int diameter) {
 		}
 
 		// Main circle.
-		const auto circleProgress =
-			Clamp(_switchConnectingProgress - kBlobPartAnimation)
+		const auto circleProgress
+			= Clamp(_switchConnectingProgress - kBlobPartAnimation)
 				/ kFillCirclePartAnimation;
 		const auto skipColoredCircle = (circleProgress == 1.);
 
@@ -744,8 +744,8 @@ void CallMuteButton::init() {
 	}, lifetime());
 
 	// State type.
-	const auto previousType =
-		lifetime().make_state<CallMuteButtonType>(_state.current().type);
+	const auto previousType
+		= lifetime().make_state<CallMuteButtonType>(_state.current().type);
 	setHandleMouseState(HandleMouseState::Disabled);
 
 	refreshGradients();
diff --git a/Telegram/SourceFiles/ui/menu_icons.style b/Telegram/SourceFiles/ui/menu_icons.style
index 862caf79d..4ec165b62 100644
--- a/Telegram/SourceFiles/ui/menu_icons.style
+++ b/Telegram/SourceFiles/ui/menu_icons.style
@@ -151,6 +151,7 @@ menuIconAsTopics: icon {{ "menu/mode_topics", menuIconColor }};
 menuIconAsMessages: icon {{ "menu/mode_messages", menuIconColor }};
 menuIconTagFilter: icon{{ "menu/tag_filter", menuIconColor }};
 menuIconTagRename: icon{{ "menu/tag_rename", menuIconColor }};
+menuIconGroupsHide: icon {{ "menu/hide_members", menuIconColor }};
 
 menuIconTTLAny: icon {{ "menu/auto_delete_plain", menuIconColor }};
 menuIconTTLAnyTextPosition: point(11px, 22px);
diff --git a/Telegram/SourceFiles/ui/widgets/label_with_custom_emoji.cpp b/Telegram/SourceFiles/ui/widgets/label_with_custom_emoji.cpp
new file mode 100644
index 000000000..4502d6553
--- /dev/null
+++ b/Telegram/SourceFiles/ui/widgets/label_with_custom_emoji.cpp
@@ -0,0 +1,32 @@
+/*
+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 "ui/widgets/label_with_custom_emoji.h"
+
+#include "core/ui_integration.h"
+#include "ui/widgets/labels.h"
+#include "styles/style_widgets.h"
+
+namespace Ui {
+
+object_ptr<Ui::FlatLabel> CreateLabelWithCustomEmoji(
+		QWidget *parent,
+		rpl::producer<TextWithEntities> &&text,
+		Core::MarkedTextContext context,
+		const style::FlatLabel &st) {
+	auto label = object_ptr<Ui::FlatLabel>(parent, st);
+	const auto raw = label.data();
+	if (!context.customEmojiRepaint) {
+		context.customEmojiRepaint = [=] { raw->update(); };
+	}
+	std::move(text) | rpl::start_with_next([=](const TextWithEntities &text) {
+		raw->setMarkedText(text, context);
+	}, label->lifetime());
+	return label;
+}
+
+} // namespace Ui
diff --git a/Telegram/SourceFiles/ui/widgets/label_with_custom_emoji.h b/Telegram/SourceFiles/ui/widgets/label_with_custom_emoji.h
new file mode 100644
index 000000000..f22e4e801
--- /dev/null
+++ b/Telegram/SourceFiles/ui/widgets/label_with_custom_emoji.h
@@ -0,0 +1,35 @@
+/*
+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
+*/
+#pragma once
+
+#include "core/ui_integration.h" // Core::MarkedTextContext.
+
+template <typename Object>
+class object_ptr;
+
+namespace Main {
+class Session;
+} // namespace Main
+
+namespace style {
+struct FlatLabel;
+} // namespace style
+
+namespace Ui {
+class FlatLabel;
+} // namespace Ui
+
+namespace Ui {
+
+[[nodiscard]] object_ptr<Ui::FlatLabel> CreateLabelWithCustomEmoji(
+	QWidget *parent,
+	rpl::producer<TextWithEntities> &&text,
+	Core::MarkedTextContext context,
+	const style::FlatLabel &st);
+
+} // namespace Ui
diff --git a/Telegram/SourceFiles/ui/widgets/level_meter.cpp b/Telegram/SourceFiles/ui/widgets/level_meter.cpp
index 15acd28ab..7a94b8c46 100644
--- a/Telegram/SourceFiles/ui/widgets/level_meter.cpp
+++ b/Telegram/SourceFiles/ui/widgets/level_meter.cpp
@@ -24,7 +24,7 @@ void LevelMeter::setValue(float value) {
 
 void LevelMeter::paintEvent(QPaintEvent* event) {
 	auto p = QPainter(this);
-	PainterHighQualityEnabler hq(p);
+	auto hq = PainterHighQualityEnabler(p);
 
 	p.setPen(Qt::NoPen);
 
@@ -34,7 +34,7 @@ void LevelMeter::paintEvent(QPaintEvent* event) {
 	const auto rect = QRect(0, 0, _st.lineWidth, height());
 	p.setBrush(activeFg);
 	for (auto i = 0; i < _st.lineCount; ++i) {
-		const auto valueAtLine = (float)(i + 1) / _st.lineCount;
+		const auto valueAtLine = (float64)(i + 1) / _st.lineCount;
 		if (valueAtLine > _value) {
 			p.setBrush(inactiveFg);
 		}
diff --git a/Telegram/SourceFiles/window/window_main_menu_helpers.cpp b/Telegram/SourceFiles/window/window_main_menu_helpers.cpp
index c10a3e41a..ba42403d4 100644
--- a/Telegram/SourceFiles/window/window_main_menu_helpers.cpp
+++ b/Telegram/SourceFiles/window/window_main_menu_helpers.cpp
@@ -27,10 +27,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/rect.h"
 #include "ui/widgets/popup_menu.h"
 #include "ui/widgets/tooltip.h"
+#include "ui/wrap/slide_wrap.h"
 #include "window/window_controller.h"
 #include "window/window_session_controller.h"
-#include "styles/style_chat.h" // popupMenuExpandedSeparator
-#include "styles/style_info.h" // infoTopBarMenu
+#include "styles/style_chat.h"
+#include "styles/style_info.h"
 #include "styles/style_menu_icons.h"
 #include "styles/style_window.h"
 
@@ -177,7 +178,9 @@ not_null<Ui::SettingsButton*> AddMyChannelsBox(
 				const auto count = c ? c->membersCount() : g->count;
 				_status.setText(
 					st::defaultTextStyle,
-					!p->username().isEmpty()
+					(g && !g->amIn())
+						? tr::lng_chat_status_unaccessible(tr::now)
+						: !p->username().isEmpty()
 						? ('@' + p->username())
 						: (count > 0)
 						? ((c && !c->isMegagroup())
@@ -219,10 +222,11 @@ not_null<Ui::SettingsButton*> AddMyChannelsBox(
 
 		};
 
-		const auto add = [&](not_null<PeerData*> peer) {
-			const auto row = box->addRow(
-				object_ptr<Button>(box, rpl::single(QString())),
-				{});
+		const auto add = [&](
+				not_null<PeerData*> peer,
+				not_null<Ui::VerticalLayout*> container) {
+			const auto row = container->add(
+				object_ptr<Button>(container, rpl::single(QString())));
 			row->setPeer(peer);
 			row->setClickedCallback([=] {
 				controller->showPeerHistory(peer);
@@ -233,8 +237,16 @@ not_null<Ui::SettingsButton*> AddMyChannelsBox(
 			userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
 		};
 
+		const auto inaccessibleWrap = box->verticalLayout()->add(
+			object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
+				box->verticalLayout(),
+				object_ptr<Ui::VerticalLayout>(box->verticalLayout())));
+		inaccessibleWrap->toggle(false, anim::type::instant);
+
 		const auto &data = controller->session().data();
 		auto ids = std::vector<PeerId>();
+		auto inaccessibleIds = std::vector<PeerId>();
+
 		if (chats) {
 			data.enumerateGroups([&](not_null<PeerData*> peer) {
 				peer = peer->migrateToOrMe();
@@ -244,8 +256,13 @@ not_null<Ui::SettingsButton*> AddMyChannelsBox(
 				const auto c = peer->asChannel();
 				const auto g = peer->asChat();
 				if ((c && c->amCreator()) || (g && g->amCreator())) {
+					if (g && !g->amIn()) {
+						inaccessibleIds.push_back(peer->id);
+						add(peer, inaccessibleWrap->entity());
+					} else {
+						add(peer, box->verticalLayout());
+					}
 					ids.push_back(peer->id);
-					add(peer);
 				}
 			});
 		} else {
@@ -253,13 +270,29 @@ not_null<Ui::SettingsButton*> AddMyChannelsBox(
 				if (channel->amCreator()
 					&& !ranges::contains(ids, channel->id)) {
 					ids.push_back(channel->id);
-					add(channel);
+					add(channel, box->verticalLayout());
 				}
 			});
 		}
 		if (ids.empty()) {
 			addIcon(box);
 		}
+		if (!inaccessibleIds.empty()) {
+			const auto icon = [=] {
+				return !inaccessibleWrap->toggled()
+					? &st::menuIconGroups
+					: &st::menuIconGroupsHide;
+			};
+			auto button = object_ptr<Ui::IconButton>(box, st::backgroundSwitchToDark);
+			button->setClickedCallback([=, raw = button.data()] {
+				inaccessibleWrap->toggle(
+					!inaccessibleWrap->toggled(),
+					anim::type::normal);
+				raw->setIconOverride(icon(), icon());
+			});
+			button->setIconOverride(icon(), icon());
+			box->addTopButton(std::move(button));
+		}
 	};
 
 	using Menu = base::unique_qptr<Ui::PopupMenu>;
diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp
index c3c0950c2..510902a15 100644
--- a/Telegram/SourceFiles/window/window_peer_menu.cpp
+++ b/Telegram/SourceFiles/window/window_peer_menu.cpp
@@ -68,6 +68,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "info/profile/info_profile_values.h"
 #include "info/statistics/info_statistics_widget.h"
 #include "info/stories/info_stories_widget.h"
+#include "data/components/scheduled_messages.h"
 #include "data/notify/data_notify_settings.h"
 #include "data/data_changes.h"
 #include "data/data_session.h"
@@ -79,7 +80,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_forum_topic.h"
 #include "data/data_user.h"
 #include "data/data_saved_sublist.h"
-#include "data/data_scheduled_messages.h"
 #include "data/data_histories.h"
 #include "data/data_chat_filters.h"
 #include "dialogs/dialogs_key.h"
@@ -143,8 +143,8 @@ void ShareBotGame(
 
 } // namespace
 
-const char kOptionViewProfileInChatsListContextMenu[] =
-	"view-profile-in-chats-list-context-menu";
+const char kOptionViewProfileInChatsListContextMenu[]
+	= "view-profile-in-chats-list-context-menu";
 
 namespace {
 
@@ -2255,8 +2255,8 @@ QPointer<Ui::BoxContent> ShowSendNowMessagesBox(
 		auto ids = QVector<MTPint>();
 		for (const auto item : session->data().idsToItems(list)) {
 			if (item->allowsSendNow()) {
-				ids.push_back(MTP_int(
-					session->data().scheduledMessages().lookupId(item)));
+				ids.push_back(
+					MTP_int(session->scheduledMessages().lookupId(item)));
 			}
 		}
 		session->api().request(MTPmessages_SendScheduledMessages(
diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp
index 2472bb39e..042397297 100644
--- a/Telegram/SourceFiles/window/window_session_controller.cpp
+++ b/Telegram/SourceFiles/window/window_session_controller.cpp
@@ -2023,10 +2023,10 @@ void SessionController::resizeForThirdSection() {
 
 	auto &settings = Core::App().settings();
 	auto layout = computeColumnLayout();
-	auto tabbedSelectorSectionEnabled =
-		settings.tabbedSelectorSectionEnabled();
-	auto thirdSectionInfoEnabled =
-		settings.thirdSectionInfoEnabled();
+	auto tabbedSelectorSectionEnabled
+		= settings.tabbedSelectorSectionEnabled();
+	auto thirdSectionInfoEnabled
+		= settings.thirdSectionInfoEnabled();
 	settings.setTabbedSelectorSectionEnabled(false);
 	settings.setThirdSectionInfoEnabled(false);
 
diff --git a/Telegram/ThirdParty/libprisma b/Telegram/ThirdParty/libprisma
index adf35ba88..23b0d70f9 160000
--- a/Telegram/ThirdParty/libprisma
+++ b/Telegram/ThirdParty/libprisma
@@ -1 +1 @@
-Subproject commit adf35ba88160777ce5b8d122630852394c58279f
+Subproject commit 23b0d70f9709da9b38561d5706891a134d18df76
diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile
index 92cd7bf31..964684ab8 100644
--- a/Telegram/build/docker/centos_env/Dockerfile
+++ b/Telegram/build/docker/centos_env/Dockerfile
@@ -90,10 +90,11 @@ RUN git clone -b v21.9 --depth=1 --recursive {{ GIT }}/protocolbuffers/protobuf.
 		-Dprotobuf_BUILD_TESTS=OFF \
 		-Dprotobuf_BUILD_PROTOBUF_BINARIES=ON \
 		-Dprotobuf_BUILD_LIBPROTOC=ON \
-		-Dprotobuf_WITH_ZLIB_DEFAULT=OFF \
-		-Dprotobuf_DEBUG_POSTFIX="" \
+		-Dprotobuf_WITH_ZLIB=OFF \
 	&& cmake --build build --parallel \
-	&& rm -rf .git
+	&& DESTDIR="{{ LibrariesPath }}/protobuf-cache" cmake --install build \
+	&& cd .. \
+	&& rm -rf protobuf
 
 FROM builder AS lcms2
 RUN git clone -b lcms2.15 --depth=1 {{ GIT }}/mm2/Little-CMS.git \
@@ -262,7 +263,7 @@ COPY --link --from=lcms2 {{ LibrariesPath }}/lcms2-cache /
 COPY --link --from=brotli {{ LibrariesPath }}/brotli-cache /
 COPY --link --from=highway {{ LibrariesPath }}/highway-cache /
 
-RUN git clone -b v0.8.2 --depth=1 {{ GIT }}/libjxl/libjxl.git \
+RUN git clone -b v0.10.2 --depth=1 {{ GIT }}/libjxl/libjxl.git \
 	&& cd libjxl \
 	&& cmake -GNinja -B build . \
 		-DCMAKE_BUILD_TYPE=None \
@@ -800,7 +801,7 @@ RUN cmake --build out --config Debug --parallel \
 FROM builder-base
 COPY --link --from=zlib {{ LibrariesPath }}/zlib-cache /
 COPY --link --from=xz {{ LibrariesPath }}/xz-cache /
-COPY --link --from=protobuf {{ LibrariesPath }}/protobuf protobuf
+COPY --link --from=protobuf {{ LibrariesPath }}/protobuf-cache /
 COPY --link --from=lcms2 {{ LibrariesPath }}/lcms2-cache /
 COPY --link --from=brotli {{ LibrariesPath }}/brotli-cache /
 COPY --link --from=highway {{ LibrariesPath }}/highway-cache /
diff --git a/Telegram/build/version b/Telegram/build/version
index 700c4cc2a..2b788edea 100644
--- a/Telegram/build/version
+++ b/Telegram/build/version
@@ -1,7 +1,7 @@
-AppVersion         4016006
+AppVersion         4016008
 AppVersionStrMajor 4.16
-AppVersionStrSmall 4.16.6
-AppVersionStr      4.16.6
+AppVersionStrSmall 4.16.8
+AppVersionStr      4.16.8
 BetaChannel        0
 AlphaVersion       0
-AppVersionOriginal 4.16.6
+AppVersionOriginal 4.16.8
diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake
index 12a984556..9eb06eef8 100644
--- a/Telegram/cmake/td_ui.cmake
+++ b/Telegram/cmake/td_ui.cmake
@@ -208,6 +208,7 @@ PRIVATE
     statistics/statistics_data_deserialize.h
     statistics/statistics_format_values.cpp
     statistics/statistics_format_values.h
+    statistics/statistics_types.h
     statistics/view/abstract_chart_view.cpp
     statistics/view/abstract_chart_view.h
     statistics/view/bar_chart_view.cpp
@@ -385,6 +386,8 @@ PRIVATE
     ui/widgets/discrete_sliders.h
     ui/widgets/gradient_round_button.cpp
     ui/widgets/gradient_round_button.h
+    ui/widgets/level_meter.cpp
+    ui/widgets/level_meter.h
     ui/widgets/multi_select.cpp
     ui/widgets/multi_select.h
     ui/widgets/sent_code_field.cpp
diff --git a/changelog.txt b/changelog.txt
index 0b1319a36..d6905d010 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,13 @@
+4.16.8 (16.04.24)
+
+- Fix in-app playing of some video and audio files.
+- Fix crash on Linux opening chats with custom backgrounds.
+- Fix crash on quit after using scheduled messages.
+
+4.16.7 (15.04.24)
+
+- Reimplement file open confirmations.
+
 4.16.6 (09.04.24)
 
 - Show custom emoji preview on long press.
diff --git a/cmake b/cmake
index f921cb6ab..4ec493812 160000
--- a/cmake
+++ b/cmake
@@ -1 +1 @@
-Subproject commit f921cb6aba9ada6099b3f9c8c237986ecda238f5
+Subproject commit 4ec493812d25de1eccfedccc49a23c15d032b118