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 01/18] 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 ba47a4ded22..c3966e61850 100644 --- a/Flow.Launcher/ActionKeywords.xaml.cs +++ b/Flow.Launcher/ActionKeywords.xaml.cs @@ -44,7 +44,7 @@ private void btnDone_OnClick(object sender, RoutedEventArgs _) 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 4d1adc6cd51..58da35e85b6 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -15,13 +15,15 @@ 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 81e7600b8fa..47460ff7d32 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs @@ -63,7 +63,7 @@ public void UpdateItem(CustomPluginHotkey item) 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 dec3506eb35..4589b45ec72 100644 --- a/Flow.Launcher/CustomShortcutSetting.xaml.cs +++ b/Flow.Launcher/CustomShortcutSetting.xaml.cs @@ -43,13 +43,13 @@ private void BtnAdd_OnClick(object sender, RoutedEventArgs e) { 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 8b30b8be1f5..b40406e4282 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -46,7 +46,7 @@ internal static void SetHotkey(HotkeyModel hotkey, EventHandler { 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 2154b058d1c..fbe2a941d26 100644 --- a/Flow.Launcher/PriorityChangeWindow.xaml.cs +++ b/Flow.Launcher/PriorityChangeWindow.xaml.cs @@ -24,7 +24,7 @@ public PriorityChangeWindow(string pluginId, PluginViewModel pluginViewModel) this.pluginViewModel = pluginViewModel; if (plugin == null) { - MessageBoxEx.Show(translater.GetTranslation("cannotFindSpecifiedPlugin")); + App.API.ShowMsgBox(translater.GetTranslation("cannotFindSpecifiedPlugin")); Close(); } } @@ -44,7 +44,7 @@ private void btnDone_OnClick(object sender, RoutedEventArgs e) 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 6e81db5e00d..05fb16f5cc1 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs @@ -62,7 +62,7 @@ private void OpenWelcomeWindow() [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 6d8af9a3f62..fb57f499bbc 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs @@ -42,11 +42,11 @@ private void CustomHotkeyDelete() 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 @@ private void CustomHotkeyEdit() 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 @@ private void CustomShortcutDelete() 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 @@ private void CustomShortcutEdit() 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 1c840fb2703..e2f9e516ca4 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneProxyViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneProxyViewModel.cs @@ -22,7 +22,7 @@ public SettingsPaneProxyViewModel(Settings settings, Updater updater) 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 8d8ccb78041..980b2a811a4 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs @@ -49,7 +49,7 @@ public bool DropShadowEffect { 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 02/18] 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 00000000000..b02612d72f9 --- /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 00000000000..233fd5ed1c3 --- /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 03/18] 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 d7c73fb46ac..cb375c58639 100644 --- a/Flow.Launcher.Core/Configuration/Portable.cs +++ b/Flow.Launcher.Core/Configuration/Portable.cs @@ -40,7 +40,7 @@ public void DisablePortableMode() #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 @@ public void EnablePortableMode() #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 @@ public void RemoveUninstallerEntry() 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 @@ public void PreStartCleanUpAfterPortabilityUpdate() // 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 @@ public void PreStartCleanUpAfterPortabilityUpdate() // 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 @@ public bool CanUpdatePortability() 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 6d41e23834e..cada0503197 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -57,7 +57,7 @@ internal IEnumerable Setup() 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 @@ internal IEnumerable Setup() } 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 @@ private void EnsureLatestInstalled(string expectedPath, string currentPath, stri 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 96c29646e8f..56bc20b4f4d 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs @@ -28,7 +28,7 @@ internal PythonEnvironment(List pluginMetadataList, PluginsSetti 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 0d6f109e07f..1d43b815a21 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs @@ -25,7 +25,7 @@ internal TypeScriptEnvironment(List pluginMetadataList, PluginsS 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 582a4407c45..49bf4e958f0 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs @@ -25,7 +25,7 @@ internal TypeScriptV2Environment(List pluginMetadataList, Plugin 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 5c4eaa1dadc..9e1cf3b9d13 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -519,7 +519,7 @@ internal static void InstallPlugin(UserPlugin plugin, string zipFilePath, bool c 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 7973c66baa7..8cbeb7473f7 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -119,7 +119,7 @@ public static IEnumerable DotNetPlugins(List source) _ = 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 1505e84f819..a1cefabe381 100644 --- a/Flow.Launcher.Core/Resource/Internationalization.cs +++ b/Flow.Launcher.Core/Resource/Internationalization.cs @@ -124,7 +124,7 @@ public bool PromptShouldUsePinyin(string languageCodeToSet) // "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 1d840930663..8622d4caf05 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -108,7 +108,7 @@ public bool ChangeTheme(string theme) 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 @@ public bool ChangeTheme(string theme) 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 b92d8656873..8745d54b792 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -53,7 +53,7 @@ public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true) 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 @@ public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true) 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 @@ public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true) 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 04/18] 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 fff107a68a4..be12ca16c20 100644 --- a/Flow.Launcher.Core/MessageBoxEx.xaml +++ b/Flow.Launcher/MessageBoxEx.xaml @@ -1,9 +1,9 @@  Date: Sun, 12 Jan 2025 18:44:52 +0800 Subject: [PATCH 05/18] 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 0bcc9368d22..3e43e3d32b5 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 58da35e85b6..9d7a0671e2d 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -14,6 +14,7 @@ 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 @@ private async void OnStartupAsync(object sender, StartupEventArgs e) { 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 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => 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 04dd6312b37..95a1eb67553 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 async void UpdateApp() 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 06/18] 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 | 65 +++++++++++-------- Flow.Launcher/Flow.Launcher.csproj | 5 +- Flow.Launcher/PublicAPIInstance.cs | 12 ++-- Flow.Launcher/ViewModel/MainViewModel.cs | 5 +- .../ViewModel/SettingWindowViewModel.cs | 11 ++-- 8 files changed, 67 insertions(+), 45 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index 1475252caee..84b6031613a 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 7d72359684d..8eaa757bec1 100644 --- a/Flow.Launcher.Infrastructure/PinyinAlphabet.cs +++ b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs @@ -6,6 +6,7 @@ using JetBrains.Annotations; using Flow.Launcher.Infrastructure.UserSettings; using ToolGood.Words.Pinyin; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Infrastructure { @@ -129,7 +130,12 @@ public class PinyinAlphabet : IAlphabet 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 bd5dbdda9be..7134fc7603a 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 @@ public class StringMatcher 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 9d7a0671e2d..8cd054148a9 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -4,6 +4,7 @@ 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.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 @@ private async void OnStartupAsync(object sender, StartupEventArgs e) { await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => { + // Initialize settings var storage = new FlowLauncherJsonStorage(); _settings = storage.Load(); _settings.Initialize(storage); - - _portable.PreStartCleanUpAfterPortabilityUpdate(); - - Log.Info( - "|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------"); + _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); + + 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 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => 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 @@ private void AutoUpdates() { // 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 @@ public void Dispose() public void OnSecondAppStarted() { - _mainVM.Show(); + Ioc.Default.GetRequiredService().Show(); } } } diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 4ec249c2b79..cab3915d5c0 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 f0295cf244a..50765294cf8 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -25,7 +25,7 @@ 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 @@ public class PublicAPIInstance : IPublicAPI { 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 55bc8d1b3f8..f5141a8fa58 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -25,6 +25,7 @@ using System.ComponentModel; using Flow.Launcher.Infrastructure.Image; using System.Windows.Media; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.ViewModel { @@ -58,13 +59,13 @@ public partial class MainViewModel : BaseModel, ISavable #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 95a1eb67553..7549db1a3ab 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 07/18] 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 8cd054148a9..9b86c6cc4ee 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 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => 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 08/18] 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 b02612d72f9..00000000000 --- 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 cb375c58639..069154364ab 100644 --- a/Flow.Launcher.Core/Configuration/Portable.cs +++ b/Flow.Launcher.Core/Configuration/Portable.cs @@ -9,11 +9,15 @@ 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 @@ public void DisablePortableMode() #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 @@ public void EnablePortableMode() #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 @@ public void RemoveUninstallerEntry() 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 @@ public void PreStartCleanUpAfterPortabilityUpdate() // 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 @@ public void PreStartCleanUpAfterPortabilityUpdate() // 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 @@ public bool CanUpdatePortability() 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 cada0503197..451df6147fa 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -8,11 +8,14 @@ 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 @@ public abstract class AbstractPluginEnvironment 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 @@ internal IEnumerable Setup() 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 @@ internal IEnumerable Setup() } 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 @@ private void EnsureLatestInstalled(string expectedPath, string currentPath, stri 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 56bc20b4f4d..607c1906215 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs @@ -28,7 +28,7 @@ internal PythonEnvironment(List pluginMetadataList, PluginsSetti 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 1d43b815a21..399f7cc03fd 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs @@ -25,7 +25,7 @@ internal TypeScriptEnvironment(List pluginMetadataList, PluginsS 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 49bf4e958f0..e8cb72e11d6 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs @@ -25,7 +25,7 @@ internal TypeScriptV2Environment(List pluginMetadataList, Plugin 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 233fd5ed1c3..00000000000 --- 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 9e1cf3b9d13..a776c10ab2f 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -519,7 +519,7 @@ internal static void InstallPlugin(UserPlugin plugin, string zipFilePath, bool c 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 8cbeb7473f7..4827cf69d41 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -4,6 +4,7 @@ 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 @@ public static IEnumerable DotNetPlugins(List source) _ = 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 a1cefabe381..de066dda125 100644 --- a/Flow.Launcher.Core/Resource/Internationalization.cs +++ b/Flow.Launcher.Core/Resource/Internationalization.cs @@ -11,6 +11,7 @@ using Flow.Launcher.Plugin; using System.Globalization; using System.Threading.Tasks; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Core.Resource { @@ -124,7 +125,7 @@ public bool PromptShouldUsePinyin(string languageCodeToSet) // "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 8622d4caf05..2749da53209 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -11,6 +11,8 @@ 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 @@ public class Theme 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 @@ public bool ChangeTheme(string theme) 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 @@ public bool ChangeTheme(string theme) 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 8745d54b792..37341805517 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -17,14 +17,17 @@ 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 @@ public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true) 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 @@ public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true) 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 @@ public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true) 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 9b86c6cc4ee..0d6d9855c72 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -74,6 +74,8 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => 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 7549db1a3ab..bbd47e731cf 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 09/18] 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 7134fc7603a..b3a265e29fe 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 @@ private bool IsAcronymCount(string stringToCompare, int compareStringIndex) 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 10/18] 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 b3a265e29fe..5822057a27f 100644 --- a/Flow.Launcher.Infrastructure/StringMatcher.cs +++ b/Flow.Launcher.Infrastructure/StringMatcher.cs @@ -14,9 +14,9 @@ public class StringMatcher 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 11/18] 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 5822057a27f..b3a265e29fe 100644 --- a/Flow.Launcher.Infrastructure/StringMatcher.cs +++ b/Flow.Launcher.Infrastructure/StringMatcher.cs @@ -14,9 +14,9 @@ public class StringMatcher 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 12/18] 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 b3a265e29fe..f5f02cbfcae 100644 --- a/Flow.Launcher.Infrastructure/StringMatcher.cs +++ b/Flow.Launcher.Infrastructure/StringMatcher.cs @@ -19,6 +19,12 @@ public StringMatcher() _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 d7f1432184c..d97ce227c1a 100644 --- a/Flow.Launcher.Test/FuzzyMatcherTest.cs +++ b/Flow.Launcher.Test/FuzzyMatcherTest.cs @@ -21,6 +21,8 @@ public class FuzzyMatcherTest 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 @@ public void MatchTest() }; 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 @@ public void MatchTest() 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 @@ public void GivenQueryString_WhenAppliedPrecisionFiltering_ThenShouldReturnGreat 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 @@ public void WhenGivenQueryString_ThenShouldReturn_TheDesiredScoring( 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 @@ public void WhenGivenDesiredPrecision_ThenShouldReturn_AllResultsGreaterOrEqual( 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 @@ public void WhenGivenQuery_ShouldReturnResults_ContainingAllQuerySubstrings( 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 @@ public void WhenGivenAQuery_Scoring_ShouldGiveMoreWeightToStartOfNewWord( 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 @@ public void WhenGivenTwoStrings_Scoring_ShouldGiveMoreWeightToTheStringCloserToI 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 @@ public void WhenMultipleResults_ExactMatchingResult_ShouldHaveGreatestScore( 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 @@ public void WhenMultipleResults_ExactMatchingResult_ShouldHaveGreatestScore( 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 13/18] 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 37341805517..7a25447b4d3 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -34,14 +34,14 @@ public void Initialize(string gitHubRepository) 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 @@ public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true) 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 @@ public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true) 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 @@ public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true) 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 @@ public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true) 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 0d6d9855c72..9384aae3d3b 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -74,8 +74,6 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => 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 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => PluginManager.LoadPlugins(_settings.PluginSettings); API = Ioc.Default.GetRequiredService(); + ((PublicAPIInstance)API).Initialize(); Http.API = API; Http.Proxy = _settings.Proxy; @@ -160,11 +159,11 @@ private void AutoUpdates() { // 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 47460ff7d32..eab2705d004 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs @@ -6,20 +6,17 @@ 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 50765294cf8..54e97f6c61e 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -26,6 +26,7 @@ using System.Diagnostics; using System.Collections.Specialized; using CommunityToolkit.Mvvm.DependencyInjection; +using Flow.Launcher.Core; namespace Flow.Launcher { @@ -33,19 +34,26 @@ public class PublicAPIInstance : IPublicAPI { 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 @@ public void RestartApp() 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 @@ public void OpenSettingDialog() { 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 05fb16f5cc1..cb434f399f0 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs @@ -96,7 +96,7 @@ private void OpenLogsFolder() } [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 3d94355e687..4e498ba23fa 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs @@ -160,7 +160,7 @@ private string GetFileFromDialog(string title, string filter = "") 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 fb57f499bbc..b13aaefe3e9 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs @@ -7,7 +7,6 @@ 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 @@ private void CustomHotkeyEdit() return; } - var window = new CustomQueryHotkeySetting(null, Settings); + var window = new CustomQueryHotkeySetting(Settings); window.UpdateItem(item); window.ShowDialog(); } @@ -79,7 +78,7 @@ private void CustomHotkeyEdit() [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 cb3f1e4a113..ab639e987ed 100644 --- a/Flow.Launcher/SettingWindow.xaml.cs +++ b/Flow.Launcher/SettingWindow.xaml.cs @@ -3,6 +3,7 @@ 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 void InitializePosition() 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 @@ private double WindowLeft() 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 @@ private double WindowTop() 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 bbd47e731cf..37276a1addf 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 14/18] 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 9384aae3d3b..a64c9e7505d 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -70,6 +70,8 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => ).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 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => 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 54e97f6c61e..0329d6973f9 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -35,9 +35,7 @@ public class PublicAPIInstance : IPublicAPI private readonly SettingWindowViewModel _settingsVM; private readonly MainViewModel _mainVM; - private Updater _updater; - - #region Constructor & Initialization + #region Constructor public PublicAPIInstance() { @@ -47,13 +45,6 @@ public PublicAPIInstance() 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 @@ public void RestartApp() 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 15/18] 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 a64c9e7505d..d983ab0005e 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -30,7 +30,31 @@ public partial class App : IDisposable, ISingleInstanceApp 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 @@ private async void OnStartupAsync(object sender, StartupEventArgs e) { 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 16/18] 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 d983ab0005e..f800ccd5dc1 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -54,6 +54,9 @@ public App() .AddSingleton() ).Build(); Ioc.Default.ConfigureServices(host.Services); + + // Initialize the public API first + API = Ioc.Default.GetRequiredService(); } [STAThread] @@ -73,8 +76,6 @@ private async void OnStartupAsync(object sender, StartupEventArgs e) { 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 17/18] 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 a776c10ab2f..0e8a4b7764b 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -14,6 +14,7 @@ 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 @@ public static class PluginManager 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 @@ public static void LoadPlugins(PluginsSettings settings) /// 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 14b8eef4e16..3711a6b0d20 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -8,6 +8,7 @@ using System; using System.Threading; using Flow.Launcher.Plugin; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Infrastructure.Http { @@ -17,7 +18,7 @@ public static class 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 f800ccd5dc1..5f7f097e0dd 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -99,10 +99,9 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => 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 18/18] 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 3711a6b0d20..78545a87b76 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -18,8 +18,6 @@ public static class 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 @@ var userName when string.IsNullOrEmpty(userName) => } 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); } }