Skip to content

Commit 562b233

Browse files
committed
Add progress box support for downloading plugin
1 parent 37058f7 commit 562b233

File tree

3 files changed

+239
-2
lines changed

3 files changed

+239
-2
lines changed

Flow.Launcher.Core/ProgressBoxEx.xaml

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<Window
2+
x:Class="Flow.Launcher.Core.ProgressBoxEx"
3+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
4+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
5+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
6+
xmlns:local="clr-namespace:Flow.Launcher.Core"
7+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
8+
x:Name="MessageBoxWindow"
9+
Width="420"
10+
Height="Auto"
11+
Background="{DynamicResource PopuBGColor}"
12+
Foreground="{DynamicResource PopupTextColor}"
13+
ResizeMode="NoResize"
14+
SizeToContent="Height"
15+
WindowStartupLocation="CenterScreen"
16+
mc:Ignorable="d">
17+
<WindowChrome.WindowChrome>
18+
<WindowChrome CaptionHeight="32" ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
19+
</WindowChrome.WindowChrome>
20+
<Window.InputBindings>
21+
<KeyBinding Key="Escape" Command="Close" />
22+
</Window.InputBindings>
23+
<Window.CommandBindings>
24+
<CommandBinding Command="Close" Executed="KeyEsc_OnPress" />
25+
</Window.CommandBindings>
26+
<Grid>
27+
<Grid.RowDefinitions>
28+
<RowDefinition Height="Auto" />
29+
<RowDefinition />
30+
<RowDefinition MinHeight="68" />
31+
</Grid.RowDefinitions>
32+
<StackPanel Grid.Row="0">
33+
<StackPanel>
34+
<Grid>
35+
<Grid.ColumnDefinitions>
36+
<ColumnDefinition Width="*" />
37+
<ColumnDefinition Width="Auto" />
38+
</Grid.ColumnDefinitions>
39+
<Button
40+
Grid.Column="1"
41+
Click="Button_Cancel"
42+
Style="{StaticResource TitleBarCloseButtonStyle}">
43+
<Path
44+
Width="46"
45+
Height="32"
46+
Data="M 18,11 27,20 M 18,20 27,11"
47+
Stroke="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
48+
StrokeThickness="1">
49+
<Path.Style>
50+
<Style TargetType="Path">
51+
<Style.Triggers>
52+
<DataTrigger Binding="{Binding Path=IsActive, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="False">
53+
<Setter Property="Opacity" Value="0.5" />
54+
</DataTrigger>
55+
</Style.Triggers>
56+
</Style>
57+
</Path.Style>
58+
</Path>
59+
</Button>
60+
</Grid>
61+
</StackPanel>
62+
</StackPanel>
63+
<Grid Grid.Row="1" Margin="30 0 30 24">
64+
<Grid.RowDefinitions>
65+
<RowDefinition Height="Auto" />
66+
<RowDefinition Height="Auto" />
67+
</Grid.RowDefinitions>
68+
<TextBlock
69+
x:Name="TitleTextBlock"
70+
Grid.Row="0"
71+
MaxWidth="400"
72+
Margin="0 0 26 12"
73+
VerticalAlignment="Center"
74+
FontFamily="Segoe UI"
75+
FontSize="20"
76+
FontWeight="SemiBold"
77+
TextAlignment="Left"
78+
TextWrapping="Wrap" />
79+
<ProgressBar
80+
x:Name="ProgressBar"
81+
Grid.Row="1"
82+
Margin="0 0 26 0"
83+
Maximum="100"
84+
Minimum="0"
85+
Value="0" />
86+
</Grid>
87+
<Border
88+
Grid.Row="2"
89+
Margin="0 0 0 0"
90+
Background="{DynamicResource PopupButtonAreaBGColor}"
91+
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
92+
BorderThickness="0 1 0 0">
93+
<WrapPanel
94+
HorizontalAlignment="Center"
95+
VerticalAlignment="Center"
96+
Orientation="Horizontal">
97+
<Button
98+
x:Name="btnCancel"
99+
MinWidth="120"
100+
Margin="5 0 5 0"
101+
Click="Button_Click"
102+
Content="{DynamicResource commonCancel}" />
103+
</WrapPanel>
104+
</Border>
105+
</Grid>
106+
</Window>
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using System;
2+
using System.Windows;
3+
using System.Windows.Input;
4+
using Flow.Launcher.Infrastructure.Logger;
5+
6+
namespace Flow.Launcher.Core
7+
{
8+
public partial class ProgressBoxEx : Window
9+
{
10+
private ProgressBoxEx()
11+
{
12+
InitializeComponent();
13+
}
14+
15+
public static ProgressBoxEx Show(string caption)
16+
{
17+
if (!Application.Current.Dispatcher.CheckAccess())
18+
{
19+
return Application.Current.Dispatcher.Invoke(() => Show(caption));
20+
}
21+
22+
try
23+
{
24+
var prgBox = new ProgressBoxEx
25+
{
26+
Title = caption
27+
};
28+
prgBox.TitleTextBlock.Text = caption;
29+
prgBox.Show();
30+
return prgBox;
31+
}
32+
catch (Exception e)
33+
{
34+
Log.Error($"|ProgressBoxEx.Show|An error occurred: {e.Message}");
35+
return null;
36+
}
37+
}
38+
39+
public void ReportProgress(double progress)
40+
{
41+
if (!Application.Current.Dispatcher.CheckAccess())
42+
{
43+
Application.Current.Dispatcher.Invoke(() => ReportProgress(progress));
44+
return;
45+
}
46+
47+
if (progress < 0)
48+
{
49+
ProgressBar.Value = 0;
50+
}
51+
else if (progress >= 100)
52+
{
53+
ProgressBar.Value = 100;
54+
}
55+
else
56+
{
57+
ProgressBar.Value = progress;
58+
}
59+
}
60+
61+
private void KeyEsc_OnPress(object sender, ExecutedRoutedEventArgs e)
62+
{
63+
Close();
64+
}
65+
66+
private void Button_Click(object sender, RoutedEventArgs e)
67+
{
68+
Close();
69+
}
70+
71+
private void Button_Cancel(object sender, RoutedEventArgs e)
72+
{
73+
Close();
74+
}
75+
}
76+
}

Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Flow.Launcher.Core.ExternalPlugins;
1+
using Flow.Launcher.Core;
2+
using Flow.Launcher.Core.ExternalPlugins;
23
using Flow.Launcher.Core.Plugin;
34
using Flow.Launcher.Infrastructure;
45
using Flow.Launcher.Infrastructure.Http;
@@ -142,14 +143,50 @@ internal async Task InstallOrUpdateAsync(UserPlugin plugin)
142143

143144
var filePath = Path.Combine(Path.GetTempPath(), downloadFilename);
144145

146+
ProgressBoxEx prgBox = null;
145147
try
146148
{
147149
if (!plugin.IsFromLocalInstallPath)
148150
{
149151
if (File.Exists(filePath))
150152
File.Delete(filePath);
151153

152-
await Http.DownloadAsync(plugin.UrlDownload, filePath).ConfigureAwait(false);
154+
using var httpClient = new HttpClient();
155+
using var response = await httpClient.GetAsync(plugin.UrlDownload, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
156+
157+
response.EnsureSuccessStatusCode();
158+
159+
var totalBytes = response.Content.Headers.ContentLength ?? -1L;
160+
var canReportProgress = totalBytes != -1;
161+
162+
if (canReportProgress && (prgBox = ProgressBoxEx.Show("Download plugin...")) != null)
163+
{
164+
await using var contentStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
165+
await using var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true);
166+
167+
var buffer = new byte[8192];
168+
long totalRead = 0;
169+
int read;
170+
171+
while ((read = await contentStream.ReadAsync(buffer).ConfigureAwait(false)) > 0)
172+
{
173+
await fileStream.WriteAsync(buffer.AsMemory(0, read)).ConfigureAwait(false);
174+
totalRead += read;
175+
176+
var progressValue = totalRead * 100 / totalBytes;
177+
prgBox.ReportProgress(progressValue);
178+
}
179+
180+
Application.Current.Dispatcher.Invoke(() =>
181+
{
182+
prgBox.Close();
183+
prgBox = null;
184+
});
185+
}
186+
else
187+
{
188+
await Http.DownloadAsync(plugin.UrlDownload, filePath).ConfigureAwait(false);
189+
}
153190
}
154191
else
155192
{
@@ -164,6 +201,15 @@ internal async Task InstallOrUpdateAsync(UserPlugin plugin)
164201
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), plugin.Name),
165202
Context.API.GetTranslation("plugin_pluginsmanager_download_error"));
166203
Log.Exception("PluginsManager", "An error occurred while downloading plugin", e);
204+
// force close progress box
205+
Application.Current.Dispatcher.Invoke(() =>
206+
{
207+
if (prgBox != null)
208+
{
209+
prgBox.Close();
210+
prgBox = null;
211+
}
212+
});
167213
return;
168214
}
169215
catch (Exception e)
@@ -172,6 +218,15 @@ internal async Task InstallOrUpdateAsync(UserPlugin plugin)
172218
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"),
173219
plugin.Name));
174220
Log.Exception("PluginsManager", "An error occurred while downloading plugin", e);
221+
// force close progress box
222+
Application.Current.Dispatcher.Invoke(() =>
223+
{
224+
if (prgBox != null)
225+
{
226+
prgBox.Close();
227+
prgBox = null;
228+
}
229+
});
175230
return;
176231
}
177232

0 commit comments

Comments
 (0)