From 62c4b1f30b9dce56e5a0278c64819f9dfb48f3de Mon Sep 17 00:00:00 2001
From: AlexeyZavar <sltkval1@gmail.com>
Date: Thu, 16 Jan 2025 16:40:43 +0300
Subject: [PATCH] fix: process more shortcuts

---
 Telegram/Resources/langs/lang.strings         |  28 ++-
 .../ayu/ui/settings/settings_ayu.cpp          |   2 +-
 .../SourceFiles/ayu/utils/windows_utils.cpp   | 166 +++++++++++++++---
 .../info/profile/info_profile_cover.cpp       |  14 +-
 .../info/profile/info_profile_cover.h         |   2 +-
 .../win/windows_app_user_model_id.cpp         |   6 +-
 .../platform/win/windows_app_user_model_id.h  |   3 +
 7 files changed, 179 insertions(+), 42 deletions(-)

diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index cbd28cefb..8f598d172 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -6108,13 +6108,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "ayu_DontSendOnlinePackets" = "Don't Send Online";
 "ayu_DontSendUploadProgress" = "Don't Send Typing";
 "ayu_SendOfflinePacketAfterOnline" = "Go Offline Automatically";
+"ayu_GhostModeOptionLongTapDescription" = "Long tap on any option to prevent it from changing on toggling Ghost Mode.";
 "ayu_MarkReadAfterAction" = "Read on Interact";
 "ayu_MarkReadAfterActionDescription" = "Automatically reads message when you send a new one or tap a reaction.";
 "ayu_MarkReadAfterSend" = "Send";
 "ayu_MarkReadAfterReaction" = "Reaction";
 "ayu_MarkReadAfterPoll" = "Poll";
 "ayu_UseScheduledMessages" = "Schedule Messages";
-"ayu_UseScheduledMessagesDescription" = "Automatically schedules outgoing messages for ~12 seconds, and more for media. Don't use on bad networks.";
+"ayu_UseScheduledMessagesDescription" = "Automatically schedules outgoing messages for ~12 seconds, and more for media. Sending messages this way, you won't appear online.\nDon't use on bad networks.";
 "ayu_SendWithoutSoundByDefault" = "Send without Sound";
 "ayu_SendWithoutSoundByDefaultDescription" = "Automatically sends outgoing messages without sound.";
 "ayu_SuggestGhostModeBeforeViewingStory" = "Story Ghost Mode Alert";
@@ -6146,6 +6147,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "ayu_DisableCustomBackgrounds" = "Disable Custom Backgrounds";
 "ayu_DisableNotificationsDelay" = "Disable Notify Delay";
 "ayu_LocalPremium" = "Local Telegram Premium";
+"ayu_DisplayGhostStatus" = "Display Ghost Mode Status";
 "ayu_CopyUsernameAsLink" = "Copy Username as Link";
 "ayu_CustomizationHeader" = "Customization";
 "ayu_DeletedMarkText" = "Deleted Mark";
@@ -6154,6 +6156,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "ayu_DeletedMarkCross" = "Cross";
 "ayu_DeletedMarkEyeCrossed" = "Eye Crossed";
 "ayu_EditedMarkText" = "Edited Mark";
+"ayu_ReplaceMarksWithIcons" = "Replace with Icons";
 "ayu_SemiTransparentDeletedMessages" = "Translucent Deleted Messages";
 "ayu_HideNotificationCounters" = "Hide Notification Counters";
 "ayu_HideNotificationBadge" = "Hide Notification Badge";
@@ -6224,6 +6227,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "ayu_RegexFilterQuickAdd" = "Add Filter";
 "ayu_RegexFilterBulletinText" = "Filter added to the **Shared filters**.";
 "ayu_RegexFilterBulletinAction" = "To Current Chat";
+"ayu_RegexFiltersListEmpty" = "No filters here yet.";
 "ayu_FiltersMenuSelectChat" = "Select Chat";
 "ayu_FiltersMenuImport" = "Import";
 "ayu_FiltersMenuExport" = "Export";
@@ -6302,6 +6306,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "ayu_DisableGhostModeTray" = "Disable Ghost Mode";
 "ayu_GhostModeEnabled" = "Ghost Mode turned on";
 "ayu_GhostModeDisabled" = "Ghost Mode turned off";
+"ayu_GhostModeGlobalSettings" = "Global Settings";
+"ayu_GhostModeGlobalSettingsDescription" = "Same for all accounts";
+"ayu_GhostModeSwitchedToGlobalSettings" = "Switched to same settings for all accounts.";
+"ayu_GhostModeSwitchedToIndividualSettings" = "Switched to individual settings for each account.";
 "ayu_StreamerModeToggle" = "Streamer Mode";
 "ayu_EnableStreamerModeTray" = "Enable Streamer Mode";
 "ayu_DisableStreamerModeTray" = "Disable Streamer Mode";
@@ -6311,6 +6319,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "ayu_ClearDeletedMenuText" = "Clear Deleted";
 "ayu_ViewDeletedMenuText" = "View Deleted";
 "ayu_ViewFiltersMenuText" = "View Filters";
+"ayu_ReadExclusionMenuText" = "Read Exclusion";
+"ayu_TypingExclusionMenuText" = "Typing Exclusion";
+"ayu_ExclusionTitle" = "Select exclusion type";
+"ayu_ExclusionUseDefault" = "Default";
+"ayu_ExclusionDontRead" = "Never Read";
+"ayu_ExclusionAlwaysRead" = "Always Read";
+"ayu_ExclusionDontType" = "Never Type";
+"ayu_ExclusionAlwaysType" = "Always Type";
 "ayu_PeekOnlineSuccess" = "Peeked via";
 "ayu_OneViewTTL" = "one view";
 "ayu_OnePlayTTL" = "one play";
@@ -6327,9 +6343,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "ayu_SuggestGhostModeStoryText" = "Do you want to enable **Ghost Mode** before viewing the story?";
 "ayu_SuggestGhostModeStoryActionTextYes" = "Yes";
 "ayu_SuggestGhostModeStoryActionTextNo" = "No";
+"ayu_ClearAttachmentsFolderWarning" = "Are you sure you want to clear the **attachments folder**? This action cannot be undone.";
+"ayu_ClearMessagesDatabaseWarning" = "Are you sure you want to clear **all deleted & edited messages**? This action cannot be undone.";
+"ayu_ClearTelegramDatabaseWarning" = "Are you sure you want to clear the **Telegram internal database**? This action cannot be undone.";
+"ayu_FirstLaunchAlert" = "AyuGram is **free** software and should only be obtained from our **official sources**. You assume **full responsibility** for using this application with your account.";
+"ayu_LocalPremiumAlert" = "With local Telegram Premium you won't have increased limits and won't be able to send animated emojis. Other users won't see your premium emoji and quote color.";
+"ayu_ExteraChatsAlert" = "Don't ask questions related to **AyuGram** in **exteraGram** chats. If you need help, ask in the official **AyuGram** chat.";
 "ayu_HideNextViewsDescriptionAyu" = "Hide my views forever, until Ghost Mode disabled.";
 "ayu_EnableGhostModeStories" = "Enable Ghost Mode";
-"ayu_GhostModeIsActive" = "Ghost Mode Is Active";
+"ayu_GhostModeIsActive" = "Ghost Mode is Active";
 "ayu_SimpleQuotesAndReplies" = "Disable Colorful Replies";
 "ayu_DisableSimilarChannels" = "Disable Similar Channels";
 "ayu_CollapseSimilarChannels" = "Collapse Similar Channels";
@@ -6376,7 +6398,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "ayu_ContextHideMessage" = "Hide";
 "ayu_ContextCopyCallbackData" = "Copy Callback Data";
 "ayu_RegisterURLScheme" = "Register URL Scheme";
-"ayu_LocalPremiumNotice" = "You're using **local** Telegram Premium.\nIt **won't** give you any benefits, except translator.\n**Enjoy the star near your nickname!**";
+"ayu_LocalPremiumNotice" = "You're using **local** Telegram Premium.\nIt **won't** give you any benefits.\n**Enjoy the star near your nickname!**";
 "ayu_SettingsWatermark" = "AyuGram developed and maintained by Radolyn Labs.";
 "ayu_ConfirmationSticker" = "Do you want to send this sticker?";
 "ayu_ConfirmationGIF" = "Do you want to send this GIF?";
diff --git a/Telegram/SourceFiles/ayu/ui/settings/settings_ayu.cpp b/Telegram/SourceFiles/ayu/ui/settings/settings_ayu.cpp
index 56866a8f3..efba614cc 100644
--- a/Telegram/SourceFiles/ayu/ui/settings/settings_ayu.cpp
+++ b/Telegram/SourceFiles/ayu/ui/settings/settings_ayu.cpp
@@ -1460,7 +1460,7 @@ void SetupMarks(not_null<Ui::VerticalLayout*> container) {
 
 	AddButtonWithIcon(
 		container,
-		rpl::single(QString("Replace with Icons")),
+		tr::ayu_ReplaceMarksWithIcons(),
 		st::settingsButtonNoIcon
 	)->toggleOn(
 		rpl::single(settings->replaceBottomInfoWithIcons)
diff --git a/Telegram/SourceFiles/ayu/utils/windows_utils.cpp b/Telegram/SourceFiles/ayu/utils/windows_utils.cpp
index c324bf1d3..016ccf1f5 100644
--- a/Telegram/SourceFiles/ayu/utils/windows_utils.cpp
+++ b/Telegram/SourceFiles/ayu/utils/windows_utils.cpp
@@ -8,46 +8,158 @@
 
 #include "windows_utils.h"
 
+#include "base/platform/win/base_windows_winrt.h"
+#include "platform/win/windows_app_user_model_id.h"
+
 #include <ShlObj_core.h>
+#include <propvarutil.h>
 
-void reloadAppIconFromTaskBar() {
-	QString appdata = QDir::fromNativeSeparators(qgetenv("APPDATA"));
-	QString ayugramIconPath = appdata + "/AyuGram.ico";
-
-	QString shortcut = appdata + "/Microsoft/Internet Explorer/Quick Launch/User Pinned/TaskBar/AyuGram Desktop.lnk";
+void processIcon(QString shortcut, QString iconPath) {
 	if (!QFile::exists(shortcut)) {
-		shortcut = appdata + "/Microsoft/Internet Explorer/Quick Launch/User Pinned/TaskBar/AyuGram.lnk";
+		return;
 	}
 
-	if (QFile::exists(shortcut)) {
-		IShellLink *pShellLink = NULL;
-		IPersistFile *pPersistFile = NULL;
+	IShellLink *pShellLink = NULL;
+	IPersistFile *pPersistFile = NULL;
 
-		HRESULT hr = CoCreateInstance(CLSID_ShellLink,
-									  NULL,
-									  CLSCTX_INPROC_SERVER,
-									  IID_IShellLink,
-									  (void**) &pShellLink);
+	HRESULT hr = CoCreateInstance(CLSID_ShellLink,
+								  NULL,
+								  CLSCTX_INPROC_SERVER,
+								  IID_IShellLink,
+								  (void**) &pShellLink);
+	if (SUCCEEDED(hr)) {
+		hr = pShellLink->QueryInterface(IID_IPersistFile, (void**) &pPersistFile);
 		if (SUCCEEDED(hr)) {
-			hr = pShellLink->QueryInterface(IID_IPersistFile, (void**) &pPersistFile);
-			if (SUCCEEDED(hr)) {
-				WCHAR wszShortcutPath[MAX_PATH];
-				shortcut.toWCharArray(wszShortcutPath);
-				wszShortcutPath[shortcut.length()] = '\0';
+			WCHAR wszShortcutPath[MAX_PATH];
+			shortcut.toWCharArray(wszShortcutPath);
+			wszShortcutPath[shortcut.length()] = '\0';
 
-				if (SUCCEEDED(pPersistFile->Load(wszShortcutPath, STGM_READWRITE))) {
-					pShellLink->SetIconLocation(ayugramIconPath.toStdWString().c_str(), 0);
-					pPersistFile->Save(wszShortcutPath, TRUE);
-				}
-
-				pPersistFile->Release();
+			if (SUCCEEDED(pPersistFile->Load(wszShortcutPath, STGM_READWRITE))) {
+				pShellLink->SetIconLocation(iconPath.toStdWString().c_str(), 0);
+				pPersistFile->Save(wszShortcutPath, TRUE);
 			}
 
-			pShellLink->Release();
+			pPersistFile->Release();
 		}
 
-		SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
+		pShellLink->Release();
 	}
 }
 
+void processLegacy(const QString &appdata, const QString &iconPath) {
+	auto shortcut = appdata + "/Microsoft/Internet Explorer/Quick Launch/User Pinned/TaskBar/AyuGram Desktop.lnk";
+	if (!QFile::exists(shortcut)) {
+		shortcut = appdata + "/Microsoft/Internet Explorer/Quick Launch/User Pinned/TaskBar/AyuGram.lnk";
+	}
+	if (!QFile::exists(shortcut)) {
+		return;
+	}
+
+	processIcon(shortcut, iconPath);
+}
+
+void processNewPinned(const QString &iconPath) {
+	if (!SUCCEEDED(CoInitialize(0))) {
+		return;
+	}
+	const auto coGuard = gsl::finally([]
+	{
+		CoUninitialize();
+	});
+
+	const auto path = Platform::AppUserModelId::PinnedIconsPath();
+	const auto native = QDir::toNativeSeparators(path).toStdWString();
+
+	const auto srcid = Platform::AppUserModelId::MyExecutablePathId();
+	if (!srcid) {
+		return;
+	}
+
+	WIN32_FIND_DATA findData;
+	HANDLE findHandle = FindFirstFileEx(
+		(native + L"*").c_str(),
+		FindExInfoStandard,
+		&findData,
+		FindExSearchNameMatch,
+		0,
+		0);
+	if (findHandle == INVALID_HANDLE_VALUE) {
+		return;
+	}
+
+	do {
+		std::wstring fname = native + findData.cFileName;
+		const auto filePath = QString::fromStdWString(fname);
+		if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+			continue;
+		}
+
+		DWORD attributes = GetFileAttributes(fname.c_str());
+		if (attributes >= 0xFFFFFFF) {
+			continue; // file does not exist
+		}
+
+		auto shellLink = base::WinRT::TryCreateInstance<IShellLink>(
+			CLSID_ShellLink);
+		if (!shellLink) {
+			continue;
+		}
+
+		auto persistFile = shellLink.try_as<IPersistFile>();
+		if (!persistFile) {
+			continue;
+		}
+
+		auto hr = persistFile->Load(fname.c_str(), STGM_READWRITE);
+		if (!SUCCEEDED(hr)) continue;
+
+		WCHAR dst[MAX_PATH] = {0};
+		hr = shellLink->GetPath(dst, MAX_PATH, nullptr, 0);
+		if (!SUCCEEDED(hr)) continue;
+
+		if (Platform::AppUserModelId::GetUniqueFileId(dst) == srcid) {
+			auto propertyStore = shellLink.try_as<IPropertyStore>();
+			if (!propertyStore) {
+				continue;
+			}
+
+			processIcon(filePath, iconPath);
+		}
+	} while (FindNextFile(findHandle, &findData));
+	DWORD errorCode = GetLastError();
+	if (errorCode && errorCode != ERROR_NO_MORE_FILES) {
+		return;
+	}
+	FindClose(findHandle);
+}
+
+void processNewShortcuts(const QString &iconPath) {
+	const auto path = Platform::AppUserModelId::systemShortcutPath();
+	if (path.isEmpty()) {
+		return;
+	}
+
+	const auto shortcut = path + u"AyuGram Desktop/AyuGram.lnk"_q;
+	const auto native = QDir::toNativeSeparators(path).toStdWString();
+
+	DWORD attributes = GetFileAttributes(native.c_str());
+	if (attributes >= 0xFFFFFFF) {
+		return; // file does not exist
+	}
+
+	const auto normalizedPath = QString::fromStdWString(native);
+	processIcon(normalizedPath, iconPath);
+}
+
+void reloadAppIconFromTaskBar() {
+	QString appdata = QDir::fromNativeSeparators(qgetenv("APPDATA"));
+	QString iconPath = appdata + "/AyuGram.ico";
+
+	processNewPinned(iconPath);
+	processNewShortcuts(iconPath);
+	processLegacy(appdata, iconPath);
+
+	SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
+}
+
 #endif
diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp
index 58d7f2fd2..675e3a0dc 100644
--- a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp
+++ b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp
@@ -357,7 +357,7 @@ Cover::Cover(
 			return controller->isGifPausedAtLeastFor(
 				Window::GifPauseReason::Layer);
 		}))
-, _devBadge(
+, _exteraBadge(
 	std::make_unique<Badge>(
 		this,
 		st::infoPeerBadge,
@@ -419,14 +419,14 @@ Cover::Cover(
 	}, _name->lifetime());
 
 	if (isExteraPeer(getBareID(_peer))) {
-		_devBadge->setContent(Info::Profile::Badge::Content{BadgeType::Extera});
+		_exteraBadge->setContent(Info::Profile::Badge::Content{BadgeType::Extera});
 	} else if (isSupporterPeer(getBareID(_peer))) {
-		_devBadge->setContent(Info::Profile::Badge::Content{BadgeType::ExteraSupporter});
+		_exteraBadge->setContent(Info::Profile::Badge::Content{BadgeType::ExteraSupporter});
 	} else {
-		_devBadge->setContent(Info::Profile::Badge::Content{BadgeType::None});
+		_exteraBadge->setContent(Info::Profile::Badge::Content{BadgeType::None});
 	}
 
-	_devBadge->updated() | rpl::start_with_next(
+	_exteraBadge->updated() | rpl::start_with_next(
 		[=]
 		{
 			refreshNameGeometry(width());
@@ -769,7 +769,7 @@ void Cover::refreshNameGeometry(int newWidth) {
 	if (const auto widget = _badge->widget()) {
 		nameWidth -= st::infoVerifiedCheckPosition.x() + widget->width();
 	}
-	if (const auto widget = _devBadge->widget()) {
+	if (const auto widget = _exteraBadge->widget()) {
 		nameWidth -= st::infoVerifiedCheckPosition.x()
 			+ widget->width()
 			+ (_badge->widget()
@@ -800,7 +800,7 @@ void Cover::refreshNameGeometry(int newWidth) {
 			   : 0);
 	const auto devBadgeTop = _st.nameTop;
 	const auto devBadgeBottom = _st.nameTop + _name->height();
-	_devBadge->move(devBadgeLeft, devBadgeTop, devBadgeBottom);
+	_exteraBadge->move(devBadgeLeft, devBadgeTop, devBadgeBottom);
 }
 
 void Cover::refreshStatusGeometry(int newWidth) {
diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.h b/Telegram/SourceFiles/info/profile/info_profile_cover.h
index 360460877..15e3875c5 100644
--- a/Telegram/SourceFiles/info/profile/info_profile_cover.h
+++ b/Telegram/SourceFiles/info/profile/info_profile_cover.h
@@ -149,7 +149,7 @@ private:
 	const std::unique_ptr<EmojiStatusPanel> _emojiStatusPanel;
 	const std::unique_ptr<Badge> _verify;
 	const std::unique_ptr<Badge> _badge;
-	const std::unique_ptr<Badge> _devBadge;
+	const std::unique_ptr<Badge> _exteraBadge;
 	rpl::variable<int> _onlineCount;
 
 	const object_ptr<Ui::UserpicButton> _userpic;
diff --git a/Telegram/SourceFiles/platform/win/windows_app_user_model_id.cpp b/Telegram/SourceFiles/platform/win/windows_app_user_model_id.cpp
index 83ec19e86..7faeab8e2 100644
--- a/Telegram/SourceFiles/platform/win/windows_app_user_model_id.cpp
+++ b/Telegram/SourceFiles/platform/win/windows_app_user_model_id.cpp
@@ -31,7 +31,9 @@ const WCHAR AppUserModelIdBase[] = L"AyuGram.AyuGramDesktop.Store";
 const WCHAR AppUserModelIdBase[] = L"AyuGram.AyuGramDesktop";
 #endif // OS_WIN_STORE
 
-[[nodiscard]] QString PinnedIconsPath() {
+} // namespace
+
+QString PinnedIconsPath() {
 	WCHAR wstrPath[kMaxFileLen] = {};
 	if (GetEnvironmentVariable(L"APPDATA", wstrPath, kMaxFileLen)) {
 		auto appData = QDir(QString::fromStdWString(std::wstring(wstrPath)));
@@ -41,8 +43,6 @@ const WCHAR AppUserModelIdBase[] = L"AyuGram.AyuGramDesktop";
 	return QString();
 }
 
-} // namespace
-
 const std::wstring &MyExecutablePath() {
 	static const auto Path = [&] {
 		auto result = std::wstring(kMaxFileLen, 0);
diff --git a/Telegram/SourceFiles/platform/win/windows_app_user_model_id.h b/Telegram/SourceFiles/platform/win/windows_app_user_model_id.h
index 76a7a3312..6680ae0ce 100644
--- a/Telegram/SourceFiles/platform/win/windows_app_user_model_id.h
+++ b/Telegram/SourceFiles/platform/win/windows_app_user_model_id.h
@@ -12,6 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 namespace Platform {
 namespace AppUserModelId {
 
+[[nodiscard]] QString PinnedIconsPath();
+QString systemShortcutPath();
+
 void CleanupShortcut();
 void CheckPinned();