From 79f81a6274e7bdaa3a62ff8afa012f56dca40c43 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 14 Jul 2025 15:35:23 +0800 Subject: [PATCH 01/11] Improve code quality --- 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 7b82748fca3..ad7b44f10c4 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -251,7 +251,7 @@ await API.StopwatchLogInfoAsync(ClassName, "Startup cost", async () => /// Check startup only for Release /// [Conditional("RELEASE")] - private void AutoStartup() + private static void AutoStartup() { // we try to enable auto-startup on first launch, or reenable if it was removed // but the user still has the setting set @@ -272,7 +272,7 @@ private void AutoStartup() } [Conditional("RELEASE")] - private void AutoUpdates() + private static void AutoUpdates() { _ = Task.Run(async () => { From c0c7546cfc07c68478300572f6dee21a21b91967 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 14 Jul 2025 15:48:15 +0800 Subject: [PATCH 02/11] Add auto plugin update setting & UI --- .../UserSettings/Settings.cs | 1 + Flow.Launcher/Languages/en.xaml | 4 ++- .../Views/SettingsPaneGeneral.xaml | 27 +++++++++++++------ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 6b10d693da3..54c3131468e 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -233,6 +233,7 @@ public bool ShowHistoryResultsForHomePage public bool AutoRestartAfterChanging { get; set; } = false; public bool ShowUnknownSourceWarning { get; set; } = true; + public bool AutoUpdatePlugins { get; set; } = true; public int CustomExplorerIndex { get; set; } = 0; diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 2fca066059a..57ad1ce9a99 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -117,7 +117,7 @@ Xing Kong Jian Dao Da Niu Xiao Lang - + Always Preview Always open preview panel when Flow activates. Press {0} to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled @@ -150,6 +150,8 @@ Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugin Store Show unknown source warning Show warning when installing plugins from unknown sources + Auto update plugins + Automatically check plugin updates every 1 hour and notify if there are any updates available Search Plugin diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml index df0243ce8cd..78b6d8db0d8 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml @@ -241,12 +241,23 @@ Title="{DynamicResource showUnknownSourceWarning}" Icon="" Sub="{DynamicResource showUnknownSourceWarningToolTip}" - Type="Last"> + Type="Middle"> + + + + - + Type="Middle" + Visibility="{ext:VisibleWhen {Binding ShouldUsePinyin}, + IsEqualToBool=True}"> + Type="Last" + Visibility="{ext:VisibleWhen {Binding UseDoublePinyin}, + IsEqualToBool=True}"> Date: Mon, 14 Jul 2025 16:14:13 +0800 Subject: [PATCH 03/11] Update string resource --- Flow.Launcher/Languages/en.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 57ad1ce9a99..7a5dc0c0708 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -151,7 +151,7 @@ Show unknown source warning Show warning when installing plugins from unknown sources Auto update plugins - Automatically check plugin updates every 1 hour and notify if there are any updates available + Automatically check plugin updates and notify if there are any updates available Search Plugin From 6eb52b8961882f0d1e723c733cecad3408b94840 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 14 Jul 2025 16:18:22 +0800 Subject: [PATCH 04/11] Check plugin updates in the background --- Flow.Launcher.Core/Plugin/PluginInstaller.cs | 91 ++++++++++++++++++++ Flow.Launcher/App.xaml.cs | 18 ++++ Flow.Launcher/Languages/en.xaml | 4 + 3 files changed, 113 insertions(+) diff --git a/Flow.Launcher.Core/Plugin/PluginInstaller.cs b/Flow.Launcher.Core/Plugin/PluginInstaller.cs index 33963c01a5b..692fe3ce199 100644 --- a/Flow.Launcher.Core/Plugin/PluginInstaller.cs +++ b/Flow.Launcher.Core/Plugin/PluginInstaller.cs @@ -277,6 +277,97 @@ await DownloadFileAsync( } } + /// + /// Updates the plugin to the latest version available from its source. + /// + /// If true, do not show any messages when there is no udpate available. + /// If true, only use the primary URL for updates. + /// Cancellation token to cancel the update operation. + /// + public static async Task UpdatePluginAsync(bool silentUpdate = true, bool usePrimaryUrlOnly = false, CancellationToken token = default) + { + // Update the plugin manifest + await API.UpdatePluginManifestAsync(usePrimaryUrlOnly, token); + + // Get all plugins that can be updated + var resultsForUpdate = ( + from existingPlugin in API.GetAllPlugins() + join pluginUpdateSource in API.GetPluginManifest() + on existingPlugin.Metadata.ID equals pluginUpdateSource.ID + where string.Compare(existingPlugin.Metadata.Version, pluginUpdateSource.Version, + StringComparison.InvariantCulture) < + 0 // if current version precedes version of the plugin from update source (e.g. PluginsManifest) + && !API.PluginModified(existingPlugin.Metadata.ID) + select + new + { + existingPlugin.Metadata.ID, + pluginUpdateSource.Name, + pluginUpdateSource.Author, + CurrentVersion = existingPlugin.Metadata.Version, + NewVersion = pluginUpdateSource.Version, + existingPlugin.Metadata.IcoPath, + PluginExistingMetadata = existingPlugin.Metadata, + PluginNewUserPlugin = pluginUpdateSource + }).ToList(); + + // No updates + if (!resultsForUpdate.Any()) + { + if (!silentUpdate) + { + API.ShowMsg(API.GetTranslation("updateNoResultTitle"), API.GetTranslation("updateNoResultSubtitle")); + } + return; + } + + // If all plugins are modified, just return + if (resultsForUpdate.All(x => API.PluginModified(x.ID))) + { + return; + } + + if (API.ShowMsgBox( + string.Format(API.GetTranslation("updateAllPluginsSubtitle"), + Environment.NewLine, string.Join(", ", resultsForUpdate.Select(x => x.PluginExistingMetadata.Name))), + API.GetTranslation("updateAllPluginsTitle"), + MessageBoxButton.YesNo) == MessageBoxResult.No) + { + return; + } + + // Update all plugins + await Task.WhenAll(resultsForUpdate.Select(async plugin => + { + var downloadToFilePath = Path.Combine(Path.GetTempPath(), $"{plugin.Name}-{plugin.NewVersion}.zip"); + + try + { + using var cts = new CancellationTokenSource(); + + await DownloadFileAsync( + $"{API.GetTranslation("DownloadingPlugin")} {plugin.PluginNewUserPlugin.Name}", + plugin.PluginNewUserPlugin.UrlDownload, downloadToFilePath, cts); + + // check if user cancelled download before installing plugin + if (cts.IsCancellationRequested) + { + return; + } + + if (!await API.UpdatePluginAsync(plugin.PluginExistingMetadata, plugin.PluginNewUserPlugin, downloadToFilePath)) + { + return; + } + } + catch (Exception e) + { + API.LogException(ClassName, "Failed to update plugin", e); + API.ShowMsgError(API.GetTranslation("ErrorUpdatingPlugin")); + } + })); + } + /// /// Downloads a file from a URL to a local path, optionally showing a progress box and handling cancellation. /// diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index ad7b44f10c4..6b04f11d91a 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -239,6 +239,7 @@ await API.StopwatchLogInfoAsync(ClassName, "Startup cost", async () => AutoStartup(); AutoUpdates(); + AutoPluginUpdates(); API.SaveAppAllSettings(); API.LogInfo(ClassName, "End Flow Launcher startup ----------------------------------------------------"); @@ -289,6 +290,23 @@ private static void AutoUpdates() }); } + private static void AutoPluginUpdates() + { + _ = Task.Run(async () => + { + if (_settings.AutoUpdatePlugins) + { + // check plugin updates every 5 hour + var timer = new PeriodicTimer(TimeSpan.FromHours(5)); + await PluginInstaller.UpdatePluginAsync(); + + while (await timer.WaitForNextTickAsync()) + // check updates on startup + await PluginInstaller.UpdatePluginAsync(); + } + }); + } + #endregion #region Register Events diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 7a5dc0c0708..efbb1de3bf7 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -233,6 +233,10 @@ Zip files Please select zip file Install plugin from local path + No update available + All plugins are up to date + Update all plugins + Would you like to update these plugins?{0}{0}{1} Theme From b393d361865bf66688e0cb5ecf83f38ff468571f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 14 Jul 2025 16:24:09 +0800 Subject: [PATCH 05/11] Add check plugin update button --- Flow.Launcher/Languages/en.xaml | 1 + .../ViewModels/SettingsPanePluginStoreViewModel.cs | 6 ++++++ .../SettingPages/Views/SettingsPanePluginStore.xaml | 7 +++++++ 3 files changed, 14 insertions(+) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index efbb1de3bf7..ee500324e29 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -237,6 +237,7 @@ All plugins are up to date Update all plugins Would you like to update these plugins?{0}{0}{1} + Check plugin updates Theme diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs index efe67d01664..bfec08c52c4 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs @@ -109,6 +109,12 @@ private async Task InstallPluginAsync() await PluginInstaller.InstallPluginAndCheckRestartAsync(file); } + [RelayCommand] + private async Task CheckPluginUpdatesAsync() + { + await PluginInstaller.UpdatePluginAsync(silentUpdate: false); + } + private static string GetFileFromDialog(string title, string filter = "") { var dlg = new Microsoft.Win32.OpenFileDialog diff --git a/Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml b/Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml index 68f78d46c2e..21290bb6229 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml @@ -99,6 +99,13 @@ ToolTip="{DynamicResource installLocalPluginTooltip}"> + Date: Mon, 14 Jul 2025 16:24:46 +0800 Subject: [PATCH 06/11] Rename function name --- Flow.Launcher.Core/Plugin/PluginInstaller.cs | 2 +- Flow.Launcher/App.xaml.cs | 4 ++-- .../ViewModels/SettingsPanePluginStoreViewModel.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginInstaller.cs b/Flow.Launcher.Core/Plugin/PluginInstaller.cs index 692fe3ce199..4c551f9936c 100644 --- a/Flow.Launcher.Core/Plugin/PluginInstaller.cs +++ b/Flow.Launcher.Core/Plugin/PluginInstaller.cs @@ -284,7 +284,7 @@ await DownloadFileAsync( /// If true, only use the primary URL for updates. /// Cancellation token to cancel the update operation. /// - public static async Task UpdatePluginAsync(bool silentUpdate = true, bool usePrimaryUrlOnly = false, CancellationToken token = default) + public static async Task CheckForPluginUpdatesAsync(bool silentUpdate = true, bool usePrimaryUrlOnly = false, CancellationToken token = default) { // Update the plugin manifest await API.UpdatePluginManifestAsync(usePrimaryUrlOnly, token); diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 6b04f11d91a..7e3915b2b5d 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -298,11 +298,11 @@ private static void AutoPluginUpdates() { // check plugin updates every 5 hour var timer = new PeriodicTimer(TimeSpan.FromHours(5)); - await PluginInstaller.UpdatePluginAsync(); + await PluginInstaller.CheckForPluginUpdatesAsync(); while (await timer.WaitForNextTickAsync()) // check updates on startup - await PluginInstaller.UpdatePluginAsync(); + await PluginInstaller.CheckForPluginUpdatesAsync(); } }); } diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs index bfec08c52c4..96cd4407233 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs @@ -112,7 +112,7 @@ private async Task InstallPluginAsync() [RelayCommand] private async Task CheckPluginUpdatesAsync() { - await PluginInstaller.UpdatePluginAsync(silentUpdate: false); + await PluginInstaller.CheckForPluginUpdatesAsync(silentUpdate: false); } private static string GetFileFromDialog(string title, string filter = "") From 69d5e33150c83ceaf455aec48a1b83004efc83ad Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 14 Jul 2025 16:31:09 +0800 Subject: [PATCH 07/11] Show message box with button instead --- Flow.Launcher.Core/Plugin/PluginInstaller.cs | 22 ++++++++++++-------- Flow.Launcher/Languages/en.xaml | 4 ++-- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginInstaller.cs b/Flow.Launcher.Core/Plugin/PluginInstaller.cs index 4c551f9936c..84f0f1fd9b6 100644 --- a/Flow.Launcher.Core/Plugin/PluginInstaller.cs +++ b/Flow.Launcher.Core/Plugin/PluginInstaller.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; @@ -327,17 +328,20 @@ where string.Compare(existingPlugin.Metadata.Version, pluginUpdateSource.Version return; } - if (API.ShowMsgBox( - string.Format(API.GetTranslation("updateAllPluginsSubtitle"), - Environment.NewLine, string.Join(", ", resultsForUpdate.Select(x => x.PluginExistingMetadata.Name))), + // Show message box with button to update all plugins + API.ShowMsgWithButton( API.GetTranslation("updateAllPluginsTitle"), - MessageBoxButton.YesNo) == MessageBoxResult.No) - { - return; - } + API.GetTranslation("updateAllPluginsButtonContent"), + () => + { + UpdateAllPlugins(resultsForUpdate); + }, + string.Join(", ", resultsForUpdate.Select(x => x.PluginExistingMetadata.Name))); + } - // Update all plugins - await Task.WhenAll(resultsForUpdate.Select(async plugin => + private static void UpdateAllPlugins(IEnumerable resultsForUpdate) + { + _ = Task.WhenAll(resultsForUpdate.Select(async plugin => { var downloadToFilePath = Path.Combine(Path.GetTempPath(), $"{plugin.Name}-{plugin.NewVersion}.zip"); diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index ee500324e29..53f26c5f4e7 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -235,8 +235,8 @@ Install plugin from local path No update available All plugins are up to date - Update all plugins - Would you like to update these plugins?{0}{0}{1} + Plugin updates available + Update all plugins Check plugin updates From 44fbc6eed5763b089c78cc8f9a2eab3e8cb33185 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 14 Jul 2025 16:35:48 +0800 Subject: [PATCH 08/11] Add auto update subtitle --- Flow.Launcher/Languages/en.xaml | 1 + Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 53f26c5f4e7..3905df7c69a 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -93,6 +93,7 @@ Always Start Typing in English Mode Temporarily change your input method to English mode when activating Flow. Auto Update + Automatically check app updates and notify if there are any updates available Select Hide Flow Launcher on startup Flow Launcher search window is hidden in the tray after starting up. diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml index 78b6d8db0d8..cfb29263304 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml @@ -182,7 +182,8 @@ + Icon="" + Sub="{DynamicResource autoUpdatesTooltip}"> Date: Mon, 14 Jul 2025 16:56:12 +0800 Subject: [PATCH 09/11] Replace dynamic type with a strongly-typed model --- Flow.Launcher.Core/Plugin/PluginInstaller.cs | 24 +++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginInstaller.cs b/Flow.Launcher.Core/Plugin/PluginInstaller.cs index 84f0f1fd9b6..c00c83d9efc 100644 --- a/Flow.Launcher.Core/Plugin/PluginInstaller.cs +++ b/Flow.Launcher.Core/Plugin/PluginInstaller.cs @@ -300,14 +300,14 @@ where string.Compare(existingPlugin.Metadata.Version, pluginUpdateSource.Version 0 // if current version precedes version of the plugin from update source (e.g. PluginsManifest) && !API.PluginModified(existingPlugin.Metadata.ID) select - new + new PluginUpdateInfo() { - existingPlugin.Metadata.ID, - pluginUpdateSource.Name, - pluginUpdateSource.Author, + ID = existingPlugin.Metadata.ID, + Name = existingPlugin.Metadata.Name, + Author = existingPlugin.Metadata.Author, CurrentVersion = existingPlugin.Metadata.Version, NewVersion = pluginUpdateSource.Version, - existingPlugin.Metadata.IcoPath, + IcoPath = existingPlugin.Metadata.IcoPath, PluginExistingMetadata = existingPlugin.Metadata, PluginNewUserPlugin = pluginUpdateSource }).ToList(); @@ -339,7 +339,7 @@ where string.Compare(existingPlugin.Metadata.Version, pluginUpdateSource.Version string.Join(", ", resultsForUpdate.Select(x => x.PluginExistingMetadata.Name))); } - private static void UpdateAllPlugins(IEnumerable resultsForUpdate) + private static void UpdateAllPlugins(IEnumerable resultsForUpdate) { _ = Task.WhenAll(resultsForUpdate.Select(async plugin => { @@ -445,4 +445,16 @@ private static bool InstallSourceKnown(string url) x.Metadata.Website.StartsWith(constructedUrlPart) ); } + + private record PluginUpdateInfo + { + public string ID { get; init; } + public string Name { get; init; } + public string Author { get; init; } + public string CurrentVersion { get; init; } + public string NewVersion { get; init; } + public string IcoPath { get; init; } + public PluginMetadata PluginExistingMetadata { get; init; } + public UserPlugin PluginNewUserPlugin { get; init; } + } } From a858aa8f5554b7e87d01c4c3d563516625ffdbc2 Mon Sep 17 00:00:00 2001 From: Jack Ye <1160210343@qq.com> Date: Tue, 15 Jul 2025 19:58:12 +0800 Subject: [PATCH 10/11] Update auto update desc Co-authored-by: Jeremy Wu --- Flow.Launcher/Languages/en.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 3905df7c69a..725d8d3e1b3 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -93,7 +93,7 @@ Always Start Typing in English Mode Temporarily change your input method to English mode when activating Flow. Auto Update - Automatically check app updates and notify if there are any updates available + Automatically check and update the app when available Select Hide Flow Launcher on startup Flow Launcher search window is hidden in the tray after starting up. From 37d6cea2d32822c9b26ede0ee6f2b1a1e4cf5f63 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 15 Jul 2025 22:10:07 +1000 Subject: [PATCH 11/11] fix typo --- Flow.Launcher.Core/Plugin/PluginInstaller.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/PluginInstaller.cs b/Flow.Launcher.Core/Plugin/PluginInstaller.cs index c00c83d9efc..a79f4b47ce8 100644 --- a/Flow.Launcher.Core/Plugin/PluginInstaller.cs +++ b/Flow.Launcher.Core/Plugin/PluginInstaller.cs @@ -281,7 +281,7 @@ await DownloadFileAsync( /// /// Updates the plugin to the latest version available from its source. /// - /// If true, do not show any messages when there is no udpate available. + /// If true, do not show any messages when there is no update available. /// If true, only use the primary URL for updates. /// Cancellation token to cancel the update operation. ///