mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge pull request #3170 from Jack251970/dev3
Add New API for ProgressBoxEx to Show Progress & Add Progress Display for Plugin Downloading & Improve DownloadUrl Api Function
This commit is contained in:
commit
741ddfeafe
8 changed files with 382 additions and 59 deletions
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
@ -121,10 +120,10 @@ namespace Flow.Launcher.Core.Plugin.JsonRPCV2Models
|
||||||
return _api.HttpGetStreamAsync(url, token);
|
return _api.HttpGetStreamAsync(url, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath,
|
public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, Action<double> reportProgress = null,
|
||||||
CancellationToken token = default)
|
CancellationToken token = default)
|
||||||
{
|
{
|
||||||
return _api.HttpDownloadAsync(url, filePath, token);
|
return _api.HttpDownloadAsync(url, filePath, reportProgress, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddActionKeyword(string pluginId, string newActionKeyword)
|
public void AddActionKeyword(string pluginId, string newActionKeyword)
|
||||||
|
|
@ -162,13 +161,11 @@ namespace Flow.Launcher.Core.Plugin.JsonRPCV2Models
|
||||||
_api.OpenDirectory(DirectoryPath, FileNameOrFilePath);
|
_api.OpenDirectory(DirectoryPath, FileNameOrFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void OpenUrl(string url, bool? inPrivate = null)
|
public void OpenUrl(string url, bool? inPrivate = null)
|
||||||
{
|
{
|
||||||
_api.OpenUrl(url, inPrivate);
|
_api.OpenUrl(url, inPrivate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void OpenAppUri(string appUri)
|
public void OpenAppUri(string appUri)
|
||||||
{
|
{
|
||||||
_api.OpenAppUri(appUri);
|
_api.OpenAppUri(appUri);
|
||||||
|
|
|
||||||
|
|
@ -83,15 +83,50 @@ namespace Flow.Launcher.Infrastructure.Http
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task DownloadAsync([NotNull] string url, [NotNull] string filePath, CancellationToken token = default)
|
public static async Task DownloadAsync([NotNull] string url, [NotNull] string filePath, Action<double> reportProgress = null, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
|
using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
|
||||||
|
|
||||||
if (response.StatusCode == HttpStatusCode.OK)
|
if (response.StatusCode == HttpStatusCode.OK)
|
||||||
{
|
{
|
||||||
await using var fileStream = new FileStream(filePath, FileMode.CreateNew);
|
var totalBytes = response.Content.Headers.ContentLength ?? -1L;
|
||||||
await response.Content.CopyToAsync(fileStream, token);
|
var canReportProgress = totalBytes != -1;
|
||||||
|
|
||||||
|
if (canReportProgress && reportProgress != null)
|
||||||
|
{
|
||||||
|
await using var contentStream = await response.Content.ReadAsStreamAsync(token);
|
||||||
|
await using var fileStream = new FileStream(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, 8192, true);
|
||||||
|
|
||||||
|
var buffer = new byte[8192];
|
||||||
|
long totalRead = 0;
|
||||||
|
int read;
|
||||||
|
double progressValue = 0;
|
||||||
|
|
||||||
|
reportProgress(0);
|
||||||
|
|
||||||
|
while ((read = await contentStream.ReadAsync(buffer, token)) > 0)
|
||||||
|
{
|
||||||
|
await fileStream.WriteAsync(buffer.AsMemory(0, read), token);
|
||||||
|
totalRead += read;
|
||||||
|
|
||||||
|
progressValue = totalRead * 100.0 / totalBytes;
|
||||||
|
|
||||||
|
if (token.IsCancellationRequested)
|
||||||
|
return;
|
||||||
|
else
|
||||||
|
reportProgress(progressValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progressValue < 100)
|
||||||
|
reportProgress(100);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await using var fileStream = new FileStream(filePath, FileMode.CreateNew);
|
||||||
|
await response.Content.CopyToAsync(fileStream, token);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Flow.Launcher.Plugin.SharedModels;
|
using Flow.Launcher.Plugin.SharedModels;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
@ -181,9 +181,13 @@ namespace Flow.Launcher.Plugin
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="url">URL to download file</param>
|
/// <param name="url">URL to download file</param>
|
||||||
/// <param name="filePath">path to save downloaded file</param>
|
/// <param name="filePath">path to save downloaded file</param>
|
||||||
|
/// <param name="reportProgress">
|
||||||
|
/// Action to report progress. The input of the action is the progress value which is a double value between 0 and 100.
|
||||||
|
/// It will be called if url support range request and the reportProgress is not null.
|
||||||
|
/// </param>
|
||||||
/// <param name="token">place to store file</param>
|
/// <param name="token">place to store file</param>
|
||||||
/// <returns>Task showing the progress</returns>
|
/// <returns>Task showing the progress</returns>
|
||||||
Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, CancellationToken token = default);
|
Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, Action<double> reportProgress = null, CancellationToken token = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add ActionKeyword for specific plugin
|
/// Add ActionKeyword for specific plugin
|
||||||
|
|
@ -316,5 +320,19 @@ namespace Flow.Launcher.Plugin
|
||||||
/// <param name="defaultResult">Specifies the default result of the message box.</param>
|
/// <param name="defaultResult">Specifies the default result of the message box.</param>
|
||||||
/// <returns>Specifies which message box button is clicked by the user.</returns>
|
/// <returns>Specifies which message box button is clicked by the user.</returns>
|
||||||
public MessageBoxResult ShowMsgBox(string messageBoxText, string caption = "", MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.OK);
|
public MessageBoxResult ShowMsgBox(string messageBoxText, string caption = "", MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.OK);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Displays a standardised Flow message box.
|
||||||
|
/// If there is issue when showing the message box, it will return null.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caption">The caption of the message box.</param>
|
||||||
|
/// <param name="reportProgressAsync">
|
||||||
|
/// Time-consuming task function, whose input is the action to report progress.
|
||||||
|
/// The input of the action is the progress value which is a double value between 0 and 100.
|
||||||
|
/// If there are any exceptions, this action will be null.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="forceClosed">When user closes the progress box manually by button or esc key, this action will be called.</param>
|
||||||
|
/// <returns>A progress box interface.</returns>
|
||||||
|
public Task ShowProgressBoxAsync(string caption, Func<Action<double>, Task> reportProgressAsync, Action forceClosed = null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
106
Flow.Launcher/ProgressBoxEx.xaml
Normal file
106
Flow.Launcher/ProgressBoxEx.xaml
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
<Window
|
||||||
|
x:Class="Flow.Launcher.ProgressBoxEx"
|
||||||
|
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:local="clr-namespace:Flow.Launcher"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
x:Name="MessageBoxWindow"
|
||||||
|
Width="420"
|
||||||
|
Height="Auto"
|
||||||
|
Background="{DynamicResource PopuBGColor}"
|
||||||
|
Foreground="{DynamicResource PopupTextColor}"
|
||||||
|
ResizeMode="NoResize"
|
||||||
|
SizeToContent="Height"
|
||||||
|
WindowStartupLocation="CenterScreen"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
<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="KeyEsc_OnPress" />
|
||||||
|
</Window.CommandBindings>
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition />
|
||||||
|
<RowDefinition MinHeight="68" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<StackPanel Grid.Row="0">
|
||||||
|
<StackPanel>
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Button
|
||||||
|
Grid.Column="1"
|
||||||
|
Click="Button_Cancel"
|
||||||
|
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>
|
||||||
|
<Grid Grid.Row="1" Margin="30 0 30 24">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<TextBlock
|
||||||
|
x:Name="TitleTextBlock"
|
||||||
|
Grid.Row="0"
|
||||||
|
MaxWidth="400"
|
||||||
|
Margin="0 0 26 12"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontFamily="Segoe UI"
|
||||||
|
FontSize="20"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
TextAlignment="Left"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<ProgressBar
|
||||||
|
x:Name="ProgressBar"
|
||||||
|
Grid.Row="1"
|
||||||
|
Margin="0 0 26 0"
|
||||||
|
Maximum="100"
|
||||||
|
Minimum="0"
|
||||||
|
Value="0" />
|
||||||
|
</Grid>
|
||||||
|
<Border
|
||||||
|
Grid.Row="2"
|
||||||
|
Margin="0 0 0 0"
|
||||||
|
Background="{DynamicResource PopupButtonAreaBGColor}"
|
||||||
|
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
|
||||||
|
BorderThickness="0 1 0 0">
|
||||||
|
<WrapPanel
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<Button
|
||||||
|
x:Name="btnCancel"
|
||||||
|
MinWidth="120"
|
||||||
|
Margin="5 0 5 0"
|
||||||
|
Click="Button_Click"
|
||||||
|
Content="{DynamicResource commonCancel}" />
|
||||||
|
</WrapPanel>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
114
Flow.Launcher/ProgressBoxEx.xaml.cs
Normal file
114
Flow.Launcher/ProgressBoxEx.xaml.cs
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using Flow.Launcher.Infrastructure.Logger;
|
||||||
|
|
||||||
|
namespace Flow.Launcher
|
||||||
|
{
|
||||||
|
public partial class ProgressBoxEx : Window
|
||||||
|
{
|
||||||
|
private readonly Action _forceClosed;
|
||||||
|
|
||||||
|
private ProgressBoxEx(Action forceClosed)
|
||||||
|
{
|
||||||
|
_forceClosed = forceClosed;
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task ShowAsync(string caption, Func<Action<double>, Task> reportProgressAsync, Action forceClosed = null)
|
||||||
|
{
|
||||||
|
ProgressBoxEx prgBox = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!Application.Current.Dispatcher.CheckAccess())
|
||||||
|
{
|
||||||
|
await Application.Current.Dispatcher.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
prgBox = new ProgressBoxEx(forceClosed)
|
||||||
|
{
|
||||||
|
Title = caption
|
||||||
|
};
|
||||||
|
prgBox.TitleTextBlock.Text = caption;
|
||||||
|
prgBox.Show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prgBox = new ProgressBoxEx(forceClosed)
|
||||||
|
{
|
||||||
|
Title = caption
|
||||||
|
};
|
||||||
|
prgBox.TitleTextBlock.Text = caption;
|
||||||
|
prgBox.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
await reportProgressAsync(prgBox.ReportProgress).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error($"|ProgressBoxEx.Show|An error occurred: {e.Message}");
|
||||||
|
|
||||||
|
await reportProgressAsync(null).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (!Application.Current.Dispatcher.CheckAccess())
|
||||||
|
{
|
||||||
|
await Application.Current.Dispatcher.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
prgBox?.Close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prgBox?.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReportProgress(double progress)
|
||||||
|
{
|
||||||
|
if (!Application.Current.Dispatcher.CheckAccess())
|
||||||
|
{
|
||||||
|
Application.Current.Dispatcher.Invoke(() => ReportProgress(progress));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progress < 0)
|
||||||
|
{
|
||||||
|
ProgressBar.Value = 0;
|
||||||
|
}
|
||||||
|
else if (progress >= 100)
|
||||||
|
{
|
||||||
|
ProgressBar.Value = 100;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ProgressBar.Value = progress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void KeyEsc_OnPress(object sender, ExecutedRoutedEventArgs e)
|
||||||
|
{
|
||||||
|
ForceClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Button_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
ForceClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Button_Cancel(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
ForceClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ForceClose()
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
_forceClosed?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -164,8 +164,8 @@ namespace Flow.Launcher
|
||||||
public Task<Stream> HttpGetStreamAsync(string url, CancellationToken token = default) =>
|
public Task<Stream> HttpGetStreamAsync(string url, CancellationToken token = default) =>
|
||||||
Http.GetStreamAsync(url);
|
Http.GetStreamAsync(url);
|
||||||
|
|
||||||
public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath,
|
public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, Action<double> reportProgress = null,
|
||||||
CancellationToken token = default) => Http.DownloadAsync(url, filePath, token);
|
CancellationToken token = default) => Http.DownloadAsync(url, filePath, reportProgress, token);
|
||||||
|
|
||||||
public void AddActionKeyword(string pluginId, string newActionKeyword) =>
|
public void AddActionKeyword(string pluginId, string newActionKeyword) =>
|
||||||
PluginManager.AddActionKeyword(pluginId, newActionKeyword);
|
PluginManager.AddActionKeyword(pluginId, newActionKeyword);
|
||||||
|
|
@ -324,6 +324,8 @@ namespace Flow.Launcher
|
||||||
public MessageBoxResult ShowMsgBox(string messageBoxText, string caption = "", MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.OK) =>
|
public MessageBoxResult ShowMsgBox(string messageBoxText, string caption = "", MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.OK) =>
|
||||||
MessageBoxEx.Show(messageBoxText, caption, button, icon, defaultResult);
|
MessageBoxEx.Show(messageBoxText, caption, button, icon, defaultResult);
|
||||||
|
|
||||||
|
public Task ShowProgressBoxAsync(string caption, Func<Action<double>, Task> reportProgressAsync, Action forceClosed = null) => ProgressBoxEx.ShowAsync(caption, reportProgressAsync, forceClosed);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Private Methods
|
#region Private Methods
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
||||||
return query.FirstSearch.ToLower() switch
|
return query.FirstSearch.ToLower() switch
|
||||||
{
|
{
|
||||||
//search could be url, no need ToLower() when passed in
|
//search could be url, no need ToLower() when passed in
|
||||||
Settings.InstallCommand => await pluginManager.RequestInstallOrUpdate(query.SecondToEndSearch, token, query.IsReQuery),
|
Settings.InstallCommand => await pluginManager.RequestInstallOrUpdateAsync(query.SecondToEndSearch, token, query.IsReQuery),
|
||||||
Settings.UninstallCommand => pluginManager.RequestUninstall(query.SecondToEndSearch),
|
Settings.UninstallCommand => pluginManager.RequestUninstall(query.SecondToEndSearch),
|
||||||
Settings.UpdateCommand => await pluginManager.RequestUpdateAsync(query.SecondToEndSearch, token, query.IsReQuery),
|
Settings.UpdateCommand => await pluginManager.RequestUpdateAsync(query.SecondToEndSearch, token, query.IsReQuery),
|
||||||
_ => pluginManager.GetDefaultHotKeys().Where(hotkey =>
|
_ => pluginManager.GetDefaultHotKeys().Where(hotkey =>
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
||||||
{
|
{
|
||||||
internal class PluginsManager
|
internal class PluginsManager
|
||||||
{
|
{
|
||||||
const string zip = "zip";
|
private const string zip = "zip";
|
||||||
|
|
||||||
private PluginInitContext Context { get; set; }
|
private PluginInitContext Context { get; set; }
|
||||||
|
|
||||||
|
|
@ -144,34 +144,43 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
using var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
if (!plugin.IsFromLocalInstallPath)
|
if (!plugin.IsFromLocalInstallPath)
|
||||||
{
|
{
|
||||||
if (File.Exists(filePath))
|
await DownloadFileAsync(
|
||||||
File.Delete(filePath);
|
$"{Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin")} {plugin.Name}",
|
||||||
|
plugin.UrlDownload, filePath, cts);
|
||||||
await Http.DownloadAsync(plugin.UrlDownload, filePath).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
filePath = plugin.LocalInstallPath;
|
filePath = plugin.LocalInstallPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
Install(plugin, filePath);
|
// check if user cancelled download before installing plugin
|
||||||
|
if (cts.IsCancellationRequested)
|
||||||
|
return;
|
||||||
|
else
|
||||||
|
Install(plugin, filePath);
|
||||||
}
|
}
|
||||||
catch (HttpRequestException e)
|
catch (HttpRequestException e)
|
||||||
{
|
{
|
||||||
|
// show error message
|
||||||
Context.API.ShowMsgError(
|
Context.API.ShowMsgError(
|
||||||
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), plugin.Name),
|
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), plugin.Name),
|
||||||
Context.API.GetTranslation("plugin_pluginsmanager_download_error"));
|
Context.API.GetTranslation("plugin_pluginsmanager_download_error"));
|
||||||
Log.Exception("PluginsManager", "An error occurred while downloading plugin", e);
|
Log.Exception("PluginsManager", "An error occurred while downloading plugin", e);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
// show error message
|
||||||
Context.API.ShowMsgError(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"),
|
Context.API.ShowMsgError(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"),
|
||||||
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"),
|
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"),
|
||||||
plugin.Name));
|
plugin.Name));
|
||||||
Log.Exception("PluginsManager", "An error occurred while downloading plugin", e);
|
Log.Exception("PluginsManager", "An error occurred while downloading plugin", e);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,6 +199,41 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task DownloadFileAsync(string prgBoxTitle, string downloadUrl, string filePath, CancellationTokenSource cts, bool deleteFile = true, bool showProgress = true)
|
||||||
|
{
|
||||||
|
if (deleteFile && File.Exists(filePath))
|
||||||
|
File.Delete(filePath);
|
||||||
|
|
||||||
|
if (showProgress)
|
||||||
|
{
|
||||||
|
var exceptionHappened = false;
|
||||||
|
await Context.API.ShowProgressBoxAsync(prgBoxTitle,
|
||||||
|
async (reportProgress) =>
|
||||||
|
{
|
||||||
|
if (reportProgress == null)
|
||||||
|
{
|
||||||
|
// when reportProgress is null, it means there is expcetion with the progress box
|
||||||
|
// so we record it with exceptionHappened and return so that progress box will close instantly
|
||||||
|
exceptionHappened = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await Context.API.HttpDownloadAsync(downloadUrl, filePath, reportProgress, cts.Token).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}, cts.Cancel);
|
||||||
|
|
||||||
|
// if exception happened while downloading and user does not cancel downloading,
|
||||||
|
// we need to redownload the plugin
|
||||||
|
if (exceptionHappened && (!cts.IsCancellationRequested))
|
||||||
|
await Context.API.HttpDownloadAsync(downloadUrl, filePath, token: cts.Token).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await Context.API.HttpDownloadAsync(downloadUrl, filePath, token: cts.Token).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal async ValueTask<List<Result>> RequestUpdateAsync(string search, CancellationToken token,
|
internal async ValueTask<List<Result>> RequestUpdateAsync(string search, CancellationToken token,
|
||||||
bool usePrimaryUrlOnly = false)
|
bool usePrimaryUrlOnly = false)
|
||||||
{
|
{
|
||||||
|
|
@ -277,43 +321,48 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
||||||
|
|
||||||
_ = Task.Run(async delegate
|
_ = Task.Run(async delegate
|
||||||
{
|
{
|
||||||
|
using var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
if (!x.PluginNewUserPlugin.IsFromLocalInstallPath)
|
if (!x.PluginNewUserPlugin.IsFromLocalInstallPath)
|
||||||
{
|
{
|
||||||
if (File.Exists(downloadToFilePath))
|
await DownloadFileAsync(
|
||||||
{
|
$"{Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin")} {x.PluginNewUserPlugin.Name}",
|
||||||
File.Delete(downloadToFilePath);
|
x.PluginNewUserPlugin.UrlDownload, downloadToFilePath, cts);
|
||||||
}
|
|
||||||
|
|
||||||
await Http.DownloadAsync(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
downloadToFilePath = x.PluginNewUserPlugin.LocalInstallPath;
|
downloadToFilePath = x.PluginNewUserPlugin.LocalInstallPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if user cancelled download before installing plugin
|
||||||
PluginManager.UpdatePlugin(x.PluginExistingMetadata, x.PluginNewUserPlugin,
|
if (cts.IsCancellationRequested)
|
||||||
downloadToFilePath);
|
|
||||||
|
|
||||||
if (Settings.AutoRestartAfterChanging)
|
|
||||||
{
|
{
|
||||||
Context.API.ShowMsg(
|
return;
|
||||||
Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
|
|
||||||
string.Format(
|
|
||||||
Context.API.GetTranslation(
|
|
||||||
"plugin_pluginsmanager_update_success_restart"),
|
|
||||||
x.Name));
|
|
||||||
Context.API.RestartApp();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Context.API.ShowMsg(
|
PluginManager.UpdatePlugin(x.PluginExistingMetadata, x.PluginNewUserPlugin,
|
||||||
Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
|
downloadToFilePath);
|
||||||
string.Format(
|
|
||||||
Context.API.GetTranslation(
|
if (Settings.AutoRestartAfterChanging)
|
||||||
"plugin_pluginsmanager_update_success_no_restart"),
|
{
|
||||||
x.Name));
|
Context.API.ShowMsg(
|
||||||
|
Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
|
||||||
|
string.Format(
|
||||||
|
Context.API.GetTranslation(
|
||||||
|
"plugin_pluginsmanager_update_success_restart"),
|
||||||
|
x.Name));
|
||||||
|
Context.API.RestartApp();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Context.API.ShowMsg(
|
||||||
|
Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
|
||||||
|
string.Format(
|
||||||
|
Context.API.GetTranslation(
|
||||||
|
"plugin_pluginsmanager_update_success_no_restart"),
|
||||||
|
x.Name));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}).ContinueWith(t =>
|
}).ContinueWith(t =>
|
||||||
{
|
{
|
||||||
|
|
@ -324,7 +373,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
||||||
string.Format(
|
string.Format(
|
||||||
Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"),
|
Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"),
|
||||||
x.Name));
|
x.Name));
|
||||||
}, TaskContinuationOptions.OnlyOnFaulted);
|
}, token, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
@ -337,7 +386,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update all result
|
// Update all result
|
||||||
if (resultsForUpdate.Count() > 1)
|
if (resultsForUpdate.Count > 1)
|
||||||
{
|
{
|
||||||
var updateAllResult = new Result
|
var updateAllResult = new Result
|
||||||
{
|
{
|
||||||
|
|
@ -351,13 +400,13 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
||||||
{
|
{
|
||||||
message = string.Format(
|
message = string.Format(
|
||||||
Context.API.GetTranslation("plugin_pluginsmanager_update_all_prompt"),
|
Context.API.GetTranslation("plugin_pluginsmanager_update_all_prompt"),
|
||||||
resultsForUpdate.Count(), Environment.NewLine);
|
resultsForUpdate.Count, Environment.NewLine);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
message = string.Format(
|
message = string.Format(
|
||||||
Context.API.GetTranslation("plugin_pluginsmanager_update_all_prompt_no_restart"),
|
Context.API.GetTranslation("plugin_pluginsmanager_update_all_prompt_no_restart"),
|
||||||
resultsForUpdate.Count());
|
resultsForUpdate.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Context.API.ShowMsgBox(message,
|
if (Context.API.ShowMsgBox(message,
|
||||||
|
|
@ -374,16 +423,18 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (File.Exists(downloadToFilePath))
|
using var cts = new CancellationTokenSource();
|
||||||
{
|
|
||||||
File.Delete(downloadToFilePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
await Http.DownloadAsync(plugin.PluginNewUserPlugin.UrlDownload, downloadToFilePath)
|
await DownloadFileAsync(
|
||||||
.ConfigureAwait(false);
|
$"{Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin")} {plugin.PluginNewUserPlugin.Name}",
|
||||||
|
plugin.PluginNewUserPlugin.UrlDownload, downloadToFilePath, cts);
|
||||||
|
|
||||||
PluginManager.UpdatePlugin(plugin.PluginExistingMetadata, plugin.PluginNewUserPlugin,
|
// check if user cancelled download before installing plugin
|
||||||
downloadToFilePath);
|
if (cts.IsCancellationRequested)
|
||||||
|
return;
|
||||||
|
else
|
||||||
|
PluginManager.UpdatePlugin(plugin.PluginExistingMetadata, plugin.PluginNewUserPlugin,
|
||||||
|
downloadToFilePath);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -401,7 +452,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
||||||
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
|
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
|
||||||
string.Format(
|
string.Format(
|
||||||
Context.API.GetTranslation("plugin_pluginsmanager_update_all_success_restart"),
|
Context.API.GetTranslation("plugin_pluginsmanager_update_all_success_restart"),
|
||||||
resultsForUpdate.Count()));
|
resultsForUpdate.Count));
|
||||||
Context.API.RestartApp();
|
Context.API.RestartApp();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -409,7 +460,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
||||||
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
|
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
|
||||||
string.Format(
|
string.Format(
|
||||||
Context.API.GetTranslation("plugin_pluginsmanager_update_all_success_no_restart"),
|
Context.API.GetTranslation("plugin_pluginsmanager_update_all_success_no_restart"),
|
||||||
resultsForUpdate.Count()));
|
resultsForUpdate.Count));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -545,7 +596,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
||||||
Context.API.GetAllPlugins().Any(x => x.Metadata.Website.StartsWith(constructedUrlPart));
|
Context.API.GetAllPlugins().Any(x => x.Metadata.Website.StartsWith(constructedUrlPart));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async ValueTask<List<Result>> RequestInstallOrUpdate(string search, CancellationToken token,
|
internal async ValueTask<List<Result>> RequestInstallOrUpdateAsync(string search, CancellationToken token,
|
||||||
bool usePrimaryUrlOnly = false)
|
bool usePrimaryUrlOnly = false)
|
||||||
{
|
{
|
||||||
await PluginsManifest.UpdateManifestAsync(token, usePrimaryUrlOnly);
|
await PluginsManifest.UpdateManifestAsync(token, usePrimaryUrlOnly);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue