mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge pull request #3827 from Flow-Launcher/plugin_update
Add Support for Automatically Checking Plugin Updates
This commit is contained in:
commit
fa6301b2de
7 changed files with 164 additions and 5 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
@ -277,6 +278,100 @@ public static class PluginInstaller
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the plugin to the latest version available from its source.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="silentUpdate">If true, do not show any messages when there is no update available.</param>
|
||||||
|
/// <param name="usePrimaryUrlOnly">If true, only use the primary URL for updates.</param>
|
||||||
|
/// <param name="token">Cancellation token to cancel the update operation.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task CheckForPluginUpdatesAsync(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 PluginUpdateInfo()
|
||||||
|
{
|
||||||
|
ID = existingPlugin.Metadata.ID,
|
||||||
|
Name = existingPlugin.Metadata.Name,
|
||||||
|
Author = existingPlugin.Metadata.Author,
|
||||||
|
CurrentVersion = existingPlugin.Metadata.Version,
|
||||||
|
NewVersion = pluginUpdateSource.Version,
|
||||||
|
IcoPath = 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show message box with button to update all plugins
|
||||||
|
API.ShowMsgWithButton(
|
||||||
|
API.GetTranslation("updateAllPluginsTitle"),
|
||||||
|
API.GetTranslation("updateAllPluginsButtonContent"),
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
UpdateAllPlugins(resultsForUpdate);
|
||||||
|
},
|
||||||
|
string.Join(", ", resultsForUpdate.Select(x => x.PluginExistingMetadata.Name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdateAllPlugins(IEnumerable<PluginUpdateInfo> resultsForUpdate)
|
||||||
|
{
|
||||||
|
_ = 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"));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Downloads a file from a URL to a local path, optionally showing a progress box and handling cancellation.
|
/// Downloads a file from a URL to a local path, optionally showing a progress box and handling cancellation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -350,4 +445,16 @@ public static class PluginInstaller
|
||||||
x.Metadata.Website.StartsWith(constructedUrlPart)
|
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; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -233,6 +233,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
||||||
|
|
||||||
public bool AutoRestartAfterChanging { get; set; } = false;
|
public bool AutoRestartAfterChanging { get; set; } = false;
|
||||||
public bool ShowUnknownSourceWarning { get; set; } = true;
|
public bool ShowUnknownSourceWarning { get; set; } = true;
|
||||||
|
public bool AutoUpdatePlugins { get; set; } = true;
|
||||||
|
|
||||||
public int CustomExplorerIndex { get; set; } = 0;
|
public int CustomExplorerIndex { get; set; } = 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -239,6 +239,7 @@ namespace Flow.Launcher
|
||||||
|
|
||||||
AutoStartup();
|
AutoStartup();
|
||||||
AutoUpdates();
|
AutoUpdates();
|
||||||
|
AutoPluginUpdates();
|
||||||
|
|
||||||
API.SaveAppAllSettings();
|
API.SaveAppAllSettings();
|
||||||
API.LogInfo(ClassName, "End Flow Launcher startup ----------------------------------------------------");
|
API.LogInfo(ClassName, "End Flow Launcher startup ----------------------------------------------------");
|
||||||
|
|
@ -251,7 +252,7 @@ namespace Flow.Launcher
|
||||||
/// Check startup only for Release
|
/// Check startup only for Release
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Conditional("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
|
// we try to enable auto-startup on first launch, or reenable if it was removed
|
||||||
// but the user still has the setting set
|
// but the user still has the setting set
|
||||||
|
|
@ -272,7 +273,7 @@ namespace Flow.Launcher
|
||||||
}
|
}
|
||||||
|
|
||||||
[Conditional("RELEASE")]
|
[Conditional("RELEASE")]
|
||||||
private void AutoUpdates()
|
private static void AutoUpdates()
|
||||||
{
|
{
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
|
|
@ -289,6 +290,23 @@ namespace Flow.Launcher
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.CheckForPluginUpdatesAsync();
|
||||||
|
|
||||||
|
while (await timer.WaitForNextTickAsync())
|
||||||
|
// check updates on startup
|
||||||
|
await PluginInstaller.CheckForPluginUpdatesAsync();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Register Events
|
#region Register Events
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,7 @@
|
||||||
<system:String x:Key="typingStartEn">Always Start Typing in English Mode</system:String>
|
<system:String x:Key="typingStartEn">Always Start Typing in English Mode</system:String>
|
||||||
<system:String x:Key="typingStartEnTooltip">Temporarily change your input method to English mode when activating Flow.</system:String>
|
<system:String x:Key="typingStartEnTooltip">Temporarily change your input method to English mode when activating Flow.</system:String>
|
||||||
<system:String x:Key="autoUpdates">Auto Update</system:String>
|
<system:String x:Key="autoUpdates">Auto Update</system:String>
|
||||||
|
<system:String x:Key="autoUpdatesTooltip">Automatically check and update the app when available</system:String>
|
||||||
<system:String x:Key="select">Select</system:String>
|
<system:String x:Key="select">Select</system:String>
|
||||||
<system:String x:Key="hideOnStartup">Hide Flow Launcher on startup</system:String>
|
<system:String x:Key="hideOnStartup">Hide Flow Launcher on startup</system:String>
|
||||||
<system:String x:Key="hideOnStartupToolTip">Flow Launcher search window is hidden in the tray after starting up.</system:String>
|
<system:String x:Key="hideOnStartupToolTip">Flow Launcher search window is hidden in the tray after starting up.</system:String>
|
||||||
|
|
@ -117,7 +118,7 @@
|
||||||
<system:String x:Key="DoublePinyinSchemasXingKongJianDao">Xing Kong Jian Dao</system:String>
|
<system:String x:Key="DoublePinyinSchemasXingKongJianDao">Xing Kong Jian Dao</system:String>
|
||||||
<system:String x:Key="DoublePinyinSchemasDaNiu">Da Niu</system:String>
|
<system:String x:Key="DoublePinyinSchemasDaNiu">Da Niu</system:String>
|
||||||
<system:String x:Key="DoublePinyinSchemasXiaoLang">Xiao Lang</system:String>
|
<system:String x:Key="DoublePinyinSchemasXiaoLang">Xiao Lang</system:String>
|
||||||
|
|
||||||
<system:String x:Key="AlwaysPreview">Always Preview</system:String>
|
<system:String x:Key="AlwaysPreview">Always Preview</system:String>
|
||||||
<system:String x:Key="AlwaysPreviewToolTip">Always open preview panel when Flow activates. Press {0} to toggle preview.</system:String>
|
<system:String x:Key="AlwaysPreviewToolTip">Always open preview panel when Flow activates. Press {0} to toggle preview.</system:String>
|
||||||
<system:String x:Key="shadowEffectNotAllowed">Shadow effect is not allowed while current theme has blur effect enabled</system:String>
|
<system:String x:Key="shadowEffectNotAllowed">Shadow effect is not allowed while current theme has blur effect enabled</system:String>
|
||||||
|
|
@ -150,6 +151,8 @@
|
||||||
<system:String x:Key="autoRestartAfterChangingToolTip">Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugin Store</system:String>
|
<system:String x:Key="autoRestartAfterChangingToolTip">Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugin Store</system:String>
|
||||||
<system:String x:Key="showUnknownSourceWarning">Show unknown source warning</system:String>
|
<system:String x:Key="showUnknownSourceWarning">Show unknown source warning</system:String>
|
||||||
<system:String x:Key="showUnknownSourceWarningToolTip">Show warning when installing plugins from unknown sources</system:String>
|
<system:String x:Key="showUnknownSourceWarningToolTip">Show warning when installing plugins from unknown sources</system:String>
|
||||||
|
<system:String x:Key="autoUpdatePlugins">Auto update plugins</system:String>
|
||||||
|
<system:String x:Key="autoUpdatePluginsToolTip">Automatically check plugin updates and notify if there are any updates available</system:String>
|
||||||
|
|
||||||
<!-- Setting Plugin -->
|
<!-- Setting Plugin -->
|
||||||
<system:String x:Key="searchplugin">Search Plugin</system:String>
|
<system:String x:Key="searchplugin">Search Plugin</system:String>
|
||||||
|
|
@ -231,6 +234,11 @@
|
||||||
<system:String x:Key="ZipFiles">Zip files</system:String>
|
<system:String x:Key="ZipFiles">Zip files</system:String>
|
||||||
<system:String x:Key="SelectZipFile">Please select zip file</system:String>
|
<system:String x:Key="SelectZipFile">Please select zip file</system:String>
|
||||||
<system:String x:Key="installLocalPluginTooltip">Install plugin from local path</system:String>
|
<system:String x:Key="installLocalPluginTooltip">Install plugin from local path</system:String>
|
||||||
|
<system:String x:Key="updateNoResultTitle">No update available</system:String>
|
||||||
|
<system:String x:Key="updateNoResultSubtitle">All plugins are up to date</system:String>
|
||||||
|
<system:String x:Key="updateAllPluginsTitle">Plugin updates available</system:String>
|
||||||
|
<system:String x:Key="updateAllPluginsButtonContent">Update all plugins</system:String>
|
||||||
|
<system:String x:Key="checkPluginUpdatesTooltip">Check plugin updates</system:String>
|
||||||
|
|
||||||
<!-- Setting Theme -->
|
<!-- Setting Theme -->
|
||||||
<system:String x:Key="theme">Theme</system:String>
|
<system:String x:Key="theme">Theme</system:String>
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,12 @@ public partial class SettingsPanePluginStoreViewModel : BaseModel
|
||||||
await PluginInstaller.InstallPluginAndCheckRestartAsync(file);
|
await PluginInstaller.InstallPluginAndCheckRestartAsync(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private async Task CheckPluginUpdatesAsync()
|
||||||
|
{
|
||||||
|
await PluginInstaller.CheckForPluginUpdatesAsync(silentUpdate: false);
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetFileFromDialog(string title, string filter = "")
|
private static string GetFileFromDialog(string title, string filter = "")
|
||||||
{
|
{
|
||||||
var dlg = new Microsoft.Win32.OpenFileDialog
|
var dlg = new Microsoft.Win32.OpenFileDialog
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,8 @@
|
||||||
<cc:Card
|
<cc:Card
|
||||||
Title="{DynamicResource autoUpdates}"
|
Title="{DynamicResource autoUpdates}"
|
||||||
Margin="0 14 0 0"
|
Margin="0 14 0 0"
|
||||||
Icon="">
|
Icon=""
|
||||||
|
Sub="{DynamicResource autoUpdatesTooltip}">
|
||||||
<ui:ToggleSwitch
|
<ui:ToggleSwitch
|
||||||
IsOn="{Binding AutoUpdates}"
|
IsOn="{Binding AutoUpdates}"
|
||||||
OffContent="{DynamicResource disable}"
|
OffContent="{DynamicResource disable}"
|
||||||
|
|
@ -241,12 +242,23 @@
|
||||||
Title="{DynamicResource showUnknownSourceWarning}"
|
Title="{DynamicResource showUnknownSourceWarning}"
|
||||||
Icon=""
|
Icon=""
|
||||||
Sub="{DynamicResource showUnknownSourceWarningToolTip}"
|
Sub="{DynamicResource showUnknownSourceWarningToolTip}"
|
||||||
Type="Last">
|
Type="Middle">
|
||||||
<ui:ToggleSwitch
|
<ui:ToggleSwitch
|
||||||
IsOn="{Binding Settings.ShowUnknownSourceWarning}"
|
IsOn="{Binding Settings.ShowUnknownSourceWarning}"
|
||||||
OffContent="{DynamicResource disable}"
|
OffContent="{DynamicResource disable}"
|
||||||
OnContent="{DynamicResource enable}" />
|
OnContent="{DynamicResource enable}" />
|
||||||
</cc:Card>
|
</cc:Card>
|
||||||
|
|
||||||
|
<cc:Card
|
||||||
|
Title="{DynamicResource autoUpdatePlugins}"
|
||||||
|
Icon=""
|
||||||
|
Sub="{DynamicResource autoUpdatePluginsToolTip}"
|
||||||
|
Type="Last">
|
||||||
|
<ui:ToggleSwitch
|
||||||
|
IsOn="{Binding Settings.AutoUpdatePlugins}"
|
||||||
|
OffContent="{DynamicResource disable}"
|
||||||
|
OnContent="{DynamicResource enable}" />
|
||||||
|
</cc:Card>
|
||||||
</cc:CardGroup>
|
</cc:CardGroup>
|
||||||
|
|
||||||
<cc:ExCard
|
<cc:ExCard
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,13 @@
|
||||||
ToolTip="{DynamicResource installLocalPluginTooltip}">
|
ToolTip="{DynamicResource installLocalPluginTooltip}">
|
||||||
<ui:FontIcon FontSize="14" Glyph="" />
|
<ui:FontIcon FontSize="14" Glyph="" />
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
Height="34"
|
||||||
|
Margin="0 0 10 0"
|
||||||
|
Command="{Binding CheckPluginUpdatesCommand}"
|
||||||
|
ToolTip="{DynamicResource checkPluginUpdatesTooltip}">
|
||||||
|
<ui:FontIcon FontSize="14" Glyph="" />
|
||||||
|
</Button>
|
||||||
<TextBox
|
<TextBox
|
||||||
Name="PluginStoreFilterTextbox"
|
Name="PluginStoreFilterTextbox"
|
||||||
Width="150"
|
Width="150"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue