Skip to content

Commit b9048f5

Browse files
authored
Merge pull request #972 from Flow-Launcher/refactor_plugin_manifest
Improvements to PluginManager plugin
2 parents 45f691c + deb4bee commit b9048f5

File tree

7 files changed

+71
-149
lines changed

7 files changed

+71
-149
lines changed

Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
using Flow.Launcher.Infrastructure.Logger;
33
using System;
44
using System.Collections.Generic;
5+
using System.Net;
6+
using System.Net.Http;
57
using System.Text.Json;
68
using System.Threading;
79
using System.Threading.Tasks;
@@ -10,43 +12,43 @@ namespace Flow.Launcher.Core.ExternalPlugins
1012
{
1113
public static class PluginsManifest
1214
{
13-
static PluginsManifest()
14-
{
15-
UpdateTask = UpdateManifestAsync();
16-
}
17-
18-
public static List<UserPlugin> UserPlugins { get; private set; } = new List<UserPlugin>();
19-
20-
public static Task UpdateTask { get; private set; }
15+
private const string manifestFileUrl = "https://cdn.jsdelivr.net/gh/Flow-Launcher/Flow.Launcher.PluginsManifest@plugin_api_v2/plugins.json";
2116

2217
private static readonly SemaphoreSlim manifestUpdateLock = new(1);
2318

24-
public static Task UpdateManifestAsync()
25-
{
26-
if (manifestUpdateLock.CurrentCount == 0)
27-
{
28-
return UpdateTask;
29-
}
19+
private static string latestEtag = "";
3020

31-
return UpdateTask = DownloadManifestAsync();
32-
}
21+
public static List<UserPlugin> UserPlugins { get; private set; } = new List<UserPlugin>();
3322

34-
private static async Task DownloadManifestAsync()
23+
public static async Task UpdateManifestAsync(CancellationToken token = default)
3524
{
3625
try
3726
{
38-
await manifestUpdateLock.WaitAsync().ConfigureAwait(false);
27+
await manifestUpdateLock.WaitAsync(token).ConfigureAwait(false);
28+
29+
var request = new HttpRequestMessage(HttpMethod.Get, manifestFileUrl);
30+
request.Headers.Add("If-None-Match", latestEtag);
31+
32+
var response = await Http.SendAsync(request, token).ConfigureAwait(false);
3933

40-
await using var jsonStream = await Http.GetStreamAsync("https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher.PluginsManifest/plugin_api_v2/plugins.json")
41-
.ConfigureAwait(false);
34+
if (response.StatusCode == HttpStatusCode.OK)
35+
{
36+
Log.Info($"|PluginsManifest.{nameof(UpdateManifestAsync)}|Fetched plugins from manifest repo");
4237

43-
UserPlugins = await JsonSerializer.DeserializeAsync<List<UserPlugin>>(jsonStream).ConfigureAwait(false);
38+
var json = await response.Content.ReadAsStreamAsync(token).ConfigureAwait(false);
39+
40+
UserPlugins = await JsonSerializer.DeserializeAsync<List<UserPlugin>>(json, cancellationToken: token).ConfigureAwait(false);
41+
42+
latestEtag = response.Headers.ETag.Tag;
43+
}
44+
else if (response.StatusCode != HttpStatusCode.NotModified)
45+
{
46+
Log.Warn($"|PluginsManifest.{nameof(UpdateManifestAsync)}|Http response for manifest file was {response.StatusCode}");
47+
}
4448
}
4549
catch (Exception e)
4650
{
47-
Log.Exception("|PluginManagement.GetManifest|Encountered error trying to download plugins manifest", e);
48-
49-
UserPlugins = new List<UserPlugin>();
51+
Log.Exception($"|PluginsManifest.{nameof(UpdateManifestAsync)}|Http request failed", e);
5052
}
5153
finally
5254
{

Flow.Launcher.Infrastructure/Http/Http.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,5 +153,13 @@ public static async Task<Stream> GetStreamAsync([NotNull] string url, Cancellati
153153
var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
154154
return await response.Content.ReadAsStreamAsync();
155155
}
156+
157+
/// <summary>
158+
/// Asynchrously send an HTTP request.
159+
/// </summary>
160+
public static async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken token = default)
161+
{
162+
return await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token);
163+
}
156164
}
157165
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using Flow.Launcher.Infrastructure.UserSettings;
33
using System;
44
using System.Collections.Generic;
5-
using System.Text;
5+
using System.Text.RegularExpressions;
66

77
namespace Flow.Launcher.Plugin.PluginsManager
88
{
@@ -53,8 +53,8 @@ public List<Result> LoadContextMenus(Result selectedResult)
5353
{
5454
// standard UrlSourceCode format in PluginsManifest's plugins.json file: https://github.com/jjw24/Flow.Launcher.Plugin.Putty/tree/master
5555
var link = pluginManifestInfo.UrlSourceCode.StartsWith("https://github.com")
56-
? pluginManifestInfo.UrlSourceCode.Replace("/tree/master", "/issues/new/choose")
57-
: pluginManifestInfo.UrlSourceCode;
56+
? Regex.Replace(pluginManifestInfo.UrlSourceCode, @"\/tree\/\w+$", "") + "/issues/new/choose"
57+
: pluginManifestInfo.UrlSourceCode;
5858

5959
Context.API.OpenUrl(link);
6060
return true;

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

Lines changed: 9 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
namespace Flow.Launcher.Plugin.PluginsManager
1414
{
15-
public class Main : ISettingProvider, IAsyncPlugin, IContextMenu, IPluginI18n, IAsyncReloadable
15+
public class Main : ISettingProvider, IAsyncPlugin, IContextMenu, IPluginI18n
1616
{
1717
internal PluginInitContext Context { get; set; }
1818

@@ -24,64 +24,41 @@ public class Main : ISettingProvider, IAsyncPlugin, IContextMenu, IPluginI18n, I
2424

2525
internal PluginsManager pluginManager;
2626

27-
private DateTime lastUpdateTime = DateTime.MinValue;
28-
2927
public Control CreateSettingPanel()
3028
{
3129
return new PluginsManagerSettings(viewModel);
3230
}
3331

34-
public Task InitAsync(PluginInitContext context)
32+
public async Task InitAsync(PluginInitContext context)
3533
{
3634
Context = context;
3735
Settings = context.API.LoadSettingJsonStorage<Settings>();
3836
viewModel = new SettingsViewModel(context, Settings);
3937
contextMenu = new ContextMenu(Context);
4038
pluginManager = new PluginsManager(Context, Settings);
41-
_manifestUpdateTask = pluginManager
42-
.UpdateManifestAsync(true)
43-
.ContinueWith(_ =>
44-
{
45-
lastUpdateTime = DateTime.Now;
46-
}, TaskContinuationOptions.OnlyOnRanToCompletion);
4739

48-
return Task.CompletedTask;
40+
await pluginManager.UpdateManifestAsync();
4941
}
5042

5143
public List<Result> LoadContextMenus(Result selectedResult)
5244
{
5345
return contextMenu.LoadContextMenus(selectedResult);
5446
}
5547

56-
private Task _manifestUpdateTask = Task.CompletedTask;
57-
5848
public async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
5949
{
60-
var search = query.Search;
61-
62-
if (string.IsNullOrWhiteSpace(search))
50+
if (string.IsNullOrWhiteSpace(query.Search))
6351
return pluginManager.GetDefaultHotKeys();
6452

65-
if ((DateTime.Now - lastUpdateTime).TotalHours > 12 && _manifestUpdateTask.IsCompleted) // 12 hours
66-
{
67-
_manifestUpdateTask = pluginManager.UpdateManifestAsync().ContinueWith(t =>
68-
{
69-
lastUpdateTime = DateTime.Now;
70-
}, TaskContinuationOptions.OnlyOnRanToCompletion);
71-
}
72-
73-
return search switch
53+
return query.FirstSearch.ToLower() switch
7454
{
7555
//search could be url, no need ToLower() when passed in
76-
var s when s.StartsWith(Settings.HotKeyInstall, StringComparison.OrdinalIgnoreCase)
77-
=> await pluginManager.RequestInstallOrUpdate(search, token),
78-
var s when s.StartsWith(Settings.HotkeyUninstall, StringComparison.OrdinalIgnoreCase)
79-
=> pluginManager.RequestUninstall(search),
80-
var s when s.StartsWith(Settings.HotkeyUpdate, StringComparison.OrdinalIgnoreCase)
81-
=> await pluginManager.RequestUpdate(search, token),
56+
Settings.InstallCommand => await pluginManager.RequestInstallOrUpdate(query.SecondToEndSearch, token),
57+
Settings.UninstallCommand => pluginManager.RequestUninstall(query.SecondToEndSearch),
58+
Settings.UpdateCommand => await pluginManager.RequestUpdate(query.SecondToEndSearch, token),
8259
_ => pluginManager.GetDefaultHotKeys().Where(hotkey =>
8360
{
84-
hotkey.Score = StringMatcher.FuzzySearch(search, hotkey.Title).Score;
61+
hotkey.Score = StringMatcher.FuzzySearch(query.Search, hotkey.Title).Score;
8562
return hotkey.Score > 0;
8663
}).ToList()
8764
};
@@ -96,11 +73,5 @@ public string GetTranslatedPluginDescription()
9673
{
9774
return Context.API.GetTranslation("plugin_pluginsmanager_plugin_description");
9875
}
99-
100-
public async Task ReloadDataAsync()
101-
{
102-
await pluginManager.UpdateManifestAsync();
103-
lastUpdateTime = DateTime.Now;
104-
}
10576
}
10677
}

0 commit comments

Comments
 (0)