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 e320ca1d492594351d81f05ab3da8838c8672dd6 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 1 Feb 2025 13:10:32 +0800 Subject: [PATCH 24/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 25/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 26/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 7598a6aff507cfff363d94b9f225fa7f6955039e Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 6 Feb 2025 08:08:45 +0800 Subject: [PATCH 27/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 28/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 29/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 30/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 31/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 32/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 ddbbd693e8992c8c8f18d21f71f3dff2a01e02d2 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 12 Feb 2025 15:06:15 +0800 Subject: [PATCH 33/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 d73f3a165b9d3faa8b9cf35f0bd714b286bd5af9 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 17 Feb 2025 00:00:18 +0800 Subject: [PATCH 34/72] Fix custom hotkey preview issue --- Flow.Launcher/CustomQueryHotkeySetting.xaml.cs | 18 ++++++++++-------- Flow.Launcher/CustomShortcutSetting.xaml.cs | 8 +++++--- Flow.Launcher/PublicAPIInstance.cs | 2 +- .../ViewModels/SettingsPaneHotkeyViewModel.cs | 11 +++++++---- .../Views/SettingsPaneHotkey.xaml.cs | 4 ++-- Flow.Launcher/SettingWindow.xaml.cs | 8 +++++--- Flow.Launcher/ViewModel/MainViewModel.cs | 5 +++-- 7 files changed, 33 insertions(+), 23 deletions(-) diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs index 81e7600b8..db1df0cf2 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs @@ -7,20 +7,23 @@ using System.Windows; using System.Windows.Input; using System.Windows.Controls; using Flow.Launcher.Core; +using Flow.Launcher.ViewModel; namespace Flow.Launcher { public partial class CustomQueryHotkeySetting : Window { private SettingWindow _settingWidow; + private readonly Settings _settings; + private readonly MainViewModel _mainViewModel; private bool update; private CustomPluginHotkey updateCustomHotkey; - public Settings Settings { get; } - public CustomQueryHotkeySetting(SettingWindow settingWidow, Settings settings) + public CustomQueryHotkeySetting(SettingWindow settingWidow, Settings settings, MainViewModel mainVM) { _settingWidow = settingWidow; - Settings = settings; + _settings = settings; + _mainViewModel = mainVM; InitializeComponent(); } @@ -33,13 +36,13 @@ namespace Flow.Launcher { if (!update) { - Settings.CustomPluginHotkeys ??= new ObservableCollection(); + _settings.CustomPluginHotkeys ??= new ObservableCollection(); var pluginHotkey = new CustomPluginHotkey { Hotkey = HotkeyControl.CurrentHotkey.ToString(), ActionKeyword = tbAction.Text }; - Settings.CustomPluginHotkeys.Add(pluginHotkey); + _settings.CustomPluginHotkeys.Add(pluginHotkey); HotKeyMapper.SetCustomQueryHotkey(pluginHotkey); } @@ -59,7 +62,7 @@ namespace Flow.Launcher public void UpdateItem(CustomPluginHotkey item) { - updateCustomHotkey = Settings.CustomPluginHotkeys.FirstOrDefault(o => + updateCustomHotkey = _settings.CustomPluginHotkeys.FirstOrDefault(o => o.ActionKeyword == item.ActionKeyword && o.Hotkey == item.Hotkey); if (updateCustomHotkey == null) { @@ -77,8 +80,7 @@ namespace Flow.Launcher private void BtnTestActionKeyword_OnClick(object sender, RoutedEventArgs e) { App.API.ChangeQuery(tbAction.Text); - Application.Current.MainWindow.Show(); - Application.Current.MainWindow.Opacity = 1; + _mainViewModel.Show(false); Application.Current.MainWindow.Focus(); } diff --git a/Flow.Launcher/CustomShortcutSetting.xaml.cs b/Flow.Launcher/CustomShortcutSetting.xaml.cs index dec3506eb..cb2cfcb29 100644 --- a/Flow.Launcher/CustomShortcutSetting.xaml.cs +++ b/Flow.Launcher/CustomShortcutSetting.xaml.cs @@ -4,21 +4,24 @@ using System.Windows; using System.Windows.Input; using Flow.Launcher.SettingPages.ViewModels; using Flow.Launcher.Core; +using Flow.Launcher.ViewModel; namespace Flow.Launcher { public partial class CustomShortcutSetting : Window { private readonly SettingsPaneHotkeyViewModel _hotkeyVm; + private readonly MainViewModel _mainViewModel; public string Key { get; set; } = String.Empty; public string Value { get; set; } = String.Empty; private string originalKey { get; } = null; private string originalValue { get; } = null; private bool update { get; } = false; - public CustomShortcutSetting(SettingsPaneHotkeyViewModel vm) + public CustomShortcutSetting(SettingsPaneHotkeyViewModel vm, MainViewModel mainVM) { _hotkeyVm = vm; + _mainViewModel = mainVM; InitializeComponent(); } @@ -65,8 +68,7 @@ namespace Flow.Launcher private void BtnTestShortcut_OnClick(object sender, RoutedEventArgs e) { App.API.ChangeQuery(tbExpand.Text); - Application.Current.MainWindow.Show(); - Application.Current.MainWindow.Opacity = 1; + _mainViewModel.Show(false); Application.Current.MainWindow.Focus(); } } diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index f0295cf24..2d8126033 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -105,7 +105,7 @@ namespace Flow.Launcher { Application.Current.Dispatcher.Invoke(() => { - SettingWindow sw = SingletonWindowOpener.Open(this, _settingsVM); + SettingWindow sw = SingletonWindowOpener.Open(this, _settingsVM, _mainVM); }); } diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs index 6d8af9a3f..5aedd3be7 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs @@ -8,12 +8,14 @@ using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using Flow.Launcher.Core; +using Flow.Launcher.ViewModel; namespace Flow.Launcher.SettingPages.ViewModels; public partial class SettingsPaneHotkeyViewModel : BaseModel { public Settings Settings { get; } + private MainViewModel MainVM { get; } public CustomPluginHotkey SelectedCustomPluginHotkey { get; set; } public CustomShortcutModel SelectedCustomShortcut { get; set; } @@ -25,9 +27,10 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel $"{KeyConstant.Ctrl}+{KeyConstant.Alt}" }; - public SettingsPaneHotkeyViewModel(Settings settings) + public SettingsPaneHotkeyViewModel(Settings settings, MainViewModel mainVM) { Settings = settings; + MainVM = mainVM; } [RelayCommand] @@ -71,7 +74,7 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel return; } - var window = new CustomQueryHotkeySetting(null, Settings); + var window = new CustomQueryHotkeySetting(null, Settings, MainVM); window.UpdateItem(item); window.ShowDialog(); } @@ -79,7 +82,7 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel [RelayCommand] private void CustomHotkeyAdd() { - new CustomQueryHotkeySetting(null, Settings).ShowDialog(); + new CustomQueryHotkeySetting(null, Settings, MainVM).ShowDialog(); } [RelayCommand] @@ -126,7 +129,7 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel [RelayCommand] private void CustomShortcutAdd() { - var window = new CustomShortcutSetting(this); + var window = new CustomShortcutSetting(this, MainVM); if (window.ShowDialog() is true) { var shortcut = new CustomShortcutModel(window.Key, window.Value); diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs index 061eabf51..26939c9f9 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs @@ -12,9 +12,9 @@ public partial class SettingsPaneHotkey { if (!IsInitialized) { - if (e.ExtraData is not SettingWindow.PaneData { Settings: { } settings }) + if (e.ExtraData is not SettingWindow.PaneData { Settings: { } settings, MainViewModel: { } mainVM }) throw new ArgumentException("Settings are required for SettingsPaneHotkey."); - _viewModel = new SettingsPaneHotkeyViewModel(settings); + _viewModel = new SettingsPaneHotkeyViewModel(settings, mainVM); DataContext = _viewModel; InitializeComponent(); } diff --git a/Flow.Launcher/SettingWindow.xaml.cs b/Flow.Launcher/SettingWindow.xaml.cs index d5b303516..ae481b65b 100644 --- a/Flow.Launcher/SettingWindow.xaml.cs +++ b/Flow.Launcher/SettingWindow.xaml.cs @@ -20,12 +20,14 @@ public partial class SettingWindow private readonly IPublicAPI _api; private readonly Settings _settings; private readonly SettingWindowViewModel _viewModel; + private readonly MainViewModel _mainVM; - public SettingWindow(IPublicAPI api, SettingWindowViewModel viewModel) + public SettingWindow(IPublicAPI api, SettingWindowViewModel viewModel, MainViewModel mainVM) { _settings = viewModel.Settings; DataContext = viewModel; _viewModel = viewModel; + _mainVM = mainVM; _api = api; InitializePosition(); InitializeComponent(); @@ -160,7 +162,7 @@ public partial class SettingWindow private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args) { - var paneData = new PaneData(_settings, _viewModel.Updater, _viewModel.Portable); + var paneData = new PaneData(_settings, _viewModel.Updater, _viewModel.Portable, _mainVM); if (args.IsSettingsSelected) { ContentFrame.Navigate(typeof(SettingsPaneGeneral), paneData); @@ -206,5 +208,5 @@ public partial class SettingWindow NavView.SelectedItem ??= NavView.MenuItems[0]; /* Set First Page */ } - public record PaneData(Settings Settings, Updater Updater, IPortable Portable); + public record PaneData(Settings Settings, Updater Updater, IPortable Portable, MainViewModel MainViewModel); } diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 55bc8d1b3..7c2abb078 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1359,7 +1359,7 @@ namespace Flow.Launcher.ViewModel } } - public void Show() + public void Show(bool invokeEvent = true) { Application.Current.Dispatcher.Invoke(() => { @@ -1368,7 +1368,8 @@ namespace Flow.Launcher.ViewModel MainWindowOpacity = 1; MainWindowVisibilityStatus = true; - VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = true }); + if (invokeEvent) + VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = true }); }); } From 0611340d7159d050bc3a8a9dac7d4df852a568f1 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 17 Feb 2025 16:10:07 +0800 Subject: [PATCH 35/72] Invoke visibility changed event when previewing --- Flow.Launcher/CustomQueryHotkeySetting.xaml.cs | 2 +- Flow.Launcher/CustomShortcutSetting.xaml.cs | 2 +- Flow.Launcher/ViewModel/MainViewModel.cs | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs index db1df0cf2..d33794d61 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs @@ -80,7 +80,7 @@ namespace Flow.Launcher private void BtnTestActionKeyword_OnClick(object sender, RoutedEventArgs e) { App.API.ChangeQuery(tbAction.Text); - _mainViewModel.Show(false); + _mainViewModel.Show(); Application.Current.MainWindow.Focus(); } diff --git a/Flow.Launcher/CustomShortcutSetting.xaml.cs b/Flow.Launcher/CustomShortcutSetting.xaml.cs index cb2cfcb29..4cc30c8f5 100644 --- a/Flow.Launcher/CustomShortcutSetting.xaml.cs +++ b/Flow.Launcher/CustomShortcutSetting.xaml.cs @@ -68,7 +68,7 @@ namespace Flow.Launcher private void BtnTestShortcut_OnClick(object sender, RoutedEventArgs e) { App.API.ChangeQuery(tbExpand.Text); - _mainViewModel.Show(false); + _mainViewModel.Show(); Application.Current.MainWindow.Focus(); } } diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 7c2abb078..55bc8d1b3 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1359,7 +1359,7 @@ namespace Flow.Launcher.ViewModel } } - public void Show(bool invokeEvent = true) + public void Show() { Application.Current.Dispatcher.Invoke(() => { @@ -1368,8 +1368,7 @@ namespace Flow.Launcher.ViewModel MainWindowOpacity = 1; MainWindowVisibilityStatus = true; - if (invokeEvent) - VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = true }); + VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = true }); }); } From 6b032b33520a272f8d9a239253272aa0fa234f68 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 18 Feb 2025 11:32:47 +0800 Subject: [PATCH 36/72] Revert changes and use api function instead --- Flow.Launcher/CustomQueryHotkeySetting.xaml.cs | 7 ++----- Flow.Launcher/CustomShortcutSetting.xaml.cs | 7 ++----- Flow.Launcher/PublicAPIInstance.cs | 2 +- .../ViewModels/SettingsPaneHotkeyViewModel.cs | 11 ++++------- .../SettingPages/Views/SettingsPaneHotkey.xaml.cs | 4 ++-- Flow.Launcher/SettingWindow.xaml.cs | 8 +++----- 6 files changed, 14 insertions(+), 25 deletions(-) diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs index d33794d61..1bd6ee95b 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs @@ -7,7 +7,6 @@ using System.Windows; using System.Windows.Input; using System.Windows.Controls; using Flow.Launcher.Core; -using Flow.Launcher.ViewModel; namespace Flow.Launcher { @@ -15,15 +14,13 @@ namespace Flow.Launcher { private SettingWindow _settingWidow; private readonly Settings _settings; - private readonly MainViewModel _mainViewModel; private bool update; private CustomPluginHotkey updateCustomHotkey; - public CustomQueryHotkeySetting(SettingWindow settingWidow, Settings settings, MainViewModel mainVM) + public CustomQueryHotkeySetting(SettingWindow settingWidow, Settings settings) { _settingWidow = settingWidow; _settings = settings; - _mainViewModel = mainVM; InitializeComponent(); } @@ -80,7 +77,7 @@ namespace Flow.Launcher private void BtnTestActionKeyword_OnClick(object sender, RoutedEventArgs e) { App.API.ChangeQuery(tbAction.Text); - _mainViewModel.Show(); + App.API.ShowMainWindow(); Application.Current.MainWindow.Focus(); } diff --git a/Flow.Launcher/CustomShortcutSetting.xaml.cs b/Flow.Launcher/CustomShortcutSetting.xaml.cs index 4cc30c8f5..05d4d3d83 100644 --- a/Flow.Launcher/CustomShortcutSetting.xaml.cs +++ b/Flow.Launcher/CustomShortcutSetting.xaml.cs @@ -4,24 +4,21 @@ using System.Windows; using System.Windows.Input; using Flow.Launcher.SettingPages.ViewModels; using Flow.Launcher.Core; -using Flow.Launcher.ViewModel; namespace Flow.Launcher { public partial class CustomShortcutSetting : Window { private readonly SettingsPaneHotkeyViewModel _hotkeyVm; - private readonly MainViewModel _mainViewModel; public string Key { get; set; } = String.Empty; public string Value { get; set; } = String.Empty; private string originalKey { get; } = null; private string originalValue { get; } = null; private bool update { get; } = false; - public CustomShortcutSetting(SettingsPaneHotkeyViewModel vm, MainViewModel mainVM) + public CustomShortcutSetting(SettingsPaneHotkeyViewModel vm) { _hotkeyVm = vm; - _mainViewModel = mainVM; InitializeComponent(); } @@ -68,7 +65,7 @@ namespace Flow.Launcher private void BtnTestShortcut_OnClick(object sender, RoutedEventArgs e) { App.API.ChangeQuery(tbExpand.Text); - _mainViewModel.Show(); + App.API.ShowMainWindow(); Application.Current.MainWindow.Focus(); } } diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 2d8126033..f0295cf24 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -105,7 +105,7 @@ namespace Flow.Launcher { Application.Current.Dispatcher.Invoke(() => { - SettingWindow sw = SingletonWindowOpener.Open(this, _settingsVM, _mainVM); + SettingWindow sw = SingletonWindowOpener.Open(this, _settingsVM); }); } diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs index 5aedd3be7..6d8af9a3f 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs @@ -8,14 +8,12 @@ using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using Flow.Launcher.Core; -using Flow.Launcher.ViewModel; namespace Flow.Launcher.SettingPages.ViewModels; public partial class SettingsPaneHotkeyViewModel : BaseModel { public Settings Settings { get; } - private MainViewModel MainVM { get; } public CustomPluginHotkey SelectedCustomPluginHotkey { get; set; } public CustomShortcutModel SelectedCustomShortcut { get; set; } @@ -27,10 +25,9 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel $"{KeyConstant.Ctrl}+{KeyConstant.Alt}" }; - public SettingsPaneHotkeyViewModel(Settings settings, MainViewModel mainVM) + public SettingsPaneHotkeyViewModel(Settings settings) { Settings = settings; - MainVM = mainVM; } [RelayCommand] @@ -74,7 +71,7 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel return; } - var window = new CustomQueryHotkeySetting(null, Settings, MainVM); + var window = new CustomQueryHotkeySetting(null, Settings); window.UpdateItem(item); window.ShowDialog(); } @@ -82,7 +79,7 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel [RelayCommand] private void CustomHotkeyAdd() { - new CustomQueryHotkeySetting(null, Settings, MainVM).ShowDialog(); + new CustomQueryHotkeySetting(null, Settings).ShowDialog(); } [RelayCommand] @@ -129,7 +126,7 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel [RelayCommand] private void CustomShortcutAdd() { - var window = new CustomShortcutSetting(this, MainVM); + var window = new CustomShortcutSetting(this); if (window.ShowDialog() is true) { var shortcut = new CustomShortcutModel(window.Key, window.Value); diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs index 26939c9f9..061eabf51 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs @@ -12,9 +12,9 @@ public partial class SettingsPaneHotkey { if (!IsInitialized) { - if (e.ExtraData is not SettingWindow.PaneData { Settings: { } settings, MainViewModel: { } mainVM }) + if (e.ExtraData is not SettingWindow.PaneData { Settings: { } settings }) throw new ArgumentException("Settings are required for SettingsPaneHotkey."); - _viewModel = new SettingsPaneHotkeyViewModel(settings, mainVM); + _viewModel = new SettingsPaneHotkeyViewModel(settings); DataContext = _viewModel; InitializeComponent(); } diff --git a/Flow.Launcher/SettingWindow.xaml.cs b/Flow.Launcher/SettingWindow.xaml.cs index ae481b65b..d5b303516 100644 --- a/Flow.Launcher/SettingWindow.xaml.cs +++ b/Flow.Launcher/SettingWindow.xaml.cs @@ -20,14 +20,12 @@ public partial class SettingWindow private readonly IPublicAPI _api; private readonly Settings _settings; private readonly SettingWindowViewModel _viewModel; - private readonly MainViewModel _mainVM; - public SettingWindow(IPublicAPI api, SettingWindowViewModel viewModel, MainViewModel mainVM) + public SettingWindow(IPublicAPI api, SettingWindowViewModel viewModel) { _settings = viewModel.Settings; DataContext = viewModel; _viewModel = viewModel; - _mainVM = mainVM; _api = api; InitializePosition(); InitializeComponent(); @@ -162,7 +160,7 @@ public partial class SettingWindow private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args) { - var paneData = new PaneData(_settings, _viewModel.Updater, _viewModel.Portable, _mainVM); + var paneData = new PaneData(_settings, _viewModel.Updater, _viewModel.Portable); if (args.IsSettingsSelected) { ContentFrame.Navigate(typeof(SettingsPaneGeneral), paneData); @@ -208,5 +206,5 @@ public partial class SettingWindow NavView.SelectedItem ??= NavView.MenuItems[0]; /* Set First Page */ } - public record PaneData(Settings Settings, Updater Updater, IPortable Portable, MainViewModel MainViewModel); + public record PaneData(Settings Settings, Updater Updater, IPortable Portable); } 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 37/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 38/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 39/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 b4bffb1cf54bb0a050ea2277ffa1893b1df05c39 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 20 Feb 2025 17:17:53 +0800 Subject: [PATCH 40/72] Use null as default value for record key --- Flow.Launcher.Plugin/Result.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index bb005752e..9b16cc1cb 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -266,8 +266,9 @@ namespace Flow.Launcher.Plugin /// The key to identify the record. This is used when FL checks whether the result is the topmost record. Or FL calculates the hashcode of the result for user selected records. /// This can be useful when your plugin will change the Title or SubTitle of the result dynamically. /// If the plugin does not specific this, FL just uses Title and SubTitle to identify this result. + /// Note: Because old data does not have this key, we should use null as the default value for consistency. /// - public string RecordKey { get; set; } = string.Empty; + public string RecordKey { get; set; } = null; /// /// Info of the preview section of a From 4f41be67ac5135158d22d6f99859cda1a2f84f57 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 20 Feb 2025 18:02:59 +0800 Subject: [PATCH 41/72] Improve code quality --- Flow.Launcher.Core/Plugin/PluginManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 5c4eaa1da..8f2d78d76 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -281,7 +281,7 @@ namespace Flow.Launcher.Core.Plugin return results; } - public static void UpdatePluginMetadata(List results, PluginMetadata metadata, Query query) + public static void UpdatePluginMetadata(IReadOnlyList results, PluginMetadata metadata, Query query) { foreach (var r in results) { From 2ffe170407b0ca012b267051569e29de43c77c46 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 20 Feb 2025 18:06:15 +0800 Subject: [PATCH 42/72] Use deep clone for result updating --- Flow.Launcher/ViewModel/MainViewModel.cs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 5c3251bfc..b498f4001 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -231,8 +231,8 @@ namespace Flow.Launcher.ViewModel var token = e.Token == default ? _updateToken : e.Token; - // make a copy of results to avoid plugin change the result when updating view model - var resultsCopy = e.Results.ToList(); + // make a clone to avoid possible issue that plugin will also change the list and items when updating view model + var resultsCopy = DeepCloneResults(e.Results, token); PluginManager.UpdatePluginMetadata(resultsCopy, pair.Metadata, e.Query); if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, pair.Metadata, e.Query, @@ -414,6 +414,22 @@ namespace Flow.Launcher.ViewModel } } + private static IReadOnlyList DeepCloneResults(IReadOnlyList results, CancellationToken token = default) + { + var resultsCopy = new List(); + foreach (var result in results.ToList()) + { + if (token.IsCancellationRequested) + { + break; + } + + var resultCopy = result.Clone(); + resultsCopy.Add(resultCopy); + } + return resultsCopy; + } + #endregion #region BasicCommands @@ -1469,9 +1485,9 @@ namespace Flow.Launcher.ViewModel { if (_topMostRecord.IsTopMost(result)) { - result.Score = Result.MaxScore; + result.Score = 100000; //Result.MaxScore; } - else if (result.Score != Result.MaxScore) + else { var priorityScore = metaResults.Metadata.Priority * 150; result.Score += result.AddSelectedCount ? From 9850e9d3eb6af9c78003738e23dd819336446ba4 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 20 Feb 2025 18:17:29 +0800 Subject: [PATCH 43/72] Fix issue that plugin will cannot cache records --- Flow.Launcher/ViewModel/MainViewModel.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index b498f4001..e030d8eae 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1196,9 +1196,18 @@ namespace Flow.Launcher.ViewModel currentCancellationToken.ThrowIfCancellationRequested(); - results ??= _emptyResult; + IReadOnlyList resultsCopy; + if (results == null) + { + resultsCopy = _emptyResult; + } + else + { + // make a copy of results to avoid possible issue that FL changes some properties of the records, like score, etc. + resultsCopy = DeepCloneResults(results); + } - if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, + if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, plugin.Metadata, query, currentCancellationToken, reSelect))) { Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); 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 44/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}" /> +