From 5a80adcbfec1309b537721ab9e98f9d07187c135 Mon Sep 17 00:00:00 2001 From: Kevin Zhang <45326534+taooceros@users.noreply.github.com> Date: Sun, 17 Nov 2024 23:59:43 -0600 Subject: [PATCH 01/72] Send a reload request with ctx when reloading --- Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs index 5a6633525..ae4fd639d 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs @@ -133,10 +133,16 @@ namespace Flow.Launcher.Core.Plugin RPC.StartListening(); } - public virtual Task ReloadDataAsync() + public virtual async Task ReloadDataAsync() { SetupJsonRPC(); - return Task.CompletedTask; + try + { + await RPC.InvokeAsync("reload", context); + } + catch (RemoteMethodNotFoundException e) + { + } } public virtual async ValueTask DisposeAsync() From 4b9c23d49bcf823641258ab003225edd9face248 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Dec 2024 22:46:08 +0000 Subject: [PATCH 02/72] Bump nunit from 3.14.0 to 4.3.2 Bumps [nunit](https://github.com/nunit/nunit) from 3.14.0 to 4.3.2. - [Release notes](https://github.com/nunit/nunit/releases) - [Changelog](https://github.com/nunit/nunit/blob/main/CHANGES.md) - [Commits](https://github.com/nunit/nunit/compare/v3.14.0...4.3.2) --- updated-dependencies: - dependency-name: nunit dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- Flow.Launcher.Test/Flow.Launcher.Test.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Test/Flow.Launcher.Test.csproj b/Flow.Launcher.Test/Flow.Launcher.Test.csproj index 8286e142e..0241a374e 100644 --- a/Flow.Launcher.Test/Flow.Launcher.Test.csproj +++ b/Flow.Launcher.Test/Flow.Launcher.Test.csproj @@ -49,7 +49,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 20ffff6d1b8464d6aa20c7e32e12aac1e5abd6a0 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 9 Jan 2025 21:30:11 +0800 Subject: [PATCH 03/72] Use api to call api functions --- Flow.Launcher/ActionKeywords.xaml.cs | 2 +- Flow.Launcher/App.xaml.cs | 4 +++- Flow.Launcher/CustomQueryHotkeySetting.xaml.cs | 2 +- Flow.Launcher/CustomShortcutSetting.xaml.cs | 4 ++-- Flow.Launcher/Helper/HotKeyMapper.cs | 2 +- Flow.Launcher/PriorityChangeWindow.xaml.cs | 4 ++-- .../ViewModels/SettingsPaneAboutViewModel.cs | 2 +- .../ViewModels/SettingsPaneHotkeyViewModel.cs | 12 ++++++------ .../ViewModels/SettingsPaneProxyViewModel.cs | 2 +- .../ViewModels/SettingsPaneThemeViewModel.cs | 2 +- 10 files changed, 19 insertions(+), 17 deletions(-) diff --git a/Flow.Launcher/ActionKeywords.xaml.cs b/Flow.Launcher/ActionKeywords.xaml.cs index ba47a4ded..c3966e618 100644 --- a/Flow.Launcher/ActionKeywords.xaml.cs +++ b/Flow.Launcher/ActionKeywords.xaml.cs @@ -44,7 +44,7 @@ namespace Flow.Launcher else { string msg = translater.GetTranslation("newActionKeywordsHasBeenAssigned"); - MessageBoxEx.Show(msg); + App.API.ShowMsgBox(msg); } } } diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 4d1adc6cd..58da35e85 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -15,13 +15,15 @@ using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Infrastructure.Image; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Plugin; using Flow.Launcher.ViewModel; using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch; namespace Flow.Launcher { - public partial class App : IDisposable, ISingleInstanceApp + public partial class App : IDisposable, ISingleInstanceApp, IApp { + public IPublicAPI PublicAPI => API; public static PublicAPIInstance API { get; private set; } private const string Unique = "Flow.Launcher_Unique_Application_Mutex"; private static bool _disposed; diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs index 81e7600b8..47460ff7d 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs @@ -63,7 +63,7 @@ namespace Flow.Launcher o.ActionKeyword == item.ActionKeyword && o.Hotkey == item.Hotkey); if (updateCustomHotkey == null) { - MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("invalidPluginHotkey")); + App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("invalidPluginHotkey")); Close(); return; } diff --git a/Flow.Launcher/CustomShortcutSetting.xaml.cs b/Flow.Launcher/CustomShortcutSetting.xaml.cs index dec3506eb..4589b45ec 100644 --- a/Flow.Launcher/CustomShortcutSetting.xaml.cs +++ b/Flow.Launcher/CustomShortcutSetting.xaml.cs @@ -43,13 +43,13 @@ namespace Flow.Launcher { if (String.IsNullOrEmpty(Key) || String.IsNullOrEmpty(Value)) { - MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("emptyShortcut")); + App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("emptyShortcut")); return; } // Check if key is modified or adding a new one if (((update && originalKey != Key) || !update) && _hotkeyVm.DoesShortcutExist(Key)) { - MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("duplicateShortcut")); + App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("duplicateShortcut")); return; } DialogResult = !update || originalKey != Key || originalValue != Value; diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 8b30b8be1..b40406e42 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -46,7 +46,7 @@ internal static class HotKeyMapper { string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr); string errorMsgTitle = InternationalizationManager.Instance.GetTranslation("MessageBoxTitle"); - MessageBoxEx.Show(errorMsg, errorMsgTitle); + App.API.ShowMsgBox(errorMsg, errorMsgTitle); } } diff --git a/Flow.Launcher/PriorityChangeWindow.xaml.cs b/Flow.Launcher/PriorityChangeWindow.xaml.cs index 2154b058d..fbe2a941d 100644 --- a/Flow.Launcher/PriorityChangeWindow.xaml.cs +++ b/Flow.Launcher/PriorityChangeWindow.xaml.cs @@ -24,7 +24,7 @@ namespace Flow.Launcher this.pluginViewModel = pluginViewModel; if (plugin == null) { - MessageBoxEx.Show(translater.GetTranslation("cannotFindSpecifiedPlugin")); + App.API.ShowMsgBox(translater.GetTranslation("cannotFindSpecifiedPlugin")); Close(); } } @@ -44,7 +44,7 @@ namespace Flow.Launcher else { string msg = translater.GetTranslation("invalidPriority"); - MessageBoxEx.Show(msg); + App.API.ShowMsgBox(msg); } } diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs index 6e81db5e0..05fb16f5c 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs @@ -62,7 +62,7 @@ public partial class SettingsPaneAboutViewModel : BaseModel [RelayCommand] private void AskClearLogFolderConfirmation() { - var confirmResult = MessageBoxEx.Show( + var confirmResult = App.API.ShowMsgBox( InternationalizationManager.Instance.GetTranslation("clearlogfolderMessage"), InternationalizationManager.Instance.GetTranslation("clearlogfolder"), MessageBoxButton.YesNo diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs index 6d8af9a3f..fb57f499b 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs @@ -42,11 +42,11 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel var item = SelectedCustomPluginHotkey; if (item is null) { - MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem")); + App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem")); return; } - var result = MessageBoxEx.Show( + var result = App.API.ShowMsgBox( string.Format( InternationalizationManager.Instance.GetTranslation("deleteCustomHotkeyWarning"), item.Hotkey ), @@ -67,7 +67,7 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel var item = SelectedCustomPluginHotkey; if (item is null) { - MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem")); + App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem")); return; } @@ -88,11 +88,11 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel var item = SelectedCustomShortcut; if (item is null) { - MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem")); + App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem")); return; } - var result = MessageBoxEx.Show( + var result = App.API.ShowMsgBox( string.Format( InternationalizationManager.Instance.GetTranslation("deleteCustomShortcutWarning"), item.Key, item.Value ), @@ -112,7 +112,7 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel var item = SelectedCustomShortcut; if (item is null) { - MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem")); + App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem")); return; } diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneProxyViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneProxyViewModel.cs index 1c840fb27..e2f9e516c 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneProxyViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneProxyViewModel.cs @@ -22,7 +22,7 @@ public partial class SettingsPaneProxyViewModel : BaseModel private void OnTestProxyClicked() { var message = TestProxy(); - MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation(message)); + App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation(message)); } private string TestProxy() diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs index 8d8ccb780..980b2a811 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs @@ -49,7 +49,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel { if (ThemeManager.Instance.BlurEnabled && value) { - MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("shadowEffectNotAllowed")); + App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("shadowEffectNotAllowed")); return; } From 8b910500c6f3a2fe170cdc8261c6d0add722b744 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 9 Jan 2025 21:43:33 +0800 Subject: [PATCH 04/72] Add IApp & AppExtensions for accessing the properties & functions from anywhere in the application --- Flow.Launcher.Core/AppExtensions.cs | 15 +++++++++++++++ Flow.Launcher.Core/IApp.cs | 13 +++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 Flow.Launcher.Core/AppExtensions.cs create mode 100644 Flow.Launcher.Core/IApp.cs diff --git a/Flow.Launcher.Core/AppExtensions.cs b/Flow.Launcher.Core/AppExtensions.cs new file mode 100644 index 000000000..b02612d72 --- /dev/null +++ b/Flow.Launcher.Core/AppExtensions.cs @@ -0,0 +1,15 @@ +using System.Windows; +using Flow.Launcher.Plugin; + +namespace Flow.Launcher.Core; + +/// +/// Extension properties and functions of the current application singleton object. +/// +public static class AppExtensions +{ + /// + /// Gets the public API of the current application singleton object. + /// + public static IPublicAPI API => (Application.Current as IApp)!.PublicAPI; +} diff --git a/Flow.Launcher.Core/IApp.cs b/Flow.Launcher.Core/IApp.cs new file mode 100644 index 000000000..233fd5ed1 --- /dev/null +++ b/Flow.Launcher.Core/IApp.cs @@ -0,0 +1,13 @@ +using Flow.Launcher.Plugin; + +namespace Flow.Launcher.Core +{ + /// + /// Interface for the current application singleton object exposing the properties + /// and functions that can be accessed from anywhere in the application. + /// + public interface IApp + { + public IPublicAPI PublicAPI { get; } + } +} From 88e84378daee2906bdbfa131ba4d012225464df1 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 9 Jan 2025 21:44:24 +0800 Subject: [PATCH 05/72] Use api in app extensions to call api functions --- Flow.Launcher.Core/Configuration/Portable.cs | 20 +++++++++---------- .../Environments/AbstractPluginEnvironment.cs | 6 +++--- .../Environments/PythonEnvironment.cs | 2 +- .../Environments/TypeScriptEnvironment.cs | 2 +- .../Environments/TypeScriptV2Environment.cs | 2 +- Flow.Launcher.Core/Plugin/PluginManager.cs | 2 +- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 2 +- .../Resource/Internationalization.cs | 2 +- Flow.Launcher.Core/Resource/Theme.cs | 4 ++-- Flow.Launcher.Core/Updater.cs | 10 +++++----- 10 files changed, 26 insertions(+), 26 deletions(-) diff --git a/Flow.Launcher.Core/Configuration/Portable.cs b/Flow.Launcher.Core/Configuration/Portable.cs index d7c73fb46..cb375c586 100644 --- a/Flow.Launcher.Core/Configuration/Portable.cs +++ b/Flow.Launcher.Core/Configuration/Portable.cs @@ -40,7 +40,7 @@ namespace Flow.Launcher.Core.Configuration #endif IndicateDeletion(DataLocation.PortableDataPath); - MessageBoxEx.Show("Flow Launcher needs to restart to finish disabling portable mode, " + + AppExtensions.API.ShowMsgBox("Flow Launcher needs to restart to finish disabling portable mode, " + "after the restart your portable data profile will be deleted and roaming data profile kept"); UpdateManager.RestartApp(Constant.ApplicationFileName); @@ -64,7 +64,7 @@ namespace Flow.Launcher.Core.Configuration #endif IndicateDeletion(DataLocation.RoamingDataPath); - MessageBoxEx.Show("Flow Launcher needs to restart to finish enabling portable mode, " + + AppExtensions.API.ShowMsgBox("Flow Launcher needs to restart to finish enabling portable mode, " + "after the restart your roaming data profile will be deleted and portable data profile kept"); UpdateManager.RestartApp(Constant.ApplicationFileName); @@ -95,13 +95,13 @@ namespace Flow.Launcher.Core.Configuration public void MoveUserDataFolder(string fromLocation, string toLocation) { - FilesFolders.CopyAll(fromLocation, toLocation, MessageBoxEx.Show); + FilesFolders.CopyAll(fromLocation, toLocation, (s) => AppExtensions.API.ShowMsgBox(s)); VerifyUserDataAfterMove(fromLocation, toLocation); } public void VerifyUserDataAfterMove(string fromLocation, string toLocation) { - FilesFolders.VerifyBothFolderFilesEqual(fromLocation, toLocation, MessageBoxEx.Show); + FilesFolders.VerifyBothFolderFilesEqual(fromLocation, toLocation, (s) => AppExtensions.API.ShowMsgBox(s)); } public void CreateShortcuts() @@ -157,13 +157,13 @@ namespace Flow.Launcher.Core.Configuration // delete it and prompt the user to pick the portable data location if (File.Exists(roamingDataDeleteFilePath)) { - FilesFolders.RemoveFolderIfExists(roamingDataDir, MessageBoxEx.Show); + FilesFolders.RemoveFolderIfExists(roamingDataDir, (s) => AppExtensions.API.ShowMsgBox(s)); - if (MessageBoxEx.Show("Flow Launcher has detected you enabled portable mode, " + + if (AppExtensions.API.ShowMsgBox("Flow Launcher has detected you enabled portable mode, " + "would you like to move it to a different location?", string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes) { - FilesFolders.OpenPath(Constant.RootDirectory, MessageBoxEx.Show); + FilesFolders.OpenPath(Constant.RootDirectory, (s) => AppExtensions.API.ShowMsgBox(s)); Environment.Exit(0); } @@ -172,9 +172,9 @@ namespace Flow.Launcher.Core.Configuration // delete it and notify the user about it. else if (File.Exists(portableDataDeleteFilePath)) { - FilesFolders.RemoveFolderIfExists(portableDataDir, MessageBoxEx.Show); + FilesFolders.RemoveFolderIfExists(portableDataDir, (s) => AppExtensions.API.ShowMsgBox(s)); - MessageBoxEx.Show("Flow Launcher has detected you disabled portable mode, " + + AppExtensions.API.ShowMsgBox("Flow Launcher has detected you disabled portable mode, " + "the relevant shortcuts and uninstaller entry have been created"); } } @@ -186,7 +186,7 @@ namespace Flow.Launcher.Core.Configuration if (roamingLocationExists && portableLocationExists) { - MessageBoxEx.Show(string.Format("Flow Launcher detected your user data exists both in {0} and " + + AppExtensions.API.ShowMsgBox(string.Format("Flow Launcher detected your user data exists both in {0} and " + "{1}. {2}{2}Please delete {1} in order to proceed. No changes have occurred.", DataLocation.PortableDataPath, DataLocation.RoamingDataPath, Environment.NewLine)); diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs index 6d41e2383..cada05031 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -57,7 +57,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments EnvName, Environment.NewLine ); - if (MessageBoxEx.Show(noRuntimeMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) + if (AppExtensions.API.ShowMsgBox(noRuntimeMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) { var msg = string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginChooseRuntimeExecutable"), EnvName); string selectedFile; @@ -82,7 +82,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments } else { - MessageBoxEx.Show(string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language)); + AppExtensions.API.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language)); Log.Error("PluginsLoader", $"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.", $"{Language}Environment"); @@ -98,7 +98,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments if (expectedPath == currentPath) return; - FilesFolders.RemoveFolderIfExists(installedDirPath, MessageBoxEx.Show); + FilesFolders.RemoveFolderIfExists(installedDirPath, (s) => AppExtensions.API.ShowMsgBox(s)); InstallEnvironment(); diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs index 96c29646e..56bc20b4f 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs @@ -28,7 +28,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments internal override void InstallEnvironment() { - FilesFolders.RemoveFolderIfExists(InstallPath, MessageBoxEx.Show); + FilesFolders.RemoveFolderIfExists(InstallPath, (s) => AppExtensions.API.ShowMsgBox(s)); // Python 3.11.4 is no longer Windows 7 compatible. If user is on Win 7 and // uses Python plugin they need to custom install and use v3.8.9 diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs index 0d6f109e0..1d43b815a 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs @@ -25,7 +25,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments internal override void InstallEnvironment() { - FilesFolders.RemoveFolderIfExists(InstallPath, MessageBoxEx.Show); + FilesFolders.RemoveFolderIfExists(InstallPath, (s) => AppExtensions.API.ShowMsgBox(s)); DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath).Wait(); diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs index 582a4407c..49bf4e958 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs @@ -25,7 +25,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments internal override void InstallEnvironment() { - FilesFolders.RemoveFolderIfExists(InstallPath, MessageBoxEx.Show); + FilesFolders.RemoveFolderIfExists(InstallPath, (s) => AppExtensions.API.ShowMsgBox(s)); DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath).Wait(); diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 5c4eaa1da..9e1cf3b9d 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -519,7 +519,7 @@ namespace Flow.Launcher.Core.Plugin var newPluginPath = Path.Combine(installDirectory, folderName); - FilesFolders.CopyAll(pluginFolderPath, newPluginPath, MessageBoxEx.Show); + FilesFolders.CopyAll(pluginFolderPath, newPluginPath, (s) => AppExtensions.API.ShowMsgBox(s)); Directory.Delete(tempFolderPluginPath, true); diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 7973c66ba..8cbeb7473 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -119,7 +119,7 @@ namespace Flow.Launcher.Core.Plugin _ = Task.Run(() => { - MessageBoxEx.Show($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + + AppExtensions.API.ShowMsgBox($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + $"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" + $"Please refer to the logs for more information", "", MessageBoxButton.OK, MessageBoxImage.Warning); diff --git a/Flow.Launcher.Core/Resource/Internationalization.cs b/Flow.Launcher.Core/Resource/Internationalization.cs index 1505e84f8..a1cefabe3 100644 --- a/Flow.Launcher.Core/Resource/Internationalization.cs +++ b/Flow.Launcher.Core/Resource/Internationalization.cs @@ -124,7 +124,7 @@ namespace Flow.Launcher.Core.Resource // "Do you want to search with pinyin?" string text = languageToSet == AvailableLanguages.Chinese ? "是否启用拼音搜索?" : "是否啓用拼音搜索?" ; - if (MessageBoxEx.Show(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) + if (AppExtensions.API.ShowMsgBox(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) return false; return true; diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index 1d8409306..8622d4caf 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -108,7 +108,7 @@ namespace Flow.Launcher.Core.Resource Log.Error($"|Theme.ChangeTheme|Theme <{theme}> path can't be found"); if (theme != defaultTheme) { - MessageBoxEx.Show(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_path_not_exists"), theme)); + AppExtensions.API.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_path_not_exists"), theme)); ChangeTheme(defaultTheme); } return false; @@ -118,7 +118,7 @@ namespace Flow.Launcher.Core.Resource Log.Error($"|Theme.ChangeTheme|Theme <{theme}> fail to parse"); if (theme != defaultTheme) { - MessageBoxEx.Show(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_parse_error"), theme)); + AppExtensions.API.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_parse_error"), theme)); ChangeTheme(defaultTheme); } return false; diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index b92d86568..8745d54b7 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -53,7 +53,7 @@ namespace Flow.Launcher.Core if (newReleaseVersion <= currentVersion) { if (!silentUpdate) - MessageBoxEx.Show(api.GetTranslation("update_flowlauncher_already_on_latest")); + AppExtensions.API.ShowMsgBox(api.GetTranslation("update_flowlauncher_already_on_latest")); return; } @@ -68,9 +68,9 @@ namespace Flow.Launcher.Core if (DataLocation.PortableDataLocationInUse()) { var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}"; - FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination, MessageBoxEx.Show); - if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination, MessageBoxEx.Show)) - MessageBoxEx.Show(string.Format(api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"), + FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination, (s) => AppExtensions.API.ShowMsgBox(s)); + if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination, (s) => AppExtensions.API.ShowMsgBox(s))) + AppExtensions.API.ShowMsgBox(string.Format(api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"), DataLocation.PortableDataPath, targetDestination)); } @@ -83,7 +83,7 @@ namespace Flow.Launcher.Core Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}"); - if (MessageBoxEx.Show(newVersionTips, api.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes) + if (AppExtensions.API.ShowMsgBox(newVersionTips, api.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes) { UpdateManager.RestartApp(Constant.ApplicationFileName); } From 2716c40c0bd7229fed198d3ba75fd27fb0816084 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 9 Jan 2025 21:50:07 +0800 Subject: [PATCH 06/72] Move MessageBoxEx to main project for better development experience --- {Flow.Launcher.Core => Flow.Launcher}/MessageBoxEx.xaml | 4 ++-- {Flow.Launcher.Core => Flow.Launcher}/MessageBoxEx.xaml.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename {Flow.Launcher.Core => Flow.Launcher}/MessageBoxEx.xaml (98%) rename {Flow.Launcher.Core => Flow.Launcher}/MessageBoxEx.xaml.cs (99%) diff --git a/Flow.Launcher.Core/MessageBoxEx.xaml b/Flow.Launcher/MessageBoxEx.xaml similarity index 98% rename from Flow.Launcher.Core/MessageBoxEx.xaml rename to Flow.Launcher/MessageBoxEx.xaml index fff107a68..be12ca16c 100644 --- a/Flow.Launcher.Core/MessageBoxEx.xaml +++ b/Flow.Launcher/MessageBoxEx.xaml @@ -1,9 +1,9 @@  Date: Thu, 9 Jan 2025 12:48:43 -0600 Subject: [PATCH 07/72] resolve link before using File.Replace --- .../Storage/JsonStorage.cs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs index 642250627..33bc1ff6c 100644 --- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs @@ -31,11 +31,12 @@ namespace Flow.Launcher.Infrastructure.Storage protected JsonStorage() { } + public JsonStorage(string filePath) { FilePath = filePath; DirectoryPath = Path.GetDirectoryName(filePath) ?? throw new ArgumentException("Invalid file path"); - + Helper.ValidateDirectory(DirectoryPath); } @@ -97,6 +98,7 @@ namespace Flow.Launcher.Infrastructure.Storage return default; } } + private void RestoreBackup() { Log.Info($"|JsonStorage.Load|Failed to load settings.json, {BackupFilePath} restored successfully"); @@ -179,25 +181,21 @@ namespace Flow.Launcher.Infrastructure.Storage public void Save() { string serialized = JsonSerializer.Serialize(Data, - new JsonSerializerOptions - { - WriteIndented = true - }); + new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(TempFilePath, serialized); AtomicWriteSetting(); } + public async Task SaveAsync() { var tempOutput = File.OpenWrite(TempFilePath); await JsonSerializer.SerializeAsync(tempOutput, Data, - new JsonSerializerOptions - { - WriteIndented = true - }); + new JsonSerializerOptions { WriteIndented = true }); AtomicWriteSetting(); } + private void AtomicWriteSetting() { if (!File.Exists(FilePath)) @@ -206,9 +204,9 @@ namespace Flow.Launcher.Infrastructure.Storage } else { - File.Replace(TempFilePath, FilePath, BackupFilePath); + var finalFilePath = new FileInfo(FilePath).LinkTarget ?? FilePath; + File.Replace(TempFilePath, finalFilePath, BackupFilePath); } } - } } From 4b38e0e714abf705e8f337dbcb26f29893105c7e Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Fri, 10 Jan 2025 13:11:35 -0600 Subject: [PATCH 08/72] properly dispose the filestream --- Flow.Launcher.Infrastructure/Storage/JsonStorage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs index 33bc1ff6c..507838d94 100644 --- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs @@ -190,7 +190,7 @@ namespace Flow.Launcher.Infrastructure.Storage public async Task SaveAsync() { - var tempOutput = File.OpenWrite(TempFilePath); + await using var tempOutput = File.OpenWrite(TempFilePath); await JsonSerializer.SerializeAsync(tempOutput, Data, new JsonSerializerOptions { WriteIndented = true }); AtomicWriteSetting(); From 2a1d502affe50f43aafd38e7ee3ff5435993cbef Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Fri, 10 Jan 2025 13:18:14 -0600 Subject: [PATCH 09/72] fix build error --- Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs index ae4fd639d..f95266c7f 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs @@ -138,7 +138,7 @@ namespace Flow.Launcher.Core.Plugin SetupJsonRPC(); try { - await RPC.InvokeAsync("reload", context); + await RPC.InvokeAsync("reload", Context); } catch (RemoteMethodNotFoundException e) { From 32cac76c827681b436bd8bdc8de03e608181260c Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 12 Jan 2025 18:44:52 +0800 Subject: [PATCH 10/72] Improve Settings management --- .../UserSettings/Settings.cs | 16 ++++++++++++++-- Flow.Launcher/App.xaml.cs | 8 ++++++-- .../ViewModel/SettingWindowViewModel.cs | 13 +++---------- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 0bcc9368d..3e43e3d32 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -1,10 +1,10 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Drawing; using System.Text.Json.Serialization; using System.Windows; using Flow.Launcher.Infrastructure.Hotkey; +using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedModels; using Flow.Launcher.ViewModel; @@ -13,6 +13,18 @@ namespace Flow.Launcher.Infrastructure.UserSettings { public class Settings : BaseModel, IHotkeySettings { + private FlowLauncherJsonStorage _storage; + + public void Initialize(FlowLauncherJsonStorage storage) + { + _storage = storage; + } + + public void Save() + { + _storage.Save(); + } + private string language = "en"; private string _theme = Constant.DefaultTheme; public string Hotkey { get; set; } = $"{KeyConstant.Alt} + {KeyConstant.Space}"; diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 58da35e85..9d7a0671e 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -14,6 +14,7 @@ using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Infrastructure.Image; using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using Flow.Launcher.ViewModel; @@ -52,6 +53,10 @@ namespace Flow.Launcher { await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => { + var storage = new FlowLauncherJsonStorage(); + _settings = storage.Load(); + _settings.Initialize(storage); + _portable.PreStartCleanUpAfterPortabilityUpdate(); Log.Info( @@ -62,8 +67,7 @@ namespace Flow.Launcher var imageLoadertask = ImageLoader.InitializeAsync(); - _settingsVM = new SettingWindowViewModel(_updater, _portable); - _settings = _settingsVM.Settings; + _settingsVM = new SettingWindowViewModel(_settings, _updater, _portable); _settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled(); AbstractPluginEnvironment.PreStartPluginExecutablePathUpdate(_settings); diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index 04dd6312b..95a1eb675 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -1,6 +1,5 @@ using Flow.Launcher.Core; using Flow.Launcher.Core.Configuration; -using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; @@ -8,21 +7,17 @@ namespace Flow.Launcher.ViewModel; public class SettingWindowViewModel : BaseModel { - private readonly FlowLauncherJsonStorage _storage; - public Updater Updater { get; } public IPortable Portable { get; } public Settings Settings { get; } - public SettingWindowViewModel(Updater updater, IPortable portable) + public SettingWindowViewModel(Settings settings, Updater updater, IPortable portable) { - _storage = new FlowLauncherJsonStorage(); - + Settings = settings; Updater = updater; Portable = portable; - Settings = _storage.Load(); } public async void UpdateApp() @@ -30,14 +25,12 @@ public class SettingWindowViewModel : BaseModel await Updater.UpdateAppAsync(App.API, false); } - - /// /// Save Flow settings. Plugins settings are not included. /// public void Save() { - _storage.Save(); + Settings.Save(); } public double SettingWindowWidth From 1b76a2bc1a1c3356b3bab5c3be61b3427b1de7fd Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 12 Jan 2025 19:45:36 +0800 Subject: [PATCH 11/72] Use dependency injection for all services --- .../Flow.Launcher.Infrastructure.csproj | 1 + .../PinyinAlphabet.cs | 8 ++- Flow.Launcher.Infrastructure/StringMatcher.cs | 5 +- Flow.Launcher/App.xaml.cs | 61 +++++++++++-------- Flow.Launcher/Flow.Launcher.csproj | 5 +- Flow.Launcher/PublicAPIInstance.cs | 12 ++-- Flow.Launcher/ViewModel/MainViewModel.cs | 5 +- .../ViewModel/SettingWindowViewModel.cs | 11 ++-- 8 files changed, 65 insertions(+), 43 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index 1475252ca..84b603161 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -54,6 +54,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Flow.Launcher.Infrastructure/PinyinAlphabet.cs b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs index 7d7235968..8eaa757be 100644 --- a/Flow.Launcher.Infrastructure/PinyinAlphabet.cs +++ b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs @@ -6,6 +6,7 @@ using System.Text; using JetBrains.Annotations; using Flow.Launcher.Infrastructure.UserSettings; using ToolGood.Words.Pinyin; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Infrastructure { @@ -129,7 +130,12 @@ namespace Flow.Launcher.Infrastructure private Settings _settings; - public void Initialize([NotNull] Settings settings) + public PinyinAlphabet() + { + Initialize(Ioc.Default.GetRequiredService()); + } + + private void Initialize([NotNull] Settings settings) { _settings = settings ?? throw new ArgumentNullException(nameof(settings)); } diff --git a/Flow.Launcher.Infrastructure/StringMatcher.cs b/Flow.Launcher.Infrastructure/StringMatcher.cs index bd5dbdda9..7134fc760 100644 --- a/Flow.Launcher.Infrastructure/StringMatcher.cs +++ b/Flow.Launcher.Infrastructure/StringMatcher.cs @@ -1,4 +1,5 @@ -using Flow.Launcher.Plugin.SharedModels; +using CommunityToolkit.Mvvm.DependencyInjection; +using Flow.Launcher.Plugin.SharedModels; using System; using System.Collections.Generic; using System.Linq; @@ -15,7 +16,7 @@ namespace Flow.Launcher.Infrastructure public StringMatcher(IAlphabet alphabet = null) { - _alphabet = alphabet; + _alphabet = Ioc.Default.GetRequiredService(); } public static StringMatcher Instance { get; internal set; } diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 9d7a0671e..8cd054148 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -4,6 +4,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; +using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Core; using Flow.Launcher.Core.Configuration; using Flow.Launcher.Core.ExternalPlugins.Environments; @@ -18,23 +19,18 @@ using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using Flow.Launcher.ViewModel; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch; namespace Flow.Launcher { - public partial class App : IDisposable, ISingleInstanceApp, IApp + public partial class App : IDisposable, ISingleInstanceApp { - public IPublicAPI PublicAPI => API; public static PublicAPIInstance API { get; private set; } private const string Unique = "Flow.Launcher_Unique_Application_Mutex"; private static bool _disposed; private Settings _settings; - private MainViewModel _mainVM; - private SettingWindowViewModel _settingsVM; - private readonly Updater _updater = new Updater(Flow.Launcher.Properties.Settings.Default.GithubRepo); - private readonly Portable _portable = new Portable(); - private readonly PinyinAlphabet _alphabet = new PinyinAlphabet(); - private StringMatcher _stringMatcher; [STAThread] public static void Main() @@ -53,37 +49,51 @@ namespace Flow.Launcher { await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => { + // Initialize settings var storage = new FlowLauncherJsonStorage(); _settings = storage.Load(); _settings.Initialize(storage); + _settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled(); - _portable.PreStartCleanUpAfterPortabilityUpdate(); + // Configure the dependency injection container + var host = Host.CreateDefaultBuilder() + .UseContentRoot(AppContext.BaseDirectory) + .ConfigureServices(services => services + .AddSingleton(_ => _settings) + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + ).Build(); + Ioc.Default.ConfigureServices(host.Services); - Log.Info( - "|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------"); + Ioc.Default.GetRequiredService().Initialize(Launcher.Properties.Settings.Default.GithubRepo); + + Ioc.Default.GetRequiredService().PreStartCleanUpAfterPortabilityUpdate(); + + Log.Info("|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------"); Log.Info($"|App.OnStartup|Runtime info:{ErrorReporting.RuntimeInfo()}"); + RegisterAppDomainExceptions(); RegisterDispatcherUnhandledException(); var imageLoadertask = ImageLoader.InitializeAsync(); - _settingsVM = new SettingWindowViewModel(_settings, _updater, _portable); - _settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled(); - AbstractPluginEnvironment.PreStartPluginExecutablePathUpdate(_settings); - _alphabet.Initialize(_settings); - _stringMatcher = new StringMatcher(_alphabet); - StringMatcher.Instance = _stringMatcher; - _stringMatcher.UserSettingSearchPrecision = _settings.QuerySearchPrecision; + var stringMatcher = Ioc.Default.GetRequiredService(); + StringMatcher.Instance = stringMatcher; + stringMatcher.UserSettingSearchPrecision = _settings.QuerySearchPrecision; InternationalizationManager.Instance.Settings = _settings; InternationalizationManager.Instance.ChangeLanguage(_settings.Language); PluginManager.LoadPlugins(_settings.PluginSettings); - _mainVM = new MainViewModel(_settings); - API = new PublicAPIInstance(_settingsVM, _mainVM, _alphabet); + API = Ioc.Default.GetRequiredService() as PublicAPIInstance; Http.API = API; Http.Proxy = _settings.Proxy; @@ -91,14 +101,15 @@ namespace Flow.Launcher await PluginManager.InitializePluginsAsync(API); await imageLoadertask; - var window = new MainWindow(_settings, _mainVM); + var mainVM = Ioc.Default.GetRequiredService(); + var window = new MainWindow(_settings, mainVM); Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}"); Current.MainWindow = window; Current.MainWindow.Title = Constant.FlowLauncher; - HotKeyMapper.Initialize(_mainVM); + HotKeyMapper.Initialize(mainVM); // main windows needs initialized before theme change because of blur settings ThemeManager.Instance.Settings = _settings; @@ -147,11 +158,11 @@ namespace Flow.Launcher { // check update every 5 hours var timer = new PeriodicTimer(TimeSpan.FromHours(5)); - await _updater.UpdateAppAsync(API); + await Ioc.Default.GetRequiredService().UpdateAppAsync(API); while (await timer.WaitForNextTickAsync()) // check updates on startup - await _updater.UpdateAppAsync(API); + await Ioc.Default.GetRequiredService().UpdateAppAsync(API); } }); } @@ -194,7 +205,7 @@ namespace Flow.Launcher public void OnSecondAppStarted() { - _mainVM.Show(); + Ioc.Default.GetRequiredService().Show(); } } } diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 4ec249c2b..cab3915d5 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -1,4 +1,4 @@ - + WinExe @@ -83,12 +83,13 @@ - all runtime; build; native; contentfiles; analyzers; buildtransitive + + all diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index f0295cf24..50765294c 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -25,7 +25,7 @@ using Flow.Launcher.Infrastructure.Storage; using System.Collections.Concurrent; using System.Diagnostics; using System.Collections.Specialized; -using Flow.Launcher.Core; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher { @@ -33,15 +33,15 @@ namespace Flow.Launcher { private readonly SettingWindowViewModel _settingsVM; private readonly MainViewModel _mainVM; - private readonly PinyinAlphabet _alphabet; + private readonly IAlphabet _alphabet; #region Constructor - public PublicAPIInstance(SettingWindowViewModel settingsVM, MainViewModel mainVM, PinyinAlphabet alphabet) + public PublicAPIInstance() { - _settingsVM = settingsVM; - _mainVM = mainVM; - _alphabet = alphabet; + _settingsVM = Ioc.Default.GetRequiredService(); + _mainVM = Ioc.Default.GetRequiredService(); + _alphabet = Ioc.Default.GetRequiredService(); GlobalHotkey.hookedKeyboardCallback = KListener_hookedKeyboardCallback; WebRequest.RegisterPrefix("data", new DataWebRequestFactory()); } diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 55bc8d1b3..f5141a8fa 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -25,6 +25,7 @@ using System.Windows.Input; using System.ComponentModel; using Flow.Launcher.Infrastructure.Image; using System.Windows.Media; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.ViewModel { @@ -58,13 +59,13 @@ namespace Flow.Launcher.ViewModel #region Constructor - public MainViewModel(Settings settings) + public MainViewModel() { _queryTextBeforeLeaveResults = ""; _queryText = ""; _lastQuery = new Query(); - Settings = settings; + Settings = Ioc.Default.GetRequiredService(); Settings.PropertyChanged += (_, args) => { switch (args.PropertyName) diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index 95a1eb675..7549db1a3 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -1,4 +1,5 @@ -using Flow.Launcher.Core; +using CommunityToolkit.Mvvm.DependencyInjection; +using Flow.Launcher.Core; using Flow.Launcher.Core.Configuration; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; @@ -13,11 +14,11 @@ public class SettingWindowViewModel : BaseModel public Settings Settings { get; } - public SettingWindowViewModel(Settings settings, Updater updater, IPortable portable) + public SettingWindowViewModel() { - Settings = settings; - Updater = updater; - Portable = portable; + Settings = Ioc.Default.GetRequiredService(); + Updater = Ioc.Default.GetRequiredService(); + Portable = Ioc.Default.GetRequiredService(); } public async void UpdateApp() From a748141b1e8d7342336068ef65cc0890eac63992 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 12 Jan 2025 19:47:49 +0800 Subject: [PATCH 12/72] Use IPublicAPI instead --- Flow.Launcher/App.xaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 8cd054148..9b86c6cc4 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -27,7 +27,7 @@ namespace Flow.Launcher { public partial class App : IDisposable, ISingleInstanceApp { - public static PublicAPIInstance API { get; private set; } + public static IPublicAPI API { get; private set; } private const string Unique = "Flow.Launcher_Unique_Application_Mutex"; private static bool _disposed; private Settings _settings; @@ -93,7 +93,7 @@ namespace Flow.Launcher PluginManager.LoadPlugins(_settings.PluginSettings); - API = Ioc.Default.GetRequiredService() as PublicAPIInstance; + API = Ioc.Default.GetRequiredService(); Http.API = API; Http.Proxy = _settings.Proxy; From 3cb9d1dce4b606cc80fb3a7fc9e51da08237c1b8 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 12 Jan 2025 20:04:44 +0800 Subject: [PATCH 13/72] Remove IApp & AppExtensions and use dependency injection instead --- Flow.Launcher.Core/AppExtensions.cs | 15 ------------ Flow.Launcher.Core/Configuration/Portable.cs | 24 +++++++++++-------- .../Environments/AbstractPluginEnvironment.cs | 11 +++++---- .../Environments/PythonEnvironment.cs | 2 +- .../Environments/TypeScriptEnvironment.cs | 2 +- .../Environments/TypeScriptV2Environment.cs | 2 +- Flow.Launcher.Core/IApp.cs | 13 ---------- Flow.Launcher.Core/Plugin/PluginManager.cs | 2 +- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 3 ++- .../Resource/Internationalization.cs | 3 ++- Flow.Launcher.Core/Resource/Theme.cs | 7 ++++-- Flow.Launcher.Core/Updater.cs | 17 +++++++------ Flow.Launcher/App.xaml.cs | 2 ++ .../ViewModel/SettingWindowViewModel.cs | 10 ++++++-- 14 files changed, 54 insertions(+), 59 deletions(-) delete mode 100644 Flow.Launcher.Core/AppExtensions.cs delete mode 100644 Flow.Launcher.Core/IApp.cs diff --git a/Flow.Launcher.Core/AppExtensions.cs b/Flow.Launcher.Core/AppExtensions.cs deleted file mode 100644 index b02612d72..000000000 --- a/Flow.Launcher.Core/AppExtensions.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Windows; -using Flow.Launcher.Plugin; - -namespace Flow.Launcher.Core; - -/// -/// Extension properties and functions of the current application singleton object. -/// -public static class AppExtensions -{ - /// - /// Gets the public API of the current application singleton object. - /// - public static IPublicAPI API => (Application.Current as IApp)!.PublicAPI; -} diff --git a/Flow.Launcher.Core/Configuration/Portable.cs b/Flow.Launcher.Core/Configuration/Portable.cs index cb375c586..069154364 100644 --- a/Flow.Launcher.Core/Configuration/Portable.cs +++ b/Flow.Launcher.Core/Configuration/Portable.cs @@ -9,11 +9,15 @@ using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin.SharedCommands; using System.Linq; +using CommunityToolkit.Mvvm.DependencyInjection; +using Flow.Launcher.Plugin; namespace Flow.Launcher.Core.Configuration { public class Portable : IPortable { + private readonly IPublicAPI API = Ioc.Default.GetRequiredService(); + /// /// As at Squirrel.Windows version 1.5.2, UpdateManager needs to be disposed after finish /// @@ -40,7 +44,7 @@ namespace Flow.Launcher.Core.Configuration #endif IndicateDeletion(DataLocation.PortableDataPath); - AppExtensions.API.ShowMsgBox("Flow Launcher needs to restart to finish disabling portable mode, " + + API.ShowMsgBox("Flow Launcher needs to restart to finish disabling portable mode, " + "after the restart your portable data profile will be deleted and roaming data profile kept"); UpdateManager.RestartApp(Constant.ApplicationFileName); @@ -64,7 +68,7 @@ namespace Flow.Launcher.Core.Configuration #endif IndicateDeletion(DataLocation.RoamingDataPath); - AppExtensions.API.ShowMsgBox("Flow Launcher needs to restart to finish enabling portable mode, " + + API.ShowMsgBox("Flow Launcher needs to restart to finish enabling portable mode, " + "after the restart your roaming data profile will be deleted and portable data profile kept"); UpdateManager.RestartApp(Constant.ApplicationFileName); @@ -95,13 +99,13 @@ namespace Flow.Launcher.Core.Configuration public void MoveUserDataFolder(string fromLocation, string toLocation) { - FilesFolders.CopyAll(fromLocation, toLocation, (s) => AppExtensions.API.ShowMsgBox(s)); + FilesFolders.CopyAll(fromLocation, toLocation, (s) => API.ShowMsgBox(s)); VerifyUserDataAfterMove(fromLocation, toLocation); } public void VerifyUserDataAfterMove(string fromLocation, string toLocation) { - FilesFolders.VerifyBothFolderFilesEqual(fromLocation, toLocation, (s) => AppExtensions.API.ShowMsgBox(s)); + FilesFolders.VerifyBothFolderFilesEqual(fromLocation, toLocation, (s) => API.ShowMsgBox(s)); } public void CreateShortcuts() @@ -157,13 +161,13 @@ namespace Flow.Launcher.Core.Configuration // delete it and prompt the user to pick the portable data location if (File.Exists(roamingDataDeleteFilePath)) { - FilesFolders.RemoveFolderIfExists(roamingDataDir, (s) => AppExtensions.API.ShowMsgBox(s)); + FilesFolders.RemoveFolderIfExists(roamingDataDir, (s) => API.ShowMsgBox(s)); - if (AppExtensions.API.ShowMsgBox("Flow Launcher has detected you enabled portable mode, " + + if (API.ShowMsgBox("Flow Launcher has detected you enabled portable mode, " + "would you like to move it to a different location?", string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes) { - FilesFolders.OpenPath(Constant.RootDirectory, (s) => AppExtensions.API.ShowMsgBox(s)); + FilesFolders.OpenPath(Constant.RootDirectory, (s) => API.ShowMsgBox(s)); Environment.Exit(0); } @@ -172,9 +176,9 @@ namespace Flow.Launcher.Core.Configuration // delete it and notify the user about it. else if (File.Exists(portableDataDeleteFilePath)) { - FilesFolders.RemoveFolderIfExists(portableDataDir, (s) => AppExtensions.API.ShowMsgBox(s)); + FilesFolders.RemoveFolderIfExists(portableDataDir, (s) => API.ShowMsgBox(s)); - AppExtensions.API.ShowMsgBox("Flow Launcher has detected you disabled portable mode, " + + API.ShowMsgBox("Flow Launcher has detected you disabled portable mode, " + "the relevant shortcuts and uninstaller entry have been created"); } } @@ -186,7 +190,7 @@ namespace Flow.Launcher.Core.Configuration if (roamingLocationExists && portableLocationExists) { - AppExtensions.API.ShowMsgBox(string.Format("Flow Launcher detected your user data exists both in {0} and " + + API.ShowMsgBox(string.Format("Flow Launcher detected your user data exists both in {0} and " + "{1}. {2}{2}Please delete {1} in order to proceed. No changes have occurred.", DataLocation.PortableDataPath, DataLocation.RoamingDataPath, Environment.NewLine)); diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs index cada05031..451df6147 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -8,11 +8,14 @@ using System.Linq; using System.Windows; using System.Windows.Forms; using Flow.Launcher.Core.Resource; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Core.ExternalPlugins.Environments { public abstract class AbstractPluginEnvironment { + protected readonly IPublicAPI API = Ioc.Default.GetRequiredService(); + internal abstract string Language { get; } internal abstract string EnvName { get; } @@ -25,7 +28,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments internal virtual string FileDialogFilter => string.Empty; - internal abstract string PluginsSettingsFilePath { get; set; } + internal abstract string PluginsSettingsFilePath { get; set; } internal List PluginMetadataList; @@ -57,7 +60,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments EnvName, Environment.NewLine ); - if (AppExtensions.API.ShowMsgBox(noRuntimeMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) + if (API.ShowMsgBox(noRuntimeMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) { var msg = string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginChooseRuntimeExecutable"), EnvName); string selectedFile; @@ -82,7 +85,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments } else { - AppExtensions.API.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language)); + API.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language)); Log.Error("PluginsLoader", $"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.", $"{Language}Environment"); @@ -98,7 +101,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments if (expectedPath == currentPath) return; - FilesFolders.RemoveFolderIfExists(installedDirPath, (s) => AppExtensions.API.ShowMsgBox(s)); + FilesFolders.RemoveFolderIfExists(installedDirPath, (s) => API.ShowMsgBox(s)); InstallEnvironment(); diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs index 56bc20b4f..607c19062 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs @@ -28,7 +28,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments internal override void InstallEnvironment() { - FilesFolders.RemoveFolderIfExists(InstallPath, (s) => AppExtensions.API.ShowMsgBox(s)); + FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s)); // Python 3.11.4 is no longer Windows 7 compatible. If user is on Win 7 and // uses Python plugin they need to custom install and use v3.8.9 diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs index 1d43b815a..399f7cc03 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs @@ -25,7 +25,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments internal override void InstallEnvironment() { - FilesFolders.RemoveFolderIfExists(InstallPath, (s) => AppExtensions.API.ShowMsgBox(s)); + FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s)); DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath).Wait(); diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs index 49bf4e958..e8cb72e11 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs @@ -25,7 +25,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments internal override void InstallEnvironment() { - FilesFolders.RemoveFolderIfExists(InstallPath, (s) => AppExtensions.API.ShowMsgBox(s)); + FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s)); DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath).Wait(); diff --git a/Flow.Launcher.Core/IApp.cs b/Flow.Launcher.Core/IApp.cs deleted file mode 100644 index 233fd5ed1..000000000 --- a/Flow.Launcher.Core/IApp.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Flow.Launcher.Plugin; - -namespace Flow.Launcher.Core -{ - /// - /// Interface for the current application singleton object exposing the properties - /// and functions that can be accessed from anywhere in the application. - /// - public interface IApp - { - public IPublicAPI PublicAPI { get; } - } -} diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 9e1cf3b9d..a776c10ab 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -519,7 +519,7 @@ namespace Flow.Launcher.Core.Plugin var newPluginPath = Path.Combine(installDirectory, folderName); - FilesFolders.CopyAll(pluginFolderPath, newPluginPath, (s) => AppExtensions.API.ShowMsgBox(s)); + FilesFolders.CopyAll(pluginFolderPath, newPluginPath, (s) => API.ShowMsgBox(s)); Directory.Delete(tempFolderPluginPath, true); diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 8cbeb7473..4827cf69d 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; using System.Windows; +using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Core.ExternalPlugins.Environments; #pragma warning disable IDE0005 using Flow.Launcher.Infrastructure.Logger; @@ -119,7 +120,7 @@ namespace Flow.Launcher.Core.Plugin _ = Task.Run(() => { - AppExtensions.API.ShowMsgBox($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + + Ioc.Default.GetRequiredService().ShowMsgBox($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + $"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" + $"Please refer to the logs for more information", "", MessageBoxButton.OK, MessageBoxImage.Warning); diff --git a/Flow.Launcher.Core/Resource/Internationalization.cs b/Flow.Launcher.Core/Resource/Internationalization.cs index a1cefabe3..de066dda1 100644 --- a/Flow.Launcher.Core/Resource/Internationalization.cs +++ b/Flow.Launcher.Core/Resource/Internationalization.cs @@ -11,6 +11,7 @@ using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using System.Globalization; using System.Threading.Tasks; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Core.Resource { @@ -124,7 +125,7 @@ namespace Flow.Launcher.Core.Resource // "Do you want to search with pinyin?" string text = languageToSet == AvailableLanguages.Chinese ? "是否启用拼音搜索?" : "是否啓用拼音搜索?" ; - if (AppExtensions.API.ShowMsgBox(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) + if (Ioc.Default.GetRequiredService().ShowMsgBox(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) return false; return true; diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index 8622d4caf..2749da532 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -11,6 +11,8 @@ using System.Windows.Media.Effects; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; +using CommunityToolkit.Mvvm.DependencyInjection; +using Flow.Launcher.Plugin; namespace Flow.Launcher.Core.Resource { @@ -22,6 +24,7 @@ namespace Flow.Launcher.Core.Resource private const int ShadowExtraMargin = 32; + private readonly IPublicAPI API = Ioc.Default.GetRequiredService(); private readonly List _themeDirectories = new List(); private ResourceDictionary _oldResource; private string _oldTheme; @@ -108,7 +111,7 @@ namespace Flow.Launcher.Core.Resource Log.Error($"|Theme.ChangeTheme|Theme <{theme}> path can't be found"); if (theme != defaultTheme) { - AppExtensions.API.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_path_not_exists"), theme)); + API.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_path_not_exists"), theme)); ChangeTheme(defaultTheme); } return false; @@ -118,7 +121,7 @@ namespace Flow.Launcher.Core.Resource Log.Error($"|Theme.ChangeTheme|Theme <{theme}> fail to parse"); if (theme != defaultTheme) { - AppExtensions.API.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_parse_error"), theme)); + API.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_parse_error"), theme)); ChangeTheme(defaultTheme); } return false; diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 8745d54b7..373418055 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -17,14 +17,17 @@ using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using System.Text.Json.Serialization; using System.Threading; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Core { public class Updater { - public string GitHubRepository { get; } + private readonly IPublicAPI API = Ioc.Default.GetRequiredService(); - public Updater(string gitHubRepository) + public string GitHubRepository { get; set; } + + public void Initialize(string gitHubRepository) { GitHubRepository = gitHubRepository; } @@ -53,7 +56,7 @@ namespace Flow.Launcher.Core if (newReleaseVersion <= currentVersion) { if (!silentUpdate) - AppExtensions.API.ShowMsgBox(api.GetTranslation("update_flowlauncher_already_on_latest")); + API.ShowMsgBox(api.GetTranslation("update_flowlauncher_already_on_latest")); return; } @@ -68,9 +71,9 @@ namespace Flow.Launcher.Core if (DataLocation.PortableDataLocationInUse()) { var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}"; - FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination, (s) => AppExtensions.API.ShowMsgBox(s)); - if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination, (s) => AppExtensions.API.ShowMsgBox(s))) - AppExtensions.API.ShowMsgBox(string.Format(api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"), + FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination, (s) => API.ShowMsgBox(s)); + if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination, (s) => API.ShowMsgBox(s))) + API.ShowMsgBox(string.Format(api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"), DataLocation.PortableDataPath, targetDestination)); } @@ -83,7 +86,7 @@ namespace Flow.Launcher.Core Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}"); - if (AppExtensions.API.ShowMsgBox(newVersionTips, api.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes) + if (API.ShowMsgBox(newVersionTips, api.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes) { UpdateManager.RestartApp(Constant.ApplicationFileName); } diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 9b86c6cc4..0d6d9855c 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -74,6 +74,8 @@ namespace Flow.Launcher Ioc.Default.GetRequiredService().PreStartCleanUpAfterPortabilityUpdate(); + Ioc.Default.GetRequiredService().Initialize(); + Log.Info("|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------"); Log.Info($"|App.OnStartup|Runtime info:{ErrorReporting.RuntimeInfo()}"); diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index 7549db1a3..bbd47e731 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -8,15 +8,21 @@ namespace Flow.Launcher.ViewModel; public class SettingWindowViewModel : BaseModel { - public Updater Updater { get; } + public Updater Updater { get; private set; } - public IPortable Portable { get; } + public IPortable Portable { get; private set; } public Settings Settings { get; } public SettingWindowViewModel() { Settings = Ioc.Default.GetRequiredService(); + } + + public void Initialize() + { + // We don not initialize Updater and Portable in the constructor because we want to avoid + // recrusive dependency injection Updater = Ioc.Default.GetRequiredService(); Portable = Ioc.Default.GetRequiredService(); } From 2a423f09bb3fe6c3dfb6dd9f03cce4ce410c5936 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 13 Jan 2025 09:36:14 +0800 Subject: [PATCH 14/72] Improve code quality --- Flow.Launcher.Infrastructure/StringMatcher.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher.Infrastructure/StringMatcher.cs b/Flow.Launcher.Infrastructure/StringMatcher.cs index 7134fc760..b3a265e29 100644 --- a/Flow.Launcher.Infrastructure/StringMatcher.cs +++ b/Flow.Launcher.Infrastructure/StringMatcher.cs @@ -8,13 +8,13 @@ namespace Flow.Launcher.Infrastructure { public class StringMatcher { - private readonly MatchOption _defaultMatchOption = new MatchOption(); + private readonly MatchOption _defaultMatchOption = new(); public SearchPrecisionScore UserSettingSearchPrecision { get; set; } private readonly IAlphabet _alphabet; - public StringMatcher(IAlphabet alphabet = null) + public StringMatcher() { _alphabet = Ioc.Default.GetRequiredService(); } @@ -242,16 +242,16 @@ namespace Flow.Launcher.Infrastructure return false; } - private bool IsAcronymChar(string stringToCompare, int compareStringIndex) + private static bool IsAcronymChar(string stringToCompare, int compareStringIndex) => char.IsUpper(stringToCompare[compareStringIndex]) || compareStringIndex == 0 || // 0 index means char is the start of the compare string, which is an acronym char.IsWhiteSpace(stringToCompare[compareStringIndex - 1]); - private bool IsAcronymNumber(string stringToCompare, int compareStringIndex) + private static bool IsAcronymNumber(string stringToCompare, int compareStringIndex) => stringToCompare[compareStringIndex] >= 0 && stringToCompare[compareStringIndex] <= 9; // To get the index of the closest space which preceeds the first matching index - private int CalculateClosestSpaceIndex(List spaceIndices, int firstMatchIndex) + private static int CalculateClosestSpaceIndex(List spaceIndices, int firstMatchIndex) { var closestSpaceIndex = -1; From ff110b3c49ec139da1222e3ffcb210596eb4642e Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 13 Jan 2025 10:01:50 +0800 Subject: [PATCH 15/72] Fix test project build issue --- Flow.Launcher.Infrastructure/StringMatcher.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Infrastructure/StringMatcher.cs b/Flow.Launcher.Infrastructure/StringMatcher.cs index b3a265e29..5822057a2 100644 --- a/Flow.Launcher.Infrastructure/StringMatcher.cs +++ b/Flow.Launcher.Infrastructure/StringMatcher.cs @@ -14,9 +14,9 @@ namespace Flow.Launcher.Infrastructure private readonly IAlphabet _alphabet; - public StringMatcher() + public StringMatcher(IAlphabet alphabet = null) { - _alphabet = Ioc.Default.GetRequiredService(); + _alphabet = alphabet ?? Ioc.Default.GetRequiredService(); } public static StringMatcher Instance { get; internal set; } From c3f71c213e8b24870292f4489515558850c9c5b7 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 13 Jan 2025 10:18:35 +0800 Subject: [PATCH 16/72] Revert "Fix test project build issue" This reverts commit ff110b3c49ec139da1222e3ffcb210596eb4642e. --- Flow.Launcher.Infrastructure/StringMatcher.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Infrastructure/StringMatcher.cs b/Flow.Launcher.Infrastructure/StringMatcher.cs index 5822057a2..b3a265e29 100644 --- a/Flow.Launcher.Infrastructure/StringMatcher.cs +++ b/Flow.Launcher.Infrastructure/StringMatcher.cs @@ -14,9 +14,9 @@ namespace Flow.Launcher.Infrastructure private readonly IAlphabet _alphabet; - public StringMatcher(IAlphabet alphabet = null) + public StringMatcher() { - _alphabet = alphabet ?? Ioc.Default.GetRequiredService(); + _alphabet = Ioc.Default.GetRequiredService(); } public static StringMatcher Instance { get; internal set; } From 3bebb690935e4b99a508f5a9e98593760f45360c Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 13 Jan 2025 10:21:14 +0800 Subject: [PATCH 17/72] Fix unitest build issue --- Flow.Launcher.Infrastructure/StringMatcher.cs | 6 +++++ Flow.Launcher.Test/FuzzyMatcherTest.cs | 22 ++++++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/Flow.Launcher.Infrastructure/StringMatcher.cs b/Flow.Launcher.Infrastructure/StringMatcher.cs index b3a265e29..f5f02cbfc 100644 --- a/Flow.Launcher.Infrastructure/StringMatcher.cs +++ b/Flow.Launcher.Infrastructure/StringMatcher.cs @@ -19,6 +19,12 @@ namespace Flow.Launcher.Infrastructure _alphabet = Ioc.Default.GetRequiredService(); } + // This is a workaround to allow unit tests to set the instance + public StringMatcher(IAlphabet alphabet) + { + _alphabet = alphabet; + } + public static StringMatcher Instance { get; internal set; } public static MatchResult FuzzySearch(string query, string stringToCompare) diff --git a/Flow.Launcher.Test/FuzzyMatcherTest.cs b/Flow.Launcher.Test/FuzzyMatcherTest.cs index d7f143218..d97ce227c 100644 --- a/Flow.Launcher.Test/FuzzyMatcherTest.cs +++ b/Flow.Launcher.Test/FuzzyMatcherTest.cs @@ -21,6 +21,8 @@ namespace Flow.Launcher.Test private const string MicrosoftSqlServerManagementStudio = "Microsoft SQL Server Management Studio"; private const string VisualStudioCode = "Visual Studio Code"; + private readonly IAlphabet alphabet = null; + public List GetSearchStrings() => new List { @@ -59,7 +61,7 @@ namespace Flow.Launcher.Test }; var results = new List(); - var matcher = new StringMatcher(); + var matcher = new StringMatcher(alphabet); foreach (var str in sources) { results.Add(new Result @@ -81,7 +83,7 @@ namespace Flow.Launcher.Test public void WhenNotAllCharactersFoundInSearchString_ThenShouldReturnZeroScore(string searchString) { var compareString = "Can have rum only in my glass"; - var matcher = new StringMatcher(); + var matcher = new StringMatcher(alphabet); var scoreResult = matcher.FuzzyMatch(searchString, compareString).RawScore; Assert.True(scoreResult == 0); @@ -97,7 +99,7 @@ namespace Flow.Launcher.Test string searchTerm) { var results = new List(); - var matcher = new StringMatcher(); + var matcher = new StringMatcher(alphabet); foreach (var str in GetSearchStrings()) { results.Add(new Result @@ -147,7 +149,7 @@ namespace Flow.Launcher.Test string queryString, string compareString, int expectedScore) { // When, Given - var matcher = new StringMatcher {UserSettingSearchPrecision = SearchPrecisionScore.Regular}; + var matcher = new StringMatcher(alphabet) {UserSettingSearchPrecision = SearchPrecisionScore.Regular}; var rawScore = matcher.FuzzyMatch(queryString, compareString).RawScore; // Should @@ -181,7 +183,7 @@ namespace Flow.Launcher.Test bool expectedPrecisionResult) { // When - var matcher = new StringMatcher {UserSettingSearchPrecision = expectedPrecisionScore}; + var matcher = new StringMatcher(alphabet) {UserSettingSearchPrecision = expectedPrecisionScore}; // Given var matchResult = matcher.FuzzyMatch(queryString, compareString); @@ -232,7 +234,7 @@ namespace Flow.Launcher.Test bool expectedPrecisionResult) { // When - var matcher = new StringMatcher {UserSettingSearchPrecision = expectedPrecisionScore}; + var matcher = new StringMatcher(alphabet) {UserSettingSearchPrecision = expectedPrecisionScore}; // Given var matchResult = matcher.FuzzyMatch(queryString, compareString); @@ -260,7 +262,7 @@ namespace Flow.Launcher.Test string queryString, string compareString1, string compareString2) { // When - var matcher = new StringMatcher {UserSettingSearchPrecision = SearchPrecisionScore.Regular}; + var matcher = new StringMatcher(alphabet) {UserSettingSearchPrecision = SearchPrecisionScore.Regular}; // Given var compareString1Result = matcher.FuzzyMatch(queryString, compareString1); @@ -293,7 +295,7 @@ namespace Flow.Launcher.Test string queryString, string compareString1, string compareString2) { // When - var matcher = new StringMatcher { UserSettingSearchPrecision = SearchPrecisionScore.Regular }; + var matcher = new StringMatcher(alphabet) { UserSettingSearchPrecision = SearchPrecisionScore.Regular }; // Given var compareString1Result = matcher.FuzzyMatch(queryString, compareString1); @@ -323,7 +325,7 @@ namespace Flow.Launcher.Test string secondName, string secondDescription, string secondExecutableName) { // Act - var matcher = new StringMatcher(); + var matcher = new StringMatcher(alphabet); var firstNameMatch = matcher.FuzzyMatch(queryString, firstName).RawScore; var firstDescriptionMatch = matcher.FuzzyMatch(queryString, firstDescription).RawScore; var firstExecutableNameMatch = matcher.FuzzyMatch(queryString, firstExecutableName).RawScore; @@ -358,7 +360,7 @@ namespace Flow.Launcher.Test public void WhenGivenAnAcronymQuery_ShouldReturnAcronymScore(string queryString, string compareString, int desiredScore) { - var matcher = new StringMatcher(); + var matcher = new StringMatcher(alphabet); var score = matcher.FuzzyMatch(queryString, compareString).Score; Assert.IsTrue(score == desiredScore, $@"Query: ""{queryString}"" From 8d8384965ea5d39c62d7ce2b865114555b393146 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 13 Jan 2025 12:00:45 +0800 Subject: [PATCH 18/72] Improve dependency injection in updater & settings view model & settings page --- Flow.Launcher.Core/Updater.cs | 20 ++++++++-------- Flow.Launcher/App.xaml.cs | 7 +++--- .../CustomQueryHotkeySetting.xaml.cs | 5 +--- Flow.Launcher/PublicAPIInstance.cs | 20 +++++++++++----- .../ViewModels/SettingsPaneAboutViewModel.cs | 2 +- .../SettingsPaneGeneralViewModel.cs | 2 +- .../ViewModels/SettingsPaneHotkeyViewModel.cs | 5 ++-- Flow.Launcher/SettingWindow.xaml.cs | 18 ++++++++++----- .../ViewModel/SettingWindowViewModel.cs | 23 ++----------------- 9 files changed, 46 insertions(+), 56 deletions(-) diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 373418055..7a25447b4 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -34,14 +34,14 @@ namespace Flow.Launcher.Core private SemaphoreSlim UpdateLock { get; } = new SemaphoreSlim(1); - public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true) + public async Task UpdateAppAsync(bool silentUpdate = true) { await UpdateLock.WaitAsync().ConfigureAwait(false); try { if (!silentUpdate) - api.ShowMsg(api.GetTranslation("pleaseWait"), - api.GetTranslation("update_flowlauncher_update_check")); + API.ShowMsg(API.GetTranslation("pleaseWait"), + API.GetTranslation("update_flowlauncher_update_check")); using var updateManager = await GitHubUpdateManagerAsync(GitHubRepository).ConfigureAwait(false); @@ -56,13 +56,13 @@ namespace Flow.Launcher.Core if (newReleaseVersion <= currentVersion) { if (!silentUpdate) - API.ShowMsgBox(api.GetTranslation("update_flowlauncher_already_on_latest")); + API.ShowMsgBox(API.GetTranslation("update_flowlauncher_already_on_latest")); return; } if (!silentUpdate) - api.ShowMsg(api.GetTranslation("update_flowlauncher_update_found"), - api.GetTranslation("update_flowlauncher_updating")); + API.ShowMsg(API.GetTranslation("update_flowlauncher_update_found"), + API.GetTranslation("update_flowlauncher_updating")); await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply).ConfigureAwait(false); @@ -73,7 +73,7 @@ namespace Flow.Launcher.Core var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}"; FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination, (s) => API.ShowMsgBox(s)); if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination, (s) => API.ShowMsgBox(s))) - API.ShowMsgBox(string.Format(api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"), + API.ShowMsgBox(string.Format(API.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"), DataLocation.PortableDataPath, targetDestination)); } @@ -86,7 +86,7 @@ namespace Flow.Launcher.Core Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}"); - if (API.ShowMsgBox(newVersionTips, api.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes) + if (API.ShowMsgBox(newVersionTips, API.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes) { UpdateManager.RestartApp(Constant.ApplicationFileName); } @@ -99,8 +99,8 @@ namespace Flow.Launcher.Core Log.Exception($"|Updater.UpdateApp|Error Occurred", e); if (!silentUpdate) - api.ShowMsg(api.GetTranslation("update_flowlauncher_fail"), - api.GetTranslation("update_flowlauncher_check_connection")); + API.ShowMsg(API.GetTranslation("update_flowlauncher_fail"), + API.GetTranslation("update_flowlauncher_check_connection")); } finally { diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 0d6d9855c..9384aae3d 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -74,8 +74,6 @@ namespace Flow.Launcher Ioc.Default.GetRequiredService().PreStartCleanUpAfterPortabilityUpdate(); - Ioc.Default.GetRequiredService().Initialize(); - Log.Info("|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------"); Log.Info($"|App.OnStartup|Runtime info:{ErrorReporting.RuntimeInfo()}"); @@ -96,6 +94,7 @@ namespace Flow.Launcher PluginManager.LoadPlugins(_settings.PluginSettings); API = Ioc.Default.GetRequiredService(); + ((PublicAPIInstance)API).Initialize(); Http.API = API; Http.Proxy = _settings.Proxy; @@ -160,11 +159,11 @@ namespace Flow.Launcher { // check update every 5 hours var timer = new PeriodicTimer(TimeSpan.FromHours(5)); - await Ioc.Default.GetRequiredService().UpdateAppAsync(API); + await Ioc.Default.GetRequiredService().UpdateAppAsync(); while (await timer.WaitForNextTickAsync()) // check updates on startup - await Ioc.Default.GetRequiredService().UpdateAppAsync(API); + await Ioc.Default.GetRequiredService().UpdateAppAsync(); } }); } diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs index 47460ff7d..eab2705d0 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs @@ -6,20 +6,17 @@ using System.Linq; using System.Windows; using System.Windows.Input; using System.Windows.Controls; -using Flow.Launcher.Core; namespace Flow.Launcher { public partial class CustomQueryHotkeySetting : Window { - private SettingWindow _settingWidow; private bool update; private CustomPluginHotkey updateCustomHotkey; public Settings Settings { get; } - public CustomQueryHotkeySetting(SettingWindow settingWidow, Settings settings) + public CustomQueryHotkeySetting(Settings settings) { - _settingWidow = settingWidow; Settings = settings; InitializeComponent(); } diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 50765294c..54e97f6c6 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -26,6 +26,7 @@ using System.Collections.Concurrent; using System.Diagnostics; using System.Collections.Specialized; using CommunityToolkit.Mvvm.DependencyInjection; +using Flow.Launcher.Core; namespace Flow.Launcher { @@ -33,19 +34,26 @@ namespace Flow.Launcher { private readonly SettingWindowViewModel _settingsVM; private readonly MainViewModel _mainVM; - private readonly IAlphabet _alphabet; - #region Constructor + private Updater _updater; + + #region Constructor & Initialization public PublicAPIInstance() { _settingsVM = Ioc.Default.GetRequiredService(); _mainVM = Ioc.Default.GetRequiredService(); - _alphabet = Ioc.Default.GetRequiredService(); GlobalHotkey.hookedKeyboardCallback = KListener_hookedKeyboardCallback; WebRequest.RegisterPrefix("data", new DataWebRequestFactory()); } + public void Initialize() + { + // We need to initialize Updater not in the constructor because we want to avoid + // recrusive dependency injection + _updater = Ioc.Default.GetRequiredService(); + } + #endregion #region Public API @@ -78,14 +86,14 @@ namespace Flow.Launcher public event VisibilityChangedEventHandler VisibilityChanged { add => _mainVM.VisibilityChanged += value; remove => _mainVM.VisibilityChanged -= value; } - public void CheckForNewUpdate() => _settingsVM.UpdateApp(); + public void CheckForNewUpdate() => _ = _updater.UpdateAppAsync(false); public void SaveAppAllSettings() { PluginManager.Save(); _mainVM.Save(); _settingsVM.Save(); - ImageLoader.Save(); + _ = ImageLoader.Save(); } public Task ReloadAllPluginData() => PluginManager.ReloadDataAsync(); @@ -105,7 +113,7 @@ namespace Flow.Launcher { Application.Current.Dispatcher.Invoke(() => { - SettingWindow sw = SingletonWindowOpener.Open(this, _settingsVM); + SettingWindow sw = SingletonWindowOpener.Open(); }); } diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs index 05fb16f5c..cb434f399 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs @@ -96,7 +96,7 @@ public partial class SettingsPaneAboutViewModel : BaseModel } [RelayCommand] - private Task UpdateApp() => _updater.UpdateAppAsync(App.API, false); + private Task UpdateApp() => _updater.UpdateAppAsync(false); private void ClearLogFolder() { diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs index 3d94355e6..4e498ba23 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs @@ -160,7 +160,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel private void UpdateApp() { - _ = _updater.UpdateAppAsync(App.API, false); + _ = _updater.UpdateAppAsync(false); } public bool AutoUpdates diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs index fb57f499b..b13aaefe3 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs @@ -7,7 +7,6 @@ using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; -using Flow.Launcher.Core; namespace Flow.Launcher.SettingPages.ViewModels; @@ -71,7 +70,7 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel return; } - var window = new CustomQueryHotkeySetting(null, Settings); + var window = new CustomQueryHotkeySetting(Settings); window.UpdateItem(item); window.ShowDialog(); } @@ -79,7 +78,7 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel [RelayCommand] private void CustomHotkeyAdd() { - new CustomQueryHotkeySetting(null, Settings).ShowDialog(); + new CustomQueryHotkeySetting(Settings).ShowDialog(); } [RelayCommand] diff --git a/Flow.Launcher/SettingWindow.xaml.cs b/Flow.Launcher/SettingWindow.xaml.cs index cb3f1e4a1..ab639e987 100644 --- a/Flow.Launcher/SettingWindow.xaml.cs +++ b/Flow.Launcher/SettingWindow.xaml.cs @@ -3,6 +3,7 @@ using System.Windows; using System.Windows.Forms; using System.Windows.Input; using System.Windows.Interop; +using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Core; using Flow.Launcher.Core.Configuration; using Flow.Launcher.Helper; @@ -17,16 +18,21 @@ namespace Flow.Launcher; public partial class SettingWindow { + private readonly Updater _updater; + private readonly IPortable _portable; private readonly IPublicAPI _api; private readonly Settings _settings; private readonly SettingWindowViewModel _viewModel; - public SettingWindow(IPublicAPI api, SettingWindowViewModel viewModel) + public SettingWindow() { + var viewModel = Ioc.Default.GetRequiredService(); _settings = viewModel.Settings; DataContext = viewModel; _viewModel = viewModel; - _api = api; + _updater = Ioc.Default.GetRequiredService(); + _portable = Ioc.Default.GetRequiredService(); + _api = Ioc.Default.GetRequiredService(); InitializePosition(); InitializeComponent(); } @@ -125,7 +131,7 @@ public partial class SettingWindow WindowState = _settings.SettingWindowState; } - private bool IsPositionValid(double top, double left) + private static bool IsPositionValid(double top, double left) { foreach (var screen in Screen.AllScreens) { @@ -145,7 +151,7 @@ public partial class SettingWindow var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0); var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0); - var left = (dip2.X - this.ActualWidth) / 2 + dip1.X; + var left = (dip2.X - ActualWidth) / 2 + dip1.X; return left; } @@ -154,13 +160,13 @@ public partial class SettingWindow var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y); var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height); - var top = (dip2.Y - this.ActualHeight) / 2 + dip1.Y - 20; + var top = (dip2.Y - ActualHeight) / 2 + dip1.Y - 20; return top; } private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args) { - var paneData = new PaneData(_settings, _viewModel.Updater, _viewModel.Portable); + var paneData = new PaneData(_settings, _updater, _portable); if (args.IsSettingsSelected) { ContentFrame.Navigate(typeof(SettingsPaneGeneral), paneData); diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index bbd47e731..37276a1ad 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -1,37 +1,18 @@ using CommunityToolkit.Mvvm.DependencyInjection; -using Flow.Launcher.Core; -using Flow.Launcher.Core.Configuration; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; namespace Flow.Launcher.ViewModel; -public class SettingWindowViewModel : BaseModel +public partial class SettingWindowViewModel : BaseModel { - public Updater Updater { get; private set; } - - public IPortable Portable { get; private set; } - - public Settings Settings { get; } + public Settings Settings { get; init; } public SettingWindowViewModel() { Settings = Ioc.Default.GetRequiredService(); } - public void Initialize() - { - // We don not initialize Updater and Portable in the constructor because we want to avoid - // recrusive dependency injection - Updater = Ioc.Default.GetRequiredService(); - Portable = Ioc.Default.GetRequiredService(); - } - - public async void UpdateApp() - { - await Updater.UpdateAppAsync(App.API, false); - } - /// /// Save Flow settings. Plugins settings are not included. /// From ed399371976b60436eeb9042c59696859eed3caf Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 21 Jan 2025 16:19:28 +0800 Subject: [PATCH 19/72] Initialize App.API earlier & Improve code quality --- Flow.Launcher/App.xaml.cs | 5 ++--- Flow.Launcher/PublicAPIInstance.cs | 13 ++----------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 9384aae3d..a64c9e750 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -70,6 +70,8 @@ namespace Flow.Launcher ).Build(); Ioc.Default.ConfigureServices(host.Services); + API = Ioc.Default.GetRequiredService(); + Ioc.Default.GetRequiredService().Initialize(Launcher.Properties.Settings.Default.GithubRepo); Ioc.Default.GetRequiredService().PreStartCleanUpAfterPortabilityUpdate(); @@ -93,9 +95,6 @@ namespace Flow.Launcher PluginManager.LoadPlugins(_settings.PluginSettings); - API = Ioc.Default.GetRequiredService(); - ((PublicAPIInstance)API).Initialize(); - Http.API = API; Http.Proxy = _settings.Proxy; diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 54e97f6c6..0329d6973 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -35,9 +35,7 @@ namespace Flow.Launcher private readonly SettingWindowViewModel _settingsVM; private readonly MainViewModel _mainVM; - private Updater _updater; - - #region Constructor & Initialization + #region Constructor public PublicAPIInstance() { @@ -47,13 +45,6 @@ namespace Flow.Launcher WebRequest.RegisterPrefix("data", new DataWebRequestFactory()); } - public void Initialize() - { - // We need to initialize Updater not in the constructor because we want to avoid - // recrusive dependency injection - _updater = Ioc.Default.GetRequiredService(); - } - #endregion #region Public API @@ -86,7 +77,7 @@ namespace Flow.Launcher public event VisibilityChangedEventHandler VisibilityChanged { add => _mainVM.VisibilityChanged += value; remove => _mainVM.VisibilityChanged -= value; } - public void CheckForNewUpdate() => _ = _updater.UpdateAppAsync(false); + public void CheckForNewUpdate() => _ = Ioc.Default.GetRequiredService().UpdateAppAsync(false); public void SaveAppAllSettings() { From f9983b587712bdc439022f4ac19bda46133d9ab8 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 22 Jan 2025 12:25:56 +0800 Subject: [PATCH 20/72] Move dependency injection codes to constructor --- Flow.Launcher/App.xaml.cs | 47 +++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index a64c9e750..d983ab000 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -30,7 +30,31 @@ namespace Flow.Launcher public static IPublicAPI API { get; private set; } private const string Unique = "Flow.Launcher_Unique_Application_Mutex"; private static bool _disposed; - private Settings _settings; + private readonly Settings _settings; + + public App() + { + // Initialize settings + var storage = new FlowLauncherJsonStorage(); + _settings = storage.Load(); + _settings.Initialize(storage); + _settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled(); + + // Configure the dependency injection container + var host = Host.CreateDefaultBuilder() + .UseContentRoot(AppContext.BaseDirectory) + .ConfigureServices(services => services + .AddSingleton(_ => _settings) + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + ).Build(); + Ioc.Default.ConfigureServices(host.Services); + } [STAThread] public static void Main() @@ -49,27 +73,6 @@ namespace Flow.Launcher { await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => { - // Initialize settings - var storage = new FlowLauncherJsonStorage(); - _settings = storage.Load(); - _settings.Initialize(storage); - _settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled(); - - // Configure the dependency injection container - var host = Host.CreateDefaultBuilder() - .UseContentRoot(AppContext.BaseDirectory) - .ConfigureServices(services => services - .AddSingleton(_ => _settings) - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - ).Build(); - Ioc.Default.ConfigureServices(host.Services); - API = Ioc.Default.GetRequiredService(); Ioc.Default.GetRequiredService().Initialize(Launcher.Properties.Settings.Default.GithubRepo); From 23a1e5bc5853d2f6a4ba0e83c1c36ac960da40d9 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 27 Jan 2025 09:39:12 +0800 Subject: [PATCH 21/72] Initialize public api instance in constructor so that we can use App.API all the time --- Flow.Launcher/App.xaml.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index d983ab000..f800ccd5d 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -54,6 +54,9 @@ namespace Flow.Launcher .AddSingleton() ).Build(); Ioc.Default.ConfigureServices(host.Services); + + // Initialize the public API first + API = Ioc.Default.GetRequiredService(); } [STAThread] @@ -73,8 +76,6 @@ namespace Flow.Launcher { await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => { - API = Ioc.Default.GetRequiredService(); - Ioc.Default.GetRequiredService().Initialize(Launcher.Properties.Settings.Default.GithubRepo); Ioc.Default.GetRequiredService().PreStartCleanUpAfterPortabilityUpdate(); From ed16d340cb1c43464b9046a2672edc5d189c01bc Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 27 Jan 2025 09:40:24 +0800 Subject: [PATCH 22/72] Improve code quality --- Flow.Launcher.Core/Plugin/PluginManager.cs | 6 +++--- Flow.Launcher.Infrastructure/Http/Http.cs | 3 ++- Flow.Launcher/App.xaml.cs | 3 +-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index a776c10ab..0e8a4b776 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -14,6 +14,7 @@ using ISavable = Flow.Launcher.Plugin.ISavable; using Flow.Launcher.Plugin.SharedCommands; using System.Text.Json; using Flow.Launcher.Core.Resource; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Core.Plugin { @@ -28,7 +29,7 @@ namespace Flow.Launcher.Core.Plugin public static readonly HashSet GlobalPlugins = new(); public static readonly Dictionary NonGlobalPlugins = new(); - public static IPublicAPI API { private set; get; } + public static IPublicAPI API { get; private set; } = Ioc.Default.GetRequiredService(); private static PluginsSettings Settings; private static List _metadatas; @@ -158,9 +159,8 @@ namespace Flow.Launcher.Core.Plugin /// Call initialize for all plugins /// /// return the list of failed to init plugins or null for none - public static async Task InitializePluginsAsync(IPublicAPI api) + public static async Task InitializePluginsAsync() { - API = api; var failedPlugins = new ConcurrentQueue(); var InitTasks = AllPlugins.Select(pair => Task.Run(async delegate diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 14b8eef4e..3711a6b0d 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -8,6 +8,7 @@ using Flow.Launcher.Infrastructure.UserSettings; using System; using System.Threading; using Flow.Launcher.Plugin; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Infrastructure.Http { @@ -17,7 +18,7 @@ namespace Flow.Launcher.Infrastructure.Http private static HttpClient client = new HttpClient(); - public static IPublicAPI API { get; set; } + private static IPublicAPI API { get; set; } = Ioc.Default.GetRequiredService(); static Http() { diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index f800ccd5d..5f7f097e0 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -99,10 +99,9 @@ namespace Flow.Launcher PluginManager.LoadPlugins(_settings.PluginSettings); - Http.API = API; Http.Proxy = _settings.Proxy; - await PluginManager.InitializePluginsAsync(API); + await PluginManager.InitializePluginsAsync(); await imageLoadertask; var mainVM = Ioc.Default.GetRequiredService(); From 6a2389f4b8c95d87ee72041a0e78812333fe3fd3 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 27 Jan 2025 10:45:05 +0800 Subject: [PATCH 23/72] Fix test project build issue --- Flow.Launcher.Infrastructure/Http/Http.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 3711a6b0d..78545a87b 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -18,8 +18,6 @@ namespace Flow.Launcher.Infrastructure.Http private static HttpClient client = new HttpClient(); - private static IPublicAPI API { get; set; } = Ioc.Default.GetRequiredService(); - static Http() { // need to be added so it would work on a win10 machine @@ -79,7 +77,7 @@ namespace Flow.Launcher.Infrastructure.Http } catch (UriFormatException e) { - API.ShowMsg("Please try again", "Unable to parse Http Proxy"); + Ioc.Default.GetRequiredService().ShowMsg("Please try again", "Unable to parse Http Proxy"); Log.Exception("Flow.Launcher.Infrastructure.Http", "Unable to parse Uri", e); } } From 3260faba985a595b2df48c0541ebb9bef6aea88e Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 31 Jan 2025 22:01:29 +0800 Subject: [PATCH 24/72] Add support for changing startup to logon task for faster startup experience --- .../UserSettings/Settings.cs | 1 + Flow.Launcher/App.xaml.cs | 2 +- Flow.Launcher/Flow.Launcher.csproj | 1 + Flow.Launcher/Helper/AutoStartup.cs | 115 +++++++++++++++++- .../SettingsPaneGeneralViewModel.cs | 34 +++++- .../Views/SettingsPaneGeneral.xaml | 7 ++ 6 files changed, 151 insertions(+), 9 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index c412fb32f..81895fdcc 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -238,6 +238,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings public bool EnableUpdateLog { get; set; } public bool StartFlowLauncherOnSystemStartup { get; set; } = false; + public bool UseLogonTaskForStartup { get; set; } = false; public bool HideOnStartup { get; set; } = true; bool _hideNotifyIcon { get; set; } public bool HideNotifyIcon diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 4d1adc6cd..38f846d92 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -119,7 +119,7 @@ namespace Flow.Launcher { try { - Helper.AutoStartup.Enable(); + Helper.AutoStartup.Enable(_settings.UseLogonTaskForStartup); } catch (Exception e) { diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 788beddfb..570785be7 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -100,6 +100,7 @@ + diff --git a/Flow.Launcher/Helper/AutoStartup.cs b/Flow.Launcher/Helper/AutoStartup.cs index 4bff30caf..116520ecf 100644 --- a/Flow.Launcher/Helper/AutoStartup.cs +++ b/Flow.Launcher/Helper/AutoStartup.cs @@ -1,18 +1,31 @@ using System; +using System.IO; +using System.Linq; +using System.Security.Principal; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Logger; using Microsoft.Win32; +using Microsoft.Win32.TaskScheduler; namespace Flow.Launcher.Helper; public class AutoStartup { private const string StartupPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run"; + private const string LogonTaskName = $"{Constant.FlowLauncher} Startup"; + private const string LogonTaskDesc = $"{Constant.FlowLauncher} Auto Startup"; public static bool IsEnabled { get { + // Check if logon task is enabled + if (CheckLogonTask()) + { + return true; + } + + // Check if registry is enabled try { using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true); @@ -28,12 +41,45 @@ public class AutoStartup } } - public static void Disable() + private static bool CheckLogonTask() + { + using var taskService = new TaskService(); + var task = taskService.RootFolder.AllTasks.FirstOrDefault(t => t.Name == LogonTaskName); + if (task != null) + { + try + { + // Check if the action is the same as the current executable path + var action = task.Definition.Actions.FirstOrDefault()!.ToString().Trim(); + if (!Constant.ExecutablePath.Equals(action, StringComparison.OrdinalIgnoreCase) && !File.Exists(action)) + { + UnscheduleLogonTask(); + ScheduleLogonTask(); + } + } + catch (Exception) + { + Log.Error("AutoStartup", "Failed to check logon task"); + return false; + } + } + + return true; + } + + public static void Disable(bool logonTask) { try { - using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true); - key?.DeleteValue(Constant.FlowLauncher, false); + if (logonTask) + { + UnscheduleLogonTask(); + } + else + { + using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true); + key?.DeleteValue(Constant.FlowLauncher, false); + } } catch (Exception e) { @@ -42,12 +88,19 @@ public class AutoStartup } } - internal static void Enable() + internal static void Enable(bool logonTask) { try { - using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true); - key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\""); + if (logonTask) + { + ScheduleLogonTask(); + } + else + { + using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true); + key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\""); + } } catch (Exception e) { @@ -55,4 +108,54 @@ public class AutoStartup throw; } } + + private static bool ScheduleLogonTask() + { + using var td = TaskService.Instance.NewTask(); + td.RegistrationInfo.Description = LogonTaskDesc; + td.Triggers.Add(new LogonTrigger { UserId = WindowsIdentity.GetCurrent().Name, Delay = TimeSpan.FromSeconds(2) }); + td.Actions.Add(Constant.ExecutablePath); + + if (IsCurrentUserIsAdmin()) + { + td.Principal.RunLevel = TaskRunLevel.Highest; + } + + td.Settings.StopIfGoingOnBatteries = false; + td.Settings.DisallowStartIfOnBatteries = false; + td.Settings.ExecutionTimeLimit = TimeSpan.Zero; + + try + { + TaskService.Instance.RootFolder.RegisterTaskDefinition(LogonTaskName, td); + return true; + } + catch (Exception) + { + Log.Error("AutoStartup", "Failed to schedule logon task"); + return false; + } + } + + private static bool UnscheduleLogonTask() + { + using var taskService = new TaskService(); + try + { + taskService.RootFolder.DeleteTask(LogonTaskName); + return true; + } + catch (Exception) + { + Log.Error("AutoStartup", "Failed to unschedule logon task"); + return false; + } + } + + private static bool IsCurrentUserIsAdmin() + { + var identity = WindowsIdentity.GetCurrent(); + var principal = new WindowsPrincipal(identity); + return principal.IsInRole(WindowsBuiltInRole.Administrator); + } } diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs index 3d94355e6..0aca761a0 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs @@ -42,9 +42,16 @@ public partial class SettingsPaneGeneralViewModel : BaseModel try { if (value) - AutoStartup.Enable(); + { + // Enable either registry or task scheduler + AutoStartup.Enable(UseLogonTaskForStartup); + } else - AutoStartup.Disable(); + { + // Disable both registry and task scheduler + AutoStartup.Disable(true); + AutoStartup.Disable(false); + } } catch (Exception e) { @@ -54,6 +61,29 @@ public partial class SettingsPaneGeneralViewModel : BaseModel } } + public bool UseLogonTaskForStartup + { + get => Settings.UseLogonTaskForStartup; + set + { + Settings.UseLogonTaskForStartup = value; + + if (StartFlowLauncherOnSystemStartup) + { + try + { + // Disable and enable to update the startup method + AutoStartup.Disable(!UseLogonTaskForStartup); + AutoStartup.Enable(UseLogonTaskForStartup); + } + catch (Exception e) + { + Notification.Show(InternationalizationManager.Instance.GetTranslation("setAutoStartFailed"), + e.Message); + } + } + } + } public List SearchWindowScreens { get; } = DropdownDataGeneric.GetValues("SearchWindowScreen"); diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml index 30e065b16..f57eba654 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml @@ -36,6 +36,13 @@ OnContent="{DynamicResource enable}" /> + + + + Date: Fri, 31 Jan 2025 22:06:53 +0800 Subject: [PATCH 25/72] Move string to resources --- Flow.Launcher/Languages/en.xaml | 1 + Flow.Launcher/Languages/zh-cn.xaml | 8 ++++++-- Flow.Launcher/Languages/zh-tw.xaml | 8 ++++++-- Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml | 2 +- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 4c465d61f..8e8c9abef 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -46,6 +46,7 @@ Portable Mode Store all settings and user data in one folder (Useful when used with removable drives or cloud services). Start Flow Launcher on system startup + Use logon task instead of startup entry for faster startup experience Error setting launch on startup Hide Flow Launcher when focus is lost Do not show new version notifications diff --git a/Flow.Launcher/Languages/zh-cn.xaml b/Flow.Launcher/Languages/zh-cn.xaml index 681c715fb..d2d1044af 100644 --- a/Flow.Launcher/Languages/zh-cn.xaml +++ b/Flow.Launcher/Languages/zh-cn.xaml @@ -1,5 +1,8 @@ - - + + Flow 检测到您已安装 {0} 个插件,需要 {1} 才能运行。是否要下载 {1}? @@ -44,6 +47,7 @@ 便携模式 将所有设置和用户数据存储在一个文件夹中 (可用于可移除驱动器或云服务)。 开机自启 + 使用登录任务而非启动项以更快自启 设置开机自启时出错 失去焦点时自动隐藏 Flow Launcher 不显示新版本提示 diff --git a/Flow.Launcher/Languages/zh-tw.xaml b/Flow.Launcher/Languages/zh-tw.xaml index 44be5257b..b5aa53377 100644 --- a/Flow.Launcher/Languages/zh-tw.xaml +++ b/Flow.Launcher/Languages/zh-tw.xaml @@ -1,5 +1,8 @@ - - + + Flow detected you have installed {0} plugins, which will require {1} to run. Would you like to download {1}? @@ -44,6 +47,7 @@ 便攜模式 將所有設定和使用者資料存儲在一個資料夾中(當與可移動磁碟或雲服務一起使用時很有用)。 開機時啟動 + 使用登錄任務而非啟動項以更快自啟 Error setting launch on startup 失去焦點時自動隱藏 Flow Launcher 不顯示新版本提示 diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml index f57eba654..e52614e74 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml @@ -36,7 +36,7 @@ OnContent="{DynamicResource enable}" /> - + Date: Fri, 31 Jan 2025 22:22:00 +0800 Subject: [PATCH 26/72] Fix issue when checking logon task --- Flow.Launcher/Helper/AutoStartup.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/Helper/AutoStartup.cs b/Flow.Launcher/Helper/AutoStartup.cs index 116520ecf..79466f1fb 100644 --- a/Flow.Launcher/Helper/AutoStartup.cs +++ b/Flow.Launcher/Helper/AutoStartup.cs @@ -56,15 +56,16 @@ public class AutoStartup UnscheduleLogonTask(); ScheduleLogonTask(); } + + return true; } catch (Exception) { Log.Error("AutoStartup", "Failed to check logon task"); - return false; } } - return true; + return false; } public static void Disable(bool logonTask) From e320ca1d492594351d81f05ab3da8838c8672dd6 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 1 Feb 2025 13:10:32 +0800 Subject: [PATCH 27/72] Add support for deleting plugin settings when uninstalling plugins --- Flow.Launcher.Core/Plugin/PluginManager.cs | 21 +++++++++++++++---- .../Languages/en.xaml | 6 ++++-- .../Languages/zh-cn.xaml | 6 ++++-- .../Languages/zh-tw.xaml | 6 ++++-- .../PluginsManager.cs | 6 +++++- 5 files changed, 34 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 5c4eaa1da..55bc0e2bd 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -14,6 +14,7 @@ using ISavable = Flow.Launcher.Plugin.ISavable; using Flow.Launcher.Plugin.SharedCommands; using System.Text.Json; using Flow.Launcher.Core.Resource; +using Flow.Launcher.Infrastructure.Storage; namespace Flow.Launcher.Core.Plugin { @@ -439,7 +440,7 @@ namespace Flow.Launcher.Core.Plugin public static void UpdatePlugin(PluginMetadata existingVersion, UserPlugin newVersion, string zipFilePath) { InstallPlugin(newVersion, zipFilePath, checkModified:false); - UninstallPlugin(existingVersion, removeSettings:false, checkModified:false); + UninstallPlugin(existingVersion, removeSettings:false, removePluginSettings:false, checkModified: false); _modifiedPlugins.Add(existingVersion.ID); } @@ -454,9 +455,9 @@ namespace Flow.Launcher.Core.Plugin /// /// Uninstall a plugin. /// - public static void UninstallPlugin(PluginMetadata plugin, bool removeSettings = true) + public static void UninstallPlugin(PluginMetadata plugin, bool removeSettings = true, bool removePluginSettings = false) { - UninstallPlugin(plugin, removeSettings, true); + UninstallPlugin(plugin, removeSettings, removePluginSettings, true); } #endregion @@ -529,7 +530,7 @@ namespace Flow.Launcher.Core.Plugin } } - internal static void UninstallPlugin(PluginMetadata plugin, bool removeSettings, bool checkModified) + internal static void UninstallPlugin(PluginMetadata plugin, bool removeSettings, bool removePluginSettings, bool checkModified) { if (checkModified && PluginModified(plugin.ID)) { @@ -542,6 +543,18 @@ namespace Flow.Launcher.Core.Plugin AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID); } + if (removePluginSettings) + { + var assemblyLoader = new PluginAssemblyLoader(plugin.ExecuteFilePath); + var assembly = assemblyLoader.LoadAssemblyAndDependencies(); + var assemblyName = assembly.GetName().Name; + var directoryPath = Path.Combine(DataLocation.DataDirectory(), JsonStorage.DirectoryName, Constant.Plugins, assemblyName); + if (Directory.Exists(directoryPath)) + { + Directory.Delete(directoryPath, true); + } + } + // Marked for deletion. Will be deleted on next start up using var _ = File.CreateText(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt")); diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml index de6a3a2fb..573ca9051 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml @@ -15,6 +15,8 @@ Installing Plugin Download and install {0} Plugin Uninstall + Keep plugin settings + Do you want to keep the settings of the plugin for the next usage? Plugin {0} successfully installed. Restarting Flow, please wait... Unable to find the plugin.json metadata file from the extracted zip file. Error: A plugin which has the same or greater version with {0} already exists. @@ -37,13 +39,13 @@ Plugin {0} successfully updated. Restarting Flow, please wait... Installing from an unknown source You are installing this plugin from an unknown source and it may contain potential risks!{0}{0}Please ensure you understand where this plugin is from and that it is safe.{0}{0}Would you like to continue still?{0}{0}(You can switch off this warning via settings) - + Plugin {0} successfully installed. Please restart Flow. Plugin {0} successfully uninstalled. Please restart Flow. Plugin {0} successfully updated. Please restart Flow. {0} plugins successfully updated. Please restart Flow. Plugin {0} has already been modified. Please restart Flow before making any further changes. - + Plugins Manager Management of installing, uninstalling or updating Flow Launcher plugins diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml index 676c20a73..033bfed94 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml @@ -13,6 +13,8 @@ 正在安装插件 下载与安装 {0} 插件卸载 + 保留插件设置 + 你希望保留插件设置以便于下次使用吗? 插件安装成功。正在重新启动 Flow Launcher,请稍候... 安装失败:无法从新插件中找到plugin.json元数据文件 错误:具有相同或更高版本的 {0} 的插件已经存在。 @@ -35,13 +37,13 @@ 插件{0}更新成功。正在重新启动 Flow Launcher,请稍候... 从未知源安装 您正在从未知源安装此插件,它可能包含潜在风险!{0}{0}请确保您了解来源以及安全性。{0}{0}您想要继续吗?{0}{0}(您可以通过设置关闭此警告) - + 成功安装插件{0}。请重新启动 Flow Launcher。 成功卸载插件{0}。请重新启动 Flow Launcher。 成功更新插件{0}。请重新启动 Flow Launcher。 插件 {0} 更新成功。请重新启动 Flow Launcher。 插件 {0} 已被修改。请在进行任何进一步更改之前重新启动Flow。 - + 插件管理 安装,卸载或更新 Flow Launcher 插件 diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml index ae37579dc..c2497b70b 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml @@ -13,6 +13,8 @@ Installing Plugin 下載並安裝 {0} 解除安裝擴充功能 + 保留插件設置 + 你希望保留插件設置以便於下次使用嗎? 外掛安裝成功。正在重啟 Flow,請稍後... Unable to find the plugin.json metadata file from the extracted zip file. Error: A plugin which has the same or greater version with {0} already exists. @@ -35,13 +37,13 @@ Plugin {0} successfully updated. Restarting Flow, please wait... Installing from an unknown source You are installing this plugin from an unknown source and it may contain potential risks!{0}{0}Please ensure you understand where this plugin is from and that it is safe.{0}{0}Would you like to continue still?{0}{0}(You can switch off this warning via settings) - + Plugin {0} successfully installed. Please restart Flow. Plugin {0} successfully uninstalled. Please restart Flow. Plugin {0} successfully updated. Please restart Flow. {0} plugins successfully updated. Please restart Flow. Plugin {0} has already been modified. Please restart Flow before making any further changes. - + 擴充功能管理 Management of installing, uninstalling or updating Flow Launcher plugins diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 305d248d3..fe3b9d3f2 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -682,7 +682,11 @@ namespace Flow.Launcher.Plugin.PluginsManager { try { - PluginManager.UninstallPlugin(plugin, removeSettings: true); + var removePluginSettings = Context.API.ShowMsgBox( + Context.API.GetTranslation("plugin_pluginsmanager_keep_plugin_settings_subtitle"), + Context.API.GetTranslation("plugin_pluginsmanager_keep_plugin_settings_title"), + button: MessageBoxButton.YesNo) == MessageBoxResult.No; + PluginManager.UninstallPlugin(plugin, removeSettings: true, removePluginSettings: removePluginSettings); } catch (ArgumentException e) { From 0e02a6f9cff393b1cfa7bec5bb70e1f60e87127a Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 6 Feb 2025 07:51:44 +0800 Subject: [PATCH 28/72] Revert changes in non-English language files --- .../Languages/zh-cn.xaml | 9 +++++---- .../Languages/zh-tw.xaml | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml index 033bfed94..e6be9069d 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml @@ -1,5 +1,8 @@ - - + + 正在下载插件... @@ -13,8 +16,6 @@ 正在安装插件 下载与安装 {0} 插件卸载 - 保留插件设置 - 你希望保留插件设置以便于下次使用吗? 插件安装成功。正在重新启动 Flow Launcher,请稍候... 安装失败:无法从新插件中找到plugin.json元数据文件 错误:具有相同或更高版本的 {0} 的插件已经存在。 diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml index c2497b70b..9736728a7 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml @@ -1,5 +1,8 @@ - - + + 正在下載擴充功能 @@ -13,8 +16,6 @@ Installing Plugin 下載並安裝 {0} 解除安裝擴充功能 - 保留插件設置 - 你希望保留插件設置以便於下次使用嗎? 外掛安裝成功。正在重啟 Flow,請稍後... Unable to find the plugin.json metadata file from the extracted zip file. Error: A plugin which has the same or greater version with {0} already exists. From 71b79da5d433c10de38241682245add9b6858359 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 6 Feb 2025 07:53:40 +0800 Subject: [PATCH 29/72] Revert auto format --- .../Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml | 5 +---- .../Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml index e6be9069d..f73516a56 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml @@ -1,8 +1,5 @@  - + 正在下载插件... diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml index 9736728a7..f1b044d25 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml @@ -1,8 +1,5 @@  - + 正在下載擴充功能 From 1ff8c3620a030294a6d30d9ae2e93ac14438457a Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 6 Feb 2025 07:56:24 +0800 Subject: [PATCH 30/72] Revert changes in non-English language files --- Flow.Launcher/Languages/zh-cn.xaml | 8 ++------ Flow.Launcher/Languages/zh-tw.xaml | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/Flow.Launcher/Languages/zh-cn.xaml b/Flow.Launcher/Languages/zh-cn.xaml index d2d1044af..681c715fb 100644 --- a/Flow.Launcher/Languages/zh-cn.xaml +++ b/Flow.Launcher/Languages/zh-cn.xaml @@ -1,8 +1,5 @@ - - + + Flow 检测到您已安装 {0} 个插件,需要 {1} 才能运行。是否要下载 {1}? @@ -47,7 +44,6 @@ 便携模式 将所有设置和用户数据存储在一个文件夹中 (可用于可移除驱动器或云服务)。 开机自启 - 使用登录任务而非启动项以更快自启 设置开机自启时出错 失去焦点时自动隐藏 Flow Launcher 不显示新版本提示 diff --git a/Flow.Launcher/Languages/zh-tw.xaml b/Flow.Launcher/Languages/zh-tw.xaml index b5aa53377..44be5257b 100644 --- a/Flow.Launcher/Languages/zh-tw.xaml +++ b/Flow.Launcher/Languages/zh-tw.xaml @@ -1,8 +1,5 @@ - - + + Flow detected you have installed {0} plugins, which will require {1} to run. Would you like to download {1}? @@ -47,7 +44,6 @@ 便攜模式 將所有設定和使用者資料存儲在一個資料夾中(當與可移動磁碟或雲服務一起使用時很有用)。 開機時啟動 - 使用登錄任務而非啟動項以更快自啟 Error setting launch on startup 失去焦點時自動隱藏 Flow Launcher 不顯示新版本提示 From 7598a6aff507cfff363d94b9f225fa7f6955039e Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 6 Feb 2025 08:08:45 +0800 Subject: [PATCH 31/72] Revert auto format --- .../Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml | 2 +- .../Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml index f73516a56..28196e55f 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml @@ -1,4 +1,4 @@ - + diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml index f1b044d25..96155f440 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml @@ -1,4 +1,4 @@ - + From 1f1b31994da4ab774e9c0964de342e7129c958d8 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 6 Feb 2025 09:35:21 +0800 Subject: [PATCH 32/72] Improve argument name to avoid possible confusion --- Flow.Launcher.Core/Plugin/PluginManager.cs | 10 +++++----- .../PluginsManager.cs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 55bc0e2bd..fc915bcb3 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -440,7 +440,7 @@ namespace Flow.Launcher.Core.Plugin public static void UpdatePlugin(PluginMetadata existingVersion, UserPlugin newVersion, string zipFilePath) { InstallPlugin(newVersion, zipFilePath, checkModified:false); - UninstallPlugin(existingVersion, removeSettings:false, removePluginSettings:false, checkModified: false); + UninstallPlugin(existingVersion, removePluginFromSettings:false, removePluginSettings:false, checkModified: false); _modifiedPlugins.Add(existingVersion.ID); } @@ -455,9 +455,9 @@ namespace Flow.Launcher.Core.Plugin /// /// Uninstall a plugin. /// - public static void UninstallPlugin(PluginMetadata plugin, bool removeSettings = true, bool removePluginSettings = false) + public static void UninstallPlugin(PluginMetadata plugin, bool removePluginFromSettings = true, bool removePluginSettings = false) { - UninstallPlugin(plugin, removeSettings, removePluginSettings, true); + UninstallPlugin(plugin, removePluginFromSettings, removePluginSettings, true); } #endregion @@ -530,14 +530,14 @@ namespace Flow.Launcher.Core.Plugin } } - internal static void UninstallPlugin(PluginMetadata plugin, bool removeSettings, bool removePluginSettings, bool checkModified) + internal static void UninstallPlugin(PluginMetadata plugin, bool removePluginFromSettings, bool removePluginSettings, bool checkModified) { if (checkModified && PluginModified(plugin.ID)) { throw new ArgumentException($"Plugin {plugin.Name} has been modified"); } - if (removeSettings) + if (removePluginFromSettings) { Settings.Plugins.Remove(plugin.ID); AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID); diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index fe3b9d3f2..ac4f90d92 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -686,7 +686,7 @@ namespace Flow.Launcher.Plugin.PluginsManager Context.API.GetTranslation("plugin_pluginsmanager_keep_plugin_settings_subtitle"), Context.API.GetTranslation("plugin_pluginsmanager_keep_plugin_settings_title"), button: MessageBoxButton.YesNo) == MessageBoxResult.No; - PluginManager.UninstallPlugin(plugin, removeSettings: true, removePluginSettings: removePluginSettings); + PluginManager.UninstallPlugin(plugin, removePluginFromSettings: true, removePluginSettings: removePluginSettings); } catch (ArgumentException e) { From 0e4cf57e9e29c401a4ff82052b128f5ebe7865b6 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 8 Feb 2025 11:57:29 +0800 Subject: [PATCH 33/72] Revert blank lines --- .../Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml | 4 ++-- .../Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml index 28196e55f..676c20a73 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml @@ -35,13 +35,13 @@ 插件{0}更新成功。正在重新启动 Flow Launcher,请稍候... 从未知源安装 您正在从未知源安装此插件,它可能包含潜在风险!{0}{0}请确保您了解来源以及安全性。{0}{0}您想要继续吗?{0}{0}(您可以通过设置关闭此警告) - + 成功安装插件{0}。请重新启动 Flow Launcher。 成功卸载插件{0}。请重新启动 Flow Launcher。 成功更新插件{0}。请重新启动 Flow Launcher。 插件 {0} 更新成功。请重新启动 Flow Launcher。 插件 {0} 已被修改。请在进行任何进一步更改之前重新启动Flow。 - + 插件管理 安装,卸载或更新 Flow Launcher 插件 diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml index 96155f440..ae37579dc 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml @@ -35,13 +35,13 @@ Plugin {0} successfully updated. Restarting Flow, please wait... Installing from an unknown source You are installing this plugin from an unknown source and it may contain potential risks!{0}{0}Please ensure you understand where this plugin is from and that it is safe.{0}{0}Would you like to continue still?{0}{0}(You can switch off this warning via settings) - + Plugin {0} successfully installed. Please restart Flow. Plugin {0} successfully uninstalled. Please restart Flow. Plugin {0} successfully updated. Please restart Flow. {0} plugins successfully updated. Please restart Flow. Plugin {0} has already been modified. Please restart Flow before making any further changes. - + 擴充功能管理 Management of installing, uninstalling or updating Flow Launcher plugins From f0e74a2aa545a74591c62fcd3e67c45faeff4a75 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 8 Feb 2025 19:12:53 +0800 Subject: [PATCH 34/72] Fix issue that plugin manager will save settings for the uninstalled and settings-removed plugins --- Flow.Launcher.Core/Plugin/PluginManager.cs | 14 ++++++++++---- .../Storage/PluginJsonStorage.cs | 18 ++++++++++++++---- Flow.Launcher/PublicAPIInstance.cs | 17 +++++++++++++++++ 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index fc915bcb3..146718b93 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -14,7 +14,6 @@ using ISavable = Flow.Launcher.Plugin.ISavable; using Flow.Launcher.Plugin.SharedCommands; using System.Text.Json; using Flow.Launcher.Core.Resource; -using Flow.Launcher.Infrastructure.Storage; namespace Flow.Launcher.Core.Plugin { @@ -548,10 +547,17 @@ namespace Flow.Launcher.Core.Plugin var assemblyLoader = new PluginAssemblyLoader(plugin.ExecuteFilePath); var assembly = assemblyLoader.LoadAssemblyAndDependencies(); var assemblyName = assembly.GetName().Name; - var directoryPath = Path.Combine(DataLocation.DataDirectory(), JsonStorage.DirectoryName, Constant.Plugins, assemblyName); - if (Directory.Exists(directoryPath)) + + // if user want to remove the plugin settings, we cannot call save method for the plugin json storage instance of this plugin + // so we need to remove it from the api instance + var method = API.GetType().GetMethod("RemovePluginSettings"); + var pluginJsonStorage = method?.Invoke(API, new object[] { assemblyName }); + + // if there exists a json storage for current plugin, we need to delete the directory path + if (pluginJsonStorage != null) { - Directory.Delete(directoryPath, true); + var deleteMethod = pluginJsonStorage.GetType().GetMethod("DeleteDirectory"); + deleteMethod?.Invoke(pluginJsonStorage, null); } } diff --git a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs index abe3f55b5..bc3900da8 100644 --- a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs @@ -3,14 +3,17 @@ using Flow.Launcher.Infrastructure.UserSettings; namespace Flow.Launcher.Infrastructure.Storage { - public class PluginJsonStorage :JsonStorage where T : new() + public class PluginJsonStorage : JsonStorage where T : new() { + // Use assembly name to check which plugin is using this storage + public readonly string AssemblyName; + public PluginJsonStorage() { // C# related, add python related below var dataType = typeof(T); - var assemblyName = dataType.Assembly.GetName().Name; - DirectoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName, Constant.Plugins, assemblyName); + AssemblyName = dataType.Assembly.GetName().Name; + DirectoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName, Constant.Plugins, AssemblyName); Helper.ValidateDirectory(DirectoryPath); FilePath = Path.Combine(DirectoryPath, $"{dataType.Name}{FileSuffix}"); @@ -20,6 +23,13 @@ namespace Flow.Launcher.Infrastructure.Storage { Data = data; } + + public void DeleteDirectory() + { + if (Directory.Exists(DirectoryPath)) + { + Directory.Delete(DirectoryPath, true); + } + } } } - diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index f0295cf24..12c65018c 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -189,6 +189,23 @@ namespace Flow.Launcher private readonly ConcurrentDictionary _pluginJsonStorages = new(); + public object RemovePluginSettings(string assemblyName) + { + foreach (var keyValuePair in _pluginJsonStorages) + { + var key = keyValuePair.Key; + var value = keyValuePair.Value; + var name = value.GetType().GetField("AssemblyName")?.GetValue(value)?.ToString(); + if (name == assemblyName) + { + _pluginJsonStorages.Remove(key, out var pluginJsonStorage); + return pluginJsonStorage; + } + } + + return null; + } + /// /// Save plugin settings. /// From 37837e7002307443e7b50ee7db01c0a5878c468d Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 8 Feb 2025 22:59:36 +0800 Subject: [PATCH 35/72] Fix possible exception when deleting local folder --- Flow.Launcher.Core/Plugin/PluginManager.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 146718b93..50150a069 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -521,7 +521,15 @@ namespace Flow.Launcher.Core.Plugin FilesFolders.CopyAll(pluginFolderPath, newPluginPath, MessageBoxEx.Show); - Directory.Delete(tempFolderPluginPath, true); + try + { + if (Directory.Exists(tempFolderPluginPath)) + Directory.Delete(tempFolderPluginPath, true); + } + catch (Exception e) + { + Log.Exception($"|PluginManager.InstallPlugin|Failed to delete temp folder {tempFolderPluginPath}", e); + } if (checkModified) { @@ -557,7 +565,14 @@ namespace Flow.Launcher.Core.Plugin if (pluginJsonStorage != null) { var deleteMethod = pluginJsonStorage.GetType().GetMethod("DeleteDirectory"); - deleteMethod?.Invoke(pluginJsonStorage, null); + try + { + deleteMethod?.Invoke(pluginJsonStorage, null); + } + catch (Exception e) + { + Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin json folder for {assemblyName}", e); + } } } From 54a0f3d16523ecaa32408277dc30800537094e89 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 9 Feb 2025 10:58:09 +0800 Subject: [PATCH 36/72] Add support for delete plugin directory for non-dotnet plugins --- .../Plugin/JsonRPCPluginBase.cs | 14 +++- Flow.Launcher.Core/Plugin/PluginManager.cs | 70 ++++++++++++------- 2 files changed, 56 insertions(+), 28 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs index f6e5e5879..ed8f94bcf 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs @@ -44,8 +44,10 @@ namespace Flow.Launcher.Core.Plugin private string SettingConfigurationPath => Path.Combine(Context.CurrentPluginMetadata.PluginDirectory, "SettingsTemplate.yaml"); - private string SettingPath => Path.Combine(DataLocation.PluginSettingsDirectory, - Context.CurrentPluginMetadata.Name, "Settings.json"); + private string SettingDirectory => Path.Combine(DataLocation.PluginSettingsDirectory, + Context.CurrentPluginMetadata.Name); + + private string SettingPath => Path.Combine(SettingDirectory, "Settings.json"); public abstract List LoadContextMenus(Result selectedResult); @@ -159,5 +161,13 @@ namespace Flow.Launcher.Core.Plugin { return Settings.CreateSettingPanel(); } + + public void DeletePluginSettingsDirectory() + { + if (Directory.Exists(SettingDirectory)) + { + Directory.Delete(SettingDirectory, true); + } + } } } diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 50150a069..2bce2ba79 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -544,38 +544,56 @@ namespace Flow.Launcher.Core.Plugin throw new ArgumentException($"Plugin {plugin.Name} has been modified"); } + if (removePluginSettings) + { + if (AllowedLanguage.IsDotNet(plugin.Language)) // for the plugin in .NET, we can use assembly loader + { + var assemblyLoader = new PluginAssemblyLoader(plugin.ExecuteFilePath); + var assembly = assemblyLoader.LoadAssemblyAndDependencies(); + var assemblyName = assembly.GetName().Name; + + // if user want to remove the plugin settings, we cannot call save method for the plugin json storage instance of this plugin + // so we need to remove it from the api instance + var method = API.GetType().GetMethod("RemovePluginSettings"); + var pluginJsonStorage = method?.Invoke(API, new object[] { assemblyName }); + + // if there exists a json storage for current plugin, we need to delete the directory path + if (pluginJsonStorage != null) + { + var deleteMethod = pluginJsonStorage.GetType().GetMethod("DeleteDirectory"); + try + { + deleteMethod?.Invoke(pluginJsonStorage, null); + } + catch (Exception e) + { + Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin json folder for {plugin.Name}", e); + } + } + } + else // the plugin with json prc interface + { + var pluginPair = AllPlugins.FirstOrDefault(p => p.Metadata.ID == plugin.ID); + if (pluginPair != null && pluginPair.Plugin is JsonRPCPlugin jsonRpcPlugin) + { + try + { + jsonRpcPlugin.DeletePluginSettingsDirectory(); + } + catch (Exception e) + { + Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin json folder for {plugin.Name}", e); + } + } + } + } + if (removePluginFromSettings) { Settings.Plugins.Remove(plugin.ID); AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID); } - if (removePluginSettings) - { - var assemblyLoader = new PluginAssemblyLoader(plugin.ExecuteFilePath); - var assembly = assemblyLoader.LoadAssemblyAndDependencies(); - var assemblyName = assembly.GetName().Name; - - // if user want to remove the plugin settings, we cannot call save method for the plugin json storage instance of this plugin - // so we need to remove it from the api instance - var method = API.GetType().GetMethod("RemovePluginSettings"); - var pluginJsonStorage = method?.Invoke(API, new object[] { assemblyName }); - - // if there exists a json storage for current plugin, we need to delete the directory path - if (pluginJsonStorage != null) - { - var deleteMethod = pluginJsonStorage.GetType().GetMethod("DeleteDirectory"); - try - { - deleteMethod?.Invoke(pluginJsonStorage, null); - } - catch (Exception e) - { - Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin json folder for {assemblyName}", e); - } - } - } - // Marked for deletion. Will be deleted on next start up using var _ = File.CreateText(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt")); From 585b7015b6236ea7fdbe156c39d468a3ca579ed5 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 10 Feb 2025 18:20:50 +0800 Subject: [PATCH 37/72] Add uninstallation tooltip for logon task settings item --- Flow.Launcher/Languages/en.xaml | 1 + Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 8e8c9abef..cd9d0b02a 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -47,6 +47,7 @@ Store all settings and user data in one folder (Useful when used with removable drives or cloud services). Start Flow Launcher on system startup Use logon task instead of startup entry for faster startup experience + After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler Error setting launch on startup Hide Flow Launcher when focus is lost Do not show new version notifications diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml index e52614e74..a80e618e8 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml @@ -36,7 +36,7 @@ OnContent="{DynamicResource enable}" /> - + Date: Wed, 12 Feb 2025 15:06:15 +0800 Subject: [PATCH 38/72] Make sure back to query results from context menu before changing query --- Flow.Launcher/CustomQueryHotkeySetting.xaml.cs | 1 + Flow.Launcher/CustomShortcutSetting.xaml.cs | 1 + Flow.Launcher/ResultListBox.xaml.cs | 3 +++ Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs | 1 + Flow.Launcher/ViewModel/PluginViewModel.cs | 1 + 5 files changed, 7 insertions(+) diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs index 81e7600b8..3db49b381 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs @@ -76,6 +76,7 @@ namespace Flow.Launcher private void BtnTestActionKeyword_OnClick(object sender, RoutedEventArgs e) { + App.API.BackToQueryResults(); App.API.ChangeQuery(tbAction.Text); Application.Current.MainWindow.Show(); Application.Current.MainWindow.Opacity = 1; diff --git a/Flow.Launcher/CustomShortcutSetting.xaml.cs b/Flow.Launcher/CustomShortcutSetting.xaml.cs index dec3506eb..10452726d 100644 --- a/Flow.Launcher/CustomShortcutSetting.xaml.cs +++ b/Flow.Launcher/CustomShortcutSetting.xaml.cs @@ -64,6 +64,7 @@ namespace Flow.Launcher private void BtnTestShortcut_OnClick(object sender, RoutedEventArgs e) { + App.API.BackToQueryResults(); App.API.ChangeQuery(tbExpand.Text); Application.Current.MainWindow.Show(); Application.Current.MainWindow.Opacity = 1; diff --git a/Flow.Launcher/ResultListBox.xaml.cs b/Flow.Launcher/ResultListBox.xaml.cs index ac51b195c..cc003457f 100644 --- a/Flow.Launcher/ResultListBox.xaml.cs +++ b/Flow.Launcher/ResultListBox.xaml.cs @@ -149,7 +149,10 @@ namespace Flow.Launcher var rawQuery = query; var effect = DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move | DragDropEffects.Copy); if (effect == DragDropEffects.Move) + { + App.API.BackToQueryResults(); App.API.ChangeQuery(rawQuery, true); + } } private void ResultListBox_OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) { diff --git a/Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs b/Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs index 38b5bec65..97c938e78 100644 --- a/Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs +++ b/Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs @@ -64,6 +64,7 @@ namespace Flow.Launcher.ViewModel private void ShowCommandQuery(string action) { var actionKeyword = PluginManagerData.Metadata.ActionKeywords.Any() ? PluginManagerData.Metadata.ActionKeywords[0] + " " : String.Empty; + App.API.BackToQueryResults(); App.API.ChangeQuery($"{actionKeyword}{action} {_plugin.Name}"); App.API.ShowMainWindow(); } diff --git a/Flow.Launcher/ViewModel/PluginViewModel.cs b/Flow.Launcher/ViewModel/PluginViewModel.cs index 4ce8bd470..e56e8e9e5 100644 --- a/Flow.Launcher/ViewModel/PluginViewModel.cs +++ b/Flow.Launcher/ViewModel/PluginViewModel.cs @@ -146,6 +146,7 @@ namespace Flow.Launcher.ViewModel [RelayCommand] private void OpenDeletePluginWindow() { + PluginManager.API.BackToQueryResults(); PluginManager.API.ChangeQuery($"{PluginManagerActionKeyword} uninstall {PluginPair.Metadata.Name}".Trim(), true); PluginManager.API.ShowMainWindow(); } From 3dade8bbfcf5c556e5b08caab23b38aabc55f9b1 Mon Sep 17 00:00:00 2001 From: Kevin Zhang <45326534+taooceros@users.noreply.github.com> Date: Wed, 19 Feb 2025 00:03:46 -0800 Subject: [PATCH 39/72] Don't restart the jsonrpc process when reloading data. --- Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs index f95266c7f..19d7edb31 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs @@ -135,10 +135,9 @@ namespace Flow.Launcher.Core.Plugin public virtual async Task ReloadDataAsync() { - SetupJsonRPC(); try { - await RPC.InvokeAsync("reload", Context); + await RPC.InvokeAsync("reload_data", Context); } catch (RemoteMethodNotFoundException e) { From 2843236214981585fb2316ea25e675cf56bc21f3 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 19 Feb 2025 17:09:09 +0800 Subject: [PATCH 40/72] Improve documents & Improve code quality --- Flow.Launcher/CustomQueryHotkeySetting.xaml.cs | 1 + Flow.Launcher/CustomShortcutSetting.xaml.cs | 1 + Flow.Launcher/ResultListBox.xaml.cs | 1 + Flow.Launcher/ViewModel/MainViewModel.cs | 2 ++ Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs | 1 + Flow.Launcher/ViewModel/PluginViewModel.cs | 1 + Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs | 1 - Plugins/Flow.Launcher.Plugin.Shell/Main.cs | 5 +++-- 8 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs index 3db49b381..fd829afe2 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs @@ -76,6 +76,7 @@ namespace Flow.Launcher private void BtnTestActionKeyword_OnClick(object sender, RoutedEventArgs e) { + // if user happens to open context menu, we need to return back to query results before changing query App.API.BackToQueryResults(); App.API.ChangeQuery(tbAction.Text); Application.Current.MainWindow.Show(); diff --git a/Flow.Launcher/CustomShortcutSetting.xaml.cs b/Flow.Launcher/CustomShortcutSetting.xaml.cs index 10452726d..d05a5c15c 100644 --- a/Flow.Launcher/CustomShortcutSetting.xaml.cs +++ b/Flow.Launcher/CustomShortcutSetting.xaml.cs @@ -64,6 +64,7 @@ namespace Flow.Launcher private void BtnTestShortcut_OnClick(object sender, RoutedEventArgs e) { + // if user happens to open context menu, we need to return back to query results before changing query App.API.BackToQueryResults(); App.API.ChangeQuery(tbExpand.Text); Application.Current.MainWindow.Show(); diff --git a/Flow.Launcher/ResultListBox.xaml.cs b/Flow.Launcher/ResultListBox.xaml.cs index cc003457f..834692536 100644 --- a/Flow.Launcher/ResultListBox.xaml.cs +++ b/Flow.Launcher/ResultListBox.xaml.cs @@ -150,6 +150,7 @@ namespace Flow.Launcher var effect = DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move | DragDropEffects.Copy); if (effect == DragDropEffects.Move) { + // if user happens to open context menu, we need to return back to query results before changing query App.API.BackToQueryResults(); App.API.ChangeQuery(rawQuery, true); } diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 5c3251bfc..4af93daf9 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1266,6 +1266,7 @@ namespace Flow.Launcher.ViewModel { _topMostRecord.Remove(result); App.API.ShowMsg(InternationalizationManager.Instance.GetTranslation("success")); + // if user happens to open context menu, we need to return back to query results before changing query App.API.BackToQueryResults(); App.API.ReQuery(); return false; @@ -1284,6 +1285,7 @@ namespace Flow.Launcher.ViewModel { _topMostRecord.AddOrUpdate(result); App.API.ShowMsg(InternationalizationManager.Instance.GetTranslation("success")); + // if user happens to open context menu, we need to return back to query results before changing query App.API.BackToQueryResults(); App.API.ReQuery(); return false; diff --git a/Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs b/Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs index 97c938e78..7675ecb16 100644 --- a/Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs +++ b/Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs @@ -64,6 +64,7 @@ namespace Flow.Launcher.ViewModel private void ShowCommandQuery(string action) { var actionKeyword = PluginManagerData.Metadata.ActionKeywords.Any() ? PluginManagerData.Metadata.ActionKeywords[0] + " " : String.Empty; + // if user happens to open context menu, we need to return back to query results before changing query App.API.BackToQueryResults(); App.API.ChangeQuery($"{actionKeyword}{action} {_plugin.Name}"); App.API.ShowMainWindow(); diff --git a/Flow.Launcher/ViewModel/PluginViewModel.cs b/Flow.Launcher/ViewModel/PluginViewModel.cs index e56e8e9e5..c8601c431 100644 --- a/Flow.Launcher/ViewModel/PluginViewModel.cs +++ b/Flow.Launcher/ViewModel/PluginViewModel.cs @@ -146,6 +146,7 @@ namespace Flow.Launcher.ViewModel [RelayCommand] private void OpenDeletePluginWindow() { + // if user happens to open context menu, we need to return back to query results before changing query PluginManager.API.BackToQueryResults(); PluginManager.API.ChangeQuery($"{PluginManagerActionKeyword} uninstall {PluginPair.Metadata.Name}".Trim(), true); PluginManager.API.ShowMainWindow(); diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs index 17e9fe2bc..482e821dc 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs @@ -59,7 +59,6 @@ namespace Flow.Launcher.Plugin.PluginsManager var link = pluginManifestInfo.UrlSourceCode.StartsWith("https://github.com") ? Regex.Replace(pluginManifestInfo.UrlSourceCode, @"\/tree\/\w+$", "") + "/issues" : pluginManifestInfo.UrlSourceCode; - Context.API.OpenUrl(link); return true; } diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs index 7f1f4bd4d..86808cfbc 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs @@ -5,7 +5,6 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; -using System.Windows; using WindowsInput; using WindowsInput.Native; using Flow.Launcher.Infrastructure.Hotkey; @@ -379,9 +378,11 @@ namespace Flow.Launcher.Plugin.Shell private void OnWinRPressed() { // show the main window and set focus to the query box - Task.Run(() => + _ = Task.Run(() => { context.API.ShowMainWindow(); + // if user happens to open context menu, we need to return back to query results before changing query + context.API.BackToQueryResults(); context.API.ChangeQuery($"{context.CurrentPluginMetadata.ActionKeywords[0]}{Plugin.Query.TermSeparator}"); }); From ccf8d876ae2f96b556926f9d7df5c91347552adf Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 20 Feb 2025 14:59:16 +0800 Subject: [PATCH 41/72] Add support for hiding dulplicated windows apps --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 28 +++++++++++++++++++ .../Flow.Launcher.Plugin.Program/Settings.cs | 1 + 2 files changed, 29 insertions(+) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 6ba7047f2..00fb1d344 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -72,6 +72,8 @@ namespace Flow.Launcher.Plugin.Program private const string ExeUninstallerSuffix = ".exe"; private const string InkUninstallerSuffix = ".lnk"; + private const string WindowsAppPath = "c:\\program files\\windowsapps"; + static Main() { } @@ -90,11 +92,20 @@ namespace Flow.Launcher.Plugin.Program { try { + // Collect all UWP Windows app directories + var uwpsDirectories = _settings.HideDulplicatedWindowsApp ? _uwps + .Where(uwp => !string.IsNullOrEmpty(uwp.Location)) // Exclude invalid paths + .Where(uwp => uwp.Location.StartsWith(WindowsAppPath, StringComparison.OrdinalIgnoreCase)) // Keep system apps + .Select(uwp => uwp.Location.TrimEnd('\\')) // Remove trailing slash + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToArray() : null; + return _win32s.Cast() .Concat(_uwps) .AsParallel() .WithCancellation(token) .Where(HideUninstallersFilter) + .Where(p => HideDulplicatedWindowsAppFilter(p, uwpsDirectories)) .Where(p => p.Enabled) .Select(p => p.Result(query.Search, Context.API)) .Where(r => r?.Score > 0) @@ -152,6 +163,23 @@ namespace Flow.Launcher.Plugin.Program return true; } + private static bool HideDulplicatedWindowsAppFilter(IProgram program, string[] uwpsDirectories) + { + if (uwpsDirectories == null || uwpsDirectories.Length == 0) return true; + if (program is UWPApp) return true; + + var location = program.Location.TrimEnd('\\'); // Ensure trailing slash + if (string.IsNullOrEmpty(location)) + return true; // Keep if location is invalid + + if (!location.StartsWith(WindowsAppPath, StringComparison.OrdinalIgnoreCase)) + return true; // Keep if not a Windows app + + // Check if the any Win32 executable directory contains UWP Windows app location matches + return !uwpsDirectories.Any(uwpDirectory => + location.StartsWith(uwpDirectory, StringComparison.OrdinalIgnoreCase)); + } + public async Task InitAsync(PluginInitContext context) { Context = context; diff --git a/Plugins/Flow.Launcher.Plugin.Program/Settings.cs b/Plugins/Flow.Launcher.Plugin.Program/Settings.cs index fb24f64d7..664277e02 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Settings.cs @@ -121,6 +121,7 @@ namespace Flow.Launcher.Plugin.Program public bool EnableRegistrySource { get; set; } = true; public bool EnablePathSource { get; set; } = false; public bool EnableUWP { get; set; } = true; + public bool HideDulplicatedWindowsApp { get; set; } = true; internal const char SuffixSeparator = ';'; } From 7ccfbcae7f2cef218357abd1aa842592e88a4b9f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 20 Feb 2025 23:15:12 +0800 Subject: [PATCH 42/72] Add hide dulplicated windows apps into settings panel --- .../Languages/en.xaml | 2 + .../Flow.Launcher.Plugin.Program/Settings.cs | 2 +- .../Views/ProgramSetting.xaml | 53 ++++++++++--------- .../Views/ProgramSetting.xaml.cs | 10 ++++ 4 files changed, 41 insertions(+), 26 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml index 7ed711e17..640b082e7 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml @@ -36,6 +36,8 @@ Hides programs with common uninstaller names, such as unins000.exe Search in Program Description Flow will search program's description + Hide dulplicated apps + Hide dulplicated Win32 programs that are already in the UWP list Suffixes Max Depth diff --git a/Plugins/Flow.Launcher.Plugin.Program/Settings.cs b/Plugins/Flow.Launcher.Plugin.Program/Settings.cs index 664277e02..53cb1755d 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Settings.cs @@ -121,7 +121,7 @@ namespace Flow.Launcher.Plugin.Program public bool EnableRegistrySource { get; set; } = true; public bool EnablePathSource { get; set; } = false; public bool EnableUWP { get; set; } = true; - public bool HideDulplicatedWindowsApp { get; set; } = true; + public bool HideDulplicatedWindowsApp { get; set; } = false; internal const char SuffixSeparator = ';'; } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml b/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml index e5ca6967e..0482099ad 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml @@ -8,7 +8,7 @@ DataContext="{Binding RelativeSource={RelativeSource Self}}" mc:Ignorable="d"> - + @@ -18,40 +18,40 @@ + ToolTip="{DynamicResource flowlauncher_plugin_program_index_uwp_tooltip}" + Visibility="{Binding ShowUWPCheckbox, Converter={StaticResource BooleanToVisibilityConverter}}" /> @@ -67,21 +67,20 @@ BorderBrush="{DynamicResource Color03B}" BorderThickness="1" /> @@ -91,11 +90,15 @@ IsChecked="{Binding HideUninstallers}" ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hideuninstallers_tooltip}" /> + + + + diff --git a/Flow.Launcher/ReportWindow.xaml.cs b/Flow.Launcher/ReportWindow.xaml.cs index a535dfb3e..12ffe3ffc 100644 --- a/Flow.Launcher/ReportWindow.xaml.cs +++ b/Flow.Launcher/ReportWindow.xaml.cs @@ -6,10 +6,10 @@ using System.Text; using System.Linq; using System.Windows; using System.Windows.Documents; -using Flow.Launcher.Helper; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Plugin.SharedCommands; +using Flow.Launcher.Infrastructure.Exception; namespace Flow.Launcher { @@ -43,32 +43,36 @@ namespace Flow.Launcher var log = directory.GetFiles().OrderByDescending(f => f.LastWriteTime).First(); var websiteUrl = exception switch - { - FlowPluginException pluginException =>GetIssuesUrl(pluginException.Metadata.Website), - _ => Constant.IssuesUrl - }; - + { + FlowPluginException pluginException =>GetIssuesUrl(pluginException.Metadata.Website), + _ => Constant.IssuesUrl + }; - var paragraph = Hyperlink("Please open new issue in: ", websiteUrl); - paragraph.Inlines.Add($"1. upload log file: {log.FullName}\n"); - paragraph.Inlines.Add($"2. copy below exception message"); + var paragraph = Hyperlink(App.API.GetTranslation("reportWindow_please_open_issue"), websiteUrl); + paragraph.Inlines.Add(string.Format(App.API.GetTranslation("reportWindow_upload_log"), log.FullName)); + paragraph.Inlines.Add("\n"); + paragraph.Inlines.Add(App.API.GetTranslation("reportWindow_copy_below")); ErrorTextbox.Document.Blocks.Add(paragraph); StringBuilder content = new StringBuilder(); - content.AppendLine(ErrorReporting.RuntimeInfo()); - content.AppendLine(ErrorReporting.DependenciesInfo()); - content.AppendLine($"Date: {DateTime.Now.ToString(CultureInfo.InvariantCulture)}"); - content.AppendLine("Exception:"); + content.AppendLine(RuntimeInfo()); + content.AppendLine(); + content.AppendLine(DependenciesInfo()); + content.AppendLine(); + content.AppendLine(string.Format(App.API.GetTranslation("reportWindow_date"), DateTime.Now.ToString(CultureInfo.InvariantCulture))); + content.AppendLine(App.API.GetTranslation("reportWindow_exception")); content.AppendLine(exception.ToString()); paragraph = new Paragraph(); paragraph.Inlines.Add(content.ToString()); ErrorTextbox.Document.Blocks.Add(paragraph); } - private Paragraph Hyperlink(string textBeforeUrl, string url) + private static Paragraph Hyperlink(string textBeforeUrl, string url) { - var paragraph = new Paragraph(); - paragraph.Margin = new Thickness(0); + var paragraph = new Paragraph + { + Margin = new Thickness(0) + }; var link = new Hyperlink { @@ -79,10 +83,38 @@ namespace Flow.Launcher link.Click += (s, e) => SearchWeb.OpenInBrowserTab(url); paragraph.Inlines.Add(textBeforeUrl); + paragraph.Inlines.Add(" "); paragraph.Inlines.Add(link); paragraph.Inlines.Add("\n"); return paragraph; } + + private void BtnCancel_OnClick(object sender, RoutedEventArgs e) + { + Close(); + } + + private static string RuntimeInfo() + { + var info = + $""" + Flow Launcher {App.API.GetTranslation("reportWindow_version")}: {Constant.Version} + OS {App.API.GetTranslation("reportWindow_version")}: {ExceptionFormatter.GetWindowsFullVersionFromRegistry()} + IntPtr {App.API.GetTranslation("reportWindow_length")}: {IntPtr.Size} + x64: {Environment.Is64BitOperatingSystem} + """; + return info; + } + + private static string DependenciesInfo() + { + var info = + $""" + {App.API.GetTranslation("pythonFilePath")}: {Constant.PythonPath} + {App.API.GetTranslation("nodeFilePath")}: {Constant.NodePath} + """; + return info; + } } } From aee585620a62a5054428d0a338e84a25af58bb9f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 22 Feb 2025 19:32:08 +0800 Subject: [PATCH 47/72] Remove back to query result and call this function in ChangeQuery & ReQuery --- .../Plugin/JsonRPCV2Models/JsonRPCPublicAPI.cs | 5 ----- Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs | 11 +++-------- Flow.Launcher/CustomQueryHotkeySetting.xaml.cs | 2 -- Flow.Launcher/CustomShortcutSetting.xaml.cs | 2 -- Flow.Launcher/PublicAPIInstance.cs | 2 -- Flow.Launcher/ResultListBox.xaml.cs | 4 ---- Flow.Launcher/ViewModel/MainViewModel.cs | 14 +++++--------- .../ViewModel/PluginStoreItemViewModel.cs | 2 -- Flow.Launcher/ViewModel/PluginViewModel.cs | 3 --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 1 - Plugins/Flow.Launcher.Plugin.Shell/Main.cs | 1 - 11 files changed, 8 insertions(+), 39 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCV2Models/JsonRPCPublicAPI.cs b/Flow.Launcher.Core/Plugin/JsonRPCV2Models/JsonRPCPublicAPI.cs index e0a0434a2..a82cae5d2 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCV2Models/JsonRPCPublicAPI.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCV2Models/JsonRPCPublicAPI.cs @@ -170,10 +170,5 @@ namespace Flow.Launcher.Core.Plugin.JsonRPCV2Models { _api.OpenAppUri(appUri); } - - public void BackToQueryResults() - { - _api.BackToQueryResults(); - } } } diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index 8376fd07b..1a8da381b 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -17,7 +17,8 @@ namespace Flow.Launcher.Plugin public interface IPublicAPI { /// - /// Change Flow.Launcher query + /// Change Flow.Launcher query. + /// When current results is from context menu or history, we will back to query results before changing query. /// /// query text /// @@ -299,17 +300,11 @@ namespace Flow.Launcher.Plugin /// /// Reloads the query. - /// This method should run when selected item is from query results. + /// When current results is from context menu or history, we will back to query results before changing query. /// /// Choose the first result after reload if true; keep the last selected result if false. Default is true. public void ReQuery(bool reselect = true); - /// - /// Back to the query results. - /// This method should run when selected item is from context menu or history. - /// - public void BackToQueryResults(); - /// /// Displays a standardised Flow message box. /// diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs index fd829afe2..81e7600b8 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs @@ -76,8 +76,6 @@ namespace Flow.Launcher private void BtnTestActionKeyword_OnClick(object sender, RoutedEventArgs e) { - // if user happens to open context menu, we need to return back to query results before changing query - App.API.BackToQueryResults(); App.API.ChangeQuery(tbAction.Text); Application.Current.MainWindow.Show(); Application.Current.MainWindow.Opacity = 1; diff --git a/Flow.Launcher/CustomShortcutSetting.xaml.cs b/Flow.Launcher/CustomShortcutSetting.xaml.cs index d05a5c15c..dec3506eb 100644 --- a/Flow.Launcher/CustomShortcutSetting.xaml.cs +++ b/Flow.Launcher/CustomShortcutSetting.xaml.cs @@ -64,8 +64,6 @@ namespace Flow.Launcher private void BtnTestShortcut_OnClick(object sender, RoutedEventArgs e) { - // if user happens to open context menu, we need to return back to query results before changing query - App.API.BackToQueryResults(); App.API.ChangeQuery(tbExpand.Text); Application.Current.MainWindow.Show(); Application.Current.MainWindow.Opacity = 1; diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 7706a64ba..d7f052af3 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -319,8 +319,6 @@ namespace Flow.Launcher public void ReQuery(bool reselect = true) => _mainVM.ReQuery(reselect); - public void BackToQueryResults() => _mainVM.BackToQueryResults(); - public MessageBoxResult ShowMsgBox(string messageBoxText, string caption = "", MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.OK) => MessageBoxEx.Show(messageBoxText, caption, button, icon, defaultResult); diff --git a/Flow.Launcher/ResultListBox.xaml.cs b/Flow.Launcher/ResultListBox.xaml.cs index 834692536..ac51b195c 100644 --- a/Flow.Launcher/ResultListBox.xaml.cs +++ b/Flow.Launcher/ResultListBox.xaml.cs @@ -149,11 +149,7 @@ namespace Flow.Launcher var rawQuery = query; var effect = DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move | DragDropEffects.Copy); if (effect == DragDropEffects.Move) - { - // if user happens to open context menu, we need to return back to query results before changing query - App.API.BackToQueryResults(); App.API.ChangeQuery(rawQuery, true); - } } private void ResultListBox_OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) { diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 4af93daf9..d8e33ad14 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -279,10 +279,8 @@ namespace Flow.Launcher.ViewModel public void ReQuery(bool reselect) { - if (SelectedIsFromQueryResults()) - { - QueryResults(isReQuery: true, reSelect: reselect); - } + BackToQueryResults(); + QueryResults(isReQuery: true, reSelect: reselect); } [RelayCommand] @@ -491,7 +489,7 @@ namespace Flow.Launcher.ViewModel } } - public void BackToQueryResults() + private void BackToQueryResults() { if (!SelectedIsFromQueryResults()) { @@ -610,6 +608,8 @@ namespace Flow.Launcher.ViewModel { Application.Current.Dispatcher.Invoke(() => { + BackToQueryResults(); + if (QueryText != queryText) { // re-query is done in QueryText's setter method @@ -1266,8 +1266,6 @@ namespace Flow.Launcher.ViewModel { _topMostRecord.Remove(result); App.API.ShowMsg(InternationalizationManager.Instance.GetTranslation("success")); - // if user happens to open context menu, we need to return back to query results before changing query - App.API.BackToQueryResults(); App.API.ReQuery(); return false; } @@ -1285,8 +1283,6 @@ namespace Flow.Launcher.ViewModel { _topMostRecord.AddOrUpdate(result); App.API.ShowMsg(InternationalizationManager.Instance.GetTranslation("success")); - // if user happens to open context menu, we need to return back to query results before changing query - App.API.BackToQueryResults(); App.API.ReQuery(); return false; } diff --git a/Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs b/Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs index 7675ecb16..38b5bec65 100644 --- a/Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs +++ b/Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs @@ -64,8 +64,6 @@ namespace Flow.Launcher.ViewModel private void ShowCommandQuery(string action) { var actionKeyword = PluginManagerData.Metadata.ActionKeywords.Any() ? PluginManagerData.Metadata.ActionKeywords[0] + " " : String.Empty; - // if user happens to open context menu, we need to return back to query results before changing query - App.API.BackToQueryResults(); App.API.ChangeQuery($"{actionKeyword}{action} {_plugin.Name}"); App.API.ShowMainWindow(); } diff --git a/Flow.Launcher/ViewModel/PluginViewModel.cs b/Flow.Launcher/ViewModel/PluginViewModel.cs index c8601c431..447f06860 100644 --- a/Flow.Launcher/ViewModel/PluginViewModel.cs +++ b/Flow.Launcher/ViewModel/PluginViewModel.cs @@ -146,8 +146,6 @@ namespace Flow.Launcher.ViewModel [RelayCommand] private void OpenDeletePluginWindow() { - // if user happens to open context menu, we need to return back to query results before changing query - PluginManager.API.BackToQueryResults(); PluginManager.API.ChangeQuery($"{PluginManagerActionKeyword} uninstall {PluginPair.Metadata.Name}".Trim(), true); PluginManager.API.ShowMainWindow(); } @@ -161,5 +159,4 @@ namespace Flow.Launcher.ViewModel changeKeywordsWindow.ShowDialog(); } } - } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 6ba7047f2..c08853df8 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -264,7 +264,6 @@ namespace Flow.Launcher.Plugin.Program Context.API.GetTranslation("flowlauncher_plugin_program_disable_dlgtitle_success"), Context.API.GetTranslation( "flowlauncher_plugin_program_disable_dlgtitle_success_message")); - Context.API.BackToQueryResults(); Context.API.ReQuery(); return false; }, diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs index 86808cfbc..62076b494 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs @@ -382,7 +382,6 @@ namespace Flow.Launcher.Plugin.Shell { context.API.ShowMainWindow(); // if user happens to open context menu, we need to return back to query results before changing query - context.API.BackToQueryResults(); context.API.ChangeQuery($"{context.CurrentPluginMetadata.ActionKeywords[0]}{Plugin.Query.TermSeparator}"); }); From 6f55e8a35dbaf087a265dd2ff772a46d07b42996 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 22 Feb 2025 19:44:21 +0800 Subject: [PATCH 48/72] Add BackToQueryResults api function back --- Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs | 6 ++++++ Flow.Launcher/PublicAPIInstance.cs | 2 ++ Flow.Launcher/ViewModel/MainViewModel.cs | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index 1a8da381b..28cc1fc91 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -305,6 +305,12 @@ namespace Flow.Launcher.Plugin /// Choose the first result after reload if true; keep the last selected result if false. Default is true. public void ReQuery(bool reselect = true); + /// + /// Back to the query results. + /// This method should run when selected item is from context menu or history. + /// + public void BackToQueryResults(); + /// /// Displays a standardised Flow message box. /// diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index d7f052af3..7706a64ba 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -319,6 +319,8 @@ namespace Flow.Launcher public void ReQuery(bool reselect = true) => _mainVM.ReQuery(reselect); + public void BackToQueryResults() => _mainVM.BackToQueryResults(); + public MessageBoxResult ShowMsgBox(string messageBoxText, string caption = "", MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.OK) => MessageBoxEx.Show(messageBoxText, caption, button, icon, defaultResult); diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index d8e33ad14..0d1f5655b 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -489,7 +489,7 @@ namespace Flow.Launcher.ViewModel } } - private void BackToQueryResults() + public void BackToQueryResults() { if (!SelectedIsFromQueryResults()) { From a6f33401a36eac22a94e77604285259296853eb5 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 22 Feb 2025 19:45:43 +0800 Subject: [PATCH 49/72] Add BackToQueryResults in json prc api --- .../Plugin/JsonRPCV2Models/JsonRPCPublicAPI.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCV2Models/JsonRPCPublicAPI.cs b/Flow.Launcher.Core/Plugin/JsonRPCV2Models/JsonRPCPublicAPI.cs index a82cae5d2..e0a0434a2 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCV2Models/JsonRPCPublicAPI.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCV2Models/JsonRPCPublicAPI.cs @@ -170,5 +170,10 @@ namespace Flow.Launcher.Core.Plugin.JsonRPCV2Models { _api.OpenAppUri(appUri); } + + public void BackToQueryResults() + { + _api.BackToQueryResults(); + } } } From 64eb4a7bea6b10467d17fb5e4b623b9ba43dbfd7 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 22 Feb 2025 19:48:25 +0800 Subject: [PATCH 50/72] Improve documents --- Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs | 4 ++-- Plugins/Flow.Launcher.Plugin.Shell/Main.cs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index 28cc1fc91..9b608d14f 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -18,7 +18,7 @@ namespace Flow.Launcher.Plugin { /// /// Change Flow.Launcher query. - /// When current results is from context menu or history, we will back to query results before changing query. + /// When current results are from context menu or history, we will back to query results before changing query. /// /// query text /// @@ -300,7 +300,7 @@ namespace Flow.Launcher.Plugin /// /// Reloads the query. - /// When current results is from context menu or history, we will back to query results before changing query. + /// When current results are from context menu or history, we will back to query results before requerying. /// /// Choose the first result after reload if true; keep the last selected result if false. Default is true. public void ReQuery(bool reselect = true); diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs index 62076b494..53479b81f 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs @@ -381,7 +381,6 @@ namespace Flow.Launcher.Plugin.Shell _ = Task.Run(() => { context.API.ShowMainWindow(); - // if user happens to open context menu, we need to return back to query results before changing query context.API.ChangeQuery($"{context.CurrentPluginMetadata.ActionKeywords[0]}{Plugin.Query.TermSeparator}"); }); From 61cd5360283b2bcd3e4029d13a5c2d5eac4a00b8 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 22 Feb 2025 23:16:20 +1100 Subject: [PATCH 51/72] Apply suggestions from code review --- Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index 9b608d14f..bf8bbf44a 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -300,7 +300,7 @@ namespace Flow.Launcher.Plugin /// /// Reloads the query. - /// When current results are from context menu or history, we will back to query results before requerying. + /// When current results are from context menu or history, it will go back to query results before requerying. /// /// Choose the first result after reload if true; keep the last selected result if false. Default is true. public void ReQuery(bool reselect = true); From 0f82246683e30e62c3b6281da9f5ddf189e30103 Mon Sep 17 00:00:00 2001 From: Jack Ye <1160210343@qq.com> Date: Sat, 22 Feb 2025 20:16:36 +0800 Subject: [PATCH 52/72] Update Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs Co-authored-by: Jeremy Wu --- Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index bf8bbf44a..07fc378c3 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -18,7 +18,7 @@ namespace Flow.Launcher.Plugin { /// /// Change Flow.Launcher query. - /// When current results are from context menu or history, we will back to query results before changing query. + /// When current results are from context menu or history, it will go back to query results before changing query. /// /// query text /// From 81d479c3a3c2177b8f0dc53a5ef7cd485ce99e75 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 22 Feb 2025 22:32:26 +0800 Subject: [PATCH 53/72] Fix build issue for nunit 4.3.2 & Improve code quality --- Flow.Launcher.Test/FilesFoldersTest.cs | 5 +- Flow.Launcher.Test/FuzzyMatcherTest.cs | 37 +++++----- Flow.Launcher.Test/HttpTest.cs | 15 ++-- Flow.Launcher.Test/PluginLoadTest.cs | 31 ++++---- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 70 ++++++------------- .../Plugins/JsonRPCPluginTest.cs | 16 ++--- Flow.Launcher.Test/Plugins/UrlPluginTest.cs | 33 ++++----- Flow.Launcher.Test/QueryBuilderTest.cs | 39 ++++++----- 8 files changed, 113 insertions(+), 133 deletions(-) diff --git a/Flow.Launcher.Test/FilesFoldersTest.cs b/Flow.Launcher.Test/FilesFoldersTest.cs index 3dead9918..2621fc2da 100644 --- a/Flow.Launcher.Test/FilesFoldersTest.cs +++ b/Flow.Launcher.Test/FilesFoldersTest.cs @@ -1,5 +1,6 @@ using Flow.Launcher.Plugin.SharedCommands; using NUnit.Framework; +using NUnit.Framework.Legacy; namespace Flow.Launcher.Test { @@ -35,7 +36,7 @@ namespace Flow.Launcher.Test [TestCase(@"c:\barr", @"c:\foo\..\bar\baz", false)] public void GivenTwoPaths_WhenCheckPathContains_ThenShouldBeExpectedResult(string parentPath, string path, bool expectedResult) { - Assert.AreEqual(expectedResult, FilesFolders.PathContains(parentPath, path)); + ClassicAssert.AreEqual(expectedResult, FilesFolders.PathContains(parentPath, path)); } // Equality @@ -47,7 +48,7 @@ namespace Flow.Launcher.Test [TestCase(@"c:\foo", @"c:\foo\", true)] public void GivenTwoPathsAreTheSame_WhenCheckPathContains_ThenShouldBeExpectedResult(string parentPath, string path, bool expectedResult) { - Assert.AreEqual(expectedResult, FilesFolders.PathContains(parentPath, path, allowEqual: expectedResult)); + ClassicAssert.AreEqual(expectedResult, FilesFolders.PathContains(parentPath, path, allowEqual: expectedResult)); } } } diff --git a/Flow.Launcher.Test/FuzzyMatcherTest.cs b/Flow.Launcher.Test/FuzzyMatcherTest.cs index d7f143218..9d3b7e70f 100644 --- a/Flow.Launcher.Test/FuzzyMatcherTest.cs +++ b/Flow.Launcher.Test/FuzzyMatcherTest.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using NUnit.Framework; +using NUnit.Framework.Legacy; using Flow.Launcher.Infrastructure; using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedModels; @@ -21,8 +22,8 @@ namespace Flow.Launcher.Test private const string MicrosoftSqlServerManagementStudio = "Microsoft SQL Server Management Studio"; private const string VisualStudioCode = "Visual Studio Code"; - public List GetSearchStrings() - => new List + public static List GetSearchStrings() + => new() { Chrome, "Choose which programs you want Windows to use for activities like web browsing, editing photos, sending e-mail, and playing music.", @@ -34,7 +35,7 @@ namespace Flow.Launcher.Test OneOneOneOne }; - public List GetPrecisionScores() + public static List GetPrecisionScores() { var listToReturn = new List(); @@ -71,10 +72,10 @@ namespace Flow.Launcher.Test results = results.Where(x => x.Score > 0).OrderByDescending(x => x.Score).ToList(); - Assert.IsTrue(results.Count == 3); - Assert.IsTrue(results[0].Title == "Inste"); - Assert.IsTrue(results[1].Title == "Install Package"); - Assert.IsTrue(results[2].Title == "file open in browser-test"); + ClassicAssert.IsTrue(results.Count == 3); + ClassicAssert.IsTrue(results[0].Title == "Inste"); + ClassicAssert.IsTrue(results[1].Title == "Install Package"); + ClassicAssert.IsTrue(results[2].Title == "file open in browser-test"); } [TestCase("Chrome")] @@ -84,7 +85,7 @@ namespace Flow.Launcher.Test var matcher = new StringMatcher(); var scoreResult = matcher.FuzzyMatch(searchString, compareString).RawScore; - Assert.True(scoreResult == 0); + ClassicAssert.True(scoreResult == 0); } [TestCase("chr")] @@ -125,7 +126,7 @@ namespace Flow.Launcher.Test Debug.WriteLine("###############################################"); Debug.WriteLine(""); - Assert.IsFalse(filteredResult.Any(x => x.Score < precisionScore)); + ClassicAssert.IsFalse(filteredResult.Any(x => x.Score < precisionScore)); } } @@ -151,7 +152,7 @@ namespace Flow.Launcher.Test var rawScore = matcher.FuzzyMatch(queryString, compareString).RawScore; // Should - Assert.AreEqual(expectedScore, rawScore, + ClassicAssert.AreEqual(expectedScore, rawScore, $"Expected score for compare string '{compareString}': {expectedScore}, Actual: {rawScore}"); } @@ -190,12 +191,12 @@ namespace Flow.Launcher.Test Debug.WriteLine("###############################################"); Debug.WriteLine($"QueryString: {queryString} CompareString: {compareString}"); Debug.WriteLine( - $"RAW SCORE: {matchResult.RawScore.ToString()}, PrecisionLevelSetAt: {expectedPrecisionScore} ({(int) expectedPrecisionScore})"); + $"RAW SCORE: {matchResult.RawScore}, PrecisionLevelSetAt: {expectedPrecisionScore} ({(int) expectedPrecisionScore})"); Debug.WriteLine("###############################################"); Debug.WriteLine(""); // Should - Assert.AreEqual(expectedPrecisionResult, matchResult.IsSearchPrecisionScoreMet(), + ClassicAssert.AreEqual(expectedPrecisionResult, matchResult.IsSearchPrecisionScoreMet(), $"Query: {queryString}{Environment.NewLine} " + $"Compare: {compareString}{Environment.NewLine}" + $"Raw Score: {matchResult.RawScore}{Environment.NewLine}" + @@ -241,12 +242,12 @@ namespace Flow.Launcher.Test Debug.WriteLine("###############################################"); Debug.WriteLine($"QueryString: {queryString} CompareString: {compareString}"); Debug.WriteLine( - $"RAW SCORE: {matchResult.RawScore.ToString()}, PrecisionLevelSetAt: {expectedPrecisionScore} ({(int) expectedPrecisionScore})"); + $"RAW SCORE: {matchResult.RawScore}, PrecisionLevelSetAt: {expectedPrecisionScore} ({(int) expectedPrecisionScore})"); Debug.WriteLine("###############################################"); Debug.WriteLine(""); // Should - Assert.AreEqual(expectedPrecisionResult, matchResult.IsSearchPrecisionScoreMet(), + ClassicAssert.AreEqual(expectedPrecisionResult, matchResult.IsSearchPrecisionScoreMet(), $"Query:{queryString}{Environment.NewLine} " + $"Compare:{compareString}{Environment.NewLine}" + $"Raw Score: {matchResult.RawScore}{Environment.NewLine}" + @@ -277,7 +278,7 @@ namespace Flow.Launcher.Test Debug.WriteLine(""); // Should - Assert.True(compareString1Result.Score > compareString2Result.Score, + ClassicAssert.True(compareString1Result.Score > compareString2Result.Score, $"Query: \"{queryString}\"{Environment.NewLine} " + $"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}" + $"Should be greater than{Environment.NewLine}" + @@ -310,7 +311,7 @@ namespace Flow.Launcher.Test Debug.WriteLine(""); // Should - Assert.True(compareString1Result.Score > compareString2Result.Score, + ClassicAssert.True(compareString1Result.Score > compareString2Result.Score, $"Query: \"{queryString}\"{Environment.NewLine} " + $"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}" + $"Should be greater than{Environment.NewLine}" + @@ -336,7 +337,7 @@ namespace Flow.Launcher.Test var secondScore = new[] {secondNameMatch, secondDescriptionMatch, secondExecutableNameMatch}.Max(); // Assert - Assert.IsTrue(firstScore > secondScore, + ClassicAssert.IsTrue(firstScore > secondScore, $"Query: \"{queryString}\"{Environment.NewLine} " + $"Name of first: \"{firstName}\", Final Score: {firstScore}{Environment.NewLine}" + $"Should be greater than{Environment.NewLine}" + @@ -360,7 +361,7 @@ namespace Flow.Launcher.Test { var matcher = new StringMatcher(); var score = matcher.FuzzyMatch(queryString, compareString).Score; - Assert.IsTrue(score == desiredScore, + ClassicAssert.IsTrue(score == desiredScore, $@"Query: ""{queryString}"" CompareString: ""{compareString}"" Score: {score} diff --git a/Flow.Launcher.Test/HttpTest.cs b/Flow.Launcher.Test/HttpTest.cs index e72ad7a67..4f135978a 100644 --- a/Flow.Launcher.Test/HttpTest.cs +++ b/Flow.Launcher.Test/HttpTest.cs @@ -1,4 +1,5 @@ -using NUnit.Framework; +using NUnit.Framework; +using NUnit.Framework.Legacy; using System; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Infrastructure.Http; @@ -16,16 +17,16 @@ namespace Flow.Launcher.Test proxy.Enabled = true; proxy.Server = "127.0.0.1"; - Assert.AreEqual(Http.WebProxy.Address, new Uri($"http://{proxy.Server}:{proxy.Port}")); - Assert.IsNull(Http.WebProxy.Credentials); + ClassicAssert.AreEqual(Http.WebProxy.Address, new Uri($"http://{proxy.Server}:{proxy.Port}")); + ClassicAssert.IsNull(Http.WebProxy.Credentials); proxy.UserName = "test"; - Assert.NotNull(Http.WebProxy.Credentials); - Assert.AreEqual(Http.WebProxy.Credentials.GetCredential(Http.WebProxy.Address, "Basic").UserName, proxy.UserName); - Assert.AreEqual(Http.WebProxy.Credentials.GetCredential(Http.WebProxy.Address, "Basic").Password, ""); + ClassicAssert.NotNull(Http.WebProxy.Credentials); + ClassicAssert.AreEqual(Http.WebProxy.Credentials.GetCredential(Http.WebProxy.Address, "Basic").UserName, proxy.UserName); + ClassicAssert.AreEqual(Http.WebProxy.Credentials.GetCredential(Http.WebProxy.Address, "Basic").Password, ""); proxy.Password = "test password"; - Assert.AreEqual(Http.WebProxy.Credentials.GetCredential(Http.WebProxy.Address, "Basic").Password, proxy.Password); + ClassicAssert.AreEqual(Http.WebProxy.Credentials.GetCredential(Http.WebProxy.Address, "Basic").Password, proxy.Password); } } } diff --git a/Flow.Launcher.Test/PluginLoadTest.cs b/Flow.Launcher.Test/PluginLoadTest.cs index d6ba48f19..2cc05f95a 100644 --- a/Flow.Launcher.Test/PluginLoadTest.cs +++ b/Flow.Launcher.Test/PluginLoadTest.cs @@ -1,4 +1,5 @@ using NUnit.Framework; +using NUnit.Framework.Legacy; using Flow.Launcher.Core.Plugin; using Flow.Launcher.Plugin; using System.Collections.Generic; @@ -15,37 +16,37 @@ namespace Flow.Launcher.Test // Given var duplicateList = new List { - new PluginMetadata + new() { ID = "CEA0TYUC6D3B4085823D60DC76F28855", Version = "1.0.0" }, - new PluginMetadata + new() { ID = "CEA0TYUC6D3B4085823D60DC76F28855", Version = "1.0.1" }, - new PluginMetadata + new() { ID = "CEA0TYUC6D3B4085823D60DC76F28855", Version = "1.0.2" }, - new PluginMetadata + new() { ID = "CEA0TYUC6D3B4085823D60DC76F28855", Version = "1.0.0" }, - new PluginMetadata + new() { ID = "CEA0TYUC6D3B4085823D60DC76F28855", Version = "1.0.0" }, - new PluginMetadata + new() { ID = "ABC0TYUC6D3B7855823D60DC76F28855", Version = "1.0.0" }, - new PluginMetadata + new() { ID = "ABC0TYUC6D3B7855823D60DC76F28855", Version = "1.0.0" @@ -56,11 +57,11 @@ namespace Flow.Launcher.Test (var unique, var duplicates) = PluginConfig.GetUniqueLatestPluginMetadata(duplicateList); // Then - Assert.True(unique.FirstOrDefault().ID == "CEA0TYUC6D3B4085823D60DC76F28855" && unique.FirstOrDefault().Version == "1.0.2"); - Assert.True(unique.Count() == 1); + ClassicAssert.True(unique.FirstOrDefault().ID == "CEA0TYUC6D3B4085823D60DC76F28855" && unique.FirstOrDefault().Version == "1.0.2"); + ClassicAssert.True(unique.Count == 1); - Assert.False(duplicates.Any(x => x.Version == "1.0.2" && x.ID == "CEA0TYUC6D3B4085823D60DC76F28855")); - Assert.True(duplicates.Count() == 6); + ClassicAssert.False(duplicates.Any(x => x.Version == "1.0.2" && x.ID == "CEA0TYUC6D3B4085823D60DC76F28855")); + ClassicAssert.True(duplicates.Count == 6); } [Test] @@ -69,12 +70,12 @@ namespace Flow.Launcher.Test // Given var duplicateList = new List { - new PluginMetadata + new() { ID = "CEA0TYUC6D3B7855823D60DC76F28855", Version = "1.0.0" }, - new PluginMetadata + new() { ID = "CEA0TYUC6D3B7855823D60DC76F28855", Version = "1.0.0" @@ -85,8 +86,8 @@ namespace Flow.Launcher.Test (var unique, var duplicates) = PluginConfig.GetUniqueLatestPluginMetadata(duplicateList); // Then - Assert.True(unique.Count() == 0); - Assert.True(duplicates.Count() == 2); + ClassicAssert.True(unique.Count == 0); + ClassicAssert.True(duplicates.Count == 2); } } } diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 80cb74729..420da266d 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -5,12 +5,10 @@ using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; using Flow.Launcher.Plugin.SharedCommands; using NUnit.Framework; +using NUnit.Framework.Legacy; using System; -using System.Collections.Generic; using System.IO; using System.Runtime.Versioning; -using System.Threading; -using System.Threading.Tasks; using static Flow.Launcher.Plugin.Explorer.Search.SearchManager; namespace Flow.Launcher.Test.Plugins @@ -22,28 +20,6 @@ namespace Flow.Launcher.Test.Plugins [TestFixture] public class ExplorerTest { -#pragma warning disable CS1998 // async method with no await (more readable to leave it async to match the tested signature) - private async Task> MethodWindowsIndexSearchReturnsZeroResultsAsync(Query dummyQuery, string dummyString, CancellationToken dummyToken) - { - return new List(); - } -#pragma warning restore CS1998 - - private List MethodDirectoryInfoClassSearchReturnsTwoResults(Query dummyQuery, string dummyString, CancellationToken token) - { - return new List - { - new Result - { - Title = "Result 1" - }, - new Result - { - Title = "Result 2" - } - }; - } - private bool PreviousLocationExistsReturnsTrue(string dummyString) => true; private bool PreviousLocationNotExistReturnsFalse(string dummyString) => false; @@ -57,7 +33,7 @@ namespace Flow.Launcher.Test.Plugins var result = QueryConstructor.TopLevelDirectoryConstraint(folderPath); // Then - Assert.IsTrue(result == expectedString, + ClassicAssert.IsTrue(result == expectedString, $"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " + $"Actual: {result}{Environment.NewLine}"); } @@ -74,7 +50,7 @@ namespace Flow.Launcher.Test.Plugins var queryString = queryConstructor.Directory(folderPath); // Then - Assert.IsTrue(queryString.Replace(" ", " ") == expectedString.Replace(" ", " "), + ClassicAssert.IsTrue(queryString.Replace(" ", " ") == expectedString.Replace(" ", " "), $"Expected string: {expectedString}{Environment.NewLine} " + $"Actual string was: {queryString}{Environment.NewLine}"); } @@ -94,7 +70,7 @@ namespace Flow.Launcher.Test.Plugins var queryString = queryConstructor.Directory(folderPath, userSearchString); // Then - Assert.AreEqual(expectedString, queryString); + ClassicAssert.AreEqual(expectedString, queryString); } [SupportedOSPlatform("windows7.0")] @@ -105,7 +81,7 @@ namespace Flow.Launcher.Test.Plugins const string resultString = QueryConstructor.RestrictionsForAllFilesAndFoldersSearch; // Then - Assert.AreEqual(expectedString, resultString); + ClassicAssert.AreEqual(expectedString, resultString); } [SupportedOSPlatform("windows7.0")] @@ -128,7 +104,7 @@ namespace Flow.Launcher.Test.Plugins var resultString = queryConstructor.FilesAndFolders(userSearchString); // Then - Assert.AreEqual(expectedString, resultString); + ClassicAssert.AreEqual(expectedString, resultString); } @@ -138,13 +114,13 @@ namespace Flow.Launcher.Test.Plugins string querySearchString, string expectedString) { // Given - var queryConstructor = new QueryConstructor(new Settings()); + _ = new QueryConstructor(new Settings()); //When var resultString = QueryConstructor.RestrictionsForFileContentSearch(querySearchString); // Then - Assert.IsTrue(resultString == expectedString, + ClassicAssert.IsTrue(resultString == expectedString, $"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " + $"Actual string was: {resultString}{Environment.NewLine}"); } @@ -162,12 +138,12 @@ namespace Flow.Launcher.Test.Plugins var resultString = queryConstructor.FileContent(userSearchString); // Then - Assert.IsTrue(resultString == expectedString, + ClassicAssert.IsTrue(resultString == expectedString, $"Expected query string: {expectedString}{Environment.NewLine} " + $"Actual string was: {resultString}{Environment.NewLine}"); } - public void GivenQuery_WhenActionKeywordForFileContentSearchExists_ThenFileContentSearchRequiredShouldReturnTrue() + public static void GivenQuery_WhenActionKeywordForFileContentSearchExists_ThenFileContentSearchRequiredShouldReturnTrue() { // Given var query = new Query @@ -181,7 +157,7 @@ namespace Flow.Launcher.Test.Plugins var result = searchManager.IsFileContentSearch(query.ActionKeyword); // Then - Assert.IsTrue(result, + ClassicAssert.IsTrue(result, $"Expected True for file content search. {Environment.NewLine} " + $"Actual result was: {result}{Environment.NewLine}"); } @@ -206,7 +182,7 @@ namespace Flow.Launcher.Test.Plugins var result = FilesFolders.IsLocationPathString(querySearchString); //Then - Assert.IsTrue(result == expectedResult, + ClassicAssert.IsTrue(result == expectedResult, $"Expected query search string check result is: {expectedResult} {Environment.NewLine} " + $"Actual check result is {result} {Environment.NewLine}"); @@ -233,7 +209,7 @@ namespace Flow.Launcher.Test.Plugins var previousDirectoryPath = FilesFolders.GetPreviousExistingDirectory(previousLocationExists, path); //Then - Assert.IsTrue(previousDirectoryPath == expectedString, + ClassicAssert.IsTrue(previousDirectoryPath == expectedString, $"Expected path string: {expectedString} {Environment.NewLine} " + $"Actual path string is {previousDirectoryPath} {Environment.NewLine}"); } @@ -246,7 +222,7 @@ namespace Flow.Launcher.Test.Plugins var returnedPath = FilesFolders.ReturnPreviousDirectoryIfIncompleteString(path); //Then - Assert.IsTrue(returnedPath == expectedString, + ClassicAssert.IsTrue(returnedPath == expectedString, $"Expected path string: {expectedString} {Environment.NewLine} " + $"Actual path string is {returnedPath} {Environment.NewLine}"); } @@ -260,7 +236,7 @@ namespace Flow.Launcher.Test.Plugins var resultString = QueryConstructor.RecursiveDirectoryConstraint(path); // Then - Assert.AreEqual(expectedString, resultString); + ClassicAssert.AreEqual(expectedString, resultString); } [SupportedOSPlatform("windows7.0")] @@ -274,7 +250,7 @@ namespace Flow.Launcher.Test.Plugins var resultString = DirectoryInfoSearch.ConstructSearchCriteria(path); // Then - Assert.AreEqual(expectedString, resultString); + ClassicAssert.AreEqual(expectedString, resultString); } [TestCase("c:\\somefolder\\someotherfolder", ResultType.Folder, "irrelevant", false, true, "c:\\somefolder\\someotherfolder\\")] @@ -305,7 +281,7 @@ namespace Flow.Launcher.Test.Plugins var result = ResultManager.GetPathWithActionKeyword(path, type, actionKeyword); // Then - Assert.AreEqual(result, expectedResult); + ClassicAssert.AreEqual(result, expectedResult); } [TestCase("c:\\somefolder\\somefile", ResultType.File, "irrelevant", false, true, "e c:\\somefolder\\somefile")] @@ -334,7 +310,7 @@ namespace Flow.Launcher.Test.Plugins var result = ResultManager.GetPathWithActionKeyword(path, type, actionKeyword); // Then - Assert.AreEqual(result, expectedResult); + ClassicAssert.AreEqual(result, expectedResult); } [TestCase("somefolder", "c:\\somefolder\\", ResultType.Folder, "q", false, false, "q somefolder")] @@ -366,7 +342,7 @@ namespace Flow.Launcher.Test.Plugins var result = ResultManager.GetAutoCompleteText(title, query, path, resultType); // Then - Assert.AreEqual(result, expectedResult); + ClassicAssert.AreEqual(result, expectedResult); } [TestCase("somefile", "c:\\somefolder\\somefile", ResultType.File, "q", false, false, "q somefile")] @@ -398,7 +374,7 @@ namespace Flow.Launcher.Test.Plugins var result = ResultManager.GetAutoCompleteText(title, query, path, resultType); // Then - Assert.AreEqual(result, expectedResult); + ClassicAssert.AreEqual(result, expectedResult); } [TestCase(@"c:\foo", @"c:\foo", true)] @@ -420,7 +396,7 @@ namespace Flow.Launcher.Test.Plugins }; // When, Then - Assert.AreEqual(expectedResult, comparator.Equals(result1, result2)); + ClassicAssert.AreEqual(expectedResult, comparator.Equals(result1, result2)); } [TestCase(@"c:\foo\", @"c:\foo\")] @@ -444,7 +420,7 @@ namespace Flow.Launcher.Test.Plugins var hash2 = comparator.GetHashCode(result2); // When, Then - Assert.IsTrue(hash1 == hash2); + ClassicAssert.IsTrue(hash1 == hash2); } [TestCase(@"%appdata%", true)] @@ -461,7 +437,7 @@ namespace Flow.Launcher.Test.Plugins var result = EnvironmentVariables.HasEnvironmentVar(path); // Then - Assert.AreEqual(result, expectedResult); + ClassicAssert.AreEqual(result, expectedResult); } } } diff --git a/Flow.Launcher.Test/Plugins/JsonRPCPluginTest.cs b/Flow.Launcher.Test/Plugins/JsonRPCPluginTest.cs index 42a4630fe..497f874e7 100644 --- a/Flow.Launcher.Test/Plugins/JsonRPCPluginTest.cs +++ b/Flow.Launcher.Test/Plugins/JsonRPCPluginTest.cs @@ -1,12 +1,11 @@ -using NUnit.Framework; +using NUnit.Framework; +using NUnit.Framework.Legacy; using Flow.Launcher.Core.Plugin; using Flow.Launcher.Plugin; using System.Threading.Tasks; using System.IO; using System.Threading; using System.Text; -using System.Text.Json; -using System.Linq; using System.Collections.Generic; namespace Flow.Launcher.Test.Plugins @@ -40,13 +39,13 @@ namespace Flow.Launcher.Test.Plugins Search = resultText }, default); - Assert.IsNotNull(results); + ClassicAssert.IsNotNull(results); foreach (var result in results) { - Assert.IsNotNull(result); - Assert.IsNotNull(result.AsyncAction); - Assert.IsNotNull(result.Title); + ClassicAssert.IsNotNull(result); + ClassicAssert.IsNotNull(result.AsyncAction); + ClassicAssert.IsNotNull(result.Title); } } @@ -56,12 +55,11 @@ namespace Flow.Launcher.Test.Plugins new JsonRPCQueryResponseModel(0, new List()), new JsonRPCQueryResponseModel(0, new List { - new JsonRPCResult + new() { Title = "Test1", SubTitle = "Test2" } }) }; - } } diff --git a/Flow.Launcher.Test/Plugins/UrlPluginTest.cs b/Flow.Launcher.Test/Plugins/UrlPluginTest.cs index 7ccac5bd5..0dd1fe489 100644 --- a/Flow.Launcher.Test/Plugins/UrlPluginTest.cs +++ b/Flow.Launcher.Test/Plugins/UrlPluginTest.cs @@ -1,7 +1,8 @@ using NUnit.Framework; +using NUnit.Framework.Legacy; using Flow.Launcher.Plugin.Url; -namespace Flow.Launcher.Test +namespace Flow.Launcher.Test.Plugins { [TestFixture] public class UrlPluginTest @@ -10,23 +11,23 @@ namespace Flow.Launcher.Test public void URLMatchTest() { var plugin = new Main(); - Assert.IsTrue(plugin.IsURL("http://www.google.com")); - Assert.IsTrue(plugin.IsURL("https://www.google.com")); - Assert.IsTrue(plugin.IsURL("http://google.com")); - Assert.IsTrue(plugin.IsURL("www.google.com")); - Assert.IsTrue(plugin.IsURL("google.com")); - Assert.IsTrue(plugin.IsURL("http://localhost")); - Assert.IsTrue(plugin.IsURL("https://localhost")); - Assert.IsTrue(plugin.IsURL("http://localhost:80")); - Assert.IsTrue(plugin.IsURL("https://localhost:80")); - Assert.IsTrue(plugin.IsURL("http://110.10.10.10")); - Assert.IsTrue(plugin.IsURL("110.10.10.10")); - Assert.IsTrue(plugin.IsURL("ftp://110.10.10.10")); + ClassicAssert.IsTrue(plugin.IsURL("http://www.google.com")); + ClassicAssert.IsTrue(plugin.IsURL("https://www.google.com")); + ClassicAssert.IsTrue(plugin.IsURL("http://google.com")); + ClassicAssert.IsTrue(plugin.IsURL("www.google.com")); + ClassicAssert.IsTrue(plugin.IsURL("google.com")); + ClassicAssert.IsTrue(plugin.IsURL("http://localhost")); + ClassicAssert.IsTrue(plugin.IsURL("https://localhost")); + ClassicAssert.IsTrue(plugin.IsURL("http://localhost:80")); + ClassicAssert.IsTrue(plugin.IsURL("https://localhost:80")); + ClassicAssert.IsTrue(plugin.IsURL("http://110.10.10.10")); + ClassicAssert.IsTrue(plugin.IsURL("110.10.10.10")); + ClassicAssert.IsTrue(plugin.IsURL("ftp://110.10.10.10")); - Assert.IsFalse(plugin.IsURL("wwww")); - Assert.IsFalse(plugin.IsURL("wwww.c")); - Assert.IsFalse(plugin.IsURL("wwww.c")); + ClassicAssert.IsFalse(plugin.IsURL("wwww")); + ClassicAssert.IsFalse(plugin.IsURL("wwww.c")); + ClassicAssert.IsFalse(plugin.IsURL("wwww.c")); } } } diff --git a/Flow.Launcher.Test/QueryBuilderTest.cs b/Flow.Launcher.Test/QueryBuilderTest.cs index aa0c8da12..c8ac17748 100644 --- a/Flow.Launcher.Test/QueryBuilderTest.cs +++ b/Flow.Launcher.Test/QueryBuilderTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using NUnit.Framework; +using NUnit.Framework.Legacy; using Flow.Launcher.Core.Plugin; using Flow.Launcher.Plugin; @@ -17,17 +18,17 @@ namespace Flow.Launcher.Test Query q = QueryBuilder.Build("> ping google.com -n 20 -6", nonGlobalPlugins); - Assert.AreEqual("> ping google.com -n 20 -6", q.RawQuery); - Assert.AreEqual("ping google.com -n 20 -6", q.Search, "Search should not start with the ActionKeyword."); - Assert.AreEqual(">", q.ActionKeyword); + ClassicAssert.AreEqual("> ping google.com -n 20 -6", q.RawQuery); + ClassicAssert.AreEqual("ping google.com -n 20 -6", q.Search, "Search should not start with the ActionKeyword."); + ClassicAssert.AreEqual(">", q.ActionKeyword); - Assert.AreEqual(5, q.SearchTerms.Length, "The length of SearchTerms should match."); + ClassicAssert.AreEqual(5, q.SearchTerms.Length, "The length of SearchTerms should match."); - Assert.AreEqual("ping", q.FirstSearch); - Assert.AreEqual("google.com", q.SecondSearch); - Assert.AreEqual("-n", q.ThirdSearch); + ClassicAssert.AreEqual("ping", q.FirstSearch); + ClassicAssert.AreEqual("google.com", q.SecondSearch); + ClassicAssert.AreEqual("-n", q.ThirdSearch); - Assert.AreEqual("google.com -n 20 -6", q.SecondToEndSearch, "SecondToEndSearch should be trimmed of multiple whitespace characters"); + ClassicAssert.AreEqual("google.com -n 20 -6", q.SecondToEndSearch, "SecondToEndSearch should be trimmed of multiple whitespace characters"); } [Test] @@ -40,11 +41,11 @@ namespace Flow.Launcher.Test Query q = QueryBuilder.Build("> ping google.com -n 20 -6", nonGlobalPlugins); - Assert.AreEqual("> ping google.com -n 20 -6", q.Search); - Assert.AreEqual(q.Search, q.RawQuery, "RawQuery should be equal to Search."); - Assert.AreEqual(6, q.SearchTerms.Length, "The length of SearchTerms should match."); - Assert.AreNotEqual(">", q.ActionKeyword, "ActionKeyword should not match that of a disabled plugin."); - Assert.AreEqual("ping google.com -n 20 -6", q.SecondToEndSearch, "SecondToEndSearch should be trimmed of multiple whitespace characters"); + ClassicAssert.AreEqual("> ping google.com -n 20 -6", q.Search); + ClassicAssert.AreEqual(q.Search, q.RawQuery, "RawQuery should be equal to Search."); + ClassicAssert.AreEqual(6, q.SearchTerms.Length, "The length of SearchTerms should match."); + ClassicAssert.AreNotEqual(">", q.ActionKeyword, "ActionKeyword should not match that of a disabled plugin."); + ClassicAssert.AreEqual("ping google.com -n 20 -6", q.SecondToEndSearch, "SecondToEndSearch should be trimmed of multiple whitespace characters"); } [Test] @@ -52,13 +53,13 @@ namespace Flow.Launcher.Test { Query q = QueryBuilder.Build("file.txt file2 file3", new Dictionary()); - Assert.AreEqual("file.txt file2 file3", q.Search); - Assert.AreEqual("", q.ActionKeyword); + ClassicAssert.AreEqual("file.txt file2 file3", q.Search); + ClassicAssert.AreEqual("", q.ActionKeyword); - Assert.AreEqual("file.txt", q.FirstSearch); - Assert.AreEqual("file2", q.SecondSearch); - Assert.AreEqual("file3", q.ThirdSearch); - Assert.AreEqual("file2 file3", q.SecondToEndSearch); + ClassicAssert.AreEqual("file.txt", q.FirstSearch); + ClassicAssert.AreEqual("file2", q.SecondSearch); + ClassicAssert.AreEqual("file3", q.ThirdSearch); + ClassicAssert.AreEqual("file2 file3", q.SecondToEndSearch); } } } From 58f8aaa8101b92318399353880811a58e8ef89b7 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 09:29:24 +0800 Subject: [PATCH 54/72] Show message when failed to remove plugin settings & Improve code quality --- Flow.Launcher.Core/Plugin/PluginManager.cs | 8 ++++++-- Flow.Launcher/Languages/en.xaml | 6 ++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 2bce2ba79..68f955744 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -210,9 +210,9 @@ namespace Flow.Launcher.Core.Plugin { var failed = string.Join(",", failedPlugins.Select(x => x.Metadata.Name)); API.ShowMsg( - InternationalizationManager.Instance.GetTranslation("failedToInitializePluginsTitle"), + API.GetTranslation("failedToInitializePluginsTitle"), string.Format( - InternationalizationManager.Instance.GetTranslation("failedToInitializePluginsMessage"), + API.GetTranslation("failedToInitializePluginsMessage"), failed ), "", @@ -568,6 +568,8 @@ namespace Flow.Launcher.Core.Plugin catch (Exception e) { Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin json folder for {plugin.Name}", e); + API.ShowMsg(API.GetTranslation("failedToRemovePluginSettingsTitle"), + string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name)); } } } @@ -583,6 +585,8 @@ namespace Flow.Launcher.Core.Plugin catch (Exception e) { Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin json folder for {plugin.Name}", e); + API.ShowMsg(API.GetTranslation("failedToRemovePluginSettingsTitle"), + string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name)); } } } diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 4c465d61f..a94887bf9 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -128,7 +128,8 @@ Version Website Uninstall - + Fail to remove plugin settings + Plugins: {0} - Fail to remove plugin settings files, please remove them manually Plugin Store @@ -145,8 +146,6 @@ This plugin has been updated within the last 7 days New Update is Available - - Theme Appearance @@ -196,7 +195,6 @@ This theme supports two(light/dark) modes. This theme supports Blur Transparent Background. - Hotkey Hotkeys From c0720120737667f23815bdd62fe6c6bdf2f8182a Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 13:19:55 +0800 Subject: [PATCH 55/72] Fix parameter name issue. --- Flow.Launcher/CustomQueryHotkeySetting.xaml.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs index af3c8f6c9..53fa173a5 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs @@ -11,12 +11,13 @@ namespace Flow.Launcher { public partial class CustomQueryHotkeySetting : Window { + private readonly Settings _settings; private bool update; private CustomPluginHotkey updateCustomHotkey; public CustomQueryHotkeySetting(Settings settings) { - Settings = settings; + _settings = settings; InitializeComponent(); } From 39d7187713432d7bcf8c11911ff4a54c3d64c1c3 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 13:27:39 +0800 Subject: [PATCH 56/72] Use constructor injection for updater --- Flow.Launcher.Core/Updater.cs | 7 ++++++- Flow.Launcher/App.xaml.cs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 7a25447b4..dd0864c19 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -23,10 +23,15 @@ namespace Flow.Launcher.Core { public class Updater { - private readonly IPublicAPI API = Ioc.Default.GetRequiredService(); + private readonly IPublicAPI API; public string GitHubRepository { get; set; } + public Updater(IPublicAPI publicAPI) + { + API = publicAPI; + } + public void Initialize(string gitHubRepository) { GitHubRepository = gitHubRepository; diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 5f7f097e0..b52ae7196 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -45,7 +45,7 @@ namespace Flow.Launcher .UseContentRoot(AppContext.BaseDirectory) .ConfigureServices(services => services .AddSingleton(_ => _settings) - .AddSingleton() + .AddSingleton(sp => new Updater(sp.GetRequiredService())) .AddSingleton() .AddSingleton() .AddSingleton() From 39d34b1a037e2e1a1ed3419faebeb39f693ea98d Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 13:31:07 +0800 Subject: [PATCH 57/72] Add circular dependency comments --- Flow.Launcher/PublicAPIInstance.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 8bcd3e046..6123a93d9 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -77,6 +77,7 @@ namespace Flow.Launcher public event VisibilityChangedEventHandler VisibilityChanged { add => _mainVM.VisibilityChanged += value; remove => _mainVM.VisibilityChanged -= value; } + // Must use Ioc.Default.GetRequiredService() to avoid circular dependency public void CheckForNewUpdate() => _ = Ioc.Default.GetRequiredService().UpdateAppAsync(false); public void SaveAppAllSettings() From e022ad6acdf5d486578e59fc3a4f7a3dede2864a Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 13:40:10 +0800 Subject: [PATCH 58/72] Remove instance for StringMatcher --- Flow.Launcher.Infrastructure/StringMatcher.cs | 4 +--- .../UserSettings/Settings.cs | 14 ++++++++++---- Flow.Launcher/App.xaml.cs | 9 ++++----- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Flow.Launcher.Infrastructure/StringMatcher.cs b/Flow.Launcher.Infrastructure/StringMatcher.cs index f5f02cbfc..55aa0a1ec 100644 --- a/Flow.Launcher.Infrastructure/StringMatcher.cs +++ b/Flow.Launcher.Infrastructure/StringMatcher.cs @@ -25,11 +25,9 @@ namespace Flow.Launcher.Infrastructure _alphabet = alphabet; } - public static StringMatcher Instance { get; internal set; } - public static MatchResult FuzzySearch(string query, string stringToCompare) { - return Instance.FuzzyMatch(query, stringToCompare); + return Ioc.Default.GetRequiredService().FuzzyMatch(query, stringToCompare); } public MatchResult FuzzyMatch(string query, string stringToCompare) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 2a9d901fa..d3f44530c 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -3,6 +3,7 @@ using System.Collections.ObjectModel; using System.Drawing; using System.Text.Json.Serialization; using System.Windows; +using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Plugin; @@ -14,12 +15,18 @@ namespace Flow.Launcher.Infrastructure.UserSettings public class Settings : BaseModel, IHotkeySettings { private FlowLauncherJsonStorage _storage; + private StringMatcher _stringMatcher = null; - public void Initialize(FlowLauncherJsonStorage storage) + public void SetStorage(FlowLauncherJsonStorage storage) { _storage = storage; } + public void Initialize() + { + _stringMatcher = Ioc.Default.GetRequiredService(); + } + public void Save() { _storage.Save(); @@ -192,7 +199,6 @@ namespace Flow.Launcher.Infrastructure.UserSettings } }; - /// /// when false Alphabet static service will always return empty results /// @@ -210,8 +216,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings set { _querySearchPrecision = value; - if (StringMatcher.Instance != null) - StringMatcher.Instance.UserSettingSearchPrecision = value; + if (_stringMatcher != null) + _stringMatcher.UserSettingSearchPrecision = value; } } diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index b52ae7196..b435b566f 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -37,7 +37,7 @@ namespace Flow.Launcher // Initialize settings var storage = new FlowLauncherJsonStorage(); _settings = storage.Load(); - _settings.Initialize(storage); + _settings.SetStorage(storage); _settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled(); // Configure the dependency injection container @@ -55,8 +55,9 @@ namespace Flow.Launcher ).Build(); Ioc.Default.ConfigureServices(host.Services); - // Initialize the public API first + // Initialize the public API and Settings first API = Ioc.Default.GetRequiredService(); + _settings.Initialize(); } [STAThread] @@ -90,9 +91,7 @@ namespace Flow.Launcher AbstractPluginEnvironment.PreStartPluginExecutablePathUpdate(_settings); - var stringMatcher = Ioc.Default.GetRequiredService(); - StringMatcher.Instance = stringMatcher; - stringMatcher.UserSettingSearchPrecision = _settings.QuerySearchPrecision; + Ioc.Default.GetRequiredService().UserSettingSearchPrecision = _settings.QuerySearchPrecision; InternationalizationManager.Instance.Settings = _settings; InternationalizationManager.Instance.ChangeLanguage(_settings.Language); From a3964f56672d68b32e585d6d2399ed3442311f72 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 13:51:37 +0800 Subject: [PATCH 59/72] Remove instance for Internationalization & Theme --- Flow.Launcher.Core/Plugin/PluginManager.cs | 2 +- .../Resource/Internationalization.cs | 9 ++++--- .../Resource/InternationalizationManager.cs | 26 ++++--------------- Flow.Launcher.Core/Resource/Theme.cs | 9 ++++--- Flow.Launcher.Core/Resource/ThemeManager.cs | 24 +++-------------- Flow.Launcher.Core/Updater.cs | 1 - Flow.Launcher/App.xaml.cs | 6 +++-- 7 files changed, 25 insertions(+), 52 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 5ee723833..6e7b5ec60 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -204,7 +204,7 @@ namespace Flow.Launcher.Core.Plugin } InternationalizationManager.Instance.AddPluginLanguageDirectories(GetPluginsForInterface()); - InternationalizationManager.Instance.ChangeLanguage(InternationalizationManager.Instance.Settings.Language); + InternationalizationManager.Instance.ChangeLanguage(Ioc.Default.GetRequiredService().Language); if (failedPlugins.Any()) { diff --git a/Flow.Launcher.Core/Resource/Internationalization.cs b/Flow.Launcher.Core/Resource/Internationalization.cs index 884276f03..e2a66656a 100644 --- a/Flow.Launcher.Core/Resource/Internationalization.cs +++ b/Flow.Launcher.Core/Resource/Internationalization.cs @@ -17,17 +17,18 @@ namespace Flow.Launcher.Core.Resource { public class Internationalization { - public Settings Settings { get; set; } private const string Folder = "Languages"; private const string DefaultLanguageCode = "en"; private const string DefaultFile = "en.xaml"; private const string Extension = ".xaml"; + private readonly Settings _settings; private readonly List _languageDirectories = new List(); private readonly List _oldResources = new List(); private readonly string SystemLanguageCode; - public Internationalization() + public Internationalization(Settings settings) { + _settings = settings; AddFlowLauncherLanguageDirectory(); SystemLanguageCode = GetSystemLanguageCodeAtStartup(); } @@ -142,7 +143,7 @@ namespace Flow.Launcher.Core.Resource CultureInfo.CurrentUICulture = CultureInfo.CurrentCulture; // Raise event after culture is set - Settings.Language = isSystem ? Constant.SystemLanguageCode : language.LanguageCode; + _settings.Language = isSystem ? Constant.SystemLanguageCode : language.LanguageCode; _ = Task.Run(() => { UpdatePluginMetadataTranslations(); @@ -153,7 +154,7 @@ namespace Flow.Launcher.Core.Resource { var languageToSet = GetLanguageByLanguageCode(languageCodeToSet); - if (Settings.ShouldUsePinyin) + if (_settings.ShouldUsePinyin) return false; if (languageToSet != AvailableLanguages.Chinese && languageToSet != AvailableLanguages.Chinese_TW) diff --git a/Flow.Launcher.Core/Resource/InternationalizationManager.cs b/Flow.Launcher.Core/Resource/InternationalizationManager.cs index 3d87626e6..79736edc3 100644 --- a/Flow.Launcher.Core/Resource/InternationalizationManager.cs +++ b/Flow.Launcher.Core/Resource/InternationalizationManager.cs @@ -1,26 +1,10 @@ -namespace Flow.Launcher.Core.Resource +using CommunityToolkit.Mvvm.DependencyInjection; + +namespace Flow.Launcher.Core.Resource { public static class InternationalizationManager { - private static Internationalization instance; - private static object syncObject = new object(); - public static Internationalization Instance - { - get - { - if (instance == null) - { - lock (syncObject) - { - if (instance == null) - { - instance = new Internationalization(); - } - } - } - return instance; - } - } + => Ioc.Default.GetRequiredService(); } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index 0c91f69ca..b791915bc 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -25,11 +25,11 @@ namespace Flow.Launcher.Core.Resource private const int ShadowExtraMargin = 32; - private readonly IPublicAPI API = Ioc.Default.GetRequiredService(); + private readonly IPublicAPI API; + private readonly Settings Settings; private readonly List _themeDirectories = new List(); private ResourceDictionary _oldResource; private string _oldTheme; - public Settings Settings { get; set; } private const string Folder = Constant.Themes; private const string Extension = ".xaml"; private string DirectoryPath => Path.Combine(Constant.ProgramDirectory, Folder); @@ -39,8 +39,11 @@ namespace Flow.Launcher.Core.Resource private double mainWindowWidth; - public Theme() + public Theme(IPublicAPI publicAPI, Settings settings) { + API = publicAPI; + Settings = settings; + _themeDirectories.Add(DirectoryPath); _themeDirectories.Add(UserDirectoryPath); MakeSureThemeDirectoriesExist(); diff --git a/Flow.Launcher.Core/Resource/ThemeManager.cs b/Flow.Launcher.Core/Resource/ThemeManager.cs index 71f9acaa5..68cd90a7e 100644 --- a/Flow.Launcher.Core/Resource/ThemeManager.cs +++ b/Flow.Launcher.Core/Resource/ThemeManager.cs @@ -1,26 +1,10 @@ -namespace Flow.Launcher.Core.Resource +using CommunityToolkit.Mvvm.DependencyInjection; + +namespace Flow.Launcher.Core.Resource { public class ThemeManager { - private static Theme instance; - private static object syncObject = new object(); - public static Theme Instance - { - get - { - if (instance == null) - { - lock (syncObject) - { - if (instance == null) - { - instance = new Theme(); - } - } - } - return instance; - } - } + => Ioc.Default.GetRequiredService(); } } diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index dd0864c19..073084e4d 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -17,7 +17,6 @@ using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using System.Text.Json.Serialization; using System.Threading; -using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Core { diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index b435b566f..c5fe3159f 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -50,8 +50,10 @@ namespace Flow.Launcher .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() ).Build(); Ioc.Default.ConfigureServices(host.Services); @@ -93,7 +95,7 @@ namespace Flow.Launcher Ioc.Default.GetRequiredService().UserSettingSearchPrecision = _settings.QuerySearchPrecision; - InternationalizationManager.Instance.Settings = _settings; + // TODO: Clean InternationalizationManager.Instance and InternationalizationManager.Instance.GetTranslation in future InternationalizationManager.Instance.ChangeLanguage(_settings.Language); PluginManager.LoadPlugins(_settings.PluginSettings); @@ -114,7 +116,7 @@ namespace Flow.Launcher HotKeyMapper.Initialize(mainVM); // main windows needs initialized before theme change because of blur settings - ThemeManager.Instance.Settings = _settings; + // TODO: Clean ThemeManager.Instance in future ThemeManager.Instance.ChangeTheme(_settings.Theme); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); From d4f90999c76ae3467c55fc71fda0aa4cb14d8e96 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 13:55:33 +0800 Subject: [PATCH 60/72] Improve constructor injection for updater --- Flow.Launcher.Core/Updater.cs | 8 ++------ Flow.Launcher/App.xaml.cs | 4 +--- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 073084e4d..43d86a487 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -24,15 +24,11 @@ namespace Flow.Launcher.Core { private readonly IPublicAPI API; - public string GitHubRepository { get; set; } + public string GitHubRepository { get; init; } - public Updater(IPublicAPI publicAPI) + public Updater(IPublicAPI publicAPI, string gitHubRepository) { API = publicAPI; - } - - public void Initialize(string gitHubRepository) - { GitHubRepository = gitHubRepository; } diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index c5fe3159f..67e536b1b 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -45,7 +45,7 @@ namespace Flow.Launcher .UseContentRoot(AppContext.BaseDirectory) .ConfigureServices(services => services .AddSingleton(_ => _settings) - .AddSingleton(sp => new Updater(sp.GetRequiredService())) + .AddSingleton(sp => new Updater(sp.GetRequiredService(), Launcher.Properties.Settings.Default.GithubRepo)) .AddSingleton() .AddSingleton() .AddSingleton() @@ -79,8 +79,6 @@ namespace Flow.Launcher { await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => { - Ioc.Default.GetRequiredService().Initialize(Launcher.Properties.Settings.Default.GithubRepo); - Ioc.Default.GetRequiredService().PreStartCleanUpAfterPortabilityUpdate(); Log.Info("|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------"); From e989b5ac284f70aba9be1233f6ef2ee0e1608908 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 14:35:37 +0800 Subject: [PATCH 61/72] Add instance obsolete message --- Flow.Launcher.Core/Resource/InternationalizationManager.cs | 4 +++- Flow.Launcher.Core/Resource/ThemeManager.cs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Resource/InternationalizationManager.cs b/Flow.Launcher.Core/Resource/InternationalizationManager.cs index 79736edc3..5d718466c 100644 --- a/Flow.Launcher.Core/Resource/InternationalizationManager.cs +++ b/Flow.Launcher.Core/Resource/InternationalizationManager.cs @@ -1,7 +1,9 @@ -using CommunityToolkit.Mvvm.DependencyInjection; +using System; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Core.Resource { + [Obsolete("InternationalizationManager.Instance is obsolete. Use Ioc.Default.GetRequiredService() instead.")] public static class InternationalizationManager { public static Internationalization Instance diff --git a/Flow.Launcher.Core/Resource/ThemeManager.cs b/Flow.Launcher.Core/Resource/ThemeManager.cs index 68cd90a7e..3cbe8319a 100644 --- a/Flow.Launcher.Core/Resource/ThemeManager.cs +++ b/Flow.Launcher.Core/Resource/ThemeManager.cs @@ -1,7 +1,9 @@ -using CommunityToolkit.Mvvm.DependencyInjection; +using System; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Core.Resource { + [Obsolete("ThemeManager.Instance is obsolete. Use Ioc.Default.GetRequiredService() instead.")] public class ThemeManager { public static Theme Instance From 8496cfc819276a6808efac29867edf93bbec521b Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 14:37:29 +0800 Subject: [PATCH 62/72] Use Ioc.Default in initialize function --- Flow.Launcher/App.xaml.cs | 2 +- Flow.Launcher/Helper/HotKeyMapper.cs | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 67e536b1b..48007e995 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -111,7 +111,7 @@ namespace Flow.Launcher Current.MainWindow = window; Current.MainWindow.Title = Constant.FlowLauncher; - HotKeyMapper.Initialize(mainVM); + HotKeyMapper.Initialize(); // main windows needs initialized before theme change because of blur settings // TODO: Clean ThemeManager.Instance in future diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 79b524f50..7b2fdfcf4 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -5,10 +5,9 @@ using NHotkey; using NHotkey.Wpf; using Flow.Launcher.Core.Resource; using Flow.Launcher.ViewModel; -using Flow.Launcher.Core; using ChefKeys; -using System.Globalization; using Flow.Launcher.Infrastructure.Logger; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Helper; @@ -17,10 +16,10 @@ internal static class HotKeyMapper private static Settings _settings; private static MainViewModel _mainViewModel; - internal static void Initialize(MainViewModel mainVM) + internal static void Initialize() { - _mainViewModel = mainVM; - _settings = _mainViewModel.Settings; + _mainViewModel = Ioc.Default.GetRequiredService(); + _settings = Ioc.Default.GetService(); SetHotkey(_settings.Hotkey, OnToggleHotkey); LoadCustomPluginHotkey(); From ad63b0e7f402037c73b3e46e1e270221e1d54fb7 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 19:48:38 +0800 Subject: [PATCH 63/72] Improve code quality for field names --- Flow.Launcher.Core/Resource/Theme.cs | 48 +++++++++---------- Flow.Launcher.Core/Updater.cs | 28 +++++------ .../Views/ActionKeywordSetting.xaml.cs | 12 ++--- 3 files changed, 44 insertions(+), 44 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index b791915bc..4deea1f66 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -25,9 +25,9 @@ namespace Flow.Launcher.Core.Resource private const int ShadowExtraMargin = 32; - private readonly IPublicAPI API; - private readonly Settings Settings; - private readonly List _themeDirectories = new List(); + private readonly IPublicAPI _api; + private readonly Settings _settings; + private readonly List _themeDirectories = new(); private ResourceDictionary _oldResource; private string _oldTheme; private const string Folder = Constant.Themes; @@ -41,8 +41,8 @@ namespace Flow.Launcher.Core.Resource public Theme(IPublicAPI publicAPI, Settings settings) { - API = publicAPI; - Settings = settings; + _api = publicAPI; + _settings = settings; _themeDirectories.Add(DirectoryPath); _themeDirectories.Add(UserDirectoryPath); @@ -94,7 +94,7 @@ namespace Flow.Launcher.Core.Resource // to things like fonts UpdateResourceDictionary(GetResourceDictionary(theme)); - Settings.Theme = theme; + _settings.Theme = theme; //always allow re-loading default theme, in case of failure of switching to a new theme from default theme @@ -105,7 +105,7 @@ namespace Flow.Launcher.Core.Resource BlurEnabled = Win32Helper.IsBlurTheme(); - if (Settings.UseDropShadowEffect && !BlurEnabled) + if (_settings.UseDropShadowEffect && !BlurEnabled) AddDropShadowEffectToCurrentTheme(); Win32Helper.SetBlurForWindow(Application.Current.MainWindow, BlurEnabled); @@ -115,7 +115,7 @@ namespace Flow.Launcher.Core.Resource Log.Error($"|Theme.ChangeTheme|Theme <{theme}> path can't be found"); if (theme != defaultTheme) { - API.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_path_not_exists"), theme)); + _api.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_path_not_exists"), theme)); ChangeTheme(defaultTheme); } return false; @@ -125,7 +125,7 @@ namespace Flow.Launcher.Core.Resource Log.Error($"|Theme.ChangeTheme|Theme <{theme}> fail to parse"); if (theme != defaultTheme) { - API.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_parse_error"), theme)); + _api.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_parse_error"), theme)); ChangeTheme(defaultTheme); } return false; @@ -153,7 +153,7 @@ namespace Flow.Launcher.Core.Resource return dict; } - private ResourceDictionary CurrentThemeResourceDictionary() => GetThemeResourceDictionary(Settings.Theme); + private ResourceDictionary CurrentThemeResourceDictionary() => GetThemeResourceDictionary(_settings.Theme); public ResourceDictionary GetResourceDictionary(string theme) { @@ -162,10 +162,10 @@ namespace Flow.Launcher.Core.Resource if (dict["QueryBoxStyle"] is Style queryBoxStyle && dict["QuerySuggestionBoxStyle"] is Style querySuggestionBoxStyle) { - var fontFamily = new FontFamily(Settings.QueryBoxFont); - var fontStyle = FontHelper.GetFontStyleFromInvariantStringOrNormal(Settings.QueryBoxFontStyle); - var fontWeight = FontHelper.GetFontWeightFromInvariantStringOrNormal(Settings.QueryBoxFontWeight); - var fontStretch = FontHelper.GetFontStretchFromInvariantStringOrNormal(Settings.QueryBoxFontStretch); + var fontFamily = new FontFamily(_settings.QueryBoxFont); + var fontStyle = FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.QueryBoxFontStyle); + var fontWeight = FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.QueryBoxFontWeight); + var fontStretch = FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.QueryBoxFontStretch); queryBoxStyle.Setters.Add(new Setter(TextBox.FontFamilyProperty, fontFamily)); queryBoxStyle.Setters.Add(new Setter(TextBox.FontStyleProperty, fontStyle)); @@ -190,10 +190,10 @@ namespace Flow.Launcher.Core.Resource dict["ItemHotkeyStyle"] is Style resultHotkeyItemStyle && dict["ItemHotkeySelectedStyle"] is Style resultHotkeyItemSelectedStyle) { - Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(Settings.ResultFont)); - Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(Settings.ResultFontStyle)); - Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(Settings.ResultFontWeight)); - Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(Settings.ResultFontStretch)); + Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(_settings.ResultFont)); + Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.ResultFontStyle)); + Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.ResultFontWeight)); + Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.ResultFontStretch)); Setter[] setters = { fontFamily, fontStyle, fontWeight, fontStretch }; Array.ForEach( @@ -205,10 +205,10 @@ namespace Flow.Launcher.Core.Resource dict["ItemSubTitleStyle"] is Style resultSubItemStyle && dict["ItemSubTitleSelectedStyle"] is Style resultSubItemSelectedStyle) { - Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(Settings.ResultSubFont)); - Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(Settings.ResultSubFontStyle)); - Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(Settings.ResultSubFontWeight)); - Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(Settings.ResultSubFontStretch)); + Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(_settings.ResultSubFont)); + Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.ResultSubFontStyle)); + Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.ResultSubFontWeight)); + Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.ResultSubFontStretch)); Setter[] setters = { fontFamily, fontStyle, fontWeight, fontStretch }; Array.ForEach( @@ -218,7 +218,7 @@ namespace Flow.Launcher.Core.Resource /* Ignore Theme Window Width and use setting */ var windowStyle = dict["WindowStyle"] as Style; - var width = Settings.WindowSize; + var width = _settings.WindowSize; windowStyle.Setters.Add(new Setter(Window.WidthProperty, width)); mainWindowWidth = (double)width; return dict; @@ -226,7 +226,7 @@ namespace Flow.Launcher.Core.Resource private ResourceDictionary GetCurrentResourceDictionary( ) { - return GetResourceDictionary(Settings.Theme); + return GetResourceDictionary(_settings.Theme); } public List LoadAvailableThemes() diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 43d86a487..9a77ece32 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -22,13 +22,13 @@ namespace Flow.Launcher.Core { public class Updater { - private readonly IPublicAPI API; - public string GitHubRepository { get; init; } + private readonly IPublicAPI _api; + public Updater(IPublicAPI publicAPI, string gitHubRepository) { - API = publicAPI; + _api = publicAPI; GitHubRepository = gitHubRepository; } @@ -40,8 +40,8 @@ namespace Flow.Launcher.Core try { if (!silentUpdate) - API.ShowMsg(API.GetTranslation("pleaseWait"), - API.GetTranslation("update_flowlauncher_update_check")); + _api.ShowMsg(_api.GetTranslation("pleaseWait"), + _api.GetTranslation("update_flowlauncher_update_check")); using var updateManager = await GitHubUpdateManagerAsync(GitHubRepository).ConfigureAwait(false); @@ -56,13 +56,13 @@ namespace Flow.Launcher.Core if (newReleaseVersion <= currentVersion) { if (!silentUpdate) - API.ShowMsgBox(API.GetTranslation("update_flowlauncher_already_on_latest")); + _api.ShowMsgBox(_api.GetTranslation("update_flowlauncher_already_on_latest")); return; } if (!silentUpdate) - API.ShowMsg(API.GetTranslation("update_flowlauncher_update_found"), - API.GetTranslation("update_flowlauncher_updating")); + _api.ShowMsg(_api.GetTranslation("update_flowlauncher_update_found"), + _api.GetTranslation("update_flowlauncher_updating")); await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply).ConfigureAwait(false); @@ -71,9 +71,9 @@ namespace Flow.Launcher.Core if (DataLocation.PortableDataLocationInUse()) { var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}"; - FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination, (s) => API.ShowMsgBox(s)); - if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination, (s) => API.ShowMsgBox(s))) - API.ShowMsgBox(string.Format(API.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"), + FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination, (s) => _api.ShowMsgBox(s)); + if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination, (s) => _api.ShowMsgBox(s))) + _api.ShowMsgBox(string.Format(_api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"), DataLocation.PortableDataPath, targetDestination)); } @@ -86,7 +86,7 @@ namespace Flow.Launcher.Core Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}"); - if (API.ShowMsgBox(newVersionTips, API.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes) + if (_api.ShowMsgBox(newVersionTips, _api.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes) { UpdateManager.RestartApp(Constant.ApplicationFileName); } @@ -99,8 +99,8 @@ namespace Flow.Launcher.Core Log.Exception($"|Updater.UpdateApp|Error Occurred", e); if (!silentUpdate) - API.ShowMsg(API.GetTranslation("update_flowlauncher_fail"), - API.GetTranslation("update_flowlauncher_check_connection")); + _api.ShowMsg(_api.GetTranslation("update_flowlauncher_fail"), + _api.GetTranslation("update_flowlauncher_check_connection")); } finally { diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs index 34a5b2760..73c35b1c8 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs @@ -31,13 +31,13 @@ namespace Flow.Launcher.Plugin.Explorer.Views } private string actionKeyword; - private readonly IPublicAPI api; + private readonly IPublicAPI _api; private bool _keywordEnabled; public ActionKeywordSetting(ActionKeywordModel selectedActionKeyword, IPublicAPI api) { CurrentActionKeyword = selectedActionKeyword; - this.api = api; + _api = api; ActionKeyword = selectedActionKeyword.Keyword; KeywordEnabled = selectedActionKeyword.Enabled; @@ -62,14 +62,14 @@ namespace Flow.Launcher.Plugin.Explorer.Views switch (CurrentActionKeyword.KeywordProperty, KeywordEnabled) { case (Settings.ActionKeyword.FileContentSearchActionKeyword, true): - api.ShowMsgBox(api.GetTranslation("plugin_explorer_globalActionKeywordInvalid")); + _api.ShowMsgBox(_api.GetTranslation("plugin_explorer_globalActionKeywordInvalid")); return; case (Settings.ActionKeyword.QuickAccessActionKeyword, true): - api.ShowMsgBox(api.GetTranslation("plugin_explorer_quickaccess_globalActionKeywordInvalid")); + _api.ShowMsgBox(_api.GetTranslation("plugin_explorer_quickaccess_globalActionKeywordInvalid")); return; } - if (!KeywordEnabled || !api.ActionKeywordAssigned(ActionKeyword)) + if (!KeywordEnabled || !_api.ActionKeywordAssigned(ActionKeyword)) { DialogResult = true; Close(); @@ -77,7 +77,7 @@ namespace Flow.Launcher.Plugin.Explorer.Views } // The keyword is not valid, so show message - api.ShowMsgBox(api.GetTranslation("newActionKeywordsHasBeenAssigned")); + _api.ShowMsgBox(_api.GetTranslation("newActionKeywordsHasBeenAssigned")); } private void BtnCancel_OnClick(object sender, RoutedEventArgs e) From f5f0986036a2ab86771a264c32116d7093cbf2ff Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Sun, 23 Feb 2025 21:54:08 +0800 Subject: [PATCH 64/72] remove some usage of Ioc.GetRequiredService when injection is possible. --- Flow.Launcher.Infrastructure/StringMatcher.cs | 9 +++---- Flow.Launcher/App.xaml.cs | 2 -- Flow.Launcher/PublicAPIInstance.cs | 4 ++-- Flow.Launcher/SettingWindow.xaml.cs | 2 +- .../ViewModel/SettingWindowViewModel.cs | 24 +++++++++---------- 5 files changed, 18 insertions(+), 23 deletions(-) diff --git a/Flow.Launcher.Infrastructure/StringMatcher.cs b/Flow.Launcher.Infrastructure/StringMatcher.cs index 55aa0a1ec..2707308d5 100644 --- a/Flow.Launcher.Infrastructure/StringMatcher.cs +++ b/Flow.Launcher.Infrastructure/StringMatcher.cs @@ -3,6 +3,7 @@ using Flow.Launcher.Plugin.SharedModels; using System; using System.Collections.Generic; using System.Linq; +using Flow.Launcher.Infrastructure.UserSettings; namespace Flow.Launcher.Infrastructure { @@ -14,15 +15,11 @@ namespace Flow.Launcher.Infrastructure private readonly IAlphabet _alphabet; - public StringMatcher() - { - _alphabet = Ioc.Default.GetRequiredService(); - } - // This is a workaround to allow unit tests to set the instance - public StringMatcher(IAlphabet alphabet) + public StringMatcher(IAlphabet alphabet, Settings settings) { _alphabet = alphabet; + UserSettingSearchPrecision = settings.QuerySearchPrecision; } public static MatchResult FuzzySearch(string query, string stringToCompare) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 48007e995..952ca70c4 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -91,8 +91,6 @@ namespace Flow.Launcher AbstractPluginEnvironment.PreStartPluginExecutablePathUpdate(_settings); - Ioc.Default.GetRequiredService().UserSettingSearchPrecision = _settings.QuerySearchPrecision; - // TODO: Clean InternationalizationManager.Instance and InternationalizationManager.Instance.GetTranslation in future InternationalizationManager.Instance.ChangeLanguage(_settings.Language); diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 6123a93d9..528f54172 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -247,7 +247,7 @@ namespace Flow.Launcher public void OpenDirectory(string DirectoryPath, string FileNameOrFilePath = null) { using var explorer = new Process(); - var explorerInfo = _settingsVM.Settings.CustomExplorer; + var explorerInfo = _settingsVM._settings.CustomExplorer; explorer.StartInfo = new ProcessStartInfo { @@ -268,7 +268,7 @@ namespace Flow.Launcher { if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps) { - var browserInfo = _settingsVM.Settings.CustomBrowser; + var browserInfo = _settingsVM._settings.CustomBrowser; var path = browserInfo.Path == "*" ? "" : browserInfo.Path; diff --git a/Flow.Launcher/SettingWindow.xaml.cs b/Flow.Launcher/SettingWindow.xaml.cs index 8b15150cf..f87194c30 100644 --- a/Flow.Launcher/SettingWindow.xaml.cs +++ b/Flow.Launcher/SettingWindow.xaml.cs @@ -27,7 +27,7 @@ public partial class SettingWindow public SettingWindow() { var viewModel = Ioc.Default.GetRequiredService(); - _settings = viewModel.Settings; + _settings = Ioc.Default.GetRequiredService(); DataContext = viewModel; _viewModel = viewModel; _updater = Ioc.Default.GetRequiredService(); diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index 37276a1ad..0a1968d9d 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -6,11 +6,11 @@ namespace Flow.Launcher.ViewModel; public partial class SettingWindowViewModel : BaseModel { - public Settings Settings { get; init; } + public readonly Settings _settings; - public SettingWindowViewModel() + public SettingWindowViewModel(Settings settings) { - Settings = Ioc.Default.GetRequiredService(); + _settings = settings; } /// @@ -18,30 +18,30 @@ public partial class SettingWindowViewModel : BaseModel /// public void Save() { - Settings.Save(); + _settings.Save(); } public double SettingWindowWidth { - get => Settings.SettingWindowWidth; - set => Settings.SettingWindowWidth = value; + get => _settings.SettingWindowWidth; + set => _settings.SettingWindowWidth = value; } public double SettingWindowHeight { - get => Settings.SettingWindowHeight; - set => Settings.SettingWindowHeight = value; + get => _settings.SettingWindowHeight; + set => _settings.SettingWindowHeight = value; } public double? SettingWindowTop { - get => Settings.SettingWindowTop; - set => Settings.SettingWindowTop = value; + get => _settings.SettingWindowTop; + set => _settings.SettingWindowTop = value; } public double? SettingWindowLeft { - get => Settings.SettingWindowLeft; - set => Settings.SettingWindowLeft = value; + get => _settings.SettingWindowLeft; + set => _settings.SettingWindowLeft = value; } } From ef68ccb70a231e823b7ee7555b548a856dba7606 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 21:59:35 +0800 Subject: [PATCH 65/72] Fix unit test issue --- Flow.Launcher.Infrastructure/StringMatcher.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/StringMatcher.cs b/Flow.Launcher.Infrastructure/StringMatcher.cs index 2707308d5..e85c5d6f4 100644 --- a/Flow.Launcher.Infrastructure/StringMatcher.cs +++ b/Flow.Launcher.Infrastructure/StringMatcher.cs @@ -15,13 +15,18 @@ namespace Flow.Launcher.Infrastructure private readonly IAlphabet _alphabet; - // This is a workaround to allow unit tests to set the instance public StringMatcher(IAlphabet alphabet, Settings settings) { _alphabet = alphabet; UserSettingSearchPrecision = settings.QuerySearchPrecision; } + // This is a workaround to allow unit tests to set the instance + public StringMatcher(IAlphabet alphabet) + { + _alphabet = alphabet; + } + public static MatchResult FuzzySearch(string query, string stringToCompare) { return Ioc.Default.GetRequiredService().FuzzyMatch(query, stringToCompare); From 91490c0251c8fe845d175522c54bf63b9c4b00ca Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 22:05:49 +0800 Subject: [PATCH 66/72] Remove usage of _settingsVM._settings and make _settings private --- Flow.Launcher/PublicAPIInstance.cs | 7 +++++-- Flow.Launcher/ViewModel/SettingWindowViewModel.cs | 5 ++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 528f54172..6c6ba9f8c 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -27,11 +27,13 @@ using System.Diagnostics; using System.Collections.Specialized; using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Core; +using Flow.Launcher.Infrastructure.UserSettings; namespace Flow.Launcher { public class PublicAPIInstance : IPublicAPI { + private readonly Settings _settings; private readonly SettingWindowViewModel _settingsVM; private readonly MainViewModel _mainVM; @@ -39,6 +41,7 @@ namespace Flow.Launcher public PublicAPIInstance() { + _settings = Ioc.Default.GetRequiredService(); _settingsVM = Ioc.Default.GetRequiredService(); _mainVM = Ioc.Default.GetRequiredService(); GlobalHotkey.hookedKeyboardCallback = KListener_hookedKeyboardCallback; @@ -247,7 +250,7 @@ namespace Flow.Launcher public void OpenDirectory(string DirectoryPath, string FileNameOrFilePath = null) { using var explorer = new Process(); - var explorerInfo = _settingsVM._settings.CustomExplorer; + var explorerInfo = _settings.CustomExplorer; explorer.StartInfo = new ProcessStartInfo { @@ -268,7 +271,7 @@ namespace Flow.Launcher { if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps) { - var browserInfo = _settingsVM._settings.CustomBrowser; + var browserInfo = _settings.CustomBrowser; var path = browserInfo.Path == "*" ? "" : browserInfo.Path; diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index 0a1968d9d..51afe533d 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -1,12 +1,11 @@ -using CommunityToolkit.Mvvm.DependencyInjection; -using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; namespace Flow.Launcher.ViewModel; public partial class SettingWindowViewModel : BaseModel { - public readonly Settings _settings; + private readonly Settings _settings; public SettingWindowViewModel(Settings settings) { From 16798a06ce3874647da77d806503c1314ed95d3f Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Mon, 24 Feb 2025 09:40:33 +0800 Subject: [PATCH 67/72] use constructor injection for PublicAPIInstance.cs --- Flow.Launcher/PublicAPIInstance.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 6c6ba9f8c..1a08150e5 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -34,16 +34,14 @@ namespace Flow.Launcher public class PublicAPIInstance : IPublicAPI { private readonly Settings _settings; - private readonly SettingWindowViewModel _settingsVM; private readonly MainViewModel _mainVM; #region Constructor - public PublicAPIInstance() + public PublicAPIInstance(Settings settings, MainViewModel mainVM) { - _settings = Ioc.Default.GetRequiredService(); - _settingsVM = Ioc.Default.GetRequiredService(); - _mainVM = Ioc.Default.GetRequiredService(); + _settings = settings; + _mainVM = mainVM; GlobalHotkey.hookedKeyboardCallback = KListener_hookedKeyboardCallback; WebRequest.RegisterPrefix("data", new DataWebRequestFactory()); } @@ -87,7 +85,7 @@ namespace Flow.Launcher { PluginManager.Save(); _mainVM.Save(); - _settingsVM.Save(); + _settings.Save(); _ = ImageLoader.Save(); } From 96305166e834864f7884e897c38c5fc1bec4e87f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 19:55:48 +0800 Subject: [PATCH 68/72] Improve function names for code quality --- Flow.Launcher/App.xaml.cs | 9 +++++- Flow.Launcher/Helper/AutoStartup.cs | 32 +++++++++++++++++-- .../SettingsPaneGeneralViewModel.cs | 25 ++++++++++----- 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 38f846d92..f74e9a388 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -119,7 +119,14 @@ namespace Flow.Launcher { try { - Helper.AutoStartup.Enable(_settings.UseLogonTaskForStartup); + if (_settings.UseLogonTaskForStartup) + { + Helper.AutoStartup.EnableViaLogonTask(); + } + else + { + Helper.AutoStartup.EnableViaRegistry(); + } } catch (Exception e) { diff --git a/Flow.Launcher/Helper/AutoStartup.cs b/Flow.Launcher/Helper/AutoStartup.cs index 79466f1fb..936951ee9 100644 --- a/Flow.Launcher/Helper/AutoStartup.cs +++ b/Flow.Launcher/Helper/AutoStartup.cs @@ -68,7 +68,35 @@ public class AutoStartup return false; } - public static void Disable(bool logonTask) + public static void DisableViaLogonTaskAndRegistry() + { + Disable(true); + Disable(false); + } + + public static void EnableViaLogonTask() + { + Enable(true); + } + + public static void EnableViaRegistry() + { + Enable(false); + } + + public static void ChangeToViaLogonTask() + { + Disable(false); + Enable(true); + } + + public static void ChangeToViaRegistry() + { + Disable(true); + Enable(false); + } + + private static void Disable(bool logonTask) { try { @@ -89,7 +117,7 @@ public class AutoStartup } } - internal static void Enable(bool logonTask) + private static void Enable(bool logonTask) { try { diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs index 0aca761a0..ab38cd514 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs @@ -43,14 +43,18 @@ public partial class SettingsPaneGeneralViewModel : BaseModel { if (value) { - // Enable either registry or task scheduler - AutoStartup.Enable(UseLogonTaskForStartup); + if (UseLogonTaskForStartup) + { + AutoStartup.EnableViaLogonTask(); + } + else + { + AutoStartup.EnableViaRegistry(); + } } else { - // Disable both registry and task scheduler - AutoStartup.Disable(true); - AutoStartup.Disable(false); + AutoStartup.DisableViaLogonTaskAndRegistry(); } } catch (Exception e) @@ -72,9 +76,14 @@ public partial class SettingsPaneGeneralViewModel : BaseModel { try { - // Disable and enable to update the startup method - AutoStartup.Disable(!UseLogonTaskForStartup); - AutoStartup.Enable(UseLogonTaskForStartup); + if (UseLogonTaskForStartup) + { + AutoStartup.ChangeToViaLogonTask(); + } + else + { + AutoStartup.ChangeToViaRegistry(); + } } catch (Exception e) { From e9688d1bcbd2f748ecea1d8dff55c1c1e22fb4f0 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 22:26:07 +0800 Subject: [PATCH 69/72] Fix dependency injection issue in installer --- Flow.Launcher/Flow.Launcher.csproj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 43c1dfd90..f8ace91f8 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -90,8 +90,9 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + all From 928ca474ab25934f4872412254d51bc0144a1dad Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 25 Feb 2025 13:48:02 +0800 Subject: [PATCH 70/72] Add preview background in welcome page 2 --- .../Resources/Pages/WelcomePage2.xaml.cs | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/Resources/Pages/WelcomePage2.xaml.cs b/Flow.Launcher/Resources/Pages/WelcomePage2.xaml.cs index 7dfb85a83..004e4d6d2 100644 --- a/Flow.Launcher/Resources/Pages/WelcomePage2.xaml.cs +++ b/Flow.Launcher/Resources/Pages/WelcomePage2.xaml.cs @@ -2,11 +2,12 @@ using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Infrastructure.UserSettings; using System; -using System.Windows; -using System.Windows.Media; using System.Windows.Navigation; using CommunityToolkit.Mvvm.Input; using Flow.Launcher.ViewModel; +using System.IO; +using System.Windows.Media.Imaging; +using System.Windows.Media; namespace Flow.Launcher.Resources.Pages { @@ -29,5 +30,27 @@ namespace Flow.Launcher.Resources.Pages { HotKeyMapper.SetHotkey(hotkey, HotKeyMapper.OnToggleHotkey); } + + public Brush PreviewBackground + { + get + { + var wallpaper = WallpaperPathRetrieval.GetWallpaperPath(); + if (wallpaper is not null && File.Exists(wallpaper)) + { + var memStream = new MemoryStream(File.ReadAllBytes(wallpaper)); + var bitmap = new BitmapImage(); + bitmap.BeginInit(); + bitmap.StreamSource = memStream; + bitmap.DecodePixelWidth = 800; + bitmap.DecodePixelHeight = 600; + bitmap.EndInit(); + return new ImageBrush(bitmap) { Stretch = Stretch.UniformToFill }; + } + + var wallpaperColor = WallpaperPathRetrieval.GetWallpaperColor(); + return new SolidColorBrush(wallpaperColor); + } + } } } From fe48427252f5ce8c84e697da42a24406d9675a3f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 25 Feb 2025 15:08:27 +0800 Subject: [PATCH 71/72] Log error for logon task --- Flow.Launcher/Helper/AutoStartup.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher/Helper/AutoStartup.cs b/Flow.Launcher/Helper/AutoStartup.cs index 936951ee9..c5e20504b 100644 --- a/Flow.Launcher/Helper/AutoStartup.cs +++ b/Flow.Launcher/Helper/AutoStartup.cs @@ -59,9 +59,9 @@ public class AutoStartup return true; } - catch (Exception) + catch (Exception e) { - Log.Error("AutoStartup", "Failed to check logon task"); + Log.Error("AutoStartup", $"Failed to check logon task: {e}"); } } @@ -159,9 +159,9 @@ public class AutoStartup TaskService.Instance.RootFolder.RegisterTaskDefinition(LogonTaskName, td); return true; } - catch (Exception) + catch (Exception e) { - Log.Error("AutoStartup", "Failed to schedule logon task"); + Log.Error("AutoStartup", $"Failed to schedule logon task: {e}"); return false; } } @@ -174,9 +174,9 @@ public class AutoStartup taskService.RootFolder.DeleteTask(LogonTaskName); return true; } - catch (Exception) + catch (Exception e) { - Log.Error("AutoStartup", "Failed to unschedule logon task"); + Log.Error("AutoStartup", $"Failed to unschedule logon task: {e}"); return false; } } From 3fa88064bd572741c8590ce7a1064fb65324b65a Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 26 Feb 2025 18:30:33 +0800 Subject: [PATCH 72/72] Remove useless localization --- Flow.Launcher/Languages/en.xaml | 3 --- Flow.Launcher/ReportWindow.xaml.cs | 33 +++++------------------------- 2 files changed, 5 insertions(+), 31 deletions(-) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 493528a89..5d2cb2d9e 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -386,9 +386,6 @@ Please open new issue in 1. Upload log file: {0} 2. Copy below exception message - Date: {0} - Exception: - Length Please wait... diff --git a/Flow.Launcher/ReportWindow.xaml.cs b/Flow.Launcher/ReportWindow.xaml.cs index 12ffe3ffc..6fe90783e 100644 --- a/Flow.Launcher/ReportWindow.xaml.cs +++ b/Flow.Launcher/ReportWindow.xaml.cs @@ -6,10 +6,10 @@ using System.Text; using System.Linq; using System.Windows; using System.Windows.Documents; +using Flow.Launcher.Helper; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Plugin.SharedCommands; -using Flow.Launcher.Infrastructure.Exception; namespace Flow.Launcher { @@ -55,12 +55,11 @@ namespace Flow.Launcher ErrorTextbox.Document.Blocks.Add(paragraph); StringBuilder content = new StringBuilder(); - content.AppendLine(RuntimeInfo()); + content.AppendLine(ErrorReporting.RuntimeInfo()); + content.AppendLine(ErrorReporting.DependenciesInfo()); content.AppendLine(); - content.AppendLine(DependenciesInfo()); - content.AppendLine(); - content.AppendLine(string.Format(App.API.GetTranslation("reportWindow_date"), DateTime.Now.ToString(CultureInfo.InvariantCulture))); - content.AppendLine(App.API.GetTranslation("reportWindow_exception")); + content.AppendLine($"Date: {DateTime.Now.ToString(CultureInfo.InvariantCulture)}"); + content.AppendLine("Exception:"); content.AppendLine(exception.ToString()); paragraph = new Paragraph(); paragraph.Inlines.Add(content.ToString()); @@ -94,27 +93,5 @@ namespace Flow.Launcher { Close(); } - - private static string RuntimeInfo() - { - var info = - $""" - Flow Launcher {App.API.GetTranslation("reportWindow_version")}: {Constant.Version} - OS {App.API.GetTranslation("reportWindow_version")}: {ExceptionFormatter.GetWindowsFullVersionFromRegistry()} - IntPtr {App.API.GetTranslation("reportWindow_length")}: {IntPtr.Size} - x64: {Environment.Is64BitOperatingSystem} - """; - return info; - } - - private static string DependenciesInfo() - { - var info = - $""" - {App.API.GetTranslation("pythonFilePath")}: {Constant.PythonPath} - {App.API.GetTranslation("nodeFilePath")}: {Constant.NodePath} - """; - return info; - } } }