Add update dialog

This commit is contained in:
Jack251970 2025-07-18 15:26:07 +08:00
parent 6c2f457e0a
commit f562e159e8
5 changed files with 220 additions and 3 deletions

View file

@ -298,11 +298,25 @@ namespace Flow.Launcher
{
// 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();
});
});
}
});
}

View file

@ -564,6 +564,11 @@
<system:String x:Key="update_flowlauncher_update_files">Update files</system:String>
<system:String x:Key="update_flowlauncher_update_update_description">Update description</system:String>
<!-- Plugin Update Window -->
<system:String x:Key="restartAfterUpdating">Restart Flow Launcher after updating plugins</system:String>
<system:String x:Key="updatePluginCheckboxContent">{0}: Update from v{1} to v{2}</system:String>
<system:String x:Key="updatePluginNoSelected">No plugin selected</system:String>
<!-- Welcome Window -->
<system:String x:Key="Skip">Skip</system:String>
<system:String x:Key="Welcome_Page1_Title">Welcome to Flow Launcher</system:String>

View file

@ -0,0 +1,105 @@
<Window
x:Class="Flow.Launcher.PluginUpdateWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:flowlauncher="clr-namespace:Flow.Launcher"
Title="{DynamicResource updateAllPluginsButtonContent}"
Width="530"
Background="{DynamicResource PopuBGColor}"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Foreground="{DynamicResource PopupTextColor}"
Icon="Images\app.png"
ResizeMode="NoResize"
SizeToContent="Height"
Topmost="True"
WindowStartupLocation="CenterScreen">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="32" ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
</WindowChrome.WindowChrome>
<Window.InputBindings>
<KeyBinding Key="Escape" Command="Close" />
</Window.InputBindings>
<Window.CommandBindings>
<CommandBinding Command="Close" Executed="cmdEsc_OnPress" />
</Window.CommandBindings>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="80" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="1"
Click="BtnCancel_OnClick"
Style="{StaticResource TitleBarCloseButtonStyle}">
<Path
Width="46"
Height="32"
Data="M 18,11 27,20 M 18,20 27,11"
Stroke="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
StrokeThickness="1">
<Path.Style>
<Style TargetType="Path">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsActive, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="False">
<Setter Property="Opacity" Value="0.5" />
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
</Button>
</Grid>
</StackPanel>
<StackPanel Margin="26 0 26 0">
<TextBlock
Margin="0 0 0 12"
FontSize="20"
FontWeight="SemiBold"
Text="{DynamicResource updateAllPluginsButtonContent}"
TextAlignment="Left" />
<StackPanel x:Name="UpdatePluginStackPanel" Margin="0 5 0 5" />
<Rectangle
Height="1"
Margin="0 5 0 5"
Fill="{DynamicResource SeparatorForeground}" />
<CheckBox
Margin="0 10 0 10"
Content="{DynamicResource restartAfterUpdating}"
IsChecked="{Binding Restart, Mode=TwoWay}" />
</StackPanel>
</StackPanel>
<Border
Grid.Row="1"
Margin="0 14 0 0"
Background="{DynamicResource PopupButtonAreaBGColor}"
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
BorderThickness="0 1 0 0">
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<Button
x:Name="btnCancel"
MinWidth="140"
Margin="10 0 5 0"
Click="BtnCancel_OnClick"
Content="{DynamicResource cancel}" />
<Button
x:Name="btnUpdate"
MinWidth="140"
Margin="5 0 10 0"
Click="btnUpdate_OnClick"
Content="{DynamicResource update}"
Style="{StaticResource AccentButtonStyle}" />
</StackPanel>
</Border>
</Grid>
</Window>

View file

@ -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<PluginUpdateInfo> Plugins { get; set; } = new();
public bool Restart { get; set; }
private readonly Settings _settings = Ioc.Default.GetRequiredService<Settings>();
public PluginUpdateWindow(List<PluginUpdateInfo> 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();
}
}
}

View file

@ -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 @@ public partial class SettingsPanePluginStoreViewModel : BaseModel
[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 = "")