Merge pull request #249 from taooceros/PluginManagerImprove

Plugin manager improve
This commit is contained in:
Jeremy Wu 2020-12-29 18:59:47 +11:00 committed by GitHub
commit 181d871bfd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 237 additions and 147 deletions

View file

@ -59,7 +59,7 @@ namespace Flow.Launcher.Infrastructure.Http
Log.Debug($"|Http.Get|Url <{url}>");
var request = WebRequest.CreateHttp(url);
request.Method = "GET";
request.Timeout = 1000;
request.Timeout = 6000;
request.Proxy = WebProxy();
request.UserAgent = UserAgent;
var response = await request.GetResponseAsync() as HttpWebResponse;
@ -73,5 +73,18 @@ namespace Flow.Launcher.Infrastructure.Http
return content;
}
public static async Task<Stream> GetStreamAsync([NotNull] string url)
{
Log.Debug($"|Http.Get|Url <{url}>");
var request = WebRequest.CreateHttp(url);
request.Method = "GET";
request.Timeout = 6000;
request.Proxy = WebProxy();
request.UserAgent = UserAgent;
var response = await request.GetResponseAsync() as HttpWebResponse;
response = response.NonNull();
return response.GetResponseStream().NonNull();
}
}
}
}

View file

@ -10,12 +10,9 @@ namespace Flow.Launcher.Plugin.PluginsManager
{
private PluginInitContext Context { get; set; }
private Settings Settings { get; set; }
public ContextMenu(PluginInitContext context, Settings settings)
public ContextMenu(PluginInitContext context)
{
Context = context;
Settings = settings;
}
public List<Result> LoadContextMenus(Result selectedResult)
@ -58,7 +55,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
? pluginManifestInfo.UrlSourceCode.Replace("/tree/master", "/issues/new/choose")
: pluginManifestInfo.UrlSourceCode;
SharedCommands.SearchWeb.NewBrowserWindow(link);
SharedCommands.SearchWeb.NewTabInBrowser(link);
return true;
}
},
@ -69,7 +66,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
IcoPath = selectedResult.IcoPath,
Action = _ =>
{
SharedCommands.SearchWeb.NewBrowserWindow("https://github.com/Flow-Launcher/Flow.Launcher.PluginsManifest");
SharedCommands.SearchWeb.NewTabInBrowser("https://github.com/Flow-Launcher/Flow.Launcher.PluginsManifest");
return true;
}
}

View file

@ -1,9 +1,12 @@
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin.PluginsManager.ViewModels;
using Flow.Launcher.Plugin.PluginsManager.Views;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using Flow.Launcher.Infrastructure;
using System;
using System.Threading.Tasks;
namespace Flow.Launcher.Plugin.PluginsManager
{
@ -17,6 +20,10 @@ namespace Flow.Launcher.Plugin.PluginsManager
private IContextMenu contextMenu;
internal PluginsManager pluginManager;
private DateTime lastUpdateTime;
public Control CreateSettingPanel()
{
return new PluginsManagerSettings(viewModel);
@ -27,7 +34,9 @@ namespace Flow.Launcher.Plugin.PluginsManager
Context = context;
viewModel = new SettingsViewModel(context);
Settings = viewModel.Settings;
contextMenu = new ContextMenu(Context, Settings);
contextMenu = new ContextMenu(Context);
pluginManager = new PluginsManager(Context, Settings);
lastUpdateTime = DateTime.Now;
}
public List<Result> LoadContextMenus(Result selectedResult)
@ -39,17 +48,29 @@ namespace Flow.Launcher.Plugin.PluginsManager
{
var search = query.Search.ToLower();
var pluginManager = new PluginsManager(Context, Settings);
if (string.IsNullOrWhiteSpace(search))
return pluginManager.GetDefaultHotKeys();
if (!string.IsNullOrEmpty(search)
&& ($"{Settings.HotkeyUninstall} ".StartsWith(search) || search.StartsWith($"{Settings.HotkeyUninstall} ")))
return pluginManager.RequestUninstall(search);
if ((DateTime.Now - lastUpdateTime).TotalHours > 12) // 12 hours
{
Task.Run(async () =>
{
await pluginManager.UpdateManifest();
lastUpdateTime = DateTime.Now;
});
}
if (!string.IsNullOrEmpty(search)
&& ($"{Settings.HotkeyUpdate} ".StartsWith(search) || search.StartsWith($"{Settings.HotkeyUpdate} ")))
return pluginManager.RequestUpdate(search);
return pluginManager.RequestInstallOrUpdate(search);
return search switch
{
var s when s.StartsWith(Settings.HotKeyInstall) => pluginManager.RequestInstallOrUpdate(s),
var s when s.StartsWith(Settings.HotkeyUninstall) => pluginManager.RequestUninstall(s),
var s when s.StartsWith(Settings.HotkeyUpdate) => pluginManager.RequestUpdate(s),
_ => pluginManager.GetDefaultHotKeys().Where(hotkey =>
{
hotkey.Score = StringMatcher.FuzzySearch(search, hotkey.Title).Score;
return hotkey.Score > 0;
}).ToList()
};
}
public void Save()

View file

@ -1,8 +1,8 @@
using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.Logger;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;
namespace Flow.Launcher.Plugin.PluginsManager.Models
@ -12,21 +12,17 @@ namespace Flow.Launcher.Plugin.PluginsManager.Models
internal List<UserPlugin> UserPlugins { get; private set; }
internal PluginsManifest()
{
DownloadManifest();
Task.Run(async () => await DownloadManifest()).Wait();
}
private void DownloadManifest()
internal async Task DownloadManifest()
{
var json = string.Empty;
try
{
var t = Task.Run(
async () =>
json = await Http.Get("https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher.PluginsManifest/main/plugins.json"));
await using var jsonStream = await Http.GetStreamAsync("https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher.PluginsManifest/main/plugins.json")
.ConfigureAwait(false);
t.Wait();
UserPlugins = JsonConvert.DeserializeObject<List<UserPlugin>>(json);
UserPlugins = await JsonSerializer.DeserializeAsync<List<UserPlugin>>(jsonStream).ConfigureAwait(false);
}
catch (Exception e)
{

View file

@ -7,20 +7,22 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace Flow.Launcher.Plugin.PluginsManager
{
internal class PluginsManager
{
private readonly PluginsManifest pluginsManifest;
private PluginsManifest pluginsManifest;
private PluginInitContext Context { get; set; }
private Settings Settings { get; set; }
private bool shouldHideWindow = true;
private bool ShouldHideWindow
private bool ShouldHideWindow
{
set { shouldHideWindow = value; }
get
@ -42,18 +44,63 @@ namespace Flow.Launcher.Plugin.PluginsManager
Context = context;
Settings = settings;
}
internal async Task UpdateManifest()
{
await pluginsManifest.DownloadManifest();
}
internal List<Result> GetDefaultHotKeys()
{
return new List<Result>()
{
new Result()
{
Title = Settings.HotKeyInstall,
IcoPath = icoPath,
Action = _ =>
{
Context.API.ChangeQuery("pm install ");
return false;
}
},
new Result()
{
Title = Settings.HotkeyUninstall,
IcoPath = icoPath,
Action = _ =>
{
Context.API.ChangeQuery("pm uninstall ");
return false;
}
},
new Result()
{
Title = Settings.HotkeyUpdate,
IcoPath = icoPath,
Action = _ =>
{
Context.API.ChangeQuery("pm update ");
return false;
}
}
};
}
internal void InstallOrUpdate(UserPlugin plugin)
{
if (PluginExists(plugin.ID))
{
if (Context.API.GetAllPlugins().Any(x => x.Metadata.ID == plugin.ID && x.Metadata.Version != plugin.Version))
if (Context.API.GetAllPlugins()
.Any(x => x.Metadata.ID == plugin.ID && x.Metadata.Version != plugin.Version))
{
if (MessageBox.Show(Context.API.GetTranslation("plugin_pluginsmanager_update_exists"),
Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
Context
.API
.ChangeQuery($"{Context.CurrentPluginMetadata.ActionKeywords.FirstOrDefault()} {Settings.HotkeyUpdate} {plugin.Name}");
Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
Context
.API
.ChangeQuery(
$"{Context.CurrentPluginMetadata.ActionKeywords.FirstOrDefault()} {Settings.HotkeyUpdate} {plugin.Name}");
Application.Current.MainWindow.Show();
shouldHideWindow = false;
@ -66,10 +113,11 @@ namespace Flow.Launcher.Plugin.PluginsManager
}
var message = string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_prompt"),
plugin.Name, plugin.Author,
Environment.NewLine, Environment.NewLine);
plugin.Name, plugin.Author,
Environment.NewLine, Environment.NewLine);
if (MessageBox.Show(message, Context.API.GetTranslation("plugin_pluginsmanager_install_title"), MessageBoxButton.YesNo) == MessageBoxResult.No)
if (MessageBox.Show(message, Context.API.GetTranslation("plugin_pluginsmanager_install_title"),
MessageBoxButton.YesNo) == MessageBoxResult.No)
return;
var filePath = Path.Combine(DataLocation.PluginsDirectory, $"{plugin.Name}-{plugin.Version}.zip");
@ -77,30 +125,34 @@ namespace Flow.Launcher.Plugin.PluginsManager
try
{
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"),
Context.API.GetTranslation("plugin_pluginsmanager_please_wait"));
Context.API.GetTranslation("plugin_pluginsmanager_please_wait"));
Http.Download(plugin.UrlDownload, filePath);
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"),
Context.API.GetTranslation("plugin_pluginsmanager_download_success"));
Context.API.GetTranslation("plugin_pluginsmanager_download_success"));
}
catch (Exception e)
{
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"),
Context.API.GetTranslation("plugin_pluginsmanager_download_success"));
Context.API.GetTranslation("plugin_pluginsmanager_download_success"));
Log.Exception("PluginsManager", "An error occured while downloading plugin", e, "PluginDownload");
}
Application.Current.Dispatcher.Invoke(() => { Install(plugin, filePath); Context.API.RestartApp(); });
Application.Current.Dispatcher.Invoke(() =>
{
Install(plugin, filePath);
Context.API.RestartApp();
});
}
internal List<Result> RequestUpdate(string search)
{
var autocompletedResults = AutoCompleteReturnAllResults(search,
Settings.HotkeyUpdate,
"Update",
"Select a plugin to update");
Settings.HotkeyUpdate,
"Update",
"Select a plugin to update");
if (autocompletedResults.Any())
return autocompletedResults;
@ -108,63 +160,68 @@ namespace Flow.Launcher.Plugin.PluginsManager
var uninstallSearch = search.Replace(Settings.HotkeyUpdate, string.Empty).TrimStart();
var resultsForUpdate =
from existingPlugin in Context.API.GetAllPlugins()
join pluginFromManifest in pluginsManifest.UserPlugins
on existingPlugin.Metadata.ID equals pluginFromManifest.ID
where existingPlugin.Metadata.Version != pluginFromManifest.Version
select
new
{
pluginFromManifest.Name,
pluginFromManifest.Author,
CurrentVersion = existingPlugin.Metadata.Version,
NewVersion = pluginFromManifest.Version,
existingPlugin.Metadata.IcoPath,
PluginExistingMetadata = existingPlugin.Metadata,
PluginNewUserPlugin = pluginFromManifest
};
var resultsForUpdate =
from existingPlugin in Context.API.GetAllPlugins()
join pluginFromManifest in pluginsManifest.UserPlugins
on existingPlugin.Metadata.ID equals pluginFromManifest.ID
where existingPlugin.Metadata.Version.CompareTo(pluginFromManifest.Version) < 0 // if current version precedes manifest version
select
new
{
pluginFromManifest.Name,
pluginFromManifest.Author,
CurrentVersion = existingPlugin.Metadata.Version,
NewVersion = pluginFromManifest.Version,
existingPlugin.Metadata.IcoPath,
PluginExistingMetadata = existingPlugin.Metadata,
PluginNewUserPlugin = pluginFromManifest
};
if (!resultsForUpdate.Any())
return new List<Result> {
new Result
{
Title = Context.API.GetTranslation("plugin_pluginsmanager_update_noresult_title"),
SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_update_noresult_subtitle"),
IcoPath = icoPath
}};
return new List<Result>
{
new Result
{
Title = Context.API.GetTranslation("plugin_pluginsmanager_update_noresult_title"),
SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_update_noresult_subtitle"),
IcoPath = icoPath
}
};
var results = resultsForUpdate
.Select(x =>
new Result
{
Title = $"{x.Name} by {x.Author}",
SubTitle = $"Update from version {x.CurrentVersion} to {x.NewVersion}",
IcoPath = x.IcoPath,
Action = e =>
{
string message = string.Format(Context.API.GetTranslation("plugin_pluginsmanager_update_prompt"),
x.Name, x.Author,
Environment.NewLine, Environment.NewLine);
.Select(x =>
new Result
{
Title = $"{x.Name} by {x.Author}",
SubTitle = $"Update from version {x.CurrentVersion} to {x.NewVersion}",
IcoPath = x.IcoPath,
Action = e =>
{
string message = string.Format(
Context.API.GetTranslation("plugin_pluginsmanager_update_prompt"),
x.Name, x.Author,
Environment.NewLine, Environment.NewLine);
if (MessageBox.Show(message, Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
Uninstall(x.PluginExistingMetadata);
if (MessageBox.Show(message,
Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
Uninstall(x.PluginExistingMetadata);
var downloadToFilePath = Path.Combine(DataLocation.PluginsDirectory, $"{x.Name}-{x.NewVersion}.zip");
Http.Download(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath);
Install(x.PluginNewUserPlugin, downloadToFilePath);
var downloadToFilePath = Path.Combine(DataLocation.PluginsDirectory,
$"{x.Name}-{x.NewVersion}.zip");
Http.Download(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath);
Install(x.PluginNewUserPlugin, downloadToFilePath);
Context.API.RestartApp();
Context.API.RestartApp();
return true;
}
return true;
}
return false;
}
});
return false;
}
});
return Search(results, uninstallSearch);
}
@ -180,39 +237,41 @@ namespace Flow.Launcher.Plugin.PluginsManager
return results.ToList();
return results
.Where(x =>
{
var matchResult = StringMatcher.FuzzySearch(searchName, x.Title);
if (matchResult.IsSearchPrecisionScoreMet())
x.Score = matchResult.Score;
.Where(x =>
{
var matchResult = StringMatcher.FuzzySearch(searchName, x.Title);
if (matchResult.IsSearchPrecisionScoreMet())
x.Score = matchResult.Score;
return matchResult.IsSearchPrecisionScoreMet();
})
.ToList();
return matchResult.IsSearchPrecisionScoreMet();
})
.ToList();
}
internal List<Result> RequestInstallOrUpdate(string searchName)
{
var searchNameWithoutKeyword = searchName.Replace(Settings.HotKeyInstall, string.Empty).Trim();
var results =
pluginsManifest
.UserPlugins
.Select(x =>
new Result
{
Title = $"{x.Name} by {x.Author}",
SubTitle = x.Description,
IcoPath = icoPath,
Action = e =>
.UserPlugins
.Select(x =>
new Result
{
Application.Current.MainWindow.Hide();
InstallOrUpdate(x);
Title = $"{x.Name} by {x.Author}",
SubTitle = x.Description,
IcoPath = icoPath,
Action = e =>
{
Application.Current.MainWindow.Hide();
InstallOrUpdate(x);
return ShouldHideWindow;
},
ContextData = x
});
return ShouldHideWindow;
},
ContextData = x
});
return Search(results, searchName);
return Search(results, searchNameWithoutKeyword);
}
private void Install(UserPlugin plugin, string downloadedFilePath)
@ -253,10 +312,10 @@ namespace Flow.Launcher.Plugin.PluginsManager
internal List<Result> RequestUninstall(string search)
{
var autocompletedResults = AutoCompleteReturnAllResults(search,
Settings.HotkeyUninstall,
"Uninstall",
"Select a plugin to uninstall");
var autocompletedResults = AutoCompleteReturnAllResults(search,
Settings.HotkeyUninstall,
"Uninstall",
"Select a plugin to uninstall");
if (autocompletedResults.Any())
return autocompletedResults;
@ -264,32 +323,34 @@ namespace Flow.Launcher.Plugin.PluginsManager
var uninstallSearch = search.Replace(Settings.HotkeyUninstall, string.Empty).TrimStart();
var results = Context.API
.GetAllPlugins()
.Select(x =>
new Result
{
Title = $"{x.Metadata.Name} by {x.Metadata.Author}",
SubTitle = x.Metadata.Description,
IcoPath = x.Metadata.IcoPath,
Action = e =>
{
string message = string.Format(Context.API.GetTranslation("plugin_pluginsmanager_uninstall_prompt"),
x.Metadata.Name, x.Metadata.Author,
Environment.NewLine, Environment.NewLine);
.GetAllPlugins()
.Select(x =>
new Result
{
Title = $"{x.Metadata.Name} by {x.Metadata.Author}",
SubTitle = x.Metadata.Description,
IcoPath = x.Metadata.IcoPath,
Action = e =>
{
string message = string.Format(
Context.API.GetTranslation("plugin_pluginsmanager_uninstall_prompt"),
x.Metadata.Name, x.Metadata.Author,
Environment.NewLine, Environment.NewLine);
if (MessageBox.Show(message, Context.API.GetTranslation("plugin_pluginsmanager_uninstall_title"),
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
Application.Current.MainWindow.Hide();
Uninstall(x.Metadata);
Context.API.RestartApp();
if (MessageBox.Show(message,
Context.API.GetTranslation("plugin_pluginsmanager_uninstall_title"),
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
Application.Current.MainWindow.Hide();
Uninstall(x.Metadata);
Context.API.RestartApp();
return true;
}
return true;
}
return false;
}
});
return false;
}
});
return Search(results, uninstallSearch);
}
@ -317,8 +378,9 @@ namespace Flow.Launcher.Plugin.PluginsManager
Action = e =>
{
Context
.API
.ChangeQuery($"{Context.CurrentPluginMetadata.ActionKeywords.FirstOrDefault()} {hotkey} ");
.API
.ChangeQuery(
$"{Context.CurrentPluginMetadata.ActionKeywords.FirstOrDefault()} {hotkey} ");
return false;
}

View file

@ -6,6 +6,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
{
internal class Settings
{
internal string HotKeyInstall { get; set; } = "install";
internal string HotkeyUninstall { get; set; } = "uninstall";
internal string HotkeyUpdate { get; set; } = "update";

View file

@ -6,7 +6,7 @@
"Name": "Plugins Manager",
"Description": "Management of installing, uninstalling or updating Flow Launcher plugins",
"Author": "Jeremy Wu",
"Version": "1.3.0",
"Version": "1.3.1",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll",