diff --git a/Flow.Launcher.Core/Plugin/PluginInstaller.cs b/Flow.Launcher.Core/Plugin/PluginInstaller.cs
index a79f4b47ce8..22aeef22453 100644
--- a/Flow.Launcher.Core/Plugin/PluginInstaller.cs
+++ b/Flow.Launcher.Core/Plugin/PluginInstaller.cs
@@ -281,11 +281,12 @@ await DownloadFileAsync(
///
/// Updates the plugin to the latest version available from its source.
///
+ /// Action to execute when the user chooses to update all plugins.
/// 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.
///
- public static async Task CheckForPluginUpdatesAsync(bool silentUpdate = true, bool usePrimaryUrlOnly = false, CancellationToken token = default)
+ public static async Task CheckForPluginUpdatesAsync(Action> updateAllPlugins, bool silentUpdate = true, bool usePrimaryUrlOnly = false, CancellationToken token = default)
{
// Update the plugin manifest
await API.UpdatePluginManifestAsync(usePrimaryUrlOnly, token);
@@ -334,14 +335,20 @@ where string.Compare(existingPlugin.Metadata.Version, pluginUpdateSource.Version
API.GetTranslation("updateAllPluginsButtonContent"),
() =>
{
- UpdateAllPlugins(resultsForUpdate);
+ updateAllPlugins(resultsForUpdate);
},
string.Join(", ", resultsForUpdate.Select(x => x.PluginExistingMetadata.Name)));
}
- private static void UpdateAllPlugins(IEnumerable resultsForUpdate)
+ ///
+ /// Updates all plugins that have available updates.
+ ///
+ ///
+ ///
+ public static async Task UpdateAllPluginsAsync(IEnumerable resultsForUpdate, bool restart)
{
- _ = Task.WhenAll(resultsForUpdate.Select(async plugin =>
+ var anyPluginSuccess = false;
+ await Task.WhenAll(resultsForUpdate.Select(async plugin =>
{
var downloadToFilePath = Path.Combine(Path.GetTempPath(), $"{plugin.Name}-{plugin.NewVersion}.zip");
@@ -363,6 +370,8 @@ await DownloadFileAsync(
{
return;
}
+
+ anyPluginSuccess = true;
}
catch (Exception e)
{
@@ -370,6 +379,19 @@ await DownloadFileAsync(
API.ShowMsgError(API.GetTranslation("ErrorUpdatingPlugin"));
}
}));
+
+ if (!anyPluginSuccess) return;
+
+ if (restart)
+ {
+ API.RestartApp();
+ }
+ else
+ {
+ API.ShowMsg(
+ API.GetTranslation("updatebtn"),
+ API.GetTranslation("PluginsUpdateSuccessNoRestart"));
+ }
}
///
@@ -445,16 +467,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; }
- }
+public 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; }
}
diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs
index 7e3915b2b5d..1bef1166eb2 100644
--- a/Flow.Launcher/App.xaml.cs
+++ b/Flow.Launcher/App.xaml.cs
@@ -298,11 +298,25 @@ private static void AutoPluginUpdates()
{
// check plugin updates every 5 hour
var timer = new PeriodicTimer(TimeSpan.FromHours(5));
- await PluginInstaller.CheckForPluginUpdatesAsync();
+ await PluginInstaller.CheckForPluginUpdatesAsync((plugins) =>
+ {
+ Current.Dispatcher.Invoke(() =>
+ {
+ var pluginUpdateWindow = new PluginUpdateWindow(plugins);
+ pluginUpdateWindow.ShowDialog();
+ });
+ });
while (await timer.WaitForNextTickAsync())
// check updates on startup
- await PluginInstaller.CheckForPluginUpdatesAsync();
+ await PluginInstaller.CheckForPluginUpdatesAsync((plugins) =>
+ {
+ Current.Dispatcher.Invoke(() =>
+ {
+ var pluginUpdateWindow = new PluginUpdateWindow(plugins);
+ pluginUpdateWindow.ShowDialog();
+ });
+ });
}
});
}
diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml
index 374917c745b..ac58fdc5f7f 100644
--- a/Flow.Launcher/Languages/en.xaml
+++ b/Flow.Launcher/Languages/en.xaml
@@ -237,8 +237,9 @@
No update available
All plugins are up to date
Plugin updates available
- Update all plugins
+ Update plugins
Check plugin updates
+ Plugins are successfully updated. Please restart Flow.
Theme
@@ -563,6 +564,11 @@
Update files
Update description
+
+ Restart Flow Launcher after updating plugins
+ {0}: Update from v{1} to v{2}
+ No plugin selected
+
Skip
Welcome to Flow Launcher
diff --git a/Flow.Launcher/PluginUpdateWindow.xaml b/Flow.Launcher/PluginUpdateWindow.xaml
new file mode 100644
index 00000000000..04cd1f7bc11
--- /dev/null
+++ b/Flow.Launcher/PluginUpdateWindow.xaml
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Flow.Launcher/PluginUpdateWindow.xaml.cs b/Flow.Launcher/PluginUpdateWindow.xaml.cs
new file mode 100644
index 00000000000..20f03342566
--- /dev/null
+++ b/Flow.Launcher/PluginUpdateWindow.xaml.cs
@@ -0,0 +1,85 @@
+using System.Collections.Generic;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using CommunityToolkit.Mvvm.DependencyInjection;
+using Flow.Launcher.Core.Plugin;
+using Flow.Launcher.Infrastructure.UserSettings;
+
+namespace Flow.Launcher
+{
+ public partial class PluginUpdateWindow : Window
+ {
+ public List Plugins { get; set; } = new();
+ public bool Restart { get; set; }
+
+ private readonly Settings _settings = Ioc.Default.GetRequiredService();
+
+ public PluginUpdateWindow(List allPlugins)
+ {
+ Restart = _settings.AutoRestartAfterChanging;
+ InitializeComponent();
+ foreach (var plugin in allPlugins)
+ {
+ var checkBox = new CheckBox
+ {
+ Content = string.Format(App.API.GetTranslation("updatePluginCheckboxContent"), plugin.Name, plugin.CurrentVersion, plugin.NewVersion),
+ IsChecked = true,
+ Margin = new Thickness(0, 5, 0, 5),
+ Tag = plugin,
+ VerticalAlignment = VerticalAlignment.Center,
+ };
+ checkBox.Checked += CheckBox_Checked;
+ checkBox.Unchecked += CheckBox_Unchecked;
+ UpdatePluginStackPanel.Children.Add(checkBox);
+ Plugins.Add(plugin);
+ }
+ }
+
+ private void CheckBox_Checked(object sender, RoutedEventArgs e)
+ {
+ if (sender is not CheckBox cb) return;
+ if (cb.Tag is not PluginUpdateInfo plugin) return;
+ if (!Plugins.Contains(plugin))
+ {
+ Plugins.Add(plugin);
+ }
+ }
+
+ private void CheckBox_Unchecked(object sender, RoutedEventArgs e)
+ {
+ if (sender is not CheckBox cb) return;
+ if (cb.Tag is not PluginUpdateInfo plugin) return;
+ if (Plugins.Contains(plugin))
+ {
+ Plugins.Remove(plugin);
+ }
+ }
+
+ private void BtnCancel_OnClick(object sender, RoutedEventArgs e)
+ {
+ DialogResult = false;
+ Close();
+ }
+
+ private void btnUpdate_OnClick(object sender, RoutedEventArgs e)
+ {
+ if (Plugins.Count == 0)
+ {
+ App.API.ShowMsgBox(App.API.GetTranslation("updatePluginNoSelected"));
+ return;
+ }
+
+ _ = PluginInstaller.UpdateAllPluginsAsync(Plugins, Restart);
+
+ DialogResult = true;
+ Close();
+ }
+
+ private void cmdEsc_OnPress(object sender, ExecutedRoutedEventArgs e)
+ {
+ DialogResult = false;
+ Close();
+ }
+ }
+}
diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs
index 96cd4407233..140a1765be3 100644
--- a/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs
+++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using System.Windows;
using CommunityToolkit.Mvvm.Input;
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Plugin;
@@ -112,7 +113,14 @@ private async Task InstallPluginAsync()
[RelayCommand]
private async Task CheckPluginUpdatesAsync()
{
- await PluginInstaller.CheckForPluginUpdatesAsync(silentUpdate: false);
+ await PluginInstaller.CheckForPluginUpdatesAsync((plugins) =>
+ {
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ var pluginUpdateWindow = new PluginUpdateWindow(plugins);
+ pluginUpdateWindow.ShowDialog();
+ });
+ }, silentUpdate: false);
}
private static string GetFileFromDialog(string title, string filter = "")