Skip to content

Commit 47089bd

Browse files
authored
Merge branch 'dev' into dev
2 parents a34b8f2 + fa6301b commit 47089bd

File tree

7 files changed

+164
-5
lines changed

7 files changed

+164
-5
lines changed

Flow.Launcher.Core/Plugin/PluginInstaller.cs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.IO;
34
using System.IO.Compression;
45
using System.Linq;
@@ -277,6 +278,100 @@ await DownloadFileAsync(
277278
}
278279
}
279280

281+
/// <summary>
282+
/// Updates the plugin to the latest version available from its source.
283+
/// </summary>
284+
/// <param name="silentUpdate">If true, do not show any messages when there is no update available.</param>
285+
/// <param name="usePrimaryUrlOnly">If true, only use the primary URL for updates.</param>
286+
/// <param name="token">Cancellation token to cancel the update operation.</param>
287+
/// <returns></returns>
288+
public static async Task CheckForPluginUpdatesAsync(bool silentUpdate = true, bool usePrimaryUrlOnly = false, CancellationToken token = default)
289+
{
290+
// Update the plugin manifest
291+
await API.UpdatePluginManifestAsync(usePrimaryUrlOnly, token);
292+
293+
// Get all plugins that can be updated
294+
var resultsForUpdate = (
295+
from existingPlugin in API.GetAllPlugins()
296+
join pluginUpdateSource in API.GetPluginManifest()
297+
on existingPlugin.Metadata.ID equals pluginUpdateSource.ID
298+
where string.Compare(existingPlugin.Metadata.Version, pluginUpdateSource.Version,
299+
StringComparison.InvariantCulture) <
300+
0 // if current version precedes version of the plugin from update source (e.g. PluginsManifest)
301+
&& !API.PluginModified(existingPlugin.Metadata.ID)
302+
select
303+
new PluginUpdateInfo()
304+
{
305+
ID = existingPlugin.Metadata.ID,
306+
Name = existingPlugin.Metadata.Name,
307+
Author = existingPlugin.Metadata.Author,
308+
CurrentVersion = existingPlugin.Metadata.Version,
309+
NewVersion = pluginUpdateSource.Version,
310+
IcoPath = existingPlugin.Metadata.IcoPath,
311+
PluginExistingMetadata = existingPlugin.Metadata,
312+
PluginNewUserPlugin = pluginUpdateSource
313+
}).ToList();
314+
315+
// No updates
316+
if (!resultsForUpdate.Any())
317+
{
318+
if (!silentUpdate)
319+
{
320+
API.ShowMsg(API.GetTranslation("updateNoResultTitle"), API.GetTranslation("updateNoResultSubtitle"));
321+
}
322+
return;
323+
}
324+
325+
// If all plugins are modified, just return
326+
if (resultsForUpdate.All(x => API.PluginModified(x.ID)))
327+
{
328+
return;
329+
}
330+
331+
// Show message box with button to update all plugins
332+
API.ShowMsgWithButton(
333+
API.GetTranslation("updateAllPluginsTitle"),
334+
API.GetTranslation("updateAllPluginsButtonContent"),
335+
() =>
336+
{
337+
UpdateAllPlugins(resultsForUpdate);
338+
},
339+
string.Join(", ", resultsForUpdate.Select(x => x.PluginExistingMetadata.Name)));
340+
}
341+
342+
private static void UpdateAllPlugins(IEnumerable<PluginUpdateInfo> resultsForUpdate)
343+
{
344+
_ = Task.WhenAll(resultsForUpdate.Select(async plugin =>
345+
{
346+
var downloadToFilePath = Path.Combine(Path.GetTempPath(), $"{plugin.Name}-{plugin.NewVersion}.zip");
347+
348+
try
349+
{
350+
using var cts = new CancellationTokenSource();
351+
352+
await DownloadFileAsync(
353+
$"{API.GetTranslation("DownloadingPlugin")} {plugin.PluginNewUserPlugin.Name}",
354+
plugin.PluginNewUserPlugin.UrlDownload, downloadToFilePath, cts);
355+
356+
// check if user cancelled download before installing plugin
357+
if (cts.IsCancellationRequested)
358+
{
359+
return;
360+
}
361+
362+
if (!await API.UpdatePluginAsync(plugin.PluginExistingMetadata, plugin.PluginNewUserPlugin, downloadToFilePath))
363+
{
364+
return;
365+
}
366+
}
367+
catch (Exception e)
368+
{
369+
API.LogException(ClassName, "Failed to update plugin", e);
370+
API.ShowMsgError(API.GetTranslation("ErrorUpdatingPlugin"));
371+
}
372+
}));
373+
}
374+
280375
/// <summary>
281376
/// Downloads a file from a URL to a local path, optionally showing a progress box and handling cancellation.
282377
/// </summary>
@@ -350,4 +445,16 @@ private static bool InstallSourceKnown(string url)
350445
x.Metadata.Website.StartsWith(constructedUrlPart)
351446
);
352447
}
448+
449+
private record PluginUpdateInfo
450+
{
451+
public string ID { get; init; }
452+
public string Name { get; init; }
453+
public string Author { get; init; }
454+
public string CurrentVersion { get; init; }
455+
public string NewVersion { get; init; }
456+
public string IcoPath { get; init; }
457+
public PluginMetadata PluginExistingMetadata { get; init; }
458+
public UserPlugin PluginNewUserPlugin { get; init; }
459+
}
353460
}

Flow.Launcher.Infrastructure/UserSettings/Settings.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ public bool ShowHistoryResultsForHomePage
233233

234234
public bool AutoRestartAfterChanging { get; set; } = false;
235235
public bool ShowUnknownSourceWarning { get; set; } = true;
236+
public bool AutoUpdatePlugins { get; set; } = true;
236237

237238
public int CustomExplorerIndex { get; set; } = 0;
238239

Flow.Launcher/App.xaml.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ await API.StopwatchLogInfoAsync(ClassName, "Startup cost", async () =>
239239

240240
AutoStartup();
241241
AutoUpdates();
242+
AutoPluginUpdates();
242243

243244
API.SaveAppAllSettings();
244245
API.LogInfo(ClassName, "End Flow Launcher startup ----------------------------------------------------");
@@ -251,7 +252,7 @@ await API.StopwatchLogInfoAsync(ClassName, "Startup cost", async () =>
251252
/// Check startup only for Release
252253
/// </summary>
253254
[Conditional("RELEASE")]
254-
private void AutoStartup()
255+
private static void AutoStartup()
255256
{
256257
// we try to enable auto-startup on first launch, or reenable if it was removed
257258
// but the user still has the setting set
@@ -272,7 +273,7 @@ private void AutoStartup()
272273
}
273274

274275
[Conditional("RELEASE")]
275-
private void AutoUpdates()
276+
private static void AutoUpdates()
276277
{
277278
_ = Task.Run(async () =>
278279
{
@@ -289,6 +290,23 @@ private void AutoUpdates()
289290
});
290291
}
291292

293+
private static void AutoPluginUpdates()
294+
{
295+
_ = Task.Run(async () =>
296+
{
297+
if (_settings.AutoUpdatePlugins)
298+
{
299+
// check plugin updates every 5 hour
300+
var timer = new PeriodicTimer(TimeSpan.FromHours(5));
301+
await PluginInstaller.CheckForPluginUpdatesAsync();
302+
303+
while (await timer.WaitForNextTickAsync())
304+
// check updates on startup
305+
await PluginInstaller.CheckForPluginUpdatesAsync();
306+
}
307+
});
308+
}
309+
292310
#endregion
293311

294312
#region Register Events

Flow.Launcher/Languages/en.xaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
<system:String x:Key="typingStartEn">Always Start Typing in English Mode</system:String>
9494
<system:String x:Key="typingStartEnTooltip">Temporarily change your input method to English mode when activating Flow.</system:String>
9595
<system:String x:Key="autoUpdates">Auto Update</system:String>
96+
<system:String x:Key="autoUpdatesTooltip">Automatically check and update the app when available</system:String>
9697
<system:String x:Key="select">Select</system:String>
9798
<system:String x:Key="hideOnStartup">Hide Flow Launcher on startup</system:String>
9899
<system:String x:Key="hideOnStartupToolTip">Flow Launcher search window is hidden in the tray after starting up.</system:String>
@@ -117,7 +118,7 @@
117118
<system:String x:Key="DoublePinyinSchemasXingKongJianDao">Xing Kong Jian Dao</system:String>
118119
<system:String x:Key="DoublePinyinSchemasDaNiu">Da Niu</system:String>
119120
<system:String x:Key="DoublePinyinSchemasXiaoLang">Xiao Lang</system:String>
120-
121+
121122
<system:String x:Key="AlwaysPreview">Always Preview</system:String>
122123
<system:String x:Key="AlwaysPreviewToolTip">Always open preview panel when Flow activates. Press {0} to toggle preview.</system:String>
123124
<system:String x:Key="shadowEffectNotAllowed">Shadow effect is not allowed while current theme has blur effect enabled</system:String>
@@ -150,6 +151,8 @@
150151
<system:String x:Key="autoRestartAfterChangingToolTip">Restart Flow Launcher automatically after installing/uninstalling/updating plugin via Plugin Store</system:String>
151152
<system:String x:Key="showUnknownSourceWarning">Show unknown source warning</system:String>
152153
<system:String x:Key="showUnknownSourceWarningToolTip">Show warning when installing plugins from unknown sources</system:String>
154+
<system:String x:Key="autoUpdatePlugins">Auto update plugins</system:String>
155+
<system:String x:Key="autoUpdatePluginsToolTip">Automatically check plugin updates and notify if there are any updates available</system:String>
153156

154157
<!-- Setting Plugin -->
155158
<system:String x:Key="searchplugin">Search Plugin</system:String>
@@ -231,6 +234,11 @@
231234
<system:String x:Key="ZipFiles">Zip files</system:String>
232235
<system:String x:Key="SelectZipFile">Please select zip file</system:String>
233236
<system:String x:Key="installLocalPluginTooltip">Install plugin from local path</system:String>
237+
<system:String x:Key="updateNoResultTitle">No update available</system:String>
238+
<system:String x:Key="updateNoResultSubtitle">All plugins are up to date</system:String>
239+
<system:String x:Key="updateAllPluginsTitle">Plugin updates available</system:String>
240+
<system:String x:Key="updateAllPluginsButtonContent">Update all plugins</system:String>
241+
<system:String x:Key="checkPluginUpdatesTooltip">Check plugin updates</system:String>
234242

235243
<!-- Setting Theme -->
236244
<system:String x:Key="theme">Theme</system:String>

Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,12 @@ private async Task InstallPluginAsync()
109109
await PluginInstaller.InstallPluginAndCheckRestartAsync(file);
110110
}
111111

112+
[RelayCommand]
113+
private async Task CheckPluginUpdatesAsync()
114+
{
115+
await PluginInstaller.CheckForPluginUpdatesAsync(silentUpdate: false);
116+
}
117+
112118
private static string GetFileFromDialog(string title, string filter = "")
113119
{
114120
var dlg = new Microsoft.Win32.OpenFileDialog

Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,8 @@
182182
<cc:Card
183183
Title="{DynamicResource autoUpdates}"
184184
Margin="0 14 0 0"
185-
Icon="&#xecc5;">
185+
Icon="&#xecc5;"
186+
Sub="{DynamicResource autoUpdatesTooltip}">
186187
<ui:ToggleSwitch
187188
IsOn="{Binding AutoUpdates}"
188189
OffContent="{DynamicResource disable}"
@@ -241,12 +242,23 @@
241242
Title="{DynamicResource showUnknownSourceWarning}"
242243
Icon="&#xE7BA;"
243244
Sub="{DynamicResource showUnknownSourceWarningToolTip}"
244-
Type="Last">
245+
Type="Middle">
245246
<ui:ToggleSwitch
246247
IsOn="{Binding Settings.ShowUnknownSourceWarning}"
247248
OffContent="{DynamicResource disable}"
248249
OnContent="{DynamicResource enable}" />
249250
</cc:Card>
251+
252+
<cc:Card
253+
Title="{DynamicResource autoUpdatePlugins}"
254+
Icon="&#xecc5;"
255+
Sub="{DynamicResource autoUpdatePluginsToolTip}"
256+
Type="Last">
257+
<ui:ToggleSwitch
258+
IsOn="{Binding Settings.AutoUpdatePlugins}"
259+
OffContent="{DynamicResource disable}"
260+
OnContent="{DynamicResource enable}" />
261+
</cc:Card>
250262
</cc:CardGroup>
251263

252264
<cc:ExCard

Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,13 @@
9999
ToolTip="{DynamicResource installLocalPluginTooltip}">
100100
<ui:FontIcon FontSize="14" Glyph="&#xE8DA;" />
101101
</Button>
102+
<Button
103+
Height="34"
104+
Margin="0 0 10 0"
105+
Command="{Binding CheckPluginUpdatesCommand}"
106+
ToolTip="{DynamicResource checkPluginUpdatesTooltip}">
107+
<ui:FontIcon FontSize="14" Glyph="&#xecc5;" />
108+
</Button>
102109
<TextBox
103110
Name="PluginStoreFilterTextbox"
104111
Width="150"

0 commit comments

Comments
 (0)