From 4ea5dd5ad2ad6d4869d57c8aed1e187d81662135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 13 Oct 2020 20:54:00 +0800 Subject: [PATCH 001/158] Move Old HttpWebRequest to HttpClient Instance (solve connection timeout) --- Flow.Launcher.Core/Updater.cs | 2 +- Flow.Launcher.Infrastructure/Http/Http.cs | 77 ++++++++++++++--------- 2 files changed, 48 insertions(+), 31 deletions(-) diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 99d48275a..4ad8b19be 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -139,7 +139,7 @@ namespace Flow.Launcher.Core var latest = releases.Where(r => !r.Prerelease).OrderByDescending(r => r.PublishedAt).First(); var latestUrl = latest.HtmlUrl.Replace("/tag/", "/download/"); - var client = new WebClient { Proxy = Http.WebProxy() }; + var client = new WebClient { Proxy = Http.WebProxy }; var downloader = new FileDownloader(client); var manager = new UpdateManager(latestUrl, urlDownloader: downloader); diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index b7d274205..038362a0d 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -13,6 +13,13 @@ namespace Flow.Launcher.Infrastructure.Http { private const string UserAgent = @"Mozilla/5.0 (Trident/7.0; rv:11.0) like Gecko"; + private static HttpClient client; + private static SocketsHttpHandler socketsHttpHandler = new SocketsHttpHandler() + { + UseProxy = true, + Proxy = WebProxy + }; + static Http() { // need to be added so it would work on a win10 machine @@ -20,36 +27,55 @@ namespace Flow.Launcher.Infrastructure.Http ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; + + client.DefaultRequestHeaders.Add("User-Agent", UserAgent); + + } + private static HttpProxy proxy; + public static HttpProxy Proxy + { + private get + { + return proxy; + } + set + { + proxy = value; + UpdateProxy(); + } } - public static HttpProxy Proxy { private get; set; } - public static IWebProxy WebProxy() + public static WebProxy WebProxy { get; private set; } + + /// + /// Update the Address of the Proxy to modify the client Proxy + /// + public static void UpdateProxy() + // TODO: need test with a proxy { if (Proxy != null && Proxy.Enabled && !string.IsNullOrEmpty(Proxy.Server)) { if (string.IsNullOrEmpty(Proxy.UserName) || string.IsNullOrEmpty(Proxy.Password)) { - var webProxy = new WebProxy(Proxy.Server, Proxy.Port); - return webProxy; + WebProxy.Address = new Uri($"http://{Proxy.Server}:{Proxy.Port}"); + WebProxy.Credentials = null; } else { - var webProxy = new WebProxy(Proxy.Server, Proxy.Port) - { - Credentials = new NetworkCredential(Proxy.UserName, Proxy.Password) - }; - return webProxy; + WebProxy.Address = new Uri($"http://{Proxy.Server}:{Proxy.Port}"); + WebProxy.Credentials = new NetworkCredential(Proxy.UserName, Proxy.Password); } } else { - return WebRequest.GetSystemWebProxy(); + WebProxy.Address = new WebProxy().Address; + WebProxy.Credentials = null; } } public static void Download([NotNull] string url, [NotNull] string filePath) { - var client = new WebClient { Proxy = WebProxy() }; + var client = new WebClient { Proxy = WebProxy }; client.Headers.Add("user-agent", UserAgent); client.DownloadFile(url, filePath); } @@ -57,26 +83,17 @@ namespace Flow.Launcher.Infrastructure.Http public static async Task Get([NotNull] string url, string encoding = "UTF-8") { Log.Debug($"|Http.Get|Url <{url}>"); - var request = WebRequest.CreateHttp(url); - request.Method = "GET"; - request.Timeout = 1000; - request.Proxy = WebProxy(); - request.UserAgent = UserAgent; - var response = await request.GetResponseAsync() as HttpWebResponse; - response = response.NonNull(); - var stream = response.GetResponseStream().NonNull(); - - using (var reader = new StreamReader(stream, Encoding.GetEncoding(encoding))) + var response = await client.GetAsync(url); + using var stream = await response.Content.ReadAsStreamAsync(); + using var reader = new StreamReader(stream, Encoding.GetEncoding(encoding)); + var content = await reader.ReadToEndAsync(); + if (response.StatusCode == HttpStatusCode.OK) { - var content = await reader.ReadToEndAsync(); - if (response.StatusCode == HttpStatusCode.OK) - { - return content; - } - else - { - throw new HttpRequestException($"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>"); - } + return content; + } + else + { + throw new HttpRequestException($"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>"); } } } From a16cc5be8db3b5d52acf12565b029439c8d1a921 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 13 Oct 2020 20:54:00 +0800 Subject: [PATCH 002/158] Move Old HttpWebRequest to HttpClient Instance (solve connection timeout) --- Flow.Launcher.Core/Updater.cs | 2 +- Flow.Launcher.Infrastructure/Http/Http.cs | 78 ++++++++++++++--------- 2 files changed, 49 insertions(+), 31 deletions(-) diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 99d48275a..4ad8b19be 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -139,7 +139,7 @@ namespace Flow.Launcher.Core var latest = releases.Where(r => !r.Prerelease).OrderByDescending(r => r.PublishedAt).First(); var latestUrl = latest.HtmlUrl.Replace("/tag/", "/download/"); - var client = new WebClient { Proxy = Http.WebProxy() }; + var client = new WebClient { Proxy = Http.WebProxy }; var downloader = new FileDownloader(client); var manager = new UpdateManager(latestUrl, urlDownloader: downloader); diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index b7d274205..81c07eff7 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using JetBrains.Annotations; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; +using System; namespace Flow.Launcher.Infrastructure.Http { @@ -13,6 +14,13 @@ namespace Flow.Launcher.Infrastructure.Http { private const string UserAgent = @"Mozilla/5.0 (Trident/7.0; rv:11.0) like Gecko"; + private static HttpClient client; + private static SocketsHttpHandler socketsHttpHandler = new SocketsHttpHandler() + { + UseProxy = true, + Proxy = WebProxy + }; + static Http() { // need to be added so it would work on a win10 machine @@ -20,36 +28,55 @@ namespace Flow.Launcher.Infrastructure.Http ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; + + client.DefaultRequestHeaders.Add("User-Agent", UserAgent); + + } + private static HttpProxy proxy; + public static HttpProxy Proxy + { + private get + { + return proxy; + } + set + { + proxy = value; + UpdateProxy(); + } } - public static HttpProxy Proxy { private get; set; } - public static IWebProxy WebProxy() + public static WebProxy WebProxy { get; private set; } + + /// + /// Update the Address of the Proxy to modify the client Proxy + /// + public static void UpdateProxy() + // TODO: need test with a proxy { if (Proxy != null && Proxy.Enabled && !string.IsNullOrEmpty(Proxy.Server)) { if (string.IsNullOrEmpty(Proxy.UserName) || string.IsNullOrEmpty(Proxy.Password)) { - var webProxy = new WebProxy(Proxy.Server, Proxy.Port); - return webProxy; + WebProxy.Address = new Uri($"http://{Proxy.Server}:{Proxy.Port}"); + WebProxy.Credentials = null; } else { - var webProxy = new WebProxy(Proxy.Server, Proxy.Port) - { - Credentials = new NetworkCredential(Proxy.UserName, Proxy.Password) - }; - return webProxy; + WebProxy.Address = new Uri($"http://{Proxy.Server}:{Proxy.Port}"); + WebProxy.Credentials = new NetworkCredential(Proxy.UserName, Proxy.Password); } } else { - return WebRequest.GetSystemWebProxy(); + WebProxy.Address = new WebProxy().Address; + WebProxy.Credentials = null; } } public static void Download([NotNull] string url, [NotNull] string filePath) { - var client = new WebClient { Proxy = WebProxy() }; + var client = new WebClient { Proxy = WebProxy }; client.Headers.Add("user-agent", UserAgent); client.DownloadFile(url, filePath); } @@ -57,26 +84,17 @@ namespace Flow.Launcher.Infrastructure.Http public static async Task Get([NotNull] string url, string encoding = "UTF-8") { Log.Debug($"|Http.Get|Url <{url}>"); - var request = WebRequest.CreateHttp(url); - request.Method = "GET"; - request.Timeout = 1000; - request.Proxy = WebProxy(); - request.UserAgent = UserAgent; - var response = await request.GetResponseAsync() as HttpWebResponse; - response = response.NonNull(); - var stream = response.GetResponseStream().NonNull(); - - using (var reader = new StreamReader(stream, Encoding.GetEncoding(encoding))) + var response = await client.GetAsync(url); + using var stream = await response.Content.ReadAsStreamAsync(); + using var reader = new StreamReader(stream, Encoding.GetEncoding(encoding)); + var content = await reader.ReadToEndAsync(); + if (response.StatusCode == HttpStatusCode.OK) { - var content = await reader.ReadToEndAsync(); - if (response.StatusCode == HttpStatusCode.OK) - { - return content; - } - else - { - throw new HttpRequestException($"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>"); - } + return content; + } + else + { + throw new HttpRequestException($"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>"); } } } From c55e889f4fb37aa2624eec4523cbcd94cadc3293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 14 Oct 2020 11:29:12 +0800 Subject: [PATCH 003/158] Change Download to HttpClient as well (which change it to async as well) --- Flow.Launcher.Infrastructure/Http/Http.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 81c07eff7..34665217c 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -29,6 +29,7 @@ namespace Flow.Launcher.Infrastructure.Http | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; + client = new HttpClient(socketsHttpHandler, false); client.DefaultRequestHeaders.Add("User-Agent", UserAgent); } @@ -46,7 +47,7 @@ namespace Flow.Launcher.Infrastructure.Http } } - public static WebProxy WebProxy { get; private set; } + public static WebProxy WebProxy { get; private set; } = new WebProxy(); /// /// Update the Address of the Proxy to modify the client Proxy @@ -74,11 +75,18 @@ namespace Flow.Launcher.Infrastructure.Http } } - public static void Download([NotNull] string url, [NotNull] string filePath) + public async static Task Download([NotNull] string url, [NotNull] string filePath) { - var client = new WebClient { Proxy = WebProxy }; - client.Headers.Add("user-agent", UserAgent); - client.DownloadFile(url, filePath); + using var response = await client.GetAsync(url); + if (response.StatusCode == HttpStatusCode.OK) + { + using var fileStream = new FileStream(filePath, FileMode.CreateNew); + await response.Content.CopyToAsync(fileStream); + } + else + { + throw new WebException($"Error code <{response.StatusCode}> returned from <{url}>"); + } } public static async Task Get([NotNull] string url, string encoding = "UTF-8") From 424d757add58ccb38b50332b8a7597e8768b84bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 14 Oct 2020 11:29:38 +0800 Subject: [PATCH 004/158] Add Task.Run due to the change of async download --- .../Main.cs | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginManagement/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginManagement/Main.cs index e1b631517..0255216a0 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginManagement/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginManagement/Main.cs @@ -144,7 +144,7 @@ namespace Flow.Launcher.Plugin.PluginManagement IcoPath = "Images\\plugin.png", TitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, r.name).MatchData, SubTitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, r.description).MatchData, - Action = c => + Action = _ => { MessageBoxResult result = MessageBox.Show("Are you sure you wish to install the \'" + r.name + "\' plugin", "Install plugin", MessageBoxButton.YesNo); @@ -157,17 +157,19 @@ namespace Flow.Launcher.Plugin.PluginManagement string pluginUrl = APIBASE + "/media/" + r1.plugin_file; - try + Task.Run(async () => { - Http.Download(pluginUrl, filePath); - } - catch (WebException e) - { - context.API.ShowMsg($"PluginManagement.ResultForInstallPlugin: download failed for <{r.name}>"); - Log.Exception($"|PluginManagement.ResultForInstallPlugin|download failed for <{r.name}>", e); - return false; - } - context.API.InstallPlugin(filePath); + try + { + await Http.Download(pluginUrl, filePath); + context.API.InstallPlugin(filePath); + } + catch (WebException e) + { + context.API.ShowMsg($"PluginManagement.ResultForInstallPlugin: download failed for <{r.name}>"); + Log.Exception($"|PluginManagement.ResultForInstallPlugin|download failed for <{r.name}>", e); + } + }); } return false; } From 88af30207c594aa99c59c17826cb3aedc6e5b849 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 16 Dec 2020 21:52:00 +1100 Subject: [PATCH 005/158] add context menu for PluginsManager --- .../ContextMenu.cs | 60 ++++++++++++++++-- .../Images/request.png | Bin 0 -> 50820 bytes .../Images/sourcecode.png | Bin 0 -> 11842 bytes .../Images/website.png | Bin 0 -> 133947 bytes .../Languages/en.xaml | 8 +++ .../PluginsManager.cs | 3 +- 6 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.PluginsManager/Images/request.png create mode 100644 Plugins/Flow.Launcher.Plugin.PluginsManager/Images/sourcecode.png create mode 100644 Plugins/Flow.Launcher.Plugin.PluginsManager/Images/website.png diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs index d26c1ead4..f071e0e98 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs @@ -1,4 +1,5 @@ using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Plugin.PluginsManager.Models; using System; using System.Collections.Generic; using System.Text; @@ -19,11 +20,60 @@ namespace Flow.Launcher.Plugin.PluginsManager public List LoadContextMenus(Result selectedResult) { - // Open website - // Go to source code - // Report an issue? - // Request a feature? - return new List(); + var pluginManifestInfo = selectedResult.ContextData as UserPlugin; + + return new List + { + new Result + { + Title = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_openwebsite_title"), + SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_openwebsite_subtitle"), + IcoPath = "Images\\website.png", + Action = _ => + { + SharedCommands.SearchWeb.NewBrowserWindow(pluginManifestInfo.Website); + return true; + } + }, + new Result + { + Title = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_gotosourcecode_title"), + SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_gotosourcecode_subtitle"), + IcoPath = "Images\\sourcecode.png", + Action = _ => + { + SharedCommands.SearchWeb.NewBrowserWindow(pluginManifestInfo.UrlSourceCode); + return true; + } + }, + new Result + { + Title = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_newissue_title"), + SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_newissue_subtitle"), + IcoPath = "Images\\request.png", + Action = _ => + { + // standard UrlSourceCode format in PluginsManifest's plugins.json file: https://github.com/jjw24/WoxDictionary/tree/master + var link = pluginManifestInfo.UrlSourceCode.StartsWith("https://github.com") + ? pluginManifestInfo.UrlSourceCode.Replace("/tree/master", "/issues/new/choose") + : pluginManifestInfo.UrlSourceCode; + + SharedCommands.SearchWeb.NewBrowserWindow(link); + return true; + } + }, + new Result + { + Title = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_pluginsmanifest_title"), + SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_pluginsmanifest_subtitle"), + IcoPath = selectedResult.IcoPath, + Action = _ => + { + SharedCommands.SearchWeb.NewBrowserWindow("https://github.com/Flow-Launcher/Flow.Launcher.PluginsManifest"); + return true; + } + } + }; } } } diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/request.png b/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/request.png new file mode 100644 index 0000000000000000000000000000000000000000..a9126cb9bd8e06e3a0772e6405cd0e87c053c265 GIT binary patch literal 50820 zcmeFZ`(KRt|37|JRJMaGk{H!CBP3KbAST4o|0*lulj*`o%kam99X4xi2%I`uEy(3?J(lI&aw|+&NtI{Y!g(yUwmhHDi5Ik$vC) zOqE4_zp7*CxW!Ev!u#U!i+CAX){cig+sWl!;+{*g_BSjq�~L-@m>Yh>ChJ?fcWH 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 U3ySeQFB**(xMcq6d2zq|C#DQg!2kdN literal 0 HcmV?d00001 diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/sourcecode.png b/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/sourcecode.png new file mode 100644 index 0000000000000000000000000000000000000000..8efbdaa487d0ab825010fa3f3e7331824fd6f207 GIT binary patch literal 11842 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 literal 0 HcmV?d00001 diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml index fc13f0c2f..14fc2d586 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml @@ -20,5 +20,13 @@ Management of installing, uninstalling or updating Flow Launcher plugins + Open website + Visit the plugin's website + See source code + See the plugin's source code + Suggest an enhancement or submit an issue + Suggest an enhancement or submit an issue to the plugin developer + Go to Flow's plugins repository + Visit the PluginsManifest repository to see comunity-made plugin submissions \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index fc4abc800..3ccf696cc 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -115,7 +115,8 @@ namespace Flow.Launcher.Plugin.PluginsManager InstallOrUpdate(x); return true; - } + }, + ContextData = x }) .ToList(); From 407ff8266661c67f58bd7bd0682228d0ef6476e7 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 16 Dec 2020 21:53:02 +1100 Subject: [PATCH 006/158] version bump for PluginsManager --- Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json index 0353fffcc..73e03d525 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json @@ -6,7 +6,7 @@ "Name": "Plugins Manager", "Description": "Management of installing, uninstalling or updating Flow Launcher plugins", "Author": "Jeremy Wu", - "Version": "1.1.0", + "Version": "1.2.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll", From 919f72ba62e1b1a6fdad77d8f0c3bc74e26caed9 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 17 Dec 2020 06:31:33 +1100 Subject: [PATCH 007/158] use open tab in browser --- Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs index f071e0e98..76cb0f86b 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs @@ -31,7 +31,7 @@ namespace Flow.Launcher.Plugin.PluginsManager IcoPath = "Images\\website.png", Action = _ => { - SharedCommands.SearchWeb.NewBrowserWindow(pluginManifestInfo.Website); + SharedCommands.SearchWeb.NewTabInBrowser(pluginManifestInfo.Website); return true; } }, @@ -42,7 +42,7 @@ namespace Flow.Launcher.Plugin.PluginsManager IcoPath = "Images\\sourcecode.png", Action = _ => { - SharedCommands.SearchWeb.NewBrowserWindow(pluginManifestInfo.UrlSourceCode); + SharedCommands.SearchWeb.NewTabInBrowser(pluginManifestInfo.UrlSourceCode); return true; } }, From f12ea653d29fe2152293460f72483ae397551d74 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 17 Dec 2020 18:54:30 +1100 Subject: [PATCH 008/158] move autocomplete to method --- .../PluginsManager.cs | 69 ++++++++++--------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index c141916aa..ea94ab6ef 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -42,7 +42,7 @@ namespace Flow.Launcher.Plugin.PluginsManager 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.ID}.zip"); + var filePath = Path.Combine(DataLocation.PluginsDirectory, $"{plugin.Name}-{plugin.Version}.zip"); try { @@ -65,9 +65,9 @@ namespace Flow.Launcher.Plugin.PluginsManager Application.Current.Dispatcher.Invoke(() => Install(plugin, filePath)); } - internal void Update() + internal void Update(string search) { - throw new NotImplementedException(); + } internal bool PluginExists(string id) @@ -75,12 +75,6 @@ namespace Flow.Launcher.Plugin.PluginsManager return Context.API.GetAllPlugins().Any(x => x.Metadata.ID == id); } - internal void PluginsManifestSiteOpen() - { - //Open from context menu https://git.vcmq.workers.dev/Flow-Launcher/Flow.Launcher.PluginsManifest - throw new NotImplementedException(); - } - internal List Search(List results, string searchName) { if (string.IsNullOrEmpty(searchName)) @@ -162,29 +156,13 @@ namespace Flow.Launcher.Plugin.PluginsManager internal List RequestUninstall(string search) { - if (!string.IsNullOrEmpty(search) - && Settings.UninstallHotkey.StartsWith(search) - && (Settings.UninstallHotkey != search || !search.StartsWith(Settings.UninstallHotkey))) - { - return - new List - { - new Result - { - Title = "Uninstall", - IcoPath = icoPath, - SubTitle = "Select a plugin to uninstall", - Action = e => - { - Context - .API - .ChangeQuery($"{Context.CurrentPluginMetadata.ActionKeywords.FirstOrDefault()} {Settings.UninstallHotkey} "); + var autocompletedResults = AutoCompleteReturnAllResults(search, + Settings.UninstallHotkey, + "Uninstall", + "Select a plugin to uninstall"); - return false; - } - } - }; - } + if (autocompletedResults.Any()) + return autocompletedResults; var uninstallSearch = search.Replace(Settings.UninstallHotkey, string.Empty).TrimStart(); @@ -223,5 +201,34 @@ namespace Flow.Launcher.Plugin.PluginsManager Context.API.RestartApp(); } } + + private List AutoCompleteReturnAllResults(string search, string hotkey, string title, string subtitle) + { + if (!string.IsNullOrEmpty(search) + && hotkey.StartsWith(search) + && (hotkey != search || !search.StartsWith(hotkey))) + { + return + new List + { + new Result + { + Title = title, + IcoPath = icoPath, + SubTitle = subtitle, + Action = e => + { + Context + .API + .ChangeQuery($"{Context.CurrentPluginMetadata.ActionKeywords.FirstOrDefault()} {hotkey} "); + + return false; + } + } + }; + } + + return new List(); + } } } From df454778ec2d6fedad6e3b57d1e39a5b5acc8879 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 17 Dec 2020 19:00:27 +1100 Subject: [PATCH 009/158] update plugin containing folder name from id to version --- Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index ea94ab6ef..1b13dabf8 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -147,7 +147,7 @@ namespace Flow.Launcher.Plugin.PluginsManager return; } - string newPluginPath = Path.Combine(DataLocation.PluginsDirectory, $"{plugin.Name}{plugin.ID}"); + string newPluginPath = Path.Combine(DataLocation.PluginsDirectory, $"{plugin.Name}-{plugin.Version}"); Directory.Move(pluginFolderPath, newPluginPath); From 7e8c49502e38e9473affba6f57a793a6e89e21a5 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 17 Dec 2020 20:37:01 +1100 Subject: [PATCH 010/158] add plugin update call --- .../Languages/en.xaml | 5 + .../Main.cs | 8 +- .../Models/UserPlugin.cs | 5 +- .../PluginsManager.cs | 110 ++++++++++++++---- .../Settings.cs | 4 +- 5 files changed, 105 insertions(+), 27 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml index 7f8557c28..f69f23f6b 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml @@ -11,6 +11,11 @@ Plugin Install Plugin Uninstall Install failed: unable to find the plugin.json metadata file from the new plugin + No update available + All plugins are up to date + {0} by {1} {2}{3}Would you like to update this plugin? After the update Flow will automatically restart. + Plugin Update + diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 58ec5005f..43f92e7b9 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -42,9 +42,13 @@ namespace Flow.Launcher.Plugin.PluginsManager var pluginManager = new PluginsManager(Context, Settings); if (!string.IsNullOrEmpty(search) - && ($"{Settings.UninstallHotkey} ".StartsWith(search) || search.StartsWith($"{Settings.UninstallHotkey} "))) + && ($"{Settings.HotkeyUninstall} ".StartsWith(search) || search.StartsWith($"{Settings.HotkeyUninstall} "))) return pluginManager.RequestUninstall(search); - + + if (!string.IsNullOrEmpty(search) + && ($"{Settings.HotkeyUpdate} ".StartsWith(search) || search.StartsWith($"{Settings.HotkeyUpdate} "))) + return pluginManager.RequestUpdate(search); + return pluginManager.RequestInstallOrUpdate(search); } diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/UserPlugin.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/UserPlugin.cs index 3bc44e0f6..c1af3014b 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/UserPlugin.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/UserPlugin.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - + namespace Flow.Launcher.Plugin.PluginsManager.Models { public class UserPlugin diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 1b13dabf8..37051e4da 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -62,12 +62,82 @@ namespace Flow.Launcher.Plugin.PluginsManager Log.Exception("PluginsManager", "An error occured while downloading plugin", e, "PluginDownload"); } - Application.Current.Dispatcher.Invoke(() => Install(plugin, filePath)); + Application.Current.Dispatcher.Invoke(() => { Install(plugin, filePath); Context.API.RestartApp(); }); } - internal void Update(string search) + internal List RequestUpdate(string search) { + var autocompletedResults = AutoCompleteReturnAllResults(search, + Settings.HotkeyUpdate, + "Update", + "Select a plugin to update"); + if (autocompletedResults.Any()) + return autocompletedResults; + + 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 + }; + + if (!resultsForUpdate.Any()) + return new List { + 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); + + 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); + + Context.API.RestartApp(); + + return true; + } + + return false; + } + }) + .ToList(); + + return Search(results, uninstallSearch); } internal bool PluginExists(string id) @@ -150,21 +220,19 @@ namespace Flow.Launcher.Plugin.PluginsManager string newPluginPath = Path.Combine(DataLocation.PluginsDirectory, $"{plugin.Name}-{plugin.Version}"); Directory.Move(pluginFolderPath, newPluginPath); - - Context.API.RestartApp(); } internal List RequestUninstall(string search) { var autocompletedResults = AutoCompleteReturnAllResults(search, - Settings.UninstallHotkey, + Settings.HotkeyUninstall, "Uninstall", "Select a plugin to uninstall"); if (autocompletedResults.Any()) return autocompletedResults; - var uninstallSearch = search.Replace(Settings.UninstallHotkey, string.Empty).TrimStart(); + var uninstallSearch = search.Replace(Settings.HotkeyUninstall, string.Empty).TrimStart(); var results= Context.API .GetAllPlugins() @@ -176,10 +244,21 @@ namespace Flow.Launcher.Plugin.PluginsManager IcoPath = x.Metadata.IcoPath, Action = e => { - Application.Current.MainWindow.Hide(); - Uninstall(x.Metadata); + string message = string.Format(Context.API.GetTranslation("plugin_pluginsmanager_uninstall_prompt"), + x.Metadata.Name, x.Metadata.Author, + Environment.NewLine, Environment.NewLine); - return true; + 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 false; } }) .ToList(); @@ -189,17 +268,8 @@ namespace Flow.Launcher.Plugin.PluginsManager private void Uninstall(PluginMetadata plugin) { - string message = string.Format(Context.API.GetTranslation("plugin_pluginsmanager_uninstall_prompt"), - plugin.Name, plugin.Author, - Environment.NewLine, Environment.NewLine); - - if (MessageBox.Show(message, Context.API.GetTranslation("plugin_pluginsmanager_uninstall_title"), - MessageBoxButton.YesNo) == MessageBoxResult.Yes) - { - using var _ = File.CreateText(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt")); - - Context.API.RestartApp(); - } + // Marked for deletion. Will be deleted on next start up + using var _ = File.CreateText(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt")); } private List AutoCompleteReturnAllResults(string search, string hotkey, string title, string subtitle) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs index 0c647e6ae..e2e8d22e5 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs @@ -6,6 +6,8 @@ namespace Flow.Launcher.Plugin.PluginsManager { internal class Settings { - internal string UninstallHotkey { get; set; } = "uninstall"; + internal string HotkeyUninstall { get; set; } = "uninstall"; + + internal string HotkeyUpdate { get; set; } = "update"; } } From ceab9b2ecd8d4da48ff30c8fc8587d6df8138207 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 17 Dec 2020 20:37:24 +1100 Subject: [PATCH 011/158] PluginsManager version bump --- Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json index 0353fffcc..e970e5a8e 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json @@ -6,7 +6,7 @@ "Name": "Plugins Manager", "Description": "Management of installing, uninstalling or updating Flow Launcher plugins", "Author": "Jeremy Wu", - "Version": "1.1.0", + "Version": "1.3.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll", From cca53c2a7d34d6d99c6a6dcae1ec17c018e0419f Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 17 Dec 2020 21:07:47 +1100 Subject: [PATCH 012/158] add hide main window override --- .../PluginsManager.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 37051e4da..22682cae4 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -19,6 +19,21 @@ namespace Flow.Launcher.Plugin.PluginsManager private Settings Settings { get; set; } + private bool shouldHideWindow = true; + private bool ShouldHideWindow + { + set { shouldHideWindow = value; } + get + { + var setValue = shouldHideWindow; + // Default value for hide main window is true. Revert after get call. + // This ensures when set by another method to false, it is only used once. + shouldHideWindow = true; + + return setValue; + } + } + private readonly string icoPath = "Images\\pluginsmanager.png"; internal PluginsManager(PluginInitContext context, Settings settings) @@ -178,7 +193,7 @@ namespace Flow.Launcher.Plugin.PluginsManager Application.Current.MainWindow.Hide(); InstallOrUpdate(x); - return true; + return ShouldHideWindow; } }) .ToList(); From c2ca29dcdae315ddcf6735fb4b74ed7557eaa761 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 17 Dec 2020 21:37:38 +1100 Subject: [PATCH 013/158] add go to update section if update exists when install --- .../Languages/en.xaml | 2 ++ .../PluginsManager.cs | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml index f69f23f6b..a5be6c63c 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml @@ -15,6 +15,8 @@ All plugins are up to date {0} by {1} {2}{3}Would you like to update this plugin? After the update Flow will automatically restart. Plugin Update + This plugin has an update, would you like to see it? + This plugin is already installed diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 22682cae4..dad183227 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -46,7 +46,22 @@ namespace Flow.Launcher.Plugin.PluginsManager { if (PluginExists(plugin.ID)) { - Context.API.ShowMsg("Plugin already installed"); + 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}"); + + Application.Current.MainWindow.Show(); + shouldHideWindow = false; + + return; + } + + Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_update_alreadyexists")); return; } From 5ab021f4449e19c5d935f96ec7f5cf835b5c2d9e Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 20 Dec 2020 20:08:52 +1100 Subject: [PATCH 014/158] update per comment --- .../PluginsManager.cs | 55 +++++++++---------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index bd80b3dfd..90f3277fb 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -138,34 +138,33 @@ namespace Flow.Launcher.Plugin.PluginsManager 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 => { - 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) { - string message = string.Format(Context.API.GetTranslation("plugin_pluginsmanager_update_prompt"), - x.Name, x.Author, - Environment.NewLine, Environment.NewLine); + 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 false; + return true; } - }) - .ToList(); + + return false; + } + }); return Search(results, uninstallSearch); } @@ -175,10 +174,10 @@ namespace Flow.Launcher.Plugin.PluginsManager return Context.API.GetAllPlugins().Any(x => x.Metadata.ID == id); } - internal List Search(List results, string searchName) + internal List Search(IEnumerable results, string searchName) { if (string.IsNullOrEmpty(searchName)) - return results; + return results.ToList(); return results .Where(x => @@ -211,8 +210,7 @@ namespace Flow.Launcher.Plugin.PluginsManager return ShouldHideWindow; }, ContextData = x - }) - .ToList(); + }); return Search(results, searchName); } @@ -265,7 +263,7 @@ namespace Flow.Launcher.Plugin.PluginsManager var uninstallSearch = search.Replace(Settings.HotkeyUninstall, string.Empty).TrimStart(); - var results= Context.API + var results = Context.API .GetAllPlugins() .Select(x => new Result @@ -291,8 +289,7 @@ namespace Flow.Launcher.Plugin.PluginsManager return false; } - }) - .ToList(); + }); return Search(results, uninstallSearch); } From 3a6b6b721f697dd8fc163cf2e2590cfa187a01fd Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 20 Dec 2020 20:23:14 +1100 Subject: [PATCH 015/158] Flow.Launcher.Plugin version bump --- Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 0f6450d18..70013c274 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -14,10 +14,10 @@ - 1.3.0 - 1.3.0 - 1.3.0 - 1.3.0 + 1.3.1 + 1.3.1 + 1.3.1 + 1.3.1 Flow.Launcher.Plugin Flow-Launcher MIT From 110cf2a60710d0d3ad06264304859ba4a6030a7f Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 20 Dec 2020 20:28:30 +1100 Subject: [PATCH 016/158] version bump --- SolutionAssemblyInfo.cs | 6 +++--- appveyor.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/SolutionAssemblyInfo.cs b/SolutionAssemblyInfo.cs index 018084a66..ccbfef5d0 100644 --- a/SolutionAssemblyInfo.cs +++ b/SolutionAssemblyInfo.cs @@ -16,6 +16,6 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("1.5.0")] -[assembly: AssemblyFileVersion("1.5.0")] -[assembly: AssemblyInformationalVersion("1.5.0")] \ No newline at end of file +[assembly: AssemblyVersion("1.6.0")] +[assembly: AssemblyFileVersion("1.6.0")] +[assembly: AssemblyInformationalVersion("1.6.0")] \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index f5841da3e..1f0937d6d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: '1.5.0.{build}' +version: '1.6.0.{build}' init: - ps: | From cbfa3f354dc5c19cd84d1573853792db0b93cb41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 20 Dec 2020 17:44:46 +0800 Subject: [PATCH 017/158] Inform user when checking update fail or update fail, and wrap all code in one try catch instead of catch each exception seperately since they are all same kind of exception. Use using instead of manually dispose. --- Flow.Launcher.Core/Updater.cs | 110 ++++++++++++++-------------------- 1 file changed, 45 insertions(+), 65 deletions(-) diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 20df23e40..3369526e0 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -29,89 +29,69 @@ namespace Flow.Launcher.Core GitHubRepository = gitHubRepository; } - public async Task UpdateApp(IPublicAPI api , bool silentUpdate = true) + public async Task UpdateApp(IPublicAPI api, bool silentUpdate = true) { - UpdateManager updateManager; - UpdateInfo newUpdateInfo; - - if (!silentUpdate) - api.ShowMsg("Please wait...", "Checking for new update"); - try { - updateManager = await GitHubUpdateManager(GitHubRepository); - } - catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException) - { - Log.Exception($"|Updater.UpdateApp|Please check your connection and proxy settings to api.github.com.", e); - return; - } + UpdateInfo newUpdateInfo; + + if (!silentUpdate) + api.ShowMsg("Please wait...", "Checking for new update"); + + using var updateManager = await GitHubUpdateManager(GitHubRepository); + - try - { // UpdateApp CheckForUpdate will return value only if the app is squirrel installed newUpdateInfo = await updateManager.CheckForUpdate().NonNull(); - } - catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException) - { - Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to api.github.com.", e); - updateManager.Dispose(); - return; - } - var newReleaseVersion = Version.Parse(newUpdateInfo.FutureReleaseEntry.Version.ToString()); - var currentVersion = Version.Parse(Constant.Version); + var newReleaseVersion = Version.Parse(newUpdateInfo.FutureReleaseEntry.Version.ToString()); + var currentVersion = Version.Parse(Constant.Version); - Log.Info($"|Updater.UpdateApp|Future Release <{newUpdateInfo.FutureReleaseEntry.Formatted()}>"); + Log.Info($"|Updater.UpdateApp|Future Release <{newUpdateInfo.FutureReleaseEntry.Formatted()}>"); + + if (newReleaseVersion <= currentVersion) + { + if (!silentUpdate) + MessageBox.Show("You already have the latest Flow Launcher version"); + updateManager.Dispose(); + return; + } - if (newReleaseVersion <= currentVersion) - { if (!silentUpdate) - MessageBox.Show("You already have the latest Flow Launcher version"); - updateManager.Dispose(); - return; - } + api.ShowMsg("Update found", "Updating..."); - if (!silentUpdate) - api.ShowMsg("Update found", "Updating..."); - - try - { await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply); + + await updateManager.ApplyReleases(newUpdateInfo); + + if (DataLocation.PortableDataLocationInUse()) + { + var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}"; + FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination); + if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination)) + MessageBox.Show("Flow Launcher was not able to move your user profile data to the new update version. Please manually " + + $"move your profile data folder from {DataLocation.PortableDataPath} to {targetDestination}"); + } + else + { + await updateManager.CreateUninstallerRegistryEntry(); + } + + var newVersionTips = NewVersinoTips(newReleaseVersion.ToString()); + + Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}"); + + if (MessageBox.Show(newVersionTips, "New Update", MessageBoxButton.YesNo) == MessageBoxResult.Yes) + { + UpdateManager.RestartApp(Constant.ApplicationFileName); + } } catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException) { Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e); - updateManager.Dispose(); + api.ShowMsg("Update Fail!", "Check your connection and proxy settings to github-cloud.s3.amazonaws.com."); return; } - - await updateManager.ApplyReleases(newUpdateInfo); - - if (DataLocation.PortableDataLocationInUse()) - { - var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}"; - FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination); - if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination)) - MessageBox.Show("Flow Launcher was not able to move your user profile data to the new update version. Please manually " + - $"move your profile data folder from {DataLocation.PortableDataPath} to {targetDestination}"); - } - else - { - await updateManager.CreateUninstallerRegistryEntry(); - } - - var newVersionTips = NewVersinoTips(newReleaseVersion.ToString()); - - Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}"); - - // always dispose UpdateManager - updateManager.Dispose(); - - if (MessageBox.Show(newVersionTips, "New Update", MessageBoxButton.YesNo) == MessageBoxResult.Yes) - { - UpdateManager.RestartApp(Constant.ApplicationFileName); - } } [UsedImplicitly] From adad5ae83bd8e036bcf93e2df174d4dbe7d2380e Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 20 Dec 2020 21:10:55 +1100 Subject: [PATCH 018/158] add url website in Settings --- Flow.Launcher.Infrastructure/Constant.cs | 2 ++ Flow.Launcher/SettingWindow.xaml | 4 ++-- Flow.Launcher/ViewModel/SettingWindowViewModel.cs | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Constant.cs b/Flow.Launcher.Infrastructure/Constant.cs index df1464048..3dba35f8d 100644 --- a/Flow.Launcher.Infrastructure/Constant.cs +++ b/Flow.Launcher.Infrastructure/Constant.cs @@ -35,5 +35,7 @@ namespace Flow.Launcher.Infrastructure public const string DefaultTheme = "Darker"; public const string Themes = "Themes"; + + public const string Website = "https://flow-launcher.github.io"; } } diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 32f9e9a6e..e47f0e779 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -429,8 +429,8 @@ - - + + diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index 853925852..96ee5a052 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -450,7 +450,7 @@ namespace Flow.Launcher.ViewModel #region about - public string Github => _updater.GitHubRepository; + public string Website => Constant.Website; public string ReleaseNotes => _updater.GitHubRepository + @"/releases/latest"; public static string Version => Constant.Version; public string ActivatedTimes => string.Format(_translater.GetTranslation("about_activate_times"), Settings.ActivateTimes); From a9a62a5847b80e008ee00b6bce7b305e17822e88 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 21 Dec 2020 08:57:22 +1100 Subject: [PATCH 019/158] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f0c08d8a0..5f3f7e1a7 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Windows may complain about security due to code not being signed, this will be c - Open flow's search window: Alt+Space is the default hotkey. - Open context menu: Ctrl+O/Shift+Enter. - Cancel/Return to previous screen: Esc. -- Install/Uninstall plugins: in the search window, type `wpm install/uninstall` + the plugin name. +- Install/Uninstall/Update plugins: in the search window, type `pm`/`pm uninstall`/`pm update` + the plugin name. - Saved user settings are located: - If using roaming: `%APPDATA%\FlowLauncher` - If using portable, by default: `%localappdata%\FlowLauncher\app-\UserData` From acd631be081c2fd044672c997eeb8998246b773d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Mon, 21 Dec 2020 19:09:18 +0800 Subject: [PATCH 020/158] Change wording --- Flow.Launcher.Core/Updater.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 3369526e0..4e211c3ac 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -89,7 +89,7 @@ namespace Flow.Launcher.Core catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException) { Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e); - api.ShowMsg("Update Fail!", "Check your connection and proxy settings to github-cloud.s3.amazonaws.com."); + api.ShowMsg("Update Failed", "Check your connection and try updating proxy settings to github-cloud.s3.amazonaws.com."); return; } } From 85f5766022ec2282dd7cf2595c48785964e3433f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Mon, 21 Dec 2020 19:30:18 +0800 Subject: [PATCH 021/158] Optimize a few code --- Flow.Launcher.Infrastructure/Http/Http.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 34665217c..4ec4f887f 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -75,12 +75,12 @@ namespace Flow.Launcher.Infrastructure.Http } } - public async static Task Download([NotNull] string url, [NotNull] string filePath) + public static async Task Download([NotNull] string url, [NotNull] string filePath) { using var response = await client.GetAsync(url); if (response.StatusCode == HttpStatusCode.OK) { - using var fileStream = new FileStream(filePath, FileMode.CreateNew); + await using var fileStream = new FileStream(filePath, FileMode.CreateNew); await response.Content.CopyToAsync(fileStream); } else @@ -93,7 +93,7 @@ namespace Flow.Launcher.Infrastructure.Http { Log.Debug($"|Http.Get|Url <{url}>"); var response = await client.GetAsync(url); - using var stream = await response.Content.ReadAsStreamAsync(); + await using var stream = await response.Content.ReadAsStreamAsync(); using var reader = new StreamReader(stream, Encoding.GetEncoding(encoding)); var content = await reader.ReadToEndAsync(); if (response.StatusCode == HttpStatusCode.OK) From 96609f797e672021664c788f1d0acde1afec40ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Mon, 21 Dec 2020 19:42:50 +0800 Subject: [PATCH 022/158] Change the place of Wait in PluginManifest to make code more elegent --- .../Models/PluginsManifest.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs index 290221710..13a5ae2ca 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs @@ -10,21 +10,19 @@ namespace Flow.Launcher.Plugin.PluginsManager.Models internal class PluginsManifest { internal List UserPlugins { get; private set; } + internal PluginsManifest() { - DownloadManifest(); + DownloadManifest().Wait(); } - private void DownloadManifest() + private 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")); - - t.Wait(); + json = await Http.Get( + "https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher.PluginsManifest/main/plugins.json"); UserPlugins = JsonConvert.DeserializeObject>(json); } @@ -34,7 +32,6 @@ namespace Flow.Launcher.Plugin.PluginsManager.Models UserPlugins = new List(); } - } } -} +} \ No newline at end of file From 5ab8c4faa3bc858af489096e3b8f4e9f9e290d09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Mon, 21 Dec 2020 20:55:28 +0800 Subject: [PATCH 023/158] Update Proxy every time calling a http request method since the proxy setting won't update automatically without action --- Flow.Launcher.Infrastructure/Http/Http.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 4ec4f887f..a88d868a6 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -47,7 +47,14 @@ namespace Flow.Launcher.Infrastructure.Http } } - public static WebProxy WebProxy { get; private set; } = new WebProxy(); + private static WebProxy _proxy = new WebProxy(); + public static WebProxy WebProxy { + get + { + UpdateProxy(); + return _proxy; + } + } /// /// Update the Address of the Proxy to modify the client Proxy @@ -77,6 +84,7 @@ namespace Flow.Launcher.Infrastructure.Http public static async Task Download([NotNull] string url, [NotNull] string filePath) { + UpdateProxy(); using var response = await client.GetAsync(url); if (response.StatusCode == HttpStatusCode.OK) { @@ -91,6 +99,7 @@ namespace Flow.Launcher.Infrastructure.Http public static async Task Get([NotNull] string url, string encoding = "UTF-8") { + UpdateProxy(); Log.Debug($"|Http.Get|Url <{url}>"); var response = await client.GetAsync(url); await using var stream = await response.Content.ReadAsStreamAsync(); From 88fa862277eeff5a49058e72f3ec157ec817aa0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Tue, 22 Dec 2020 00:31:00 +0800 Subject: [PATCH 024/158] Use event triggered update method instead of checking Proxy every time doing Http request --- Flow.Launcher.Infrastructure/Http/Http.cs | 64 +++++++-------- .../UserSettings/HttpProxy.cs | 81 +++++++++++++++++-- 2 files changed, 105 insertions(+), 40 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index a88d868a6..8fe910c0c 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -7,6 +7,7 @@ using JetBrains.Annotations; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; using System; +using System.ComponentModel; namespace Flow.Launcher.Infrastructure.Http { @@ -15,6 +16,7 @@ namespace Flow.Launcher.Infrastructure.Http private const string UserAgent = @"Mozilla/5.0 (Trident/7.0; rv:11.0) like Gecko"; private static HttpClient client; + private static SocketsHttpHandler socketsHttpHandler = new SocketsHttpHandler() { UseProxy = true, @@ -31,60 +33,54 @@ namespace Flow.Launcher.Infrastructure.Http client = new HttpClient(socketsHttpHandler, false); client.DefaultRequestHeaders.Add("User-Agent", UserAgent); - } + private static HttpProxy proxy; + public static HttpProxy Proxy { - private get - { - return proxy; - } + private get { return proxy; } set { proxy = value; - UpdateProxy(); + proxy.PropertyChanged += UpdateProxy; } } - private static WebProxy _proxy = new WebProxy(); - public static WebProxy WebProxy { - get - { - UpdateProxy(); - return _proxy; - } + private static readonly WebProxy _proxy = new WebProxy(); + + public static WebProxy WebProxy + { + get { return _proxy; } } /// /// Update the Address of the Proxy to modify the client Proxy /// - public static void UpdateProxy() - // TODO: need test with a proxy + public static void UpdateProxy(ProxyProperty property) { - if (Proxy != null && Proxy.Enabled && !string.IsNullOrEmpty(Proxy.Server)) + (_proxy.Address, _proxy.Credentials) = property switch { - if (string.IsNullOrEmpty(Proxy.UserName) || string.IsNullOrEmpty(Proxy.Password)) + ProxyProperty.Enabled => (Proxy.Enabled) switch { - WebProxy.Address = new Uri($"http://{Proxy.Server}:{Proxy.Port}"); - WebProxy.Credentials = null; - } - else - { - WebProxy.Address = new Uri($"http://{Proxy.Server}:{Proxy.Port}"); - WebProxy.Credentials = new NetworkCredential(Proxy.UserName, Proxy.Password); - } - } - else - { - WebProxy.Address = new WebProxy().Address; - WebProxy.Credentials = null; - } + true => Proxy.UserName switch + { + var userName when !string.IsNullOrEmpty(userName) => + (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), null), + _ => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), + new NetworkCredential(Proxy.UserName, Proxy.Password)) + }, + false => (null, null) + }, + ProxyProperty.Server => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), _proxy.Credentials), + ProxyProperty.Port => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), _proxy.Credentials), + ProxyProperty.UserName => (_proxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)), + ProxyProperty.Password => (_proxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)) + }; } public static async Task Download([NotNull] string url, [NotNull] string filePath) { - UpdateProxy(); using var response = await client.GetAsync(url); if (response.StatusCode == HttpStatusCode.OK) { @@ -99,7 +95,6 @@ namespace Flow.Launcher.Infrastructure.Http public static async Task Get([NotNull] string url, string encoding = "UTF-8") { - UpdateProxy(); Log.Debug($"|Http.Get|Url <{url}>"); var response = await client.GetAsync(url); await using var stream = await response.Content.ReadAsStreamAsync(); @@ -111,7 +106,8 @@ namespace Flow.Launcher.Infrastructure.Http } else { - throw new HttpRequestException($"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>"); + throw new HttpRequestException( + $"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>"); } } } diff --git a/Flow.Launcher.Infrastructure/UserSettings/HttpProxy.cs b/Flow.Launcher.Infrastructure/UserSettings/HttpProxy.cs index c1b0c1dd7..213193526 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/HttpProxy.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/HttpProxy.cs @@ -1,11 +1,80 @@ -namespace Flow.Launcher.Infrastructure.UserSettings +using System.ComponentModel; + +namespace Flow.Launcher.Infrastructure.UserSettings { + public enum ProxyProperty + { + Enabled, + Server, + Port, + UserName, + Password + } + public class HttpProxy { - public bool Enabled { get; set; } = false; - public string Server { get; set; } - public int Port { get; set; } - public string UserName { get; set; } - public string Password { get; set; } + private bool _enabled = false; + private string _server; + private int _port; + private string _userName; + private string _password; + + public bool Enabled + { + get => _enabled; + set + { + _enabled = value; + OnPropertyChanged(ProxyProperty.Enabled); + } + } + + public string Server + { + get => _server; + set + { + _server = value; + OnPropertyChanged(ProxyProperty.Server); + } + } + + public int Port + { + get => _port; + set + { + _port = value; + OnPropertyChanged(ProxyProperty.Port); + } + } + + public string UserName + { + get => _userName; + set + { + _userName = value; + OnPropertyChanged(ProxyProperty.UserName); + } + } + + public string Password + { + get => _password; + set + { + _password = value; + OnPropertyChanged(ProxyProperty.Password); + } + } + + public delegate void ProxyPropertyChangedHandler(ProxyProperty property); + public event ProxyPropertyChangedHandler PropertyChanged; + + private void OnPropertyChanged(ProxyProperty property) + { + PropertyChanged?.Invoke(property); + } } } \ No newline at end of file From 93486783e5fdb5f5e14abc1df1b4c5e8adccb1eb Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 23 Dec 2020 06:28:59 +1100 Subject: [PATCH 025/158] update plugin repo url --- Flow.Launcher/ViewModel/SettingWindowViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index 96ee5a052..c122f8037 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -213,7 +213,7 @@ namespace Flow.Launcher.ViewModel #region plugin - public static string Plugin => "http://www.wox.one/plugin"; + public static string Plugin => @"https://github.com/Flow-Launcher/Flow.Launcher.PluginsManifest"; public PluginViewModel SelectedPlugin { get; set; } public IList PluginViewModels From 34f51927cf5955d49174a45cbb0eee60802b7466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sun, 27 Dec 2020 21:13:25 +0800 Subject: [PATCH 026/158] 1. Move PluginsManager constuction to Init(). 2. Return HotKeys list when query is like "pm *" --- Flow.Launcher.Infrastructure/Http/Http.cs | 2 +- .../Main.cs | 34 ++- .../PluginsManager.cs | 250 ++++++++++-------- .../Settings.cs | 46 +++- 4 files changed, 201 insertions(+), 131 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 1d5f240e1..2d798ec11 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -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; diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 43f92e7b9..6cf9e1608 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -3,13 +3,15 @@ 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; namespace Flow.Launcher.Plugin.PluginsManager { public class Main : ISettingProvider, IPlugin, ISavable, IContextMenu, IPluginI18n { - internal PluginInitContext Context { get; set; } + internal static PluginInitContext Context { get; set; } internal Settings Settings; @@ -17,6 +19,8 @@ namespace Flow.Launcher.Plugin.PluginsManager private IContextMenu contextMenu; + internal PluginsManager pluginManager; + public Control CreateSettingPanel() { return new PluginsManagerSettings(viewModel); @@ -28,6 +32,7 @@ namespace Flow.Launcher.Plugin.PluginsManager viewModel = new SettingsViewModel(context); Settings = viewModel.Settings; contextMenu = new ContextMenu(Context, Settings); + pluginManager = new PluginsManager(Context, Settings); } public List LoadContextMenus(Result selectedResult) @@ -38,18 +43,21 @@ namespace Flow.Launcher.Plugin.PluginsManager public List Query(Query query) { var search = query.Search.ToLower(); + + if (string.IsNullOrWhiteSpace(search)) + return Settings.HotKeys; - var pluginManager = new PluginsManager(Context, Settings); - - if (!string.IsNullOrEmpty(search) - && ($"{Settings.HotkeyUninstall} ".StartsWith(search) || search.StartsWith($"{Settings.HotkeyUninstall} "))) - return pluginManager.RequestUninstall(search); - - 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), + _ => Settings.HotKeys.Where(hotkey => + { + hotkey.Score = StringMatcher.FuzzySearch(search, hotkey.Title).Score; + return hotkey.Score > 0; + }).ToList() + }; } public void Save() @@ -67,4 +75,4 @@ namespace Flow.Launcher.Plugin.PluginsManager return Context.API.GetTranslation("plugin_pluginsmanager_plugin_description"); } } -} +} \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 90f3277fb..23d2756ae 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -20,7 +20,8 @@ namespace Flow.Launcher.Plugin.PluginsManager private Settings Settings { get; set; } private bool shouldHideWindow = true; - private bool ShouldHideWindow + + private bool ShouldHideWindow { set { shouldHideWindow = value; } get @@ -42,18 +43,21 @@ namespace Flow.Launcher.Plugin.PluginsManager Context = context; Settings = settings; } + 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 +70,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 +82,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 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 +117,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 != pluginFromManifest.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 { - 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 + { + 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,37 +194,37 @@ 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 RequestInstallOrUpdate(string searchName) { 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); } @@ -253,10 +267,10 @@ namespace Flow.Launcher.Plugin.PluginsManager internal List 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 +278,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); } @@ -300,6 +316,7 @@ namespace Flow.Launcher.Plugin.PluginsManager using var _ = File.CreateText(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt")); } + private List AutoCompleteReturnAllResults(string search, string hotkey, string title, string subtitle) { if (!string.IsNullOrEmpty(search) @@ -317,8 +334,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; } @@ -329,4 +347,4 @@ namespace Flow.Launcher.Plugin.PluginsManager return new List(); } } -} +} \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs index e2e8d22e5..5fc3916c0 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs @@ -6,8 +6,52 @@ 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"; + + internal readonly string icoPath = "Images\\pluginsmanager.png"; + + + internal List HotKeys + { + get + { + return new List() + { + new Result() + { + Title = HotKeyInstall, + IcoPath = icoPath, + Action = _ => + { + Main.Context.API.ChangeQuery("pm install "); + return false; + } + }, + new Result() + { + Title = HotkeyUninstall, + IcoPath = icoPath, + Action = _ => + { + Main.Context.API.ChangeQuery("pm uninstall "); + return false; + } + }, + new Result() + { + Title = HotkeyUpdate, + IcoPath = icoPath, + Action = _ => + { + Main.Context.API.ChangeQuery("pm update "); + return false; + } + } + }; + } + } } -} +} \ No newline at end of file From deaac1c7e48ff6ef370a4127247fc22cb94a8f26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 27 Dec 2020 22:33:12 +0800 Subject: [PATCH 027/158] replace install from search keyword --- Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 23d2756ae..bd998514a 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -207,6 +207,8 @@ namespace Flow.Launcher.Plugin.PluginsManager internal List RequestInstallOrUpdate(string searchName) { + var searchNameWithoutKeyword = searchName.Replace(Settings.HotKeyInstall, string.Empty).Trim(); + var results = pluginsManifest .UserPlugins @@ -226,7 +228,7 @@ namespace Flow.Launcher.Plugin.PluginsManager ContextData = x }); - return Search(results, searchName); + return Search(results, searchNameWithoutKeyword); } private void Install(UserPlugin plugin, string downloadedFilePath) From 5a0358718cd9b76b71da0c885c853f39e7e09b60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 28 Dec 2020 23:06:47 +0800 Subject: [PATCH 028/158] Update PluginManifest if last update time is 12 hours ago --- .../Flow.Launcher.Plugin.PluginsManager/Main.cs | 16 +++++++++++++++- .../Models/PluginsManifest.cs | 10 +++------- .../PluginsManager.cs | 8 +++++++- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 6cf9e1608..9aa024fc8 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -6,6 +6,8 @@ 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 { @@ -21,6 +23,8 @@ namespace Flow.Launcher.Plugin.PluginsManager internal PluginsManager pluginManager; + internal DateTime _lastUpdateTime; + public Control CreateSettingPanel() { return new PluginsManagerSettings(viewModel); @@ -33,6 +37,7 @@ namespace Flow.Launcher.Plugin.PluginsManager Settings = viewModel.Settings; contextMenu = new ContextMenu(Context, Settings); pluginManager = new PluginsManager(Context, Settings); + _lastUpdateTime = DateTime.Now; } public List LoadContextMenus(Result selectedResult) @@ -43,10 +48,19 @@ namespace Flow.Launcher.Plugin.PluginsManager public List Query(Query query) { var search = query.Search.ToLower(); - + if (string.IsNullOrWhiteSpace(search)) return Settings.HotKeys; + if ((DateTime.Now - _lastUpdateTime).TotalSeconds > 43200) // 12 hours + { + Task.Run(async () => + { + await pluginManager.UpdateManifest(); + _lastUpdateTime = DateTime.Now; + }); + } + return search switch { var s when s.StartsWith(Settings.HotKeyInstall) => pluginManager.RequestInstallOrUpdate(s), diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs index 290221710..1c31432b2 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs @@ -12,19 +12,15 @@ namespace Flow.Launcher.Plugin.PluginsManager.Models internal List UserPlugins { get; private set; } internal PluginsManifest() { - DownloadManifest(); + Task.Run(() => 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")); - - t.Wait(); + json = await Http.Get("https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher.PluginsManifest/main/plugins.json"); UserPlugins = JsonConvert.DeserializeObject>(json); } diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index bd998514a..c6f127387 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -7,13 +7,14 @@ 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; } @@ -44,6 +45,11 @@ namespace Flow.Launcher.Plugin.PluginsManager Settings = settings; } + internal async Task UpdateManifest() + { + await pluginsManifest.DownloadManifest(); + } + internal void InstallOrUpdate(UserPlugin plugin) { if (PluginExists(plugin.ID)) From 00457ddc29aad8088eac5439d0be10f2368a82f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 29 Dec 2020 12:08:41 +0800 Subject: [PATCH 029/158] change totalSeconds to Hours --- Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 9aa024fc8..fe11b619e 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -52,7 +52,7 @@ namespace Flow.Launcher.Plugin.PluginsManager if (string.IsNullOrWhiteSpace(search)) return Settings.HotKeys; - if ((DateTime.Now - _lastUpdateTime).TotalSeconds > 43200) // 12 hours + if ((DateTime.Now - _lastUpdateTime).TotalHours > 12) // 12 hours { Task.Run(async () => { From bb9682f7918ec6afa8ef0441dbe09fa8306a3b4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 29 Dec 2020 12:14:59 +0800 Subject: [PATCH 030/158] Move get default hotkeys to pluginmanger and make Main.Context non-static --- .../Main.cs | 2 +- .../PluginsManager.cs | 37 +++++++++++++++++ .../Settings.cs | 40 ------------------- 3 files changed, 38 insertions(+), 41 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index fe11b619e..8d99bf37d 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -13,7 +13,7 @@ namespace Flow.Launcher.Plugin.PluginsManager { public class Main : ISettingProvider, IPlugin, ISavable, IContextMenu, IPluginI18n { - internal static PluginInitContext Context { get; set; } + internal PluginInitContext Context { get; set; } internal Settings Settings; diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index c6f127387..29ff9cb54 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -50,6 +50,43 @@ namespace Flow.Launcher.Plugin.PluginsManager await pluginsManifest.DownloadManifest(); } + internal List GetDefaultHotKeys() + { + return new List() + { + 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)) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs index 5fc3916c0..3753056f7 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs @@ -12,46 +12,6 @@ namespace Flow.Launcher.Plugin.PluginsManager internal string HotkeyUpdate { get; set; } = "update"; internal readonly string icoPath = "Images\\pluginsmanager.png"; - - - internal List HotKeys - { - get - { - return new List() - { - new Result() - { - Title = HotKeyInstall, - IcoPath = icoPath, - Action = _ => - { - Main.Context.API.ChangeQuery("pm install "); - return false; - } - }, - new Result() - { - Title = HotkeyUninstall, - IcoPath = icoPath, - Action = _ => - { - Main.Context.API.ChangeQuery("pm uninstall "); - return false; - } - }, - new Result() - { - Title = HotkeyUpdate, - IcoPath = icoPath, - Action = _ => - { - Main.Context.API.ChangeQuery("pm update "); - return false; - } - } - }; - } } } } \ No newline at end of file From f32e202746397e6542f5a831b8b675ce5e1878c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 29 Dec 2020 12:20:13 +0800 Subject: [PATCH 031/158] Use System.Text.JsonSerializer.DeserializeAsync instead of Newtonsoft.Json since our plugins.json can be large, so no need to create an extra string to store it. --- Flow.Launcher.Infrastructure/Http/Http.cs | 13 +++++++++++++ .../Models/PluginsManifest.cs | 8 ++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 2d798ec11..62cacc18f 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -73,5 +73,18 @@ namespace Flow.Launcher.Infrastructure.Http return content; } + + public static async Task GetStreamAsync([NotNull] string url, string encoding = "UTF-8") + { + 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(); + } } } \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs index 1c31432b2..2ab8806bc 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs @@ -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 @@ -17,12 +17,12 @@ namespace Flow.Launcher.Plugin.PluginsManager.Models internal async Task DownloadManifest() { - var json = string.Empty; try { - json = await Http.Get("https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher.PluginsManifest/main/plugins.json"); + var jsonStream = await Http.GetStreamAsync("https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher.PluginsManifest/main/plugins.json") + .ConfigureAwait(false); - UserPlugins = JsonConvert.DeserializeObject>(json); + UserPlugins = await JsonSerializer.DeserializeAsync>(jsonStream).ConfigureAwait(false); } catch (Exception e) { From f0267475f520f9bd53981201dabc72e22877e2c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 29 Dec 2020 12:23:15 +0800 Subject: [PATCH 032/158] Change call method for default hotkeys. Remove extra whitespace add await using for stream --- Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs | 4 ++-- .../Models/PluginsManifest.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 8d99bf37d..607b2e3f5 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -50,7 +50,7 @@ namespace Flow.Launcher.Plugin.PluginsManager var search = query.Search.ToLower(); if (string.IsNullOrWhiteSpace(search)) - return Settings.HotKeys; + return pluginManager.GetDefaultHotKeys(); if ((DateTime.Now - _lastUpdateTime).TotalHours > 12) // 12 hours { @@ -66,7 +66,7 @@ namespace Flow.Launcher.Plugin.PluginsManager 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), - _ => Settings.HotKeys.Where(hotkey => + _ => pluginManager.GetDefaultHotKeys().Where(hotkey => { hotkey.Score = StringMatcher.FuzzySearch(search, hotkey.Title).Score; return hotkey.Score > 0; diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs index 2ab8806bc..c854b213d 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs @@ -19,7 +19,7 @@ namespace Flow.Launcher.Plugin.PluginsManager.Models { try { - var jsonStream = await Http.GetStreamAsync("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); UserPlugins = await JsonSerializer.DeserializeAsync>(jsonStream).ConfigureAwait(false); @@ -33,4 +33,4 @@ namespace Flow.Launcher.Plugin.PluginsManager.Models } } -} +} \ No newline at end of file From 5da8259228120ccbfed0662de1131bd3a95fed85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 29 Dec 2020 13:03:38 +0800 Subject: [PATCH 033/158] fix an unintended text error add async await to download manifest task remove unused encoding name for new GetStreamAsync in Http fix unintened text error --- Flow.Launcher.Infrastructure/Http/Http.cs | 2 +- .../Models/PluginsManifest.cs | 2 +- Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 62cacc18f..ea412759e 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -74,7 +74,7 @@ namespace Flow.Launcher.Infrastructure.Http return content; } - public static async Task GetStreamAsync([NotNull] string url, string encoding = "UTF-8") + public static async Task GetStreamAsync([NotNull] string url) { Log.Debug($"|Http.Get|Url <{url}>"); var request = WebRequest.CreateHttp(url); diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs index c854b213d..7b2f8e372 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs @@ -12,7 +12,7 @@ namespace Flow.Launcher.Plugin.PluginsManager.Models internal List UserPlugins { get; private set; } internal PluginsManifest() { - Task.Run(() => DownloadManifest()).Wait(); + Task.Run(async () => await DownloadManifest()).Wait(); } internal async Task DownloadManifest() diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs index 3753056f7..d5d78d28a 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs @@ -12,6 +12,6 @@ namespace Flow.Launcher.Plugin.PluginsManager internal string HotkeyUpdate { get; set; } = "update"; internal readonly string icoPath = "Images\\pluginsmanager.png"; - } + } } \ No newline at end of file From c94a2f4c9599c49ad00b3e524b28ec044eb22868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 29 Dec 2020 13:06:08 +0800 Subject: [PATCH 034/158] Use string.CompareTo instead of equal to check for update --- Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 29ff9cb54..2931afe30 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -164,7 +164,7 @@ namespace Flow.Launcher.Plugin.PluginsManager from existingPlugin in Context.API.GetAllPlugins() join pluginFromManifest in pluginsManifest.UserPlugins on existingPlugin.Metadata.ID equals pluginFromManifest.ID - where existingPlugin.Metadata.Version != pluginFromManifest.Version + where existingPlugin.Metadata.Version.CompareTo(pluginFromManifest.Version) > 0 select new { From a1a45a49890001150b8d4c78f8f8b3c763402565 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 29 Dec 2020 17:49:11 +1100 Subject: [PATCH 035/158] add eof --- Flow.Launcher.Infrastructure/Http/Http.cs | 2 +- Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs | 2 +- .../Models/PluginsManifest.cs | 2 +- Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs | 3 +-- Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index ea412759e..9d2fe7860 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -87,4 +87,4 @@ namespace Flow.Launcher.Infrastructure.Http return response.GetResponseStream().NonNull(); } } -} \ No newline at end of file +} diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 607b2e3f5..2d9a29a2f 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -89,4 +89,4 @@ namespace Flow.Launcher.Plugin.PluginsManager return Context.API.GetTranslation("plugin_pluginsmanager_plugin_description"); } } -} \ No newline at end of file +} diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs index 7b2f8e372..ad8185620 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs @@ -33,4 +33,4 @@ namespace Flow.Launcher.Plugin.PluginsManager.Models } } -} \ No newline at end of file +} diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 2931afe30..d06bbf1f9 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -361,7 +361,6 @@ namespace Flow.Launcher.Plugin.PluginsManager using var _ = File.CreateText(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt")); } - private List AutoCompleteReturnAllResults(string search, string hotkey, string title, string subtitle) { if (!string.IsNullOrEmpty(search) @@ -392,4 +391,4 @@ namespace Flow.Launcher.Plugin.PluginsManager return new List(); } } -} \ No newline at end of file +} diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs index d5d78d28a..a885bfd15 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs @@ -14,4 +14,4 @@ namespace Flow.Launcher.Plugin.PluginsManager internal readonly string icoPath = "Images\\pluginsmanager.png"; } -} \ No newline at end of file +} From f9349a64e96226b725500752755834ff1d493dea Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 29 Dec 2020 17:49:37 +1100 Subject: [PATCH 036/158] fix condition of version compare --- Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index d06bbf1f9..9635648d4 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -164,7 +164,7 @@ namespace Flow.Launcher.Plugin.PluginsManager 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 + where existingPlugin.Metadata.Version.CompareTo(pluginFromManifest.Version) < 0 // if current version precedes manifest version select new { From e5b67ea10a43527c99e62317f466e2b14c467995 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 29 Dec 2020 18:04:23 +1100 Subject: [PATCH 037/158] remove obsolete icoPath from settings --- Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs index a885bfd15..9c5b0d29f 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs @@ -10,8 +10,5 @@ namespace Flow.Launcher.Plugin.PluginsManager internal string HotkeyUninstall { get; set; } = "uninstall"; internal string HotkeyUpdate { get; set; } = "update"; - - internal readonly string icoPath = "Images\\pluginsmanager.png"; - } } From 0a47636bc9e0427341d97aa471a67a94fef38b3c Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 29 Dec 2020 18:13:52 +1100 Subject: [PATCH 038/158] remove obsolete settings property in context menu class --- Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs | 7 ++----- Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs | 3 +-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs index 76cb0f86b..b9bf5ee85 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin.PluginsManager.Models; using System; using System.Collections.Generic; @@ -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 LoadContextMenus(Result selectedResult) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 2d9a29a2f..a333b3862 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -1,5 +1,4 @@ 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; @@ -35,7 +34,7 @@ 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; } From a1d1c95eebb83867c4581759076b6167c7c0f65b Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 29 Dec 2020 18:14:22 +1100 Subject: [PATCH 039/158] change to tab in browser --- Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs index b9bf5ee85..7bc357be4 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin.PluginsManager.Models; using System; using System.Collections.Generic; @@ -55,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; } }, @@ -66,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; } } From b106688513f4d463656bd61c43034bcf71288acd Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 29 Dec 2020 18:14:33 +1100 Subject: [PATCH 040/158] version bump --- Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json index e970e5a8e..d94af71a1 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json @@ -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", From 000bafd116910a5e8fcb919640be35f9ccfa2495 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 29 Dec 2020 18:56:51 +1100 Subject: [PATCH 041/158] use camelCase and make last update time variable private --- Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index a333b3862..716a424ff 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -22,7 +22,7 @@ namespace Flow.Launcher.Plugin.PluginsManager internal PluginsManager pluginManager; - internal DateTime _lastUpdateTime; + private DateTime lastUpdateTime; public Control CreateSettingPanel() { @@ -36,7 +36,7 @@ namespace Flow.Launcher.Plugin.PluginsManager Settings = viewModel.Settings; contextMenu = new ContextMenu(Context); pluginManager = new PluginsManager(Context, Settings); - _lastUpdateTime = DateTime.Now; + lastUpdateTime = DateTime.Now; } public List LoadContextMenus(Result selectedResult) @@ -51,12 +51,12 @@ namespace Flow.Launcher.Plugin.PluginsManager if (string.IsNullOrWhiteSpace(search)) return pluginManager.GetDefaultHotKeys(); - if ((DateTime.Now - _lastUpdateTime).TotalHours > 12) // 12 hours + if ((DateTime.Now - lastUpdateTime).TotalHours > 12) // 12 hours { Task.Run(async () => { await pluginManager.UpdateManifest(); - _lastUpdateTime = DateTime.Now; + lastUpdateTime = DateTime.Now; }); } From a47f8b318510db0fe7e5edfacd91c1894e33221b Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 29 Dec 2020 19:06:17 +1100 Subject: [PATCH 042/158] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f3f7e1a7..02f488758 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Windows may complain about security due to code not being signed, this will be c - Open flow's search window: Alt+Space is the default hotkey. - Open context menu: Ctrl+O/Shift+Enter. - Cancel/Return to previous screen: Esc. -- Install/Uninstall/Update plugins: in the search window, type `pm`/`pm uninstall`/`pm update` + the plugin name. +- Install/Uninstall/Update plugins: in the search window, type `pm install`/`pm uninstall`/`pm update` + the plugin name. - Saved user settings are located: - If using roaming: `%APPDATA%\FlowLauncher` - If using portable, by default: `%localappdata%\FlowLauncher\app-\UserData` From cfa93a2cc60808b84a131852a82f3e26ce1a4339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 29 Dec 2020 17:13:31 +0800 Subject: [PATCH 043/158] Add GetStreamAsync method --- Flow.Launcher.Infrastructure/Http/Http.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 74e335d32..11d922aa1 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -110,5 +110,12 @@ namespace Flow.Launcher.Infrastructure.Http $"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>"); } } + + public static async Task GetStreamAsync([NotNull] string url) + { + Log.Debug($"|Http.Get|Url <{url}>"); + var response = await client.GetAsync(url); + return await response.Content.ReadAsStreamAsync(); + } } } From a806f7d05adc9beb7419e555921192e094c10539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 29 Dec 2020 17:14:13 +0800 Subject: [PATCH 044/158] Change exception type --- Flow.Launcher.Infrastructure/Http/Http.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 11d922aa1..a98ead687 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -89,7 +89,7 @@ namespace Flow.Launcher.Infrastructure.Http } else { - throw new WebException($"Error code <{response.StatusCode}> returned from <{url}>"); + throw new HttpRequestException($"Error code <{response.StatusCode}> returned from <{url}>"); } } From 4d5119f17d2f5b307dd7eb33384e63ee31b48c27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 29 Dec 2020 17:15:34 +0800 Subject: [PATCH 045/158] Add out of bound exception for pattern matching --- Flow.Launcher.Infrastructure/Http/Http.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index a98ead687..040939e72 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -61,7 +61,7 @@ namespace Flow.Launcher.Infrastructure.Http { (_proxy.Address, _proxy.Credentials) = property switch { - ProxyProperty.Enabled => (Proxy.Enabled) switch + ProxyProperty.Enabled => Proxy.Enabled switch { true => Proxy.UserName switch { @@ -75,7 +75,8 @@ namespace Flow.Launcher.Infrastructure.Http ProxyProperty.Server => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), _proxy.Credentials), ProxyProperty.Port => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), _proxy.Credentials), ProxyProperty.UserName => (_proxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)), - ProxyProperty.Password => (_proxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)) + ProxyProperty.Password => (_proxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)), + _ => throw new ArgumentOutOfRangeException() }; } From e364b84b8458aa8df058fa9cf4507b2699d3224a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 29 Dec 2020 17:16:44 +0800 Subject: [PATCH 046/158] Use auto property --- Flow.Launcher.Infrastructure/Http/Http.cs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 040939e72..3d056b28b 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -47,19 +47,14 @@ namespace Flow.Launcher.Infrastructure.Http } } - private static readonly WebProxy _proxy = new WebProxy(); - - public static WebProxy WebProxy - { - get { return _proxy; } - } + public static WebProxy WebProxy { get; } = new WebProxy(); /// /// Update the Address of the Proxy to modify the client Proxy /// public static void UpdateProxy(ProxyProperty property) { - (_proxy.Address, _proxy.Credentials) = property switch + (WebProxy.Address, WebProxy.Credentials) = property switch { ProxyProperty.Enabled => Proxy.Enabled switch { @@ -72,10 +67,10 @@ namespace Flow.Launcher.Infrastructure.Http }, false => (null, null) }, - ProxyProperty.Server => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), _proxy.Credentials), - ProxyProperty.Port => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), _proxy.Credentials), - ProxyProperty.UserName => (_proxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)), - ProxyProperty.Password => (_proxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)), + ProxyProperty.Server => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), WebProxy.Credentials), + ProxyProperty.Port => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), WebProxy.Credentials), + ProxyProperty.UserName => (WebProxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)), + ProxyProperty.Password => (WebProxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)), _ => throw new ArgumentOutOfRangeException() }; } From 0c97db04d4b89d345ddb7908c5a6d361c11c0ee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 29 Dec 2020 17:50:56 +0800 Subject: [PATCH 047/158] 1. Change Get method Name to GetAsync 2. Manually replace "#" with "%23" to solve the similar issue in Explorer plugin 3. Add GetAsync method with Uri as argument 4. Remove unused encoding argument 5. Change exception type for WebSearch Plguin 6. Update Comment --- Flow.Launcher.Core/Updater.cs | 2 +- Flow.Launcher.Infrastructure/Http/Http.cs | 25 +++++++++++++++---- .../SuggestionSources/Baidu.cs | 5 ++-- .../SuggestionSources/Google.cs | 6 ++--- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index d8bc2b6dc..46fb6d977 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -133,7 +133,7 @@ namespace Flow.Launcher.Core var uri = new Uri(repository); var api = $"https://api.github.com/repos{uri.AbsolutePath}/releases"; - var json = await Http.Get(api); + var json = await Http.GetAsync(api); var releases = JsonConvert.DeserializeObject>(json); var latest = releases.Where(r => !r.Prerelease).OrderByDescending(r => r.PublishedAt).First(); diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 3d056b28b..8e2832690 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -89,13 +89,23 @@ namespace Flow.Launcher.Infrastructure.Http } } - public static async Task Get([NotNull] string url, string encoding = "UTF-8") + /// + /// Asynchrously get the result as string from url. + /// When supposing the result is long and large, try using GetStreamAsync to avoid reading as string + /// + /// + /// + public static Task GetAsync([NotNull] string url) { Log.Debug($"|Http.Get|Url <{url}>"); - var response = await client.GetAsync(url); - await using var stream = await response.Content.ReadAsStreamAsync(); - using var reader = new StreamReader(stream, Encoding.GetEncoding(encoding)); - var content = await reader.ReadToEndAsync(); + return GetAsync(new Uri(url.Replace("#", "%23"))); + } + + public static async Task GetAsync([NotNull] Uri url) + { + Log.Debug($"|Http.Get|Url <{url}>"); + using var response = await client.GetAsync(url); + var content = await response.Content.ReadAsStringAsync(); if (response.StatusCode == HttpStatusCode.OK) { return content; @@ -107,6 +117,11 @@ namespace Flow.Launcher.Infrastructure.Http } } + /// + /// Asynchrously get the result as stream from url. + /// + /// + /// public static async Task GetStreamAsync([NotNull] string url) { Log.Debug($"|Http.Get|Url <{url}>"); diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs index 57db223bc..6772acf82 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs @@ -8,6 +8,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Infrastructure.Logger; +using System.Net.Http; namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources { @@ -22,9 +23,9 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources try { const string api = "http://suggestion.baidu.com/su?json=1&wd="; - result = await Http.Get(api + Uri.EscapeUriString(query), "GB2312"); + result = await Http.GetAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false); } - catch (WebException e) + catch (HttpRequestException e) { Log.Exception("|Baidu.Suggestions|Can't get suggestion from baidu", e); return new List(); diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs index 81878bd8b..5b9538091 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs @@ -7,6 +7,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Infrastructure.Logger; +using System.Net.Http; namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources { @@ -18,13 +19,12 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources try { const string api = "https://www.google.com/complete/search?output=chrome&q="; - result = await Http.Get(api + Uri.EscapeUriString(query)); + result = await Http.GetAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false); } - catch (WebException e) + catch (HttpRequestException e) { Log.Exception("|Google.Suggestions|Can't get suggestion from google", e); return new List(); - ; } if (string.IsNullOrEmpty(result)) return new List(); JContainer json; From efa4908f37636f3fe6122d241a5821d14a4bf6b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 29 Dec 2020 17:54:39 +0800 Subject: [PATCH 048/158] Change usage of Http in Updater.cs and adding ConfigureAwait(false) for await in checking update --- Flow.Launcher.Core/Updater.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 46fb6d977..05397e906 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -29,7 +29,7 @@ namespace Flow.Launcher.Core GitHubRepository = gitHubRepository; } - public async Task UpdateApp(IPublicAPI api , bool silentUpdate = true) + public async Task UpdateApp(IPublicAPI api, bool silentUpdate = true) { UpdateManager updateManager; UpdateInfo newUpdateInfo; @@ -39,7 +39,7 @@ namespace Flow.Launcher.Core try { - updateManager = await GitHubUpdateManager(GitHubRepository); + updateManager = await GitHubUpdateManager(GitHubRepository).ConfigureAwait(false); } catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException) { @@ -50,7 +50,7 @@ namespace Flow.Launcher.Core try { // UpdateApp CheckForUpdate will return value only if the app is squirrel installed - newUpdateInfo = await updateManager.CheckForUpdate().NonNull(); + newUpdateInfo = await updateManager.CheckForUpdate().NonNull().ConfigureAwait(false); } catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException) { @@ -85,8 +85,8 @@ namespace Flow.Launcher.Core updateManager.Dispose(); return; } - - await updateManager.ApplyReleases(newUpdateInfo); + + await updateManager.ApplyReleases(newUpdateInfo).ConfigureAwait(false); if (DataLocation.PortableDataLocationInUse()) { @@ -98,11 +98,11 @@ namespace Flow.Launcher.Core } else { - await updateManager.CreateUninstallerRegistryEntry(); + await updateManager.CreateUninstallerRegistryEntry().ConfigureAwait(false); } var newVersionTips = NewVersinoTips(newReleaseVersion.ToString()); - + Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}"); // always dispose UpdateManager @@ -133,9 +133,9 @@ namespace Flow.Launcher.Core var uri = new Uri(repository); var api = $"https://api.github.com/repos{uri.AbsolutePath}/releases"; - var json = await Http.GetAsync(api); + var jsonStream = await Http.GetStreamAsync(api).ConfigureAwait(false); - var releases = JsonConvert.DeserializeObject>(json); + var releases = await System.Text.Json.JsonSerializer.DeserializeAsync>(jsonStream).ConfigureAwait(false); var latest = releases.Where(r => !r.Prerelease).OrderByDescending(r => r.PublishedAt).First(); var latestUrl = latest.HtmlUrl.Replace("/tag/", "/download/"); From d4f94c66acfba5dddf0110b857519feed03eb99c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 29 Dec 2020 18:33:53 +0800 Subject: [PATCH 049/158] Make InstallOrUpdate to async --- Flow.Launcher.Core/Updater.cs | 8 ++++---- Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs | 2 +- .../Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs | 7 +++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 05397e906..1e4b0453c 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using System.Windows; using JetBrains.Annotations; using Squirrel; -using Newtonsoft.Json; using Flow.Launcher.Core.Resource; using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.Infrastructure; @@ -17,6 +16,7 @@ using Flow.Launcher.Infrastructure.Logger; using System.IO; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; +using System.Text.Json.Serialization; namespace Flow.Launcher.Core { @@ -117,13 +117,13 @@ namespace Flow.Launcher.Core [UsedImplicitly] private class GithubRelease { - [JsonProperty("prerelease")] + [JsonPropertyName("prerelease")] public bool Prerelease { get; [UsedImplicitly] set; } - [JsonProperty("published_at")] + [JsonPropertyName("published_at")] public DateTime PublishedAt { get; [UsedImplicitly] set; } - [JsonProperty("html_url")] + [JsonPropertyName("html_url")] public string HtmlUrl { get; [UsedImplicitly] set; } } diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 716a424ff..d700b9dfd 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -22,7 +22,7 @@ namespace Flow.Launcher.Plugin.PluginsManager internal PluginsManager pluginManager; - private DateTime lastUpdateTime; + private DateTime lastUpdateTime = DateTime.MinValue; public Control CreateSettingPanel() { diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 9635648d4..428610f43 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -87,7 +87,7 @@ namespace Flow.Launcher.Plugin.PluginsManager }; } - internal void InstallOrUpdate(UserPlugin plugin) + internal async Task InstallOrUpdate(UserPlugin plugin) { if (PluginExists(plugin.ID)) { @@ -127,7 +127,7 @@ namespace Flow.Launcher.Plugin.PluginsManager Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), Context.API.GetTranslation("plugin_pluginsmanager_please_wait")); - Http.Download(plugin.UrlDownload, filePath); + await Http.Download(plugin.UrlDownload, filePath).ConfigureAwait(false); Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), Context.API.GetTranslation("plugin_pluginsmanager_download_success")); @@ -264,8 +264,7 @@ namespace Flow.Launcher.Plugin.PluginsManager Action = e => { Application.Current.MainWindow.Hide(); - InstallOrUpdate(x); - + _ = InstallOrUpdate(x); // No need to wait return ShouldHideWindow; }, ContextData = x From d0743f627668fa5bbd55a3daef37f82064fc3a23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 29 Dec 2020 18:48:00 +0800 Subject: [PATCH 050/158] Await Http.Download in Update method --- .../PluginsManager.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 428610f43..f12112382 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -140,11 +140,8 @@ namespace Flow.Launcher.Plugin.PluginsManager Log.Exception("PluginsManager", "An error occured while downloading plugin", e, "PluginDownload"); } - Application.Current.Dispatcher.Invoke(() => - { - Install(plugin, filePath); - Context.API.RestartApp(); - }); + Install(plugin, filePath); + Context.API.RestartApp(); } internal List RequestUpdate(string search) @@ -211,10 +208,14 @@ namespace Flow.Launcher.Plugin.PluginsManager var downloadToFilePath = Path.Combine(DataLocation.PluginsDirectory, $"{x.Name}-{x.NewVersion}.zip"); - Http.Download(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath); - Install(x.PluginNewUserPlugin, downloadToFilePath); - Context.API.RestartApp(); + Task.Run(async delegate + { + await Http.Download(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath).ConfigureAwait(false); + Install(x.PluginNewUserPlugin, downloadToFilePath); + + Context.API.RestartApp(); + }); return true; } From c485578cff927a1714344c0eca1ab82870673fe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 29 Dec 2020 18:48:55 +0800 Subject: [PATCH 051/158] Use CompareTo to check update for InstallOrUpdate method --- Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index f12112382..ac15618ca 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -92,7 +92,7 @@ namespace Flow.Launcher.Plugin.PluginsManager if (PluginExists(plugin.ID)) { if (Context.API.GetAllPlugins() - .Any(x => x.Metadata.ID == plugin.ID && x.Metadata.Version != plugin.Version)) + .Any(x => x.Metadata.ID == plugin.ID && x.Metadata.Version.CompareTo(plugin.Version) < 0)) { if (MessageBox.Show(Context.API.GetTranslation("plugin_pluginsmanager_update_exists"), Context.API.GetTranslation("plugin_pluginsmanager_update_title"), From e2b0386818dca21d0ce9aba69cbb5331d524e676 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 29 Dec 2020 22:32:14 +1100 Subject: [PATCH 052/158] remove Color plugin from Flow as a default plugin --- Flow.Launcher.sln | 16 --- .../Flow.Launcher.Plugin.Color.csproj | 101 --------------- .../Images/color.png | Bin 1856 -> 0 bytes .../Languages/de.xaml | 8 -- .../Languages/en.xaml | 8 -- .../Languages/pl.xaml | 8 -- .../Languages/sk.xaml | 8 -- .../Languages/tr.xaml | 8 -- .../Languages/zh-cn.xaml | 8 -- .../Languages/zh-tw.xaml | 7 - Plugins/Flow.Launcher.Plugin.Color/Main.cs | 121 ------------------ .../Flow.Launcher.Plugin.Color/plugin.json | 12 -- 12 files changed, 305 deletions(-) delete mode 100644 Plugins/Flow.Launcher.Plugin.Color/Flow.Launcher.Plugin.Color.csproj delete mode 100644 Plugins/Flow.Launcher.Plugin.Color/Images/color.png delete mode 100644 Plugins/Flow.Launcher.Plugin.Color/Languages/de.xaml delete mode 100644 Plugins/Flow.Launcher.Plugin.Color/Languages/en.xaml delete mode 100644 Plugins/Flow.Launcher.Plugin.Color/Languages/pl.xaml delete mode 100644 Plugins/Flow.Launcher.Plugin.Color/Languages/sk.xaml delete mode 100644 Plugins/Flow.Launcher.Plugin.Color/Languages/tr.xaml delete mode 100644 Plugins/Flow.Launcher.Plugin.Color/Languages/zh-cn.xaml delete mode 100644 Plugins/Flow.Launcher.Plugin.Color/Languages/zh-tw.xaml delete mode 100644 Plugins/Flow.Launcher.Plugin.Color/Main.cs delete mode 100644 Plugins/Flow.Launcher.Plugin.Color/plugin.json diff --git a/Flow.Launcher.sln b/Flow.Launcher.sln index 4d8997177..21c3b47dc 100644 --- a/Flow.Launcher.sln +++ b/Flow.Launcher.sln @@ -20,7 +20,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher", "Flow.Launc {F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {F9C4C081-4CC3-4146-95F1-E102B4E10A5F} {59BD9891-3837-438A-958D-ADC7F91F6F7E} = {59BD9891-3837-438A-958D-ADC7F91F6F7E} {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} - {F35190AA-4758-4D9E-A193-E3BDF6AD3567} = {F35190AA-4758-4D9E-A193-E3BDF6AD3567} {9B130CC5-14FB-41FF-B310-0A95B6894C37} = {9B130CC5-14FB-41FF-B310-0A95B6894C37} {FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {FDED22C8-B637-42E8-824A-63B5B6E05A3A} {A3DCCBCA-ACC1-421D-B16E-210896234C26} = {A3DCCBCA-ACC1-421D-B16E-210896234C26} @@ -44,8 +43,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Sys", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Url", "Plugins\Flow.Launcher.Plugin.Url\Flow.Launcher.Plugin.Url.csproj", "{A3DCCBCA-ACC1-421D-B16E-210896234C26}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Color", "Plugins\Flow.Launcher.Plugin.Color\Flow.Launcher.Plugin.Color.csproj", "{F35190AA-4758-4D9E-A193-E3BDF6AD3567}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FFD651C7-0546-441F-BC8C-D4EE8FD01EA7}" ProjectSection(SolutionItems) = preProject .gitattributes = .gitattributes @@ -214,18 +211,6 @@ Global {A3DCCBCA-ACC1-421D-B16E-210896234C26}.Release|x64.Build.0 = Release|Any CPU {A3DCCBCA-ACC1-421D-B16E-210896234C26}.Release|x86.ActiveCfg = Release|Any CPU {A3DCCBCA-ACC1-421D-B16E-210896234C26}.Release|x86.Build.0 = Release|Any CPU - {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|x64.ActiveCfg = Debug|Any CPU - {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|x64.Build.0 = Debug|Any CPU - {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|x86.ActiveCfg = Debug|Any CPU - {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|x86.Build.0 = Debug|Any CPU - {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|Any CPU.Build.0 = Release|Any CPU - {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|x64.ActiveCfg = Release|Any CPU - {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|x64.Build.0 = Release|Any CPU - {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|x86.ActiveCfg = Release|Any CPU - {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|x86.Build.0 = Release|Any CPU {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|Any CPU.Build.0 = Debug|Any CPU {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -309,7 +294,6 @@ Global {FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} {0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} {A3DCCBCA-ACC1-421D-B16E-210896234C26} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} - {F35190AA-4758-4D9E-A193-E3BDF6AD3567} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} {9B130CC5-14FB-41FF-B310-0A95B6894C37} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} {59BD9891-3837-438A-958D-ADC7F91F6F7E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} diff --git a/Plugins/Flow.Launcher.Plugin.Color/Flow.Launcher.Plugin.Color.csproj b/Plugins/Flow.Launcher.Plugin.Color/Flow.Launcher.Plugin.Color.csproj deleted file mode 100644 index c7fe8271a..000000000 --- a/Plugins/Flow.Launcher.Plugin.Color/Flow.Launcher.Plugin.Color.csproj +++ /dev/null @@ -1,101 +0,0 @@ - - - - Library - netcoreapp3.1 - {F35190AA-4758-4D9E-A193-E3BDF6AD3567} - Properties - Flow.Launcher.Plugin.Color - Flow.Launcher.Plugin.Color - true - false - false - - - - true - full - false - ..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.Color\ - DEBUG;TRACE - prompt - 4 - false - - - - pdbonly - true - ..\..\Output\Release\Plugins\Flow.Launcher.Plugin.Color\ - TRACE - prompt - 4 - false - - - - - PreserveNewest - - - - - - PreserveNewest - - - - - - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Color/Images/color.png b/Plugins/Flow.Launcher.Plugin.Color/Images/color.png deleted file mode 100644 index da28583b1c9f174367acef0205f0c47f0a839ecd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1856 zcmV-G2fz4e%OapHR~QiSv}$ z`|SPw_WIV^7ZZNx5B<*d;}3xUcLaZx2=LHTUNG+g(Z&oQa{$CW^yENaxq-_Hz{8y` zB+|nR!VA?35U15cp8n}_fs_|uXnO#l4JE}f2Wac@mfsuz4{qxQDF;jmV{I>O9q2LD zR?=um5#Y|w4k*g=V0Hs=foL4e{dD9=9}hm|1EJlND8>kFJ=QUN>K-pK?EpdaHx2MO zlh)_CKj1Uf4`8m0a%ZOtN@jMwx*aq7cvt)$ve|1)4b15oU;X3{L`HVwUbbm_JuLxg zu9%7dcXjoEXiq6|kv09Hn$25QNg+5rlbrcJJi4~Jxm14)KIqx>MYn81qySml49hy2 zUj05uQO0UEZ)q@u$chKJd33EjSWK!%W+yp-iP;x$0zf>55P9N7J5vBAqF@Ff07I`{ zi%Skb7otG?5&%X#o4)8M6@4=RJ`hQ(OFqCN2&)3&d10G;AtobG7Jyz5J#Qul3fl-f zIUin605=tWXAS^;o=vCqD_^!lc?9Nbzo{fH6q%Za-{!7E#hvx7zX+flNh}8Dt}dyK z3N0kwOI$(_HU;%v>x#A)vIsC(YM+c%OIag&;Dc^J+ri013qF2zlC)Hj6>{%ST?q`? zS0}5TZak9vEOBxEuli?hTNuV4p7nor6^U~o53Ns+qKeaRn9P|e}CKHL8G?1 z0DRzxD+U?{vfE)1g=9QZ3L+Z4;E%;FwSq9_dI$}Q9X0s?iL^TN+E>xA%G5klU+a&q zs4KEnCfW<)Ewwq(3@tYSiO)kIZW0i6?P!i)LCd`5Rz30g5c}H(i*~U^0Q0tC!(C-52vto*rY;921%%@k z_${?a+Z$^s&{$(RIgxD5h+o_2hSj<-yVnHfY;$eq3vI)UUHcy$?9KnY7{Flm^Iwuh zB6jt{$t3+RR(;>B+ws6!92ifX|F6^Oh%T>N$d5(+>DWfk^TJAnQP7FNQI^&Gj90Tc z@#9ZZmoH5xD$}Z#IX)72tiH+_zHL=qL#=D|v%Z(dap;Wg-7mkp8lIS%ZD0+V1}wlbr1=*EsPVa(@V`!kr&FQJ5xP*NQELADt5K+i8pAHE zTX}Kop64Fh5`*>hJMUIce;sxvep*e?GL=k&Ef}qGU!AR6x8fh3^9_}DmhbKW$N@}P zC?_6iRd-AL?Ofddoo!j-RHygm_bM6MmHz*rB2|qltFDD?7In-dL~vi*;QT~oAH5{@ z0{pg-jY^46soKm?EE3iT%@cP&=pS0KK0WSKXzouxS`hoX6~tp$Zf}59DVl9F5T=q? z2sbLDvY9_Nb0wq_no@R0aO|0vHXJ*9PW;dD4QraU$-^NC`v{0t4&BH}Ac|&xKIe3v z%tFSBIO8tVYngaqn{ShItwQ|S9lPAB!%=(m?72+uv5jk>X>$==?w`y+RFNOmcHM={ z03v8j$2&|fWH~0WCNOiSmY8c132Wqe(f*jGetdD=9ge9D4=(sTA0XGK7X9)h(&I7I zr5-o3X%3>ouAL!5|Gr0t3~w-`yHPR7hr368KOiTgfXHDZq){ zqrC+AfkqDi3WW|=sZh1z1C1#FpbtCSukS!(#y%o8MF0wgmi=Yp$~gdsPo17DgfIob z7{Sx+*LNm9@#3ce(%G378fpyd=9h_ z!V~~-5IfsTUT#TqU`a`AAYKo`6hJwWI8#PVMB$KaF5~%}ZH#JGE)uDGEr!R_?7Pz0 zH(ZGe2OsvHaU~tIJ;v3DM-D%6dkfiYrgoG9;+=FrocfIJoBCkUy3yq+&*g3oNZ3gO zyac$J$pv7Xi2EOV{h{6$2I8V#OE18(eT}@at)c_(ed2}2ogj9DX&Quau+l55{rM9t z%>mg0mizvND!5 diff --git a/Plugins/Flow.Launcher.Plugin.Color/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.Color/Languages/de.xaml deleted file mode 100644 index 3244dee14..000000000 --- a/Plugins/Flow.Launcher.Plugin.Color/Languages/de.xaml +++ /dev/null @@ -1,8 +0,0 @@ - - - Farben - Stellt eine HEX-Farben Vorschau bereit. (Versuche #000 in Flow Launcher) - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Color/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Color/Languages/en.xaml deleted file mode 100644 index 85e2830db..000000000 --- a/Plugins/Flow.Launcher.Plugin.Color/Languages/en.xaml +++ /dev/null @@ -1,8 +0,0 @@ - - - Colors - Allows to preview colors using hex values.(Try #000 in Flow Launcher) - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Color/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.Color/Languages/pl.xaml deleted file mode 100644 index 15525cfe9..000000000 --- a/Plugins/Flow.Launcher.Plugin.Color/Languages/pl.xaml +++ /dev/null @@ -1,8 +0,0 @@ - - - Kolory - Podgląd kolorów po wpisaniu ich kodu szesnastkowego. (Spróbuj wpisać #000 w oknie Flow Launchera) - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Color/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.Color/Languages/sk.xaml deleted file mode 100644 index 4b208691a..000000000 --- a/Plugins/Flow.Launcher.Plugin.Color/Languages/sk.xaml +++ /dev/null @@ -1,8 +0,0 @@ - - - Farby - Zobrazuje náhľad farieb v HEX formáte. (Skúste #000 vo Flow Launcheri) - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Color/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.Color/Languages/tr.xaml deleted file mode 100644 index f56e73526..000000000 --- a/Plugins/Flow.Launcher.Plugin.Color/Languages/tr.xaml +++ /dev/null @@ -1,8 +0,0 @@ - - - Renkler - Hex kodunu girdiğiniz renkleri görüntülemeye yarar.(#000 yazmayı deneyin) - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Color/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Color/Languages/zh-cn.xaml deleted file mode 100644 index 39ede4844..000000000 --- a/Plugins/Flow.Launcher.Plugin.Color/Languages/zh-cn.xaml +++ /dev/null @@ -1,8 +0,0 @@ - - - 颜色 - 提供在Flow Launcher查询hex颜色。(尝试在Flow Launcher中输入#000) - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Color/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.Color/Languages/zh-tw.xaml deleted file mode 100644 index 4e7062a22..000000000 --- a/Plugins/Flow.Launcher.Plugin.Color/Languages/zh-tw.xaml +++ /dev/null @@ -1,7 +0,0 @@ - - - 顏色 - 提供在 Flow Launcher 查詢 hex 顏色。(試著在 Flow Launcher 中輸入 #000) - diff --git a/Plugins/Flow.Launcher.Plugin.Color/Main.cs b/Plugins/Flow.Launcher.Plugin.Color/Main.cs deleted file mode 100644 index a15483ebc..000000000 --- a/Plugins/Flow.Launcher.Plugin.Color/Main.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Linq; -using System.Windows; - -namespace Flow.Launcher.Plugin.Color -{ - public sealed class ColorsPlugin : IPlugin, IPluginI18n - { - private string DIR_PATH = Path.Combine(Path.GetTempPath(), @"Plugins\Colors\"); - private PluginInitContext context; - private const int IMG_SIZE = 32; - - private DirectoryInfo ColorsDirectory { get; set; } - - public ColorsPlugin() - { - if (!Directory.Exists(DIR_PATH)) - { - ColorsDirectory = Directory.CreateDirectory(DIR_PATH); - } - else - { - ColorsDirectory = new DirectoryInfo(DIR_PATH); - } - } - - public List Query(Query query) - { - var raw = query.Search; - if (!IsAvailable(raw)) return new List(0); - try - { - var cached = Find(raw); - if (cached.Length == 0) - { - var path = CreateImage(raw); - return new List - { - new Result - { - Title = raw, - IcoPath = path, - Action = _ => - { - Clipboard.SetText(raw); - return true; - } - } - }; - } - return cached.Select(x => new Result - { - Title = raw, - IcoPath = x.FullName, - Action = _ => - { - Clipboard.SetText(raw); - return true; - } - }).ToList(); - } - catch (Exception exception) - { - // todo: log - return new List(0); - } - } - - private bool IsAvailable(string query) - { - // todo: rgb, names - var length = query.Length - 1; // minus `#` sign - return query.StartsWith("#") && (length == 3 || length == 6); - } - - public FileInfo[] Find(string name) - { - var file = string.Format("{0}.png", name.Substring(1)); - return ColorsDirectory.GetFiles(file, SearchOption.TopDirectoryOnly); - } - - private string CreateImage(string name) - { - using (var bitmap = new Bitmap(IMG_SIZE, IMG_SIZE)) - using (var graphics = Graphics.FromImage(bitmap)) - { - var color = ColorTranslator.FromHtml(name); - graphics.Clear(color); - - var path = CreateFileName(name); - bitmap.Save(path, ImageFormat.Png); - return path; - } - } - - private string CreateFileName(string name) - { - return string.Format("{0}{1}.png", ColorsDirectory.FullName, name.Substring(1)); - } - - public void Init(PluginInitContext context) - { - this.context = context; - } - - - public string GetTranslatedPluginTitle() - { - return context.API.GetTranslation("flowlauncher_plugin_color_plugin_name"); - } - - public string GetTranslatedPluginDescription() - { - return context.API.GetTranslation("flowlauncher_plugin_color_plugin_description"); - } - } -} \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Color/plugin.json b/Plugins/Flow.Launcher.Plugin.Color/plugin.json deleted file mode 100644 index 8c0c483ba..000000000 --- a/Plugins/Flow.Launcher.Plugin.Color/plugin.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "ID": "9B36CE6181FC47FBB597AA2C29CD9B0A", - "ActionKeyword": "*", - "Name": "Colors", - "Description": "Provide hex color preview.(Try #000 in Flow Launcher)", - "Author": "qianlifeng", - "Version": "1.1.1", - "Language": "csharp", - "Website": "https://github.com/Flow-Launcher/Flow.Launcher", - "ExecuteFileName": "Flow.Launcher.Plugin.Color.dll", - "IcoPath": "Images\\color.png" -} From 85584f40ee51d23e185ee67e4579859017dd1011 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 29 Dec 2020 22:41:21 +1100 Subject: [PATCH 053/158] remove post build script target of JsonRPC folder copy --- Scripts/post_build.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index 18ce33c4f..59036842a 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -36,7 +36,6 @@ function Copy-Resources ($path, $config) { $output = "$path\Output" $target = "$output\$config" Copy-Item -Recurse -Force $project\Images\* $target\Images\ - Copy-Item -Recurse -Force $path\JsonRPC $target\JsonRPC # making version static as multiple versions can exist in the nuget folder and in the case a breaking change is introduced. Copy-Item -Force $env:USERPROFILE\.nuget\packages\squirrel.windows\1.5.2\tools\Squirrel.exe $output\Update.exe } From d28b14ff2d4cb9a8d58d926f9d2542fd22941758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 30 Dec 2020 13:40:42 +0800 Subject: [PATCH 054/158] Replace All use of Json.Net with System.Text.Json --- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 6 ++-- Flow.Launcher.Core/Plugin/PluginConfig.cs | 4 +-- Flow.Launcher.Core/Updater.cs | 1 - Flow.Launcher.Infrastructure/Helper.cs | 28 +++++++++++++------ Flow.Launcher.Infrastructure/Logger/Log.cs | 2 +- .../Storage/JsonStorage.cs | 16 +++++------ .../UserSettings/Settings.cs | 16 ++++++----- Flow.Launcher.Plugin/PluginMetadata.cs | 3 +- Flow.Launcher/Storage/QueryHistory.cs | 1 - Flow.Launcher/Storage/TopMostRecord.cs | 3 +- Flow.Launcher/Storage/UserSelectedRecord.cs | 2 -- .../Search/FolderLinks/FolderLink.cs | 8 +++--- .../Flow.Launcher.Plugin.Explorer/Settings.cs | 8 +----- .../SearchSource.cs | 2 +- .../Settings.cs | 2 +- .../SuggestionSources/Baidu.cs | 20 +++++-------- .../SuggestionSources/Google.cs | 22 ++++++--------- 17 files changed, 67 insertions(+), 77 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 31bf04286..3d4522498 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -3,10 +3,10 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; -using Newtonsoft.Json; using Flow.Launcher.Infrastructure.Exception; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Plugin; @@ -65,7 +65,7 @@ namespace Flow.Launcher.Core.Plugin { List results = new List(); - JsonRPCQueryResponseModel queryResponseModel = JsonConvert.DeserializeObject(output); + JsonRPCQueryResponseModel queryResponseModel = JsonSerializer.Deserialize(output); if (queryResponseModel.Result == null) return null; foreach (JsonRPCResult result in queryResponseModel.Result) @@ -84,7 +84,7 @@ namespace Flow.Launcher.Core.Plugin else { string actionReponse = ExecuteCallback(result1.JsonRPCAction); - JsonRPCRequestModel jsonRpcRequestModel = JsonConvert.DeserializeObject(actionReponse); + JsonRPCRequestModel jsonRpcRequestModel = JsonSerializer.Deserialize(actionReponse); if (jsonRpcRequestModel != null && !String.IsNullOrEmpty(jsonRpcRequestModel.Method) && jsonRpcRequestModel.Method.StartsWith("Flow.Launcher.")) diff --git a/Flow.Launcher.Core/Plugin/PluginConfig.cs b/Flow.Launcher.Core/Plugin/PluginConfig.cs index b946fa44d..46f79c60c 100644 --- a/Flow.Launcher.Core/Plugin/PluginConfig.cs +++ b/Flow.Launcher.Core/Plugin/PluginConfig.cs @@ -2,10 +2,10 @@ using System.Collections.Generic; using System.Linq; using System.IO; -using Newtonsoft.Json; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Plugin; +using System.Text.Json; namespace Flow.Launcher.Core.Plugin { @@ -61,7 +61,7 @@ namespace Flow.Launcher.Core.Plugin PluginMetadata metadata; try { - metadata = JsonConvert.DeserializeObject(File.ReadAllText(configPath)); + metadata = JsonSerializer.Deserialize(File.ReadAllText(configPath)); metadata.PluginDirectory = pluginDirectory; // for plugins which doesn't has ActionKeywords key metadata.ActionKeywords = metadata.ActionKeywords ?? new List { metadata.ActionKeyword }; diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 1e4b0453c..e1aa42730 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -13,7 +13,6 @@ using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Infrastructure.Logger; -using System.IO; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using System.Text.Json.Serialization; diff --git a/Flow.Launcher.Infrastructure/Helper.cs b/Flow.Launcher.Infrastructure/Helper.cs index fa7e18533..331b3a823 100644 --- a/Flow.Launcher.Infrastructure/Helper.cs +++ b/Flow.Launcher.Infrastructure/Helper.cs @@ -1,12 +1,19 @@ -using System; +using Newtonsoft.Json.Converters; +using System; using System.IO; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; +using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Text.Json.Serialization; namespace Flow.Launcher.Infrastructure { public static class Helper { + static Helper() + { + jsonFormattedSerializerOptions.Converters.Add(new JsonStringEnumConverter()); + } + /// /// http://www.yinwang.org/blog-cn/2015/11/21/programming-philosophy /// @@ -65,13 +72,18 @@ namespace Flow.Launcher.Infrastructure } } + private static readonly JsonSerializerOptions jsonFormattedSerializerOptions = new JsonSerializerOptions + { + WriteIndented = true + }; + public static string Formatted(this T t) { - var formatted = JsonConvert.SerializeObject( - t, - Formatting.Indented, - new StringEnumConverter() - ); + var formatted = JsonSerializer.Serialize(t, new JsonSerializerOptions + { + WriteIndented = true + }); + return formatted; } } diff --git a/Flow.Launcher.Infrastructure/Logger/Log.cs b/Flow.Launcher.Infrastructure/Logger/Log.cs index 289ec5d68..91eeb183d 100644 --- a/Flow.Launcher.Infrastructure/Logger/Log.cs +++ b/Flow.Launcher.Infrastructure/Logger/Log.cs @@ -128,7 +128,7 @@ namespace Flow.Launcher.Infrastructure.Logger public static void Exception(string message, System.Exception e) { #if DEBUG - throw e; + throw e; #else if (FormatValid(message)) { diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs index 784c11110..268dc20b8 100644 --- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs @@ -1,7 +1,7 @@ using System; using System.Globalization; using System.IO; -using Newtonsoft.Json; +using System.Text.Json; using Flow.Launcher.Infrastructure.Logger; namespace Flow.Launcher.Infrastructure.Storage @@ -11,7 +11,7 @@ namespace Flow.Launcher.Infrastructure.Storage /// public class JsonStrorage { - private readonly JsonSerializerSettings _serializerSettings; + private readonly JsonSerializerOptions _serializerSettings; private T _data; // need a new directory name public const string DirectoryName = "Settings"; @@ -24,10 +24,9 @@ namespace Flow.Launcher.Infrastructure.Storage { // use property initialization instead of DefaultValueAttribute // easier and flexible for default value of object - _serializerSettings = new JsonSerializerSettings + _serializerSettings = new JsonSerializerOptions { - ObjectCreationHandling = ObjectCreationHandling.Replace, - NullValueHandling = NullValueHandling.Ignore + IgnoreNullValues = false }; } @@ -56,7 +55,7 @@ namespace Flow.Launcher.Infrastructure.Storage { try { - _data = JsonConvert.DeserializeObject(searlized, _serializerSettings); + _data = JsonSerializer.Deserialize(searlized, _serializerSettings); } catch (JsonException e) { @@ -77,7 +76,7 @@ namespace Flow.Launcher.Infrastructure.Storage BackupOriginFile(); } - _data = JsonConvert.DeserializeObject("{}", _serializerSettings); + _data = JsonSerializer.Deserialize("{}", _serializerSettings); Save(); } @@ -94,7 +93,8 @@ namespace Flow.Launcher.Infrastructure.Storage public void Save() { - string serialized = JsonConvert.SerializeObject(_data, Formatting.Indented); + string serialized = JsonSerializer.Serialize(_data, new JsonSerializerOptions() { WriteIndented = true }); + File.WriteAllText(FilePath, serialized); } } diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 832b6fbfa..bcfe298a9 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -1,8 +1,7 @@ using System; using System.Collections.ObjectModel; using System.Drawing; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; +using System.Text.Json.Serialization; using Flow.Launcher.Plugin; namespace Flow.Launcher.Infrastructure.UserSettings @@ -16,7 +15,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings public bool ShowOpenResultHotkey { get; set; } = true; public string Language { - get => language; set { + get => language; set + { language = value; OnPropertyChanged(); } @@ -73,9 +73,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings public int MaxResultsToShow { get; set; } = 5; public int ActivateTimes { get; set; } - // Order defaults to 0 or -1, so 1 will let this property appear last - [JsonProperty(Order = 1)] - public PluginsSettings PluginSettings { get; set; } = new PluginsSettings(); + public ObservableCollection CustomPluginHotkeys { get; set; } = new ObservableCollection(); public bool DontPromptUpdateMsg { get; set; } @@ -100,8 +98,12 @@ namespace Flow.Launcher.Infrastructure.UserSettings public HttpProxy Proxy { get; set; } = new HttpProxy(); - [JsonConverter(typeof(StringEnumConverter))] + [JsonConverter(typeof(JsonStringEnumConverter))] public LastQueryMode LastQueryMode { get; set; } = LastQueryMode.Selected; + + + // Order defaults to 0 or -1, so 1 will let this property appear last + public PluginsSettings PluginSettings { get; set; } = new PluginsSettings(); } public enum LastQueryMode diff --git a/Flow.Launcher.Plugin/PluginMetadata.cs b/Flow.Launcher.Plugin/PluginMetadata.cs index d81b442e2..4c40be53c 100644 --- a/Flow.Launcher.Plugin/PluginMetadata.cs +++ b/Flow.Launcher.Plugin/PluginMetadata.cs @@ -1,11 +1,10 @@ using System; using System.Collections.Generic; using System.IO; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace Flow.Launcher.Plugin { - [JsonObject(MemberSerialization.OptOut)] public class PluginMetadata : BaseModel { private string _pluginDirectory; diff --git a/Flow.Launcher/Storage/QueryHistory.cs b/Flow.Launcher/Storage/QueryHistory.cs index de3bcaa22..2b2103605 100644 --- a/Flow.Launcher/Storage/QueryHistory.cs +++ b/Flow.Launcher/Storage/QueryHistory.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json; using Flow.Launcher.Plugin; namespace Flow.Launcher.Storage diff --git a/Flow.Launcher/Storage/TopMostRecord.cs b/Flow.Launcher/Storage/TopMostRecord.cs index c110bdf92..09e69f6d8 100644 --- a/Flow.Launcher/Storage/TopMostRecord.cs +++ b/Flow.Launcher/Storage/TopMostRecord.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json; +using System.Text.Json; using Flow.Launcher.Plugin; namespace Flow.Launcher.Storage @@ -8,7 +8,6 @@ namespace Flow.Launcher.Storage // todo this class is not thread safe.... but used from multiple threads. public class TopMostRecord { - [JsonProperty] private Dictionary records = new Dictionary(); internal bool IsTopMost(Result result) diff --git a/Flow.Launcher/Storage/UserSelectedRecord.cs b/Flow.Launcher/Storage/UserSelectedRecord.cs index 1fda04e9b..c7ffe1a1d 100644 --- a/Flow.Launcher/Storage/UserSelectedRecord.cs +++ b/Flow.Launcher/Storage/UserSelectedRecord.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using Newtonsoft.Json; using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Plugin; @@ -7,7 +6,6 @@ namespace Flow.Launcher.Storage { public class UserSelectedRecord { - [JsonProperty] private Dictionary records = new Dictionary(); public void Add(Result result) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/FolderLink.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/FolderLink.cs index 379b5848f..43ecdad97 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/FolderLink.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/FolderLink.cs @@ -1,15 +1,15 @@ -using Newtonsoft.Json; -using System; +using System; using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; namespace Flow.Launcher.Plugin.Explorer.Search.FolderLinks { - [JsonObject(MemberSerialization.OptIn)] public class FolderLink { - [JsonProperty] public string Path { get; set; } + [JsonIgnore] public string Nickname { get diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs index 5b12870c8..e62ea93fc 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs @@ -1,28 +1,22 @@ using Flow.Launcher.Plugin.Explorer.Search; using Flow.Launcher.Plugin.Explorer.Search.FolderLinks; -using Newtonsoft.Json; using System.Collections.Generic; +using System.Text.Json.Serialization; namespace Flow.Launcher.Plugin.Explorer { public class Settings { - [JsonProperty] public int MaxResult { get; set; } = 100; - [JsonProperty] public List QuickFolderAccessLinks { get; set; } = new List(); - [JsonProperty] public bool UseWindowsIndexForDirectorySearch { get; set; } = true; - [JsonProperty] public List IndexSearchExcludedSubdirectoryPaths { get; set; } = new List(); - [JsonProperty] public string SearchActionKeyword { get; set; } = Query.GlobalPluginWildcardSign; - [JsonProperty] public string FileContentSearchActionKeyword { get; set; } = Constants.DefaultContentSearchActionKeyword; } } \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSource.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSource.cs index c7ccb4d51..98e9376fb 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSource.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSource.cs @@ -1,10 +1,10 @@ using System.IO; using System.Windows.Media; using JetBrains.Annotations; -using Newtonsoft.Json; using Flow.Launcher.Infrastructure.Image; using Flow.Launcher.Infrastructure; using System.Reflection; +using System.Text.Json.Serialization; namespace Flow.Launcher.Plugin.WebSearch { diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs index 555ee4647..e8881aae9 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs @@ -1,6 +1,6 @@ using System; using System.Collections.ObjectModel; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using Flow.Launcher.Plugin.WebSearch.SuggestionSources; namespace Flow.Launcher.Plugin.WebSearch diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs index 6772acf82..2e385510f 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs @@ -2,10 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.Net; +using System.Text.Json; using System.Text.RegularExpressions; using System.Threading.Tasks; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Infrastructure.Logger; using System.Net.Http; @@ -35,25 +34,20 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources Match match = _reg.Match(result); if (match.Success) { - JContainer json; + JsonDocument json; try { - json = JsonConvert.DeserializeObject(match.Groups[1].Value) as JContainer; + json = JsonDocument.Parse(match.Groups[1].Value); } - catch (JsonSerializationException e) + catch(JsonException e) { Log.Exception("|Baidu.Suggestions|can't parse suggestions", e); return new List(); } - if (json != null) - { - var results = json["s"] as JArray; - if (results != null) - { - return results.OfType().Select(o => o.Value).OfType().ToList(); - } - } + var results = json?.RootElement.GetProperty("s"); + + return results?.EnumerateArray().Select(o => o.GetString()).ToList() ?? new List(); } return new List(); diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs index 5b9538091..ff0906b87 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs @@ -3,11 +3,10 @@ using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading.Tasks; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Infrastructure.Logger; using System.Net.Http; +using System.Text.Json; namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources { @@ -27,25 +26,20 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources return new List(); } if (string.IsNullOrEmpty(result)) return new List(); - JContainer json; + JsonDocument json; try { - json = JsonConvert.DeserializeObject(result) as JContainer; + json = JsonDocument.Parse(result); } - catch (JsonSerializationException e) + catch (JsonException e) { Log.Exception("|Google.Suggestions|can't parse suggestions", e); return new List(); } - if (json != null) - { - var results = json[1] as JContainer; - if (results != null) - { - return results.OfType().Select(o => o.Value).OfType().ToList(); - } - } - return new List(); + + var results = json?.RootElement.EnumerateArray().ElementAt(1); + + return results?.EnumerateArray().Select(o => o.GetString()).ToList() ?? new List(); } public override string ToString() From 557842e8d797c3ca0146ed77d0a45906f5c6f3d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 30 Dec 2020 13:48:06 +0800 Subject: [PATCH 055/158] remove dependency --- Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj | 1 - Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj | 1 - 2 files changed, 2 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index 28d4c0e1f..8153de6c8 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -49,7 +49,6 @@ - diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 70013c274..b7b52ac35 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -62,7 +62,6 @@ - \ No newline at end of file From 7bbd8c6069ef038186482b50d731cec90396b987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 30 Dec 2020 19:05:02 +0800 Subject: [PATCH 056/158] remove using from Helper.cs --- Flow.Launcher.Infrastructure/Helper.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Helper.cs b/Flow.Launcher.Infrastructure/Helper.cs index 331b3a823..faa4c93b5 100644 --- a/Flow.Launcher.Infrastructure/Helper.cs +++ b/Flow.Launcher.Infrastructure/Helper.cs @@ -1,5 +1,4 @@ -using Newtonsoft.Json.Converters; -using System; +using System; using System.IO; using System.Runtime.CompilerServices; using System.Text.Json; From a0c4cc35756c42d7351bee37c795e063fd933dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 30 Dec 2020 19:09:52 +0800 Subject: [PATCH 057/158] use ParseAsync in Google --- .../SuggestionSources/Google.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs index ff0906b87..f23cb66ff 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs @@ -7,6 +7,7 @@ using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Infrastructure.Logger; using System.Net.Http; using System.Text.Json; +using System.IO; namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources { @@ -14,22 +15,22 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources { public override async Task> Suggestions(string query) { - string result; + Stream resultStream; try { const string api = "https://www.google.com/complete/search?output=chrome&q="; - result = await Http.GetAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false); + resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false); } catch (HttpRequestException e) { Log.Exception("|Google.Suggestions|Can't get suggestion from google", e); return new List(); } - if (string.IsNullOrEmpty(result)) return new List(); + if (resultStream.Length == 0) return new List(); JsonDocument json; try { - json = JsonDocument.Parse(result); + json = await JsonDocument.ParseAsync(resultStream); } catch (JsonException e) { From a139a040ecc3bca388b77c7c4ba39de35eccd638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 30 Dec 2020 20:24:01 +0800 Subject: [PATCH 058/158] add bing search suggestion --- .../Settings.cs | 3 +- .../SuggestionSources/Bing.cs | 62 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs index 555ee4647..1a3d9e5e5 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs @@ -196,7 +196,8 @@ namespace Flow.Launcher.Plugin.WebSearch [JsonIgnore] public SuggestionSource[] Suggestions { get; set; } = { new Google(), - new Baidu() + new Baidu(), + new Bing() }; [JsonIgnore] diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs new file mode 100644 index 000000000..9c4746711 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs @@ -0,0 +1,62 @@ +using Flow.Launcher.Infrastructure.Http; +using Flow.Launcher.Infrastructure.Logger; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Text.Json; +using System.Linq; + +namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources +{ + class Bing : SuggestionSource + { + public override async Task> Suggestions(string query) + { + Stream resultStream; + + try + { + const string api = "https://api.bing.com/qsonhs.aspx?q="; + resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false); + } + catch (HttpRequestException e) + { + Log.Exception("|Bing.Suggestions|Can't get suggestion from Bing", e); + return new List(); + } + + if (resultStream.Length == 0) return new List(); + + JsonElement json; + try + { + json = (await JsonDocument.ParseAsync(resultStream)).RootElement.GetProperty("AS"); + } + catch (JsonException e) + { + Log.Exception("|Bing.Suggestions|can't parse suggestions", e); + return new List(); + } + + if (json.GetProperty("FullResults").GetInt32() == 0) + return new List(); + + return json.GetProperty("Results") + .EnumerateArray() + .SelectMany(r => r.GetProperty("Suggests") + .EnumerateArray() + .Select(s => s.GetProperty("Txt").GetString())) + .ToList(); + + } + + public override string ToString() + { + return "Bing"; + } + } +} From 5c17cb6e9b823482784f7ef0dda0df35f65eb932 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 31 Dec 2020 15:44:57 +1100 Subject: [PATCH 059/158] version bump WebSearch --- Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json index 329f1c41d..99fd2210a 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json @@ -25,7 +25,7 @@ "Name": "Web Searches", "Description": "Provide the web search ability", "Author": "qianlifeng", - "Version": "1.1.2", + "Version": "1.2.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.WebSearch.dll", From 27a0b934c612b8d3a482722c53910b7487a973d7 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 1 Jan 2021 18:39:41 +1100 Subject: [PATCH 060/158] update maintenance badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 02f488758..654877f3a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

-![Maintenance](https://img.shields.io/maintenance/yes/2020) +![Maintenance](https://img.shields.io/maintenance/yes/2021) [![Build status](https://ci.appveyor.com/api/projects/status/32r7s2skrgm9ubva?svg=true&retina=true)](https://ci.appveyor.com/project/JohnTheGr8/flow-launcher/branch/dev) [![Github All Releases](https://img.shields.io/github/downloads/Flow-Launcher/Flow.Launcher/total.svg)](https://github.com/Flow-Launcher/Flow.Launcher/releases) [![GitHub release (latest by date)](https://img.shields.io/github/v/release/Flow-Launcher/Flow.Launcher)](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest) From a65e17dba00830c32c746d7dea3b737fc518fa7a Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 1 Jan 2021 18:57:58 +1100 Subject: [PATCH 061/158] fix wrong error message --- Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml | 4 +++- Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml index 8d24c145c..eaea9783b 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml @@ -1,4 +1,4 @@ - @@ -6,6 +6,8 @@ Downloading plugin Please wait... Successfully downloaded + Error downloading plugin + Error occured while trying to download the plugin {0} by {1} {2}{3}Would you like to uninstall this plugin? After the uninstallation Flow will automatically restart. {0} by {1} {2}{3}Would you like to install this plugin? After the installation Flow will automatically restart. Plugin Install diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index ac15618ca..a378a9046 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -134,8 +134,8 @@ namespace Flow.Launcher.Plugin.PluginsManager } catch (Exception e) { - Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), - Context.API.GetTranslation("plugin_pluginsmanager_download_success")); + Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_download_error_title"), + Context.API.GetTranslation("plugin_pluginsmanager_download_error_subtitle")); Log.Exception("PluginsManager", "An error occured while downloading plugin", e, "PluginDownload"); } From 54f6858937fdf3445e8503e7da398bafd0efa4b5 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 1 Jan 2021 19:03:06 +1100 Subject: [PATCH 062/158] add plugin name to error msg popup --- Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml | 2 +- Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml index eaea9783b..d0370ddc3 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml @@ -7,7 +7,7 @@ Please wait... Successfully downloaded Error downloading plugin - Error occured while trying to download the plugin + Error occured while trying to download {0} {0} by {1} {2}{3}Would you like to uninstall this plugin? After the uninstallation Flow will automatically restart. {0} by {1} {2}{3}Would you like to install this plugin? After the installation Flow will automatically restart. Plugin Install diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index a378a9046..9d9d50902 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -135,7 +135,7 @@ namespace Flow.Launcher.Plugin.PluginsManager catch (Exception e) { Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_download_error_title"), - Context.API.GetTranslation("plugin_pluginsmanager_download_error_subtitle")); + string.Format(Context.API.GetTranslation("plugin_pluginsmanager_download_error_subtitle"), plugin.Name)); Log.Exception("PluginsManager", "An error occured while downloading plugin", e, "PluginDownload"); } From cab1b93183d4a8170f3b589354e1159b8e92cd7c Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 1 Jan 2021 19:13:14 +1100 Subject: [PATCH 063/158] move install method into try catch --- .../Languages/en.xaml | 4 ++-- .../PluginsManager.cs | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml index d0370ddc3..3017f39c3 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml @@ -6,13 +6,13 @@ Downloading plugin Please wait... Successfully downloaded - Error downloading plugin - Error occured while trying to download {0} {0} by {1} {2}{3}Would you like to uninstall this plugin? After the uninstallation Flow will automatically restart. {0} by {1} {2}{3}Would you like to install this plugin? After the installation Flow will automatically restart. Plugin Install Plugin Uninstall Install failed: unable to find the plugin.json metadata file from the new plugin + Error installing plugin + Error occured while trying to install {0} No update available All plugins are up to date {0} by {1} {2}{3}Would you like to update this plugin? After the update Flow will automatically restart. diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 9d9d50902..db0327111 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -131,16 +131,17 @@ namespace Flow.Launcher.Plugin.PluginsManager Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), Context.API.GetTranslation("plugin_pluginsmanager_download_success")); + + Install(plugin, filePath); } catch (Exception e) { - Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_download_error_title"), - string.Format(Context.API.GetTranslation("plugin_pluginsmanager_download_error_subtitle"), plugin.Name)); + Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"), + string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"), plugin.Name)); - Log.Exception("PluginsManager", "An error occured while downloading plugin", e, "PluginDownload"); + Log.Exception("PluginsManager", "An error occured while downloading plugin", e, "InstallOrUpdate"); } - Install(plugin, filePath); Context.API.RestartApp(); } From 878f7bf5dfb154a2bf663deaf9256ce73ba3de1d Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 1 Jan 2021 19:49:19 +1100 Subject: [PATCH 064/158] version bump for PluginsManager --- Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json index d94af71a1..7e78d65d6 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json @@ -6,7 +6,7 @@ "Name": "Plugins Manager", "Description": "Management of installing, uninstalling or updating Flow Launcher plugins", "Author": "Jeremy Wu", - "Version": "1.3.1", + "Version": "1.3.2", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll", From 81cb20bf91edb292eb35445861a25acec31a2445 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 1 Jan 2021 21:54:34 +1100 Subject: [PATCH 065/158] add return to continue --- Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index db0327111..880157a77 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -140,6 +140,8 @@ namespace Flow.Launcher.Plugin.PluginsManager string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"), plugin.Name)); Log.Exception("PluginsManager", "An error occured while downloading plugin", e, "InstallOrUpdate"); + + return; } Context.API.RestartApp(); From 6d66aa3f4415f114f44bb6b962651f5d269783f6 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 2 Jan 2021 00:06:55 +1100 Subject: [PATCH 066/158] fix script to delete with condition dll version is same --- Scripts/post_build.ps1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index 59036842a..b20393b02 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -44,8 +44,9 @@ function Delete-Unused ($path, $config) { $target = "$path\Output\$config" $included = Get-ChildItem $target -Filter "*.dll" foreach ($i in $included){ - Remove-Item -Path $target\Plugins -Include $i -Recurse - Write-Host "Deleting duplicated $i" + $deleteList = Get-ChildItem $target\Plugins -Include $i -Recurse | Where {$_.VersionInfo.FileVersion -eq $i.VersionInfo.FileVersion} + $deleteList | foreach-object{ Write-Host Deleting duplicated $_.Name with version $_.VersionInfo.FileVersion at location $_.Directory.FullName } + $deleteList | Remove-Item } Remove-Item -Path $target -Include "*.xml" -Recurse } From 29a382085fde4f5ac5ea173b5ccf338258d0c029 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 2 Jan 2021 00:09:02 +1100 Subject: [PATCH 067/158] method name update --- Scripts/post_build.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index b20393b02..e0868f3bb 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -45,7 +45,7 @@ function Delete-Unused ($path, $config) { $included = Get-ChildItem $target -Filter "*.dll" foreach ($i in $included){ $deleteList = Get-ChildItem $target\Plugins -Include $i -Recurse | Where {$_.VersionInfo.FileVersion -eq $i.VersionInfo.FileVersion} - $deleteList | foreach-object{ Write-Host Deleting duplicated $_.Name with version $_.VersionInfo.FileVersion at location $_.Directory.FullName } + $deleteList | ForEach-Object{ Write-Host Deleting duplicated $_.Name with version $_.VersionInfo.FileVersion at location $_.Directory.FullName } $deleteList | Remove-Item } Remove-Item -Path $target -Include "*.xml" -Recurse From f1badd6ae20466ab720b516d544a4b5f58b64dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Fri, 1 Jan 2021 21:19:05 +0800 Subject: [PATCH 068/158] Add Exception Handling for Querying plugins results --- Flow.Launcher/ViewModel/MainViewModel.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 7a3aa9f2f..2a69635bb 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -21,6 +21,7 @@ using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.Storage; using System.Windows.Media; using Flow.Launcher.Infrastructure.Image; +using Flow.Launcher.Infrastructure.Logger; namespace Flow.Launcher.ViewModel { @@ -414,8 +415,15 @@ namespace Flow.Launcher.ViewModel { if (!plugin.Metadata.Disabled) { - var results = PluginManager.QueryForPlugin(plugin, query); - UpdateResultView(results, plugin.Metadata, query); + try + { + var results = PluginManager.QueryForPlugin(plugin, query); + UpdateResultView(results, plugin.Metadata, query); + } + catch(Exception e) + { + Log.Exception($"|MainViewModel|Exception when querying {plugin.Metadata.Name}", e); + } } }); } @@ -432,7 +440,10 @@ namespace Flow.Launcher.ViewModel { // update to hidden if this is still the current query ProgressBarVisibility = Visibility.Hidden; } - }, currentCancellationToken); + }, currentCancellationToken).ContinueWith(t => + { + Log.Exception("|MainViewModel|Error when querying plugin", t.Exception?.InnerException); + }, TaskContinuationOptions.OnlyOnFaulted); } } else From f3355c525dfbdaa46ece70cb5197826c1bf354e9 Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Fri, 1 Jan 2021 23:15:37 +0200 Subject: [PATCH 069/158] fix: properly include all plugin translation files --- ...low.Launcher.Plugin.BrowserBookmark.csproj | 9 +--- .../Flow.Launcher.Plugin.Calculator.csproj | 44 +----------------- .../Flow.Launcher.Plugin.ControlPanel.csproj | 45 +------------------ .../Flow.Launcher.Plugin.Explorer.csproj | 32 +------------ ...low.Launcher.Plugin.PluginIndicator.csproj | 42 +---------------- .../Flow.Launcher.Plugin.ProcessKiller.csproj | 2 +- .../Flow.Launcher.Plugin.Program.csproj | 27 +---------- .../Flow.Launcher.Plugin.Shell.csproj | 39 ++-------------- .../Flow.Launcher.Plugin.Sys.csproj | 27 +---------- .../Flow.Launcher.Plugin.Url.csproj | 36 +-------------- .../Flow.Launcher.Plugin.WebSearch.csproj | 27 +---------- 11 files changed, 17 insertions(+), 313 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj index 85b745a6b..f308aa49a 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj @@ -43,11 +43,6 @@ Always - - Designer - MSBuild:Compile - PreserveNewest - Always @@ -60,9 +55,9 @@
- + - + MSBuild:Compile Designer PreserveNewest diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj index 9e1fefdb3..983ac160e 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj @@ -49,49 +49,9 @@ PreserveNewest - + - - MSBuild:Compile - Designer - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - + MSBuild:Compile Designer PreserveNewest diff --git a/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj b/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj index 699737634..dce54cb52 100644 --- a/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj +++ b/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj @@ -48,50 +48,7 @@ PreserveNewest - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - + MSBuild:Compile Designer PreserveNewest diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj index a1a08843a..a9f675be8 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj @@ -62,37 +62,7 @@ PreserveNewest - - MSBuild:Compile - Designer - PreserveNewest - - - - MSBuild:Compile - Designer - PreserveNewest - - - - MSBuild:Compile - Designer - PreserveNewest - - - - MSBuild:Compile - Designer - PreserveNewest - - - - MSBuild:Compile - Designer - PreserveNewest - - - + MSBuild:Compile Designer PreserveNewest diff --git a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj index e6bfa7aa3..6abfc8469 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj @@ -52,47 +52,7 @@ - - MSBuild:Compile - Designer - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - + MSBuild:Compile Designer PreserveNewest diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj index cf9c96294..a393d8510 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj @@ -39,7 +39,7 @@ PreserveNewest - + Designer MSBuild:Compile PreserveNewest diff --git a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj index 3802297c7..7e1b36fc1 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj +++ b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj @@ -68,35 +68,10 @@ Always - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - + MSBuild:Compile Designer PreserveNewest diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj b/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj index 178d95010..316742bf6 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj +++ b/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj @@ -33,32 +33,6 @@ 4 false
- - - - Always - - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - @@ -72,18 +46,13 @@ + + Always + PreserveNewest - - - - - MSBuild:Compile - Designer - PreserveNewest - - + MSBuild:Compile Designer PreserveNewest diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj index bdab40457..61635dd08 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj +++ b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj @@ -52,32 +52,7 @@ PreserveNewest - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - + MSBuild:Compile Designer PreserveNewest diff --git a/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj b/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj index 7d802d815..fb40c1e17 100644 --- a/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj +++ b/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj @@ -50,41 +50,9 @@ PreserveNewest - + - - MSBuild:Compile - Designer - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - - - + MSBuild:Compile Designer PreserveNewest diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj index 431ca9ce8..e3bb4b408 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj @@ -89,32 +89,7 @@ PreserveNewest - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - + MSBuild:Compile Designer PreserveNewest From 8ab528c380acd0515b6576e74f76300763bd3bd6 Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Sat, 2 Jan 2021 00:00:12 +0200 Subject: [PATCH 070/158] fix slovak translation --- Flow.Launcher/Languages/sk.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/Languages/sk.xaml b/Flow.Launcher/Languages/sk.xaml index bf001d507..85fc1462c 100644 --- a/Flow.Launcher/Languages/sk.xaml +++ b/Flow.Launcher/Languages/sk.xaml @@ -45,8 +45,8 @@ Nová akcia skratky: Priečinok s pluginmi Autor - Príprava: {0}ms - Čas dopytu: {0}ms + Príprava: + Čas dopytu: Motív From 75f5a81a31ead865464f6a11f61f4c8fcf8d7cb6 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 2 Jan 2021 11:55:06 +1100 Subject: [PATCH 071/158] update exception message --- Flow.Launcher/ViewModel/MainViewModel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 2a69635bb..eed30f377 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -422,7 +422,7 @@ namespace Flow.Launcher.ViewModel } catch(Exception e) { - Log.Exception($"|MainViewModel|Exception when querying {plugin.Metadata.Name}", e); + Log.Exception("MainViewModel", $"Exception when querying the plugin {plugin.Metadata.Name}", e, "QueryResults"); } } }); @@ -442,7 +442,7 @@ namespace Flow.Launcher.ViewModel } }, currentCancellationToken).ContinueWith(t => { - Log.Exception("|MainViewModel|Error when querying plugin", t.Exception?.InnerException); + Log.Exception("MainViewModel", "Error when querying plugins", t.Exception?.InnerException, "QueryResults"); }, TaskContinuationOptions.OnlyOnFaulted); } } From 041bf4e9c4860b9114a95741fcb036d2f969d208 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 2 Jan 2021 12:10:06 +1100 Subject: [PATCH 072/158] add condition to match name also --- Scripts/post_build.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index e0868f3bb..836e27380 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -44,7 +44,7 @@ function Delete-Unused ($path, $config) { $target = "$path\Output\$config" $included = Get-ChildItem $target -Filter "*.dll" foreach ($i in $included){ - $deleteList = Get-ChildItem $target\Plugins -Include $i -Recurse | Where {$_.VersionInfo.FileVersion -eq $i.VersionInfo.FileVersion} + $deleteList = Get-ChildItem $target\Plugins -Include $i -Recurse | Where { $_.VersionInfo.FileVersion -eq $i.VersionInfo.FileVersion -And $_.Name -eq "$i" } $deleteList | ForEach-Object{ Write-Host Deleting duplicated $_.Name with version $_.VersionInfo.FileVersion at location $_.Directory.FullName } $deleteList | Remove-Item } From be1776fd2153f6bc9fbefbce71b313ea9ab9789e Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 2 Jan 2021 12:47:20 +1100 Subject: [PATCH 073/158] version bump for plugins --- Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json | 2 +- Plugins/Flow.Launcher.Plugin.Calculator/plugin.json | 2 +- Plugins/Flow.Launcher.Plugin.ControlPanel/plugin.json | 2 +- Plugins/Flow.Launcher.Plugin.Explorer/plugin.json | 2 +- Plugins/Flow.Launcher.Plugin.PluginIndicator/plugin.json | 2 +- .../Flow.Launcher.Plugin.PluginsManager.csproj | 2 +- Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json | 2 +- Plugins/Flow.Launcher.Plugin.ProcessKiller/plugin.json | 2 +- Plugins/Flow.Launcher.Plugin.Program/plugin.json | 2 +- Plugins/Flow.Launcher.Plugin.Shell/plugin.json | 2 +- Plugins/Flow.Launcher.Plugin.Sys/plugin.json | 2 +- Plugins/Flow.Launcher.Plugin.Url/plugin.json | 2 +- Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json index de4f3849b..b0c3d2e29 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json @@ -4,7 +4,7 @@ "Name": "Browser Bookmarks", "Description": "Search your browser bookmarks", "Author": "qianlifeng, Ioannis G.", - "Version": "1.3.1", + "Version": "1.3.2", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.BrowserBookmark.dll", diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json b/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json index 709757d1a..7d9ca58d5 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json @@ -4,7 +4,7 @@ "Name": "Calculator", "Description": "Provide mathematical calculations.(Try 5*3-2 in Flow Launcher)", "Author": "cxfksword", - "Version": "1.1.3", + "Version": "1.1.4", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Caculator.dll", diff --git a/Plugins/Flow.Launcher.Plugin.ControlPanel/plugin.json b/Plugins/Flow.Launcher.Plugin.ControlPanel/plugin.json index 4f552a014..23f35e9ac 100644 --- a/Plugins/Flow.Launcher.Plugin.ControlPanel/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.ControlPanel/plugin.json @@ -4,7 +4,7 @@ "Name": "Control Panel", "Description": "Search within the Control Panel.", "Author": "CoenraadS", - "Version": "1.1.1", + "Version": "1.1.2", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.ControlPanel.dll", diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json index 2c57ac668..aa44c4413 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json @@ -7,7 +7,7 @@ "Name": "Explorer", "Description": "Search and manage files and folders. Explorer utilises Windows Index Search", "Author": "Jeremy Wu", - "Version": "1.2.5", + "Version": "1.2.6", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll", diff --git a/Plugins/Flow.Launcher.Plugin.PluginIndicator/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginIndicator/plugin.json index 80900a445..7f73263a8 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginIndicator/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginIndicator/plugin.json @@ -4,7 +4,7 @@ "Name": "Plugin Indicator", "Description": "Provide plugin actionword suggestion", "Author": "qianlifeng", - "Version": "1.1.1", + "Version": "1.1.2", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginIndicator.dll", diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj b/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj index cc1a931ce..7beb8308b 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj @@ -35,7 +35,7 @@ - + PreserveNewest diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json index d94af71a1..7e78d65d6 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json @@ -6,7 +6,7 @@ "Name": "Plugins Manager", "Description": "Management of installing, uninstalling or updating Flow Launcher plugins", "Author": "Jeremy Wu", - "Version": "1.3.1", + "Version": "1.3.2", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll", diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/plugin.json b/Plugins/Flow.Launcher.Plugin.ProcessKiller/plugin.json index d769397a8..2bb40c644 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/plugin.json @@ -4,7 +4,7 @@ "Name":"Process Killer", "Description":"kill running processes from Flow", "Author":"Flow-Launcher", - "Version":"1.2.1", + "Version":"1.2.2", "Language":"csharp", "Website":"https://github.com/Flow-Launcher/Flow.Launcher.Plugin.ProcessKiller", "IcoPath":"Images\\app.png", diff --git a/Plugins/Flow.Launcher.Plugin.Program/plugin.json b/Plugins/Flow.Launcher.Plugin.Program/plugin.json index 7d7a42e03..6c2c18e47 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Program/plugin.json @@ -4,7 +4,7 @@ "Name": "Program", "Description": "Search programs in Flow.Launcher", "Author": "qianlifeng", - "Version": "1.2.2", + "Version": "1.2.3", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Program.dll", diff --git a/Plugins/Flow.Launcher.Plugin.Shell/plugin.json b/Plugins/Flow.Launcher.Plugin.Shell/plugin.json index 63e74d678..4ad572cf6 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Shell/plugin.json @@ -4,7 +4,7 @@ "Name": "Shell", "Description": "Provide executing commands from Flow Launcher. Commands should start with >", "Author": "qianlifeng", - "Version": "1.1.1", + "Version": "1.1.2", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Shell.dll", diff --git a/Plugins/Flow.Launcher.Plugin.Sys/plugin.json b/Plugins/Flow.Launcher.Plugin.Sys/plugin.json index 8d4b9a238..cf8ed6041 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Sys/plugin.json @@ -4,7 +4,7 @@ "Name": "System Commands", "Description": "Provide System related commands. e.g. shutdown,lock,setting etc.", "Author": "qianlifeng", - "Version": "1.1.1", + "Version": "1.1.2", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Sys.dll", diff --git a/Plugins/Flow.Launcher.Plugin.Url/plugin.json b/Plugins/Flow.Launcher.Plugin.Url/plugin.json index be64f6708..89dc20a33 100644 --- a/Plugins/Flow.Launcher.Plugin.Url/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Url/plugin.json @@ -4,7 +4,7 @@ "Name": "URL", "Description": "Open the typed URL from Flow Launcher", "Author": "qianlifeng", - "Version": "1.1.1", + "Version": "1.1.2", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Url.dll", diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json index 99fd2210a..c036fbf8b 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json @@ -25,7 +25,7 @@ "Name": "Web Searches", "Description": "Provide the web search ability", "Author": "qianlifeng", - "Version": "1.2.0", + "Version": "1.2.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.WebSearch.dll", From 08f97f0ef9236e4572d7a707741488cddb568d38 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 2 Jan 2021 12:57:13 +1100 Subject: [PATCH 074/158] change csproj images folder to use wildcard --- ...low.Launcher.Plugin.BrowserBookmark.csproj | 6 +- .../Flow.Launcher.Plugin.Calculator.csproj | 9 +-- .../Flow.Launcher.Plugin.ControlPanel.csproj | 4 +- .../Flow.Launcher.Plugin.Explorer.csproj | 35 +---------- ...low.Launcher.Plugin.PluginIndicator.csproj | 9 +-- ...Flow.Launcher.Plugin.PluginsManager.csproj | 9 +-- .../Flow.Launcher.Plugin.ProcessKiller.csproj | 6 +- .../Flow.Launcher.Plugin.Program.csproj | 21 +------ .../Flow.Launcher.Plugin.Shell.csproj | 9 +-- .../Flow.Launcher.Plugin.Sys.csproj | 50 +-------------- .../Flow.Launcher.Plugin.Url.csproj | 9 +-- .../Flow.Launcher.Plugin.WebSearch.csproj | 63 +------------------ 12 files changed, 33 insertions(+), 197 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj index f308aa49a..d2a8736a6 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj @@ -40,9 +40,6 @@ - - Always - Always @@ -62,6 +59,9 @@ Designer PreserveNewest + + PreserveNewest + diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj index 983ac160e..06d8dea7d 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj @@ -43,12 +43,6 @@ - - - - PreserveNewest - - @@ -56,6 +50,9 @@ Designer PreserveNewest + + PreserveNewest + diff --git a/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj b/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj index dce54cb52..06969a135 100644 --- a/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj +++ b/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj @@ -45,9 +45,9 @@ - + PreserveNewest - + MSBuild:Compile Designer diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj index a9f675be8..9d9c09a93 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj @@ -26,42 +26,9 @@ - - PreserveNewest - - - - PreserveNewest - - - - PreserveNewest - - - - PreserveNewest - - - - Always - - - + PreserveNewest - - - PreserveNewest - - - - PreserveNewest - - - - PreserveNewest - - MSBuild:Compile Designer diff --git a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj index 6abfc8469..0140444e1 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj @@ -45,18 +45,15 @@ - - - PreserveNewest - - - MSBuild:Compile Designer PreserveNewest + + PreserveNewest + \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj b/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj index 7beb8308b..2e352d832 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj @@ -27,17 +27,14 @@ PreserveNewest - - - - PreserveNewest - - PreserveNewest + + PreserveNewest + diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj index a393d8510..a643ebf86 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj @@ -36,14 +36,14 @@ - - PreserveNewest - Designer MSBuild:Compile PreserveNewest + + PreserveNewest + Always diff --git a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj index 7e1b36fc1..12e113855 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj +++ b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj @@ -52,30 +52,15 @@ - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - Always - - - MSBuild:Compile Designer PreserveNewest + + PreserveNewest + diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj b/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj index 316742bf6..c542dc89b 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj +++ b/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj @@ -46,17 +46,14 @@ - - Always - - - PreserveNewest - MSBuild:Compile Designer PreserveNewest + + PreserveNewest + MSBuild:Compile Designer diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj index 61635dd08..8ef4dc0fb 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj +++ b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj @@ -40,23 +40,14 @@ - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - MSBuild:Compile Designer PreserveNewest + + PreserveNewest + MSBuild:Compile Designer @@ -68,39 +59,4 @@ PreserveNewest - - - - PreserveNewest - - - - - - PreserveNewest - - - - - - PreserveNewest - - - - - - PreserveNewest - - - - - - PreserveNewest - - - - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj b/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj index fb40c1e17..671a8b1c2 100644 --- a/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj +++ b/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj @@ -44,12 +44,6 @@ - - - - PreserveNewest - - @@ -57,6 +51,9 @@ Designer PreserveNewest + + PreserveNewest + \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj index e3bb4b408..af86af842 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj @@ -35,65 +35,14 @@ - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - MSBuild:Compile Designer PreserveNewest + + PreserveNewest + MSBuild:Compile Designer @@ -109,12 +58,6 @@ PreserveNewest - - - - PreserveNewest - - From 482988c288392af9b1bf2a911a6eb798c54f11ff Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 2 Jan 2021 12:59:58 +1100 Subject: [PATCH 075/158] use wild card to include images for main project --- Flow.Launcher/Flow.Launcher.csproj | 108 +---------------------------- 1 file changed, 3 insertions(+), 105 deletions(-) diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 8548ba39e..39b2087aa 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -60,6 +60,9 @@ Designer PreserveNewest + + PreserveNewest + @@ -87,111 +90,6 @@ - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - From 97ac42751717121f5db1c695b81d5aed98df191f Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 2 Jan 2021 16:58:23 +1100 Subject: [PATCH 076/158] add manual reload of PluginsManifest data --- Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index d700b9dfd..7c1eda3aa 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; namespace Flow.Launcher.Plugin.PluginsManager { - public class Main : ISettingProvider, IPlugin, ISavable, IContextMenu, IPluginI18n + public class Main : ISettingProvider, IPlugin, ISavable, IContextMenu, IPluginI18n, IReloadable { internal PluginInitContext Context { get; set; } @@ -87,5 +87,14 @@ namespace Flow.Launcher.Plugin.PluginsManager { return Context.API.GetTranslation("plugin_pluginsmanager_plugin_description"); } + + public void ReloadData() + { + Task.Run(async () => + { + await pluginManager.UpdateManifest(); + lastUpdateTime = DateTime.Now; + }); + } } } From 661c64b1c21b5e744ac4029b68e70d095a2074e2 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 2 Jan 2021 17:00:10 +1100 Subject: [PATCH 077/158] version bump for PluginsManager --- Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json index 7e78d65d6..f5bbf3f6b 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json @@ -6,7 +6,7 @@ "Name": "Plugins Manager", "Description": "Management of installing, uninstalling or updating Flow Launcher plugins", "Author": "Jeremy Wu", - "Version": "1.3.2", + "Version": "1.4.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll", From b4b30dd34da9426a6f512966fca780c5d5ef1437 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 2 Jan 2021 18:06:44 +1100 Subject: [PATCH 078/158] wait manifest update before proceeding --- Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 7c1eda3aa..f10f022d7 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -90,11 +90,8 @@ namespace Flow.Launcher.Plugin.PluginsManager public void ReloadData() { - Task.Run(async () => - { - await pluginManager.UpdateManifest(); - lastUpdateTime = DateTime.Now; - }); + Task.Run(() => pluginManager.UpdateManifest()).Wait(); + lastUpdateTime = DateTime.Now; } } } From 3cd609377e7402669c8dbbf67140bb58551d8ae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sat, 2 Jan 2021 15:52:41 +0800 Subject: [PATCH 079/158] Plugin Async ModelAdd Full Async model, including AsyncPlugin and AsyncReloadable --- Flow.Launcher.Core/Plugin/PluginManager.cs | 150 ++++++++++++------ Flow.Launcher.Infrastructure/Stopwatch.cs | 32 +++- Flow.Launcher.Plugin/IAsyncPlugin.cs | 12 ++ Flow.Launcher.Plugin/IPlugin.cs | 1 + Flow.Launcher.Plugin/IPublicAPI.cs | 3 +- .../Interfaces/IAsyncReloadable.cs | 9 ++ Flow.Launcher.Plugin/PluginPair.cs | 2 +- Flow.Launcher/PublicAPIInstance.cs | 4 +- Flow.Launcher/ViewModel/MainViewModel.cs | 38 ++--- .../ControlPanelList.cs | 2 +- Plugins/Flow.Launcher.Plugin.Sys/Main.cs | 41 +++-- 11 files changed, 210 insertions(+), 84 deletions(-) create mode 100644 Flow.Launcher.Plugin/IAsyncPlugin.cs create mode 100644 Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 3b697a1ee..239f0499d 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Logger; @@ -32,7 +33,7 @@ namespace Flow.Launcher.Core.Plugin /// /// Directories that will hold Flow Launcher plugin directory /// - private static readonly string[] Directories = { Constant.PreinstalledDirectory, DataLocation.PluginsDirectory }; + private static readonly string[] Directories = {Constant.PreinstalledDirectory, DataLocation.PluginsDirectory}; private static void DeletePythonBinding() { @@ -52,12 +53,19 @@ namespace Flow.Launcher.Core.Plugin } } - public static void ReloadData() + public static async Task ReloadData() { foreach(var plugin in AllPlugins) { - var reloadablePlugin = plugin.Plugin as IReloadable; - reloadablePlugin?.ReloadData(); + switch (plugin.Plugin) + { + case IReloadable p: + p.ReloadData(); + break; + case IAsyncReloadable p: + await p.ReloadDataAsync(); + break; + } } } @@ -86,24 +94,50 @@ namespace Flow.Launcher.Core.Plugin /// Call initialize for all plugins ///
/// return the list of failed to init plugins or null for none - public static void InitializePlugins(IPublicAPI api) + public static async Task InitializePlugins(IPublicAPI api) { API = api; var failedPlugins = new ConcurrentQueue(); - Parallel.ForEach(AllPlugins, pair => + + var InitTasks = AllPlugins.Select(pair => Task.Run(async delegate { try { - var milliseconds = Stopwatch.Debug($"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>", () => + long milliseconds; + + switch (pair.Plugin) { - pair.Plugin.Init(new PluginInitContext - { - CurrentPluginMetadata = pair.Metadata, - API = API - }); - }); + case IAsyncPlugin plugin: + milliseconds = await Stopwatch.DebugAsync( + $"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>", + async delegate + { + await plugin.InitAsync(new PluginInitContext + { + CurrentPluginMetadata = pair.Metadata, + API = API + }); + }); + break; + case IPlugin plugin: + milliseconds = Stopwatch.Debug( + $"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>", + () => + { + plugin.Init(new PluginInitContext + { + CurrentPluginMetadata = pair.Metadata, + API = API + }); + }); + break; + default: + throw new ArgumentException(); + } + pair.Metadata.InitTime += milliseconds; - Log.Info($"|PluginManager.InitializePlugins|Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>"); + Log.Info( + $"|PluginManager.InitializePlugins|Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>"); } catch (Exception e) { @@ -111,25 +145,33 @@ namespace Flow.Launcher.Core.Plugin pair.Metadata.Disabled = true; failedPlugins.Enqueue(pair); } - }); + })); + + await Task.WhenAll(InitTasks); _contextMenuPlugins = GetPluginsForInterface(); foreach (var plugin in AllPlugins) { - if (IsGlobalPlugin(plugin.Metadata)) - GlobalPlugins.Add(plugin); - - // Plugins may have multiple ActionKeywords, eg. WebSearch - plugin.Metadata.ActionKeywords - .Where(x => x != Query.GlobalPluginWildcardSign) - .ToList() - .ForEach(x => NonGlobalPlugins[x] = plugin); + foreach (var actionKeyword in plugin.Metadata.ActionKeywords) + { + switch (actionKeyword) + { + case Query.GlobalPluginWildcardSign: + GlobalPlugins.Add(plugin); + break; + default: + NonGlobalPlugins[actionKeyword] = plugin; + break; + } + } } if (failedPlugins.Any()) { var failed = string.Join(",", failedPlugins.Select(x => x.Metadata.Name)); - API.ShowMsg($"Fail to Init Plugins", $"Plugins: {failed} - fail to load and would be disabled, please contact plugin creator for help", "", false); + API.ShowMsg($"Fail to Init Plugins", + $"Plugins: {failed} - fail to load and would be disabled, please contact plugin creator for help", + "", false); } } @@ -138,7 +180,7 @@ namespace Flow.Launcher.Core.Plugin if (NonGlobalPlugins.ContainsKey(query.ActionKeyword)) { var plugin = NonGlobalPlugins[query.ActionKeyword]; - return new List { plugin }; + return new List {plugin}; } else { @@ -146,25 +188,42 @@ namespace Flow.Launcher.Core.Plugin } } - public static List QueryForPlugin(PluginPair pair, Query query) + public static async Task> QueryForPlugin(PluginPair pair, Query query, CancellationToken token) { var results = new List(); try { var metadata = pair.Metadata; - var milliseconds = Stopwatch.Debug($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}", () => - { - results = pair.Plugin.Query(query) ?? new List(); - UpdatePluginMetadata(results, metadata, query); - }); + var milliseconds = await Stopwatch.DebugAsync($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}", + async () => + { + switch (pair.Plugin) + { + case IAsyncPlugin plugin: + results = await plugin.QueryAsync(query, token).ConfigureAwait(false) ?? + new List(); + UpdatePluginMetadata(results, metadata, query); + break; + case IPlugin plugin: + results = await Task.Run(() => plugin.Query(query), token).ConfigureAwait(false) ?? + new List(); + UpdatePluginMetadata(results, metadata, query); + break; + } + }); metadata.QueryCount += 1; - metadata.AvgQueryTime = metadata.QueryCount == 1 ? milliseconds : (metadata.AvgQueryTime + milliseconds) / 2; + metadata.AvgQueryTime = + metadata.QueryCount == 1 ? milliseconds : (metadata.AvgQueryTime + milliseconds) / 2; } catch (Exception e) { - Log.Exception($"|PluginManager.QueryForPlugin|Exception for plugin <{pair.Metadata.Name}> when query <{query}>", e); + Log.Exception( + $"|PluginManager.QueryForPlugin|Exception for plugin <{pair.Metadata.Name}> when query <{query}>", + e); } - return results; + + // null will be fine since the results will only be added into queue if the token hasn't been cancelled + return token.IsCancellationRequested ? results = null : results; } public static void UpdatePluginMetadata(List results, PluginMetadata metadata, Query query) @@ -182,11 +241,6 @@ namespace Flow.Launcher.Core.Plugin } } - private static bool IsGlobalPlugin(PluginMetadata metadata) - { - return metadata.ActionKeywords.Contains(Query.GlobalPluginWildcardSign); - } - /// /// get specified plugin, return null if not found /// @@ -208,7 +262,7 @@ namespace Flow.Launcher.Core.Plugin var pluginPair = _contextMenuPlugins.FirstOrDefault(o => o.Metadata.ID == result.PluginID); if (pluginPair != null) { - var plugin = (IContextMenu)pluginPair.Plugin; + var plugin = (IContextMenu) pluginPair.Plugin; try { @@ -222,16 +276,19 @@ namespace Flow.Launcher.Core.Plugin } catch (Exception e) { - Log.Exception($"|PluginManager.GetContextMenusForPlugin|Can't load context menus for plugin <{pluginPair.Metadata.Name}>", e); + Log.Exception( + $"|PluginManager.GetContextMenusForPlugin|Can't load context menus for plugin <{pluginPair.Metadata.Name}>", + e); } } + return results; } public static bool ActionKeywordRegistered(string actionKeyword) { return actionKeyword != Query.GlobalPluginWildcardSign - && NonGlobalPlugins.ContainsKey(actionKeyword); + && NonGlobalPlugins.ContainsKey(actionKeyword); } /// @@ -249,6 +306,7 @@ namespace Flow.Launcher.Core.Plugin { NonGlobalPlugins[newActionKeyword] = plugin; } + plugin.Metadata.ActionKeywords.Add(newActionKeyword); } @@ -262,9 +320,9 @@ namespace Flow.Launcher.Core.Plugin if (oldActionkeyword == Query.GlobalPluginWildcardSign && // Plugins may have multiple ActionKeywords that are global, eg. WebSearch plugin.Metadata.ActionKeywords - .Where(x => x == Query.GlobalPluginWildcardSign) - .ToList() - .Count == 1) + .Where(x => x == Query.GlobalPluginWildcardSign) + .ToList() + .Count == 1) { GlobalPlugins.Remove(plugin); } @@ -285,4 +343,4 @@ namespace Flow.Launcher.Core.Plugin } } } -} +} \ No newline at end of file diff --git a/Flow.Launcher.Infrastructure/Stopwatch.cs b/Flow.Launcher.Infrastructure/Stopwatch.cs index d39d90e81..dd6edaff9 100644 --- a/Flow.Launcher.Infrastructure/Stopwatch.cs +++ b/Flow.Launcher.Infrastructure/Stopwatch.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using Flow.Launcher.Infrastructure.Logger; namespace Flow.Launcher.Infrastructure @@ -22,7 +23,22 @@ namespace Flow.Launcher.Infrastructure Log.Debug(info); return milliseconds; } - + + /// + /// This stopwatch will appear only in Debug mode + /// + public static async Task DebugAsync(string message, Func action) + { + var stopWatch = new System.Diagnostics.Stopwatch(); + stopWatch.Start(); + await action(); + stopWatch.Stop(); + var milliseconds = stopWatch.ElapsedMilliseconds; + string info = $"{message} <{milliseconds}ms>"; + Log.Debug(info); + return milliseconds; + } + public static long Normal(string message, Action action) { var stopWatch = new System.Diagnostics.Stopwatch(); @@ -34,6 +50,20 @@ namespace Flow.Launcher.Infrastructure Log.Info(info); return milliseconds; } + + public static async Task NormalAsync(string message, Func action) + { + var stopWatch = new System.Diagnostics.Stopwatch(); + stopWatch.Start(); + await action(); + stopWatch.Stop(); + var milliseconds = stopWatch.ElapsedMilliseconds; + string info = $"{message} <{milliseconds}ms>"; + Log.Info(info); + return milliseconds; + } + + public static void StartCount(string name, Action action) { diff --git a/Flow.Launcher.Plugin/IAsyncPlugin.cs b/Flow.Launcher.Plugin/IAsyncPlugin.cs new file mode 100644 index 000000000..36f098e7d --- /dev/null +++ b/Flow.Launcher.Plugin/IAsyncPlugin.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Flow.Launcher.Plugin +{ + public interface IAsyncPlugin + { + Task> QueryAsync(Query query, CancellationToken token); + Task InitAsync(PluginInitContext context); + } +} \ No newline at end of file diff --git a/Flow.Launcher.Plugin/IPlugin.cs b/Flow.Launcher.Plugin/IPlugin.cs index 8f7d279fa..4cc6d8d40 100644 --- a/Flow.Launcher.Plugin/IPlugin.cs +++ b/Flow.Launcher.Plugin/IPlugin.cs @@ -5,6 +5,7 @@ namespace Flow.Launcher.Plugin public interface IPlugin { List Query(Query query); + void Init(PluginInitContext context); } } \ No newline at end of file diff --git a/Flow.Launcher.Plugin/IPublicAPI.cs b/Flow.Launcher.Plugin/IPublicAPI.cs index ccc00d5e9..12e430e07 100644 --- a/Flow.Launcher.Plugin/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/IPublicAPI.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; namespace Flow.Launcher.Plugin { @@ -34,7 +35,7 @@ namespace Flow.Launcher.Plugin /// Plugin's in memory data with new content /// added by user. /// - void ReloadAllPluginData(); + Task ReloadAllPluginData(); /// /// Check for new Flow Launcher update diff --git a/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs b/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs new file mode 100644 index 000000000..9c922f667 --- /dev/null +++ b/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Flow.Launcher.Plugin +{ + public interface IAsyncReloadable + { + Task ReloadDataAsync(); + } +} \ No newline at end of file diff --git a/Flow.Launcher.Plugin/PluginPair.cs b/Flow.Launcher.Plugin/PluginPair.cs index 910367ec6..e8954b7a0 100644 --- a/Flow.Launcher.Plugin/PluginPair.cs +++ b/Flow.Launcher.Plugin/PluginPair.cs @@ -2,7 +2,7 @@ { public class PluginPair { - public IPlugin Plugin { get; internal set; } + public object Plugin { get; internal set; } public PluginMetadata Metadata { get; internal set; } diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 90d4fff63..bcf147be7 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -78,9 +78,9 @@ namespace Flow.Launcher ImageLoader.Save(); } - public void ReloadAllPluginData() + public async Task ReloadAllPluginData() { - PluginManager.ReloadData(); + await PluginManager.ReloadData(); } public void ShowMsg(string title, string subTitle = "", string iconPath = "") diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index eed30f377..bc68eb6d6 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -405,45 +405,47 @@ namespace Flow.Launcher.ViewModel }, currentCancellationToken); var plugins = PluginManager.ValidPluginsForQuery(query); - Task.Run(() => + Task.Run(async () => { // so looping will stop once it was cancelled + + Task[] tasks = new Task[plugins.Count]; var parallelOptions = new ParallelOptions { CancellationToken = currentCancellationToken }; try { - Parallel.ForEach(plugins, parallelOptions, plugin => + Parallel.For(0, plugins.Count, parallelOptions, i => { - if (!plugin.Metadata.Disabled) + if (!plugins[i].Metadata.Disabled) { - try - { - var results = PluginManager.QueryForPlugin(plugin, query); - UpdateResultView(results, plugin.Metadata, query); - } - catch(Exception e) - { - Log.Exception("MainViewModel", $"Exception when querying the plugin {plugin.Metadata.Name}", e, "QueryResults"); - } + tasks[i] = QueryTask(i, query, currentCancellationToken); } + else tasks[i] = Task.CompletedTask; // Avoid Null }); + + // Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first + await Task.WhenAll(tasks); } catch (OperationCanceledException) { // nothing to do here } - // this should happen once after all queries are done so progress bar should continue // until the end of all querying _isQueryRunning = false; - if (currentUpdateSource == _updateSource) + if (!currentCancellationToken.IsCancellationRequested) { // update to hidden if this is still the current query ProgressBarVisibility = Visibility.Hidden; } - }, currentCancellationToken).ContinueWith(t => - { - Log.Exception("MainViewModel", "Error when querying plugins", t.Exception?.InnerException, "QueryResults"); - }, TaskContinuationOptions.OnlyOnFaulted); + + async Task QueryTask(int pairIndex, Query query, CancellationToken token) + { + var result = await PluginManager.QueryForPlugin(plugins[pairIndex], query, token); + UpdateResultView(result, plugins[pairIndex].Metadata, query); + } + + }, currentCancellationToken).ContinueWith(t => Log.Exception("|MainViewModel|Plugins Query Exceptions", t.Exception), + TaskContinuationOptions.OnlyOnFaulted); } } else diff --git a/Plugins/Flow.Launcher.Plugin.ControlPanel/ControlPanelList.cs b/Plugins/Flow.Launcher.Plugin.ControlPanel/ControlPanelList.cs index fdcffb0b3..70afda536 100644 --- a/Plugins/Flow.Launcher.Plugin.ControlPanel/ControlPanelList.cs +++ b/Plugins/Flow.Launcher.Plugin.ControlPanel/ControlPanelList.cs @@ -38,7 +38,7 @@ namespace Flow.Launcher.Plugin.ControlPanel int cxDesired, int cyDesired, uint fuLoad); [DllImport("user32.dll", CharSet = CharSet.Auto)] - extern static bool DestroyIcon(IntPtr handle); + static extern bool DestroyIcon(IntPtr handle); [DllImport("kernel32.dll")] static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, IntPtr lpType); diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs index 5642b62ed..7ebab91a1 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; +using System.Threading.Tasks; using System.Windows; using System.Windows.Forms; using System.Windows.Interop; @@ -67,13 +68,15 @@ namespace Flow.Launcher.Plugin.Sys { c.TitleHighlightData = titleMatch.MatchData; } - else + else { c.SubTitleHighlightData = subTitleMatch.MatchData; } + results.Add(c); } } + return results; } @@ -94,13 +97,15 @@ namespace Flow.Launcher.Plugin.Sys IcoPath = "Images\\shutdown.png", Action = c => { - var reuslt = MessageBox.Show(context.API.GetTranslation("flowlauncher_plugin_sys_dlgtext_shutdown_computer"), - context.API.GetTranslation("flowlauncher_plugin_sys_shutdown_computer"), - MessageBoxButton.YesNo, MessageBoxImage.Warning); + var reuslt = MessageBox.Show( + context.API.GetTranslation("flowlauncher_plugin_sys_dlgtext_shutdown_computer"), + context.API.GetTranslation("flowlauncher_plugin_sys_shutdown_computer"), + MessageBoxButton.YesNo, MessageBoxImage.Warning); if (reuslt == MessageBoxResult.Yes) { Process.Start("shutdown", "/s /t 0"); } + return true; } }, @@ -111,13 +116,15 @@ namespace Flow.Launcher.Plugin.Sys IcoPath = "Images\\restart.png", Action = c => { - var result = MessageBox.Show(context.API.GetTranslation("flowlauncher_plugin_sys_dlgtext_restart_computer"), - context.API.GetTranslation("flowlauncher_plugin_sys_restart_computer"), - MessageBoxButton.YesNo, MessageBoxImage.Warning); + var result = MessageBox.Show( + context.API.GetTranslation("flowlauncher_plugin_sys_dlgtext_restart_computer"), + context.API.GetTranslation("flowlauncher_plugin_sys_restart_computer"), + MessageBoxButton.YesNo, MessageBoxImage.Warning); if (result == MessageBoxResult.Yes) { Process.Start("shutdown", "/r /t 0"); } + return true; } }, @@ -163,14 +170,16 @@ namespace Flow.Launcher.Plugin.Sys // http://www.pinvoke.net/default.aspx/shell32/SHEmptyRecycleBin.html // FYI, couldn't find documentation for this but if the recycle bin is already empty, it will return -2147418113 (0x8000FFFF (E_UNEXPECTED)) // 0 for nothing - var result = SHEmptyRecycleBin(new WindowInteropHelper(Application.Current.MainWindow).Handle, 0); - if (result != (uint) HRESULT.S_OK && result != (uint)0x8000FFFF) + var result = SHEmptyRecycleBin(new WindowInteropHelper(Application.Current.MainWindow).Handle, + 0); + if (result != (uint) HRESULT.S_OK && result != (uint) 0x8000FFFF) { MessageBox.Show($"Error emptying recycle bin, error code: {result}\n" + "please refer to https://msdn.microsoft.com/en-us/library/windows/desktop/aa378137", - "Error", - MessageBoxButton.OK, MessageBoxImage.Error); + "Error", + MessageBoxButton.OK, MessageBoxImage.Error); } + return true; } }, @@ -229,9 +238,13 @@ namespace Flow.Launcher.Plugin.Sys { // Hide the window first then show msg after done because sometimes the reload could take a while, so not to make user think it's frozen. Application.Current.MainWindow.Hide(); - context.API.ReloadAllPluginData(); - context.API.ShowMsg(context.API.GetTranslation("flowlauncher_plugin_sys_dlgtitle_success"), - context.API.GetTranslation("flowlauncher_plugin_sys_dlgtext_all_applicableplugins_reloaded")); + + context.API.ReloadAllPluginData().ContinueWith(_ => + context.API.ShowMsg( + context.API.GetTranslation("flowlauncher_plugin_sys_dlgtitle_success"), + context.API.GetTranslation( + "flowlauncher_plugin_sys_dlgtext_all_applicableplugins_reloaded"))); + return true; } }, From b8f7d899709ee4204d2a0f1cfae8722a2b80cdbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sat, 2 Jan 2021 17:25:13 +0800 Subject: [PATCH 080/158] Allows Loading both IPlugin and IAsyncPlugin --- .../Plugin/PluginAssemblyLoader.cs | 10 +- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 100 +++++++++--------- Flow.Launcher/ViewModel/MainViewModel.cs | 9 +- 3 files changed, 61 insertions(+), 58 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs index b9b878a7b..1a1b17539 100644 --- a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs @@ -20,7 +20,7 @@ namespace Flow.Launcher.Core.Plugin dependencyResolver = new AssemblyDependencyResolver(assemblyFilePath); assemblyName = new AssemblyName(Path.GetFileNameWithoutExtension(assemblyFilePath)); - referencedPluginPackageDependencyResolver = + referencedPluginPackageDependencyResolver = new AssemblyDependencyResolver(Path.Combine(Constant.ProgramDirectory, "Flow.Launcher.Plugin.dll")); } @@ -38,15 +38,15 @@ namespace Flow.Launcher.Core.Plugin // that use Newtonsoft.Json if (assemblyPath == null || ExistsInReferencedPluginPackage(assemblyName)) return null; - + return LoadFromAssemblyPath(assemblyPath); } - internal Type FromAssemblyGetTypeOfInterface(Assembly assembly, Type type) + internal Type FromAssemblyGetTypeOfInterface(Assembly assembly, params Type[] types) { var allTypes = assembly.ExportedTypes; - return allTypes.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(type)); + return allTypes.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Intersect(types).Any()); } internal bool ExistsInReferencedPluginPackage(AssemblyName assemblyName) @@ -54,4 +54,4 @@ namespace Flow.Launcher.Core.Plugin return referencedPluginPackageDependencyResolver.ResolveAssemblyToPath(assemblyName) != null; } } -} +} \ No newline at end of file diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 224dbd85e..8295761b5 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -37,56 +37,59 @@ namespace Flow.Launcher.Core.Plugin foreach (var metadata in metadatas) { - var milliseconds = Stopwatch.Debug($"|PluginsLoader.DotNetPlugins|Constructor init cost for {metadata.Name}", () => - { - + var milliseconds = Stopwatch.Debug( + $"|PluginsLoader.DotNetPlugins|Constructor init cost for {metadata.Name}", () => + { #if DEBUG - var assemblyLoader = new PluginAssemblyLoader(metadata.ExecuteFilePath); - var assembly = assemblyLoader.LoadAssemblyAndDependencies(); - var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IPlugin)); - var plugin = (IPlugin)Activator.CreateInstance(type); -#else - Assembly assembly = null; - IPlugin plugin = null; - - try - { var assemblyLoader = new PluginAssemblyLoader(metadata.ExecuteFilePath); - assembly = assemblyLoader.LoadAssemblyAndDependencies(); + var assembly = assemblyLoader.LoadAssemblyAndDependencies(); + var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IPlugin), + typeof(IAsyncPlugin)); - var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IPlugin)); + var plugin = Activator.CreateInstance(type); +#else + Assembly assembly = null; + IPlugin plugin = null; - plugin = (IPlugin)Activator.CreateInstance(type); - } - catch (Exception e) when (assembly == null) - { - Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for the plugin: {metadata.Name}", e); - } - catch (InvalidOperationException e) - { - Log.Exception($"|PluginsLoader.DotNetPlugins|Can't find the required IPlugin interface for the plugin: <{metadata.Name}>", e); - } - catch (ReflectionTypeLoadException e) - { - Log.Exception($"|PluginsLoader.DotNetPlugins|The GetTypes method was unable to load assembly types for the plugin: <{metadata.Name}>", e); - } - catch (Exception e) - { - Log.Exception($"|PluginsLoader.DotNetPlugins|The following plugin has errored and can not be loaded: <{metadata.Name}>", e); - } + try + { + var assemblyLoader = new PluginAssemblyLoader(metadata.ExecuteFilePath); + assembly = assemblyLoader.LoadAssemblyAndDependencies(); - if (plugin == null) - { - erroredPlugins.Add(metadata.Name); - return; - } + var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IPlugin), + typeof(IAsyncPlugin)); + + plugin = Activator.CreateInstance(type); + } + catch (Exception e) when (assembly == null) + { + Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for the plugin: {metadata.Name}", e); + } + catch (InvalidOperationException e) + { + Log.Exception($"|PluginsLoader.DotNetPlugins|Can't find the required IPlugin interface for the plugin: <{metadata.Name}>", e); + } + catch (ReflectionTypeLoadException e) + { + Log.Exception($"|PluginsLoader.DotNetPlugins|The GetTypes method was unable to load assembly types for the plugin: <{metadata.Name}>", e); + } + catch (Exception e) + { + Log.Exception($"|PluginsLoader.DotNetPlugins|The following plugin has errored and can not be loaded: <{metadata.Name}>", e); + } + + if (plugin == null) + { + erroredPlugins.Add(metadata.Name); + return; + } #endif - plugins.Add(new PluginPair - { - Plugin = plugin, - Metadata = metadata + plugins.Add(new PluginPair + { + Plugin = plugin, + Metadata = metadata + }); }); - }); metadata.InitTime += milliseconds; } @@ -95,15 +98,15 @@ namespace Flow.Launcher.Core.Plugin var errorPluginString = String.Join(Environment.NewLine, erroredPlugins); var errorMessage = "The following " - + (erroredPlugins.Count > 1 ? "plugins have " : "plugin has ") - + "errored and cannot be loaded:"; + + (erroredPlugins.Count > 1 ? "plugins have " : "plugin has ") + + "errored and cannot be loaded:"; Task.Run(() => { MessageBox.Show($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + - $"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" + - $"Please refer to the logs for more information","", - MessageBoxButtons.OK, MessageBoxIcon.Warning); + $"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" + + $"Please refer to the logs for more information", "", + MessageBoxButtons.OK, MessageBoxIcon.Warning); }); } @@ -179,6 +182,5 @@ namespace Flow.Launcher.Core.Plugin Metadata = metadata }); } - } } \ No newline at end of file diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index bc68eb6d6..3e1a4b516 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -417,7 +417,7 @@ namespace Flow.Launcher.ViewModel { if (!plugins[i].Metadata.Disabled) { - tasks[i] = QueryTask(i, query, currentCancellationToken); + tasks[i] = QueryTask(plugins[i], query, currentCancellationToken); } else tasks[i] = Task.CompletedTask; // Avoid Null }); @@ -438,10 +438,11 @@ namespace Flow.Launcher.ViewModel ProgressBarVisibility = Visibility.Hidden; } - async Task QueryTask(int pairIndex, Query query, CancellationToken token) + // Local Function + async Task QueryTask(PluginPair plugin, Query query, CancellationToken token) { - var result = await PluginManager.QueryForPlugin(plugins[pairIndex], query, token); - UpdateResultView(result, plugins[pairIndex].Metadata, query); + var results = await PluginManager.QueryForPlugin(plugin, query, token); + UpdateResultView(results, plugin.Metadata, query); } }, currentCancellationToken).ContinueWith(t => Log.Exception("|MainViewModel|Plugins Query Exceptions", t.Exception), From f768b0890b8f784983da9cc2ae5a5f2b464a41ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sat, 2 Jan 2021 17:58:30 +0800 Subject: [PATCH 081/158] Move Program Plugin to Async model --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 164 ++++++++++--------- 1 file changed, 88 insertions(+), 76 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 8f124f3a4..0d693d363 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Threading; using System.Threading.Tasks; using System.Windows.Controls; using Flow.Launcher.Infrastructure.Logger; @@ -12,9 +13,8 @@ using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch; namespace Flow.Launcher.Plugin.Program { - public class Main : ISettingProvider, IPlugin, IPluginI18n, IContextMenu, ISavable, IReloadable + public class Main : ISettingProvider, IAsyncPlugin, IPluginI18n, IContextMenu, ISavable, IAsyncReloadable { - private static readonly object IndexLock = new object(); internal static Win32[] _win32s { get; set; } internal static UWP.Application[] _uwps { get; set; } internal static Settings _settings { get; set; } @@ -30,17 +30,58 @@ namespace Flow.Launcher.Plugin.Program public Main() { _settingsStorage = new PluginJsonStorage(); - _settings = _settingsStorage.Load(); + } - Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|Preload programs cost", () => + public void Save() + { + _settingsStorage.Save(); + _win32Storage.Save(_win32s); + _uwpStorage.Save(_uwps); + } + + public async Task> QueryAsync(Query query, CancellationToken token) + { + Win32[] win32; + UWP.Application[] uwps; + + win32 = _win32s; + uwps = _uwps; + + + var result = await Task.Run(delegate { - _win32Storage = new BinaryStorage("Win32"); - _win32s = _win32Storage.TryLoad(new Win32[] { }); - _uwpStorage = new BinaryStorage("UWP"); - _uwps = _uwpStorage.TryLoad(new UWP.Application[] { }); + return win32.Cast() + .Concat(uwps) + .AsParallel() + .WithCancellation(token) + .Where(p => p.Enabled) + .Select(p => p.Result(query.Search, _context.API)) + .Where(r => r?.Score > 0) + .ToList(); + }, token).ConfigureAwait(false); + + return result; + } + + public async Task InitAsync(PluginInitContext context) + { + _context = context; + + await Task.Run(() => + { + _settings = _settingsStorage.Load(); + + Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|Preload programs cost", () => + { + _win32Storage = new BinaryStorage("Win32"); + _win32s = _win32Storage.TryLoad(new Win32[] { }); + _uwpStorage = new BinaryStorage("UWP"); + _uwps = _uwpStorage.TryLoad(new UWP.Application[] { }); + }); + Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload win32 programs <{_win32s.Length}>"); + Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload uwps <{_uwps.Length}>"); }); - Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload win32 programs <{_win32s.Length}>"); - Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload uwps <{_uwps.Length}>"); + var a = Task.Run(() => { @@ -51,54 +92,22 @@ namespace Flow.Launcher.Plugin.Program var b = Task.Run(() => { if (IsStartupIndexProgramsRequired || !_uwps.Any()) - Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|Win32Program index cost", IndexUWPPrograms); + Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|Win32Program index cost", IndexUwpPrograms); }); - Task.WaitAll(a, b); + await Task.WhenAll(a, b); _settings.LastIndexTime = DateTime.Today; } - public void Save() - { - _settingsStorage.Save(); - _win32Storage.Save(_win32s); - _uwpStorage.Save(_uwps); - } - - public List Query(Query query) - { - Win32[] win32; - UWP.Application[] uwps; - - win32 = _win32s; - uwps = _uwps; - - var result = win32.Cast() - .Concat(uwps) - .AsParallel() - .Where(p => p.Enabled) - .Select(p => p.Result(query.Search, _context.API)) - .Where(r => r?.Score > 0) - .ToList(); - - return result; - } - - public void Init(PluginInitContext context) - { - _context = context; - } - public static void IndexWin32Programs() { var win32S = Win32.All(_settings); _win32s = win32S; - } - public static void IndexUWPPrograms() + public static void IndexUwpPrograms() { var windows10 = new Version(10, 0); var support = Environment.OSVersion.Version.Major >= windows10.Major; @@ -106,16 +115,15 @@ namespace Flow.Launcher.Plugin.Program var applications = support ? UWP.All() : new UWP.Application[] { }; _uwps = applications; - } - public static void IndexPrograms() + public static async Task IndexPrograms() { - var t1 = Task.Run(() => IndexWin32Programs()); + var t1 = Task.Run(IndexWin32Programs); - var t2 = Task.Run(() => IndexUWPPrograms()); + var t2 = Task.Run(IndexUwpPrograms); - Task.WaitAll(t1, t2); + await Task.WhenAll(t1, t2); _settings.LastIndexTime = DateTime.Today; } @@ -145,19 +153,21 @@ namespace Flow.Launcher.Plugin.Program } menuOptions.Add( - new Result - { - Title = _context.API.GetTranslation("flowlauncher_plugin_program_disable_program"), - Action = c => - { - DisableProgram(program); - _context.API.ShowMsg(_context.API.GetTranslation("flowlauncher_plugin_program_disable_dlgtitle_success"), - _context.API.GetTranslation("flowlauncher_plugin_program_disable_dlgtitle_success_message")); - return false; - }, - IcoPath = "Images/disable.png" - } - ); + new Result + { + Title = _context.API.GetTranslation("flowlauncher_plugin_program_disable_program"), + Action = c => + { + DisableProgram(program); + _context.API.ShowMsg( + _context.API.GetTranslation("flowlauncher_plugin_program_disable_dlgtitle_success"), + _context.API.GetTranslation( + "flowlauncher_plugin_program_disable_dlgtitle_success_message")); + return false; + }, + IcoPath = "Images/disable.png" + } + ); return menuOptions; } @@ -168,21 +178,23 @@ namespace Flow.Launcher.Plugin.Program return; if (_uwps.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)) - _uwps.Where(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier).FirstOrDefault().Enabled = false; + _uwps.Where(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier).FirstOrDefault().Enabled = + false; if (_win32s.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)) - _win32s.Where(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier).FirstOrDefault().Enabled = false; + _win32s.Where(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier).FirstOrDefault().Enabled = + false; _settings.DisabledProgramSources - .Add( - new Settings.DisabledProgramSource - { - Name = programToDelete.Name, - Location = programToDelete.Location, - UniqueIdentifier = programToDelete.UniqueIdentifier, - Enabled = false - } - ); + .Add( + new Settings.DisabledProgramSource + { + Name = programToDelete.Name, + Location = programToDelete.Location, + UniqueIdentifier = programToDelete.UniqueIdentifier, + Enabled = false + } + ); } public static void StartProcess(Func runProcess, ProcessStartInfo info) @@ -200,9 +212,9 @@ namespace Flow.Launcher.Plugin.Program } } - public void ReloadData() + public async Task ReloadDataAsync() { - IndexPrograms(); + await IndexPrograms(); } } } \ No newline at end of file From 6326d6f3d57e8a5ef6065c210f8d6db7d932ca18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 2 Jan 2021 20:22:07 +0800 Subject: [PATCH 082/158] Startup async --- Flow.Launcher/App.xaml.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 59bdbc896..7417a6f2a 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -45,9 +45,9 @@ namespace Flow.Launcher } } - private void OnStartup(object sender, StartupEventArgs e) + private async void OnStartup(object sender, StartupEventArgs e) { - Stopwatch.Normal("|App.OnStartup|Startup cost", () => + await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => { _portable.PreStartCleanUpAfterPortabilityUpdate(); @@ -70,7 +70,7 @@ namespace Flow.Launcher _mainVM = new MainViewModel(_settings); var window = new MainWindow(_settings, _mainVM); API = new PublicAPIInstance(_settingsVM, _mainVM, _alphabet); - PluginManager.InitializePlugins(API); + await PluginManager.InitializePlugins(API); Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}"); Current.MainWindow = window; From 7be2a956dd31aa6d48a263926ea2e70f0a4e437b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 2 Jan 2021 21:20:21 +0800 Subject: [PATCH 083/158] Use cancallationToken.IsCancellationRequested instead of comparing current token with global token --- Flow.Launcher/ViewModel/MainViewModel.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 3e1a4b516..f9c152046 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -442,7 +442,8 @@ namespace Flow.Launcher.ViewModel async Task QueryTask(PluginPair plugin, Query query, CancellationToken token) { var results = await PluginManager.QueryForPlugin(plugin, query, token); - UpdateResultView(results, plugin.Metadata, query); + if (!currentCancellationToken.IsCancellationRequested) + UpdateResultView(results, plugin.Metadata, query); } }, currentCancellationToken).ContinueWith(t => Log.Exception("|MainViewModel|Plugins Query Exceptions", t.Exception), From 280b98b47a1fbdb5cb8a493339123d9586b54ccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 2 Jan 2021 21:23:34 +0800 Subject: [PATCH 084/158] Move the creation of Window later due to async operation --- Flow.Launcher/App.xaml.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 7417a6f2a..0145dfa34 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -68,9 +68,10 @@ namespace Flow.Launcher PluginManager.LoadPlugins(_settings.PluginSettings); _mainVM = new MainViewModel(_settings); - var window = new MainWindow(_settings, _mainVM); API = new PublicAPIInstance(_settingsVM, _mainVM, _alphabet); await PluginManager.InitializePlugins(API); + var window = new MainWindow(_settings, _mainVM); + Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}"); Current.MainWindow = window; From 4cb4aa88ef52dcab60c662a312f8b2e953b40ffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 2 Jan 2021 22:00:43 +0800 Subject: [PATCH 085/158] change onstartup name to async --- Flow.Launcher/App.xaml | 2 +- Flow.Launcher/App.xaml.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/App.xaml b/Flow.Launcher/App.xaml index f3347d7fb..18addac73 100644 --- a/Flow.Launcher/App.xaml +++ b/Flow.Launcher/App.xaml @@ -3,7 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ui="http://schemas.modernwpf.com/2019" ShutdownMode="OnMainWindowClose" - Startup="OnStartup"> + Startup="OnStartupAsync"> diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 0145dfa34..06bb16e3b 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -45,7 +45,7 @@ namespace Flow.Launcher } } - private async void OnStartup(object sender, StartupEventArgs e) + private async void OnStartupAsync(object sender, StartupEventArgs e) { await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => { From d7805d7a8cbc7eedcee7cc7a62156397f1af5172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 2 Jan 2021 22:01:15 +0800 Subject: [PATCH 086/158] Make Explorer plugin completely async --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 4 +- Plugins/Flow.Launcher.Plugin.Explorer/Main.cs | 14 ++- .../DirectoryInfo/DirectoryInfoSearch.cs | 6 +- .../Search/SearchManager.cs | 54 ++++++----- .../Search/WindowsIndex/IndexSearch.cs | 90 +++++++++---------- .../ViewModels/SettingsViewModel.cs | 6 ++ 6 files changed, 96 insertions(+), 78 deletions(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index c91144825..756ceb2d6 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -151,7 +151,7 @@ namespace Flow.Launcher.Test.Plugins var searchManager = new SearchManager(new Settings(), new PluginInitContext()); // When - var results = searchManager.TopLevelDirectorySearchBehaviour( + var results = searchManager.TopLevelDirectorySearchBehaviourAsync( MethodWindowsIndexSearchReturnsZeroResults, MethodDirectoryInfoClassSearchReturnsTwoResults, false, @@ -171,7 +171,7 @@ namespace Flow.Launcher.Test.Plugins var searchManager = new SearchManager(new Settings(), new PluginInitContext()); // When - var results = searchManager.TopLevelDirectorySearchBehaviour( + var results = searchManager.TopLevelDirectorySearchBehaviourAsync( MethodWindowsIndexSearchReturnsZeroResults, MethodDirectoryInfoClassSearchReturnsTwoResults, true, diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs index 30a06e882..7b56df691 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -3,11 +3,13 @@ using Flow.Launcher.Plugin.Explorer.Search; using Flow.Launcher.Plugin.Explorer.ViewModels; using Flow.Launcher.Plugin.Explorer.Views; using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; using System.Windows.Controls; namespace Flow.Launcher.Plugin.Explorer { - public class Main : ISettingProvider, IPlugin, ISavable, IContextMenu, IPluginI18n + public class Main : ISettingProvider, IAsyncPlugin, ISavable, IContextMenu, IPluginI18n { internal PluginInitContext Context { get; set; } @@ -17,17 +19,21 @@ namespace Flow.Launcher.Plugin.Explorer private IContextMenu contextMenu; + private SearchManager searchManager; + public Control CreateSettingPanel() { return new ExplorerSettings(viewModel); } - public void Init(PluginInitContext context) + public async Task InitAsync(PluginInitContext context) { Context = context; viewModel = new SettingsViewModel(context); + await viewModel.LoadStorage(); Settings = viewModel.Settings; contextMenu = new ContextMenu(Context, Settings); + searchManager = new SearchManager(Settings, Context); } public List LoadContextMenus(Result selectedResult) @@ -35,9 +41,9 @@ namespace Flow.Launcher.Plugin.Explorer return contextMenu.LoadContextMenus(selectedResult); } - public List Query(Query query) + public async Task> QueryAsync(Query query, CancellationToken token) { - return new SearchManager(Settings, Context).Search(query); + return await searchManager.SearchAsync(query, token); } public void Save() diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs index 02de0eeae..3253b7a7b 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Windows; namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo { @@ -22,7 +23,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo if (search.LastIndexOf(Constants.AllFilesFolderSearchWildcard) > search.LastIndexOf(Constants.DirectorySeperator)) return DirectorySearch(SearchOption.AllDirectories, query, search, criteria); - + return DirectorySearch(SearchOption.TopDirectoryOnly, query, search, criteria); } @@ -57,9 +58,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo try { var directoryInfo = new System.IO.DirectoryInfo(path); - var fileSystemInfos = directoryInfo.GetFileSystemInfos(searchCriteria, searchOption); - foreach (var fileSystemInfo in fileSystemInfos) + foreach (var fileSystemInfo in directoryInfo.EnumerateFileSystemInfos(searchCriteria, searchOption)) { if ((fileSystemInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) continue; diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 5b50b7fad..6b3a96912 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -5,6 +5,8 @@ using Flow.Launcher.Plugin.SharedCommands; using System; using System.Collections.Generic; using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace Flow.Launcher.Plugin.Explorer.Search { @@ -28,20 +30,20 @@ namespace Flow.Launcher.Plugin.Explorer.Search this.settings = settings; } - internal List Search(Query query) + internal async Task> SearchAsync(Query query, CancellationToken token) { var results = new List(); var querySearch = query.Search; if (IsFileContentSearch(query.ActionKeyword)) - return WindowsIndexFileContentSearch(query, querySearch); + return await WindowsIndexFileContentSearchAsync(query, querySearch, token).ConfigureAwait(false); // This allows the user to type the assigned action keyword and only see the list of quick folder links if (settings.QuickFolderAccessLinks.Count > 0 && query.ActionKeyword == settings.SearchActionKeyword && string.IsNullOrEmpty(query.Search)) - return quickFolderAccess.FolderListAll(query, settings.QuickFolderAccessLinks, context); + return quickFolderAccess.FolderListAll(query, settings.QuickFolderAccessLinks, context); var quickFolderLinks = quickFolderAccess.FolderListMatched(query, settings.QuickFolderAccessLinks, context); @@ -54,11 +56,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search return EnvironmentVariables.GetEnvironmentStringPathSuggestions(querySearch, query, context); // Query is a location path with a full environment variable, eg. %appdata%\somefolder\ - var isEnvironmentVariablePath = querySearch.Substring(1).Contains("%\\"); + var isEnvironmentVariablePath = querySearch[1..].Contains("%\\"); if (!FilesFolders.IsLocationPathString(querySearch) && !isEnvironmentVariablePath) { - results.AddRange(WindowsIndexFilesAndFoldersSearch(query, querySearch)); + results.AddRange(await WindowsIndexFilesAndFoldersSearchAsync(query, querySearch, token).ConfigureAwait(false)); return results; } @@ -72,29 +74,34 @@ namespace Flow.Launcher.Plugin.Explorer.Search return results; var useIndexSearch = UseWindowsIndexForDirectorySearch(locationPath); - + results.Add(resultManager.CreateOpenCurrentFolderResult(locationPath, useIndexSearch)); - results.AddRange(TopLevelDirectorySearchBehaviour(WindowsIndexTopLevelFolderSearch, + if (token.IsCancellationRequested) + return null; + + results.AddRange(await TopLevelDirectorySearchBehaviourAsync(WindowsIndexTopLevelFolderSearchAsync, DirectoryInfoClassSearch, useIndexSearch, query, - locationPath)); + locationPath, + token).ConfigureAwait(false)); return results; } - private List WindowsIndexFileContentSearch(Query query, string querySearchString) + private async Task> WindowsIndexFileContentSearchAsync(Query query, string querySearchString, CancellationToken token) { var queryConstructor = new QueryConstructor(settings); if (string.IsNullOrEmpty(querySearchString)) return new List(); - return indexSearch.WindowsIndexSearch(querySearchString, + return await indexSearch.WindowsIndexSearchAsync(querySearchString, queryConstructor.CreateQueryHelper().ConnectionString, queryConstructor.QueryForFileContentSearch, - query); + query, + token).ConfigureAwait(false); } public bool IsFileContentSearch(string actionKeyword) @@ -109,37 +116,40 @@ namespace Flow.Launcher.Plugin.Explorer.Search return directoryInfoSearch.TopLevelDirectorySearch(query, querySearch); } - public List TopLevelDirectorySearchBehaviour( - Func> windowsIndexSearch, + public async Task> TopLevelDirectorySearchBehaviourAsync( + Func>> windowsIndexSearch, Func> directoryInfoClassSearch, bool useIndexSearch, Query query, - string querySearchString) + string querySearchString, + CancellationToken token) { if (!useIndexSearch) return directoryInfoClassSearch(query, querySearchString); - return windowsIndexSearch(query, querySearchString); + return await windowsIndexSearch(query, querySearchString, token); } - private List WindowsIndexFilesAndFoldersSearch(Query query, string querySearchString) + private async Task> WindowsIndexFilesAndFoldersSearchAsync(Query query, string querySearchString, CancellationToken token) { var queryConstructor = new QueryConstructor(settings); - return indexSearch.WindowsIndexSearch(querySearchString, + return await indexSearch.WindowsIndexSearchAsync(querySearchString, queryConstructor.CreateQueryHelper().ConnectionString, queryConstructor.QueryForAllFilesAndFolders, - query); + query, + token).ConfigureAwait(false); } - - private List WindowsIndexTopLevelFolderSearch(Query query, string path) + + private async Task> WindowsIndexTopLevelFolderSearchAsync(Query query, string path, CancellationToken token) { var queryConstructor = new QueryConstructor(settings); - return indexSearch.WindowsIndexSearch(path, + return await indexSearch.WindowsIndexSearchAsync(path, queryConstructor.CreateQueryHelper().ConnectionString, queryConstructor.QueryForTopLevelDirectorySearch, - query); + query, + token).ConfigureAwait(false); } private bool UseWindowsIndexForDirectorySearch(string locationPath) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs index 4f9325c77..5b1d47ef8 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs @@ -5,19 +5,13 @@ using System.Collections.Generic; using System.Data.OleDb; using System.Linq; using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { internal class IndexSearch { - private readonly object _lock = new object(); - - private OleDbConnection conn; - - private OleDbCommand command; - - private OleDbDataReader dataReaderResults; - private readonly ResultManager resultManager; // Reserved keywords in oleDB @@ -28,7 +22,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex resultManager = new ResultManager(context); } - internal List ExecuteWindowsIndexSearch(string indexQueryString, string connectionString, Query query) + internal async Task> ExecuteWindowsIndexSearchAsync(string indexQueryString, string connectionString, Query query, CancellationToken token) { var folderResults = new List(); var fileResults = new List(); @@ -36,47 +30,49 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex try { - using (conn = new OleDbConnection(connectionString)) + using var conn = new OleDbConnection(connectionString); + await conn.OpenAsync(token); + token.ThrowIfCancellationRequested(); + + using var command = new OleDbCommand(indexQueryString, conn); + // Results return as an OleDbDataReader. + using var dataReaderResults = await command.ExecuteReaderAsync(token) as OleDbDataReader; + token.ThrowIfCancellationRequested(); + + if (dataReaderResults.HasRows) { - conn.Open(); - - using (command = new OleDbCommand(indexQueryString, conn)) + while (await dataReaderResults.ReadAsync(token)) { - // Results return as an OleDbDataReader. - using (dataReaderResults = command.ExecuteReader()) + token.ThrowIfCancellationRequested(); + if (dataReaderResults.GetValue(0) != DBNull.Value && dataReaderResults.GetValue(1) != DBNull.Value) { - if (dataReaderResults.HasRows) - { - while (dataReaderResults.Read()) - { - if (dataReaderResults.GetValue(0) != DBNull.Value && dataReaderResults.GetValue(1) != DBNull.Value) - { - // # is URI syntax for the fragment component, need to be encoded so LocalPath returns complete path - var encodedFragmentPath = dataReaderResults - .GetString(1) - .Replace("#", "%23", StringComparison.OrdinalIgnoreCase); - - var path = new Uri(encodedFragmentPath).LocalPath; + // # is URI syntax for the fragment component, need to be encoded so LocalPath returns complete path + var encodedFragmentPath = dataReaderResults + .GetString(1) + .Replace("#", "%23", StringComparison.OrdinalIgnoreCase); - if (dataReaderResults.GetString(2) == "Directory") - { - folderResults.Add(resultManager.CreateFolderResult( - dataReaderResults.GetString(0), - path, - path, - query, true, true)); - } - else - { - fileResults.Add(resultManager.CreateFileResult(path, query, true, true)); - } - } - } + var path = new Uri(encodedFragmentPath).LocalPath; + + if (dataReaderResults.GetString(2) == "Directory") + { + folderResults.Add(resultManager.CreateFolderResult( + dataReaderResults.GetString(0), + path, + path, + query, true, true)); + } + else + { + fileResults.Add(resultManager.CreateFileResult(path, query, true, true)); } } } } } + catch (OperationCanceledException) + { + return new List(); // The source code indicates that without adding members, it won't allocate an array + } catch (InvalidOperationException e) { // Internal error from ExecuteReader(): Connection closed. @@ -91,18 +87,18 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex return results.Concat(folderResults.OrderBy(x => x.Title)).Concat(fileResults.OrderBy(x => x.Title)).ToList(); ; } - internal List WindowsIndexSearch(string searchString, string connectionString, Func constructQuery, Query query) + internal async Task> WindowsIndexSearchAsync(string searchString, string connectionString, + Func constructQuery, Query query, + CancellationToken token) { var regexMatch = Regex.Match(searchString, reservedStringPattern); if (regexMatch.Success) return new List(); - lock (_lock) - { - var constructedQuery = constructQuery(searchString); - return ExecuteWindowsIndexSearch(constructedQuery, connectionString, query); - } + var constructedQuery = constructQuery(searchString); + return await ExecuteWindowsIndexSearchAsync(constructedQuery, connectionString, query, token); + } internal bool PathIsIndexed(string path) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs index 7fcd77f07..21bc49741 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs @@ -3,6 +3,7 @@ using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Plugin.Explorer.Search; using Flow.Launcher.Plugin.Explorer.Search.FolderLinks; using System.Diagnostics; +using System.Threading.Tasks; namespace Flow.Launcher.Plugin.Explorer.ViewModels { @@ -21,6 +22,11 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels Settings = storage.Load(); } + public Task LoadStorage() + { + return Task.Run(() => Settings = storage.Load()); + } + public void Save() { storage.Save(); From 43cee65c4518b01ca49ba9a267446a390d41501e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 2 Jan 2021 22:21:45 +0800 Subject: [PATCH 087/158] Move PluginManagers to Async Model --- .../Main.cs | 19 +++++++++---------- .../Models/PluginsManifest.cs | 7 +------ 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index f10f022d7..40579e6e5 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -7,10 +7,11 @@ using System.Windows.Controls; using Flow.Launcher.Infrastructure; using System; using System.Threading.Tasks; +using System.Threading; namespace Flow.Launcher.Plugin.PluginsManager { - public class Main : ISettingProvider, IPlugin, ISavable, IContextMenu, IPluginI18n, IReloadable + public class Main : ISettingProvider, IAsyncPlugin, ISavable, IContextMenu, IPluginI18n, IAsyncReloadable { internal PluginInitContext Context { get; set; } @@ -29,13 +30,14 @@ namespace Flow.Launcher.Plugin.PluginsManager return new PluginsManagerSettings(viewModel); } - public void Init(PluginInitContext context) + public async Task InitAsync(PluginInitContext context) { Context = context; viewModel = new SettingsViewModel(context); Settings = viewModel.Settings; contextMenu = new ContextMenu(Context); pluginManager = new PluginsManager(Context, Settings); + await pluginManager.UpdateManifest(); lastUpdateTime = DateTime.Now; } @@ -44,7 +46,7 @@ namespace Flow.Launcher.Plugin.PluginsManager return contextMenu.LoadContextMenus(selectedResult); } - public List Query(Query query) + public async Task> QueryAsync(Query query, CancellationToken token) { var search = query.Search.ToLower(); @@ -53,11 +55,8 @@ namespace Flow.Launcher.Plugin.PluginsManager if ((DateTime.Now - lastUpdateTime).TotalHours > 12) // 12 hours { - Task.Run(async () => - { - await pluginManager.UpdateManifest(); - lastUpdateTime = DateTime.Now; - }); + await pluginManager.UpdateManifest(); + lastUpdateTime = DateTime.Now; } return search switch @@ -88,9 +87,9 @@ namespace Flow.Launcher.Plugin.PluginsManager return Context.API.GetTranslation("plugin_pluginsmanager_plugin_description"); } - public void ReloadData() + public async Task ReloadDataAsync() { - Task.Run(() => pluginManager.UpdateManifest()).Wait(); + await pluginManager.UpdateManifest(); lastUpdateTime = DateTime.Now; } } diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs index 814e0764d..145aadc98 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs @@ -9,12 +9,7 @@ namespace Flow.Launcher.Plugin.PluginsManager.Models { internal class PluginsManifest { - internal List UserPlugins { get; private set; } - - internal PluginsManifest() - { - Task.Run(async () => await DownloadManifest()).Wait(); - } + internal List UserPlugins { get; private set; } = new List(); internal async Task DownloadManifest() { From 69cb8e61479d477a9f0c05a5d7f40b386af6438d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 2 Jan 2021 22:30:56 +0800 Subject: [PATCH 088/158] Reload IAsyncReloadable concurrently --- Flow.Launcher.Core/Plugin/PluginManager.cs | 33 ++++++++++++---------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 239f0499d..2e938127c 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -33,7 +33,7 @@ namespace Flow.Launcher.Core.Plugin /// /// Directories that will hold Flow Launcher plugin directory /// - private static readonly string[] Directories = {Constant.PreinstalledDirectory, DataLocation.PluginsDirectory}; + private static readonly string[] Directories = { Constant.PreinstalledDirectory, DataLocation.PluginsDirectory }; private static void DeletePythonBinding() { @@ -55,18 +55,21 @@ namespace Flow.Launcher.Core.Plugin public static async Task ReloadData() { - foreach(var plugin in AllPlugins) + await Task.WhenAll(AllPlugins.Select(plugin => { - switch (plugin.Plugin) { - case IReloadable p: - p.ReloadData(); - break; - case IAsyncReloadable p: - await p.ReloadDataAsync(); - break; + switch (plugin) + { + case IReloadable p: + p.ReloadData(); // Sync reload means low time consuming + return Task.CompletedTask; + case IAsyncReloadable p: + return p.ReloadDataAsync(); + default: + throw new ArgumentOutOfRangeException(); + } } - } + })); } static PluginManager() @@ -142,7 +145,7 @@ namespace Flow.Launcher.Core.Plugin catch (Exception e) { Log.Exception(nameof(PluginManager), $"Fail to Init plugin: {pair.Metadata.Name}", e); - pair.Metadata.Disabled = true; + pair.Metadata.Disabled = true; failedPlugins.Enqueue(pair); } })); @@ -180,7 +183,7 @@ namespace Flow.Launcher.Core.Plugin if (NonGlobalPlugins.ContainsKey(query.ActionKeyword)) { var plugin = NonGlobalPlugins[query.ActionKeyword]; - return new List {plugin}; + return new List { plugin }; } else { @@ -262,7 +265,7 @@ namespace Flow.Launcher.Core.Plugin var pluginPair = _contextMenuPlugins.FirstOrDefault(o => o.Metadata.ID == result.PluginID); if (pluginPair != null) { - var plugin = (IContextMenu) pluginPair.Plugin; + var plugin = (IContextMenu)pluginPair.Plugin; try { @@ -326,10 +329,10 @@ namespace Flow.Launcher.Core.Plugin { GlobalPlugins.Remove(plugin); } - + if (oldActionkeyword != Query.GlobalPluginWildcardSign) NonGlobalPlugins.Remove(oldActionkeyword); - + plugin.Metadata.ActionKeywords.Remove(oldActionkeyword); } From 6e9e51ec4d674867deb283dd721dd485978cd4e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 3 Jan 2021 10:19:05 +0800 Subject: [PATCH 089/158] Error handling for OperationCancelledException in Program plugin --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 39 ++++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 0d693d363..954c238a9 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -47,20 +47,35 @@ namespace Flow.Launcher.Plugin.Program win32 = _win32s; uwps = _uwps; - - var result = await Task.Run(delegate + try { - return win32.Cast() - .Concat(uwps) - .AsParallel() - .WithCancellation(token) - .Where(p => p.Enabled) - .Select(p => p.Result(query.Search, _context.API)) - .Where(r => r?.Score > 0) - .ToList(); - }, token).ConfigureAwait(false); + var result = await Task.Run(delegate + { + try + { + return win32.Cast() + .Concat(uwps) + .AsParallel() + .WithCancellation(token) + .Where(p => p.Enabled) + .Select(p => p.Result(query.Search, _context.API)) + .Where(r => r?.Score > 0) + .ToList(); + } + catch (OperationCanceledException) + { + return null; + } + }, token).ConfigureAwait(false); - return result; + token.ThrowIfCancellationRequested(); + + return result; + } + catch (OperationCanceledException) + { + return null; + } } public async Task InitAsync(PluginInitContext context) From 1c200695982e1e0ad170febf0d04498690baea59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 3 Jan 2021 10:19:33 +0800 Subject: [PATCH 090/158] Rebase to Dev --- Flow.Launcher.Core/Plugin/PluginManager.cs | 47 ++++++++++++---------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 2e938127c..b34995ba6 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -197,36 +197,41 @@ namespace Flow.Launcher.Core.Plugin try { var metadata = pair.Metadata; - var milliseconds = await Stopwatch.DebugAsync($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}", - async () => - { - switch (pair.Plugin) - { - case IAsyncPlugin plugin: - results = await plugin.QueryAsync(query, token).ConfigureAwait(false) ?? - new List(); - UpdatePluginMetadata(results, metadata, query); - break; - case IPlugin plugin: - results = await Task.Run(() => plugin.Query(query), token).ConfigureAwait(false) ?? - new List(); - UpdatePluginMetadata(results, metadata, query); - break; - } - }); + + long milliseconds = -1L; + + switch (pair.Plugin) + { + case IAsyncPlugin plugin: + milliseconds = await Stopwatch.DebugAsync($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}", + async () => results = await plugin.QueryAsync(query, token).ConfigureAwait(false)); + break; + case IPlugin plugin: + await Task.Run(() => milliseconds = Stopwatch.Debug($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}", () => + results = plugin.Query(query)), token).ConfigureAwait(false); + break; + default: + throw new ArgumentOutOfRangeException(); + } + token.ThrowIfCancellationRequested(); + UpdatePluginMetadata(results, metadata, query); + metadata.QueryCount += 1; metadata.AvgQueryTime = metadata.QueryCount == 1 ? milliseconds : (metadata.AvgQueryTime + milliseconds) / 2; + token.ThrowIfCancellationRequested(); + } + catch (Exception e) when (e is OperationCanceledException || e is TaskCanceledException) + { + return results = null; } catch (Exception e) { - Log.Exception( - $"|PluginManager.QueryForPlugin|Exception for plugin <{pair.Metadata.Name}> when query <{query}>", - e); + Log.Exception($"|PluginManager.QueryForPlugin|Exception for plugin <{pair.Metadata.Name}> when query <{query}>", e); } // null will be fine since the results will only be added into queue if the token hasn't been cancelled - return token.IsCancellationRequested ? results = null : results; + return results; } public static void UpdatePluginMetadata(List results, PluginMetadata metadata, Query query) From 731c3cdcbc23e85b38b3b652d970328adc566675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 3 Jan 2021 10:37:36 +0800 Subject: [PATCH 091/158] Use OperationCancelledException instead of catch Exception and check --- Flow.Launcher.Core/Plugin/PluginManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index b34995ba6..0712908bf 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -221,7 +221,7 @@ namespace Flow.Launcher.Core.Plugin metadata.QueryCount == 1 ? milliseconds : (metadata.AvgQueryTime + milliseconds) / 2; token.ThrowIfCancellationRequested(); } - catch (Exception e) when (e is OperationCanceledException || e is TaskCanceledException) + catch (OperationCanceledException) { return results = null; } From ecf2a7a1f7cd73b787a0aa66bb588e9994e98397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 3 Jan 2021 10:43:05 +0800 Subject: [PATCH 092/158] change plugin type in pluginLoader for Release --- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 8295761b5..b18c07e3c 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -49,7 +49,7 @@ namespace Flow.Launcher.Core.Plugin var plugin = Activator.CreateInstance(type); #else Assembly assembly = null; - IPlugin plugin = null; + object plugin = null; try { From 63e32f1097e6ce061bf431aa6e452f3e47ac19f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 3 Jan 2021 10:52:59 +0800 Subject: [PATCH 093/158] fix testing --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 48 ++++++++++++---------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 756ceb2d6..09c7d9a30 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -7,6 +7,8 @@ using Flow.Launcher.Plugin.SharedCommands; using NUnit.Framework; using System; using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; namespace Flow.Launcher.Test.Plugins { @@ -17,15 +19,15 @@ namespace Flow.Launcher.Test.Plugins [TestFixture] public class ExplorerTest { - private List MethodWindowsIndexSearchReturnsZeroResults(Query dummyQuery, string dummyString) + private async Task> MethodWindowsIndexSearchReturnsZeroResultsAsync(Query dummyQuery, string dummyString, CancellationToken dummyToken) { return new List(); } private List MethodDirectoryInfoClassSearchReturnsTwoResults(Query dummyQuery, string dummyString) { - return new List - { + return new List + { new Result { Title="Result 1" @@ -64,10 +66,10 @@ namespace Flow.Launcher.Test.Plugins { // Given var queryConstructor = new QueryConstructor(new Settings()); - + //When var queryString = queryConstructor.QueryForTopLevelDirectorySearch(folderPath); - + // Then Assert.IsTrue(queryString == expectedString, $"Expected string: {expectedString}{Environment.NewLine} " + @@ -112,7 +114,7 @@ namespace Flow.Launcher.Test.Plugins } [TestCase("scope='file:'")] - public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryWhereRestrictionsShouldUseScopeString(string expectedString) + public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryWhereRestrictionsShouldUseScopeString(string expectedString) { // Given var queryConstructor = new QueryConstructor(new Settings()); @@ -130,7 +132,7 @@ namespace Flow.Launcher.Test.Plugins "FROM \"SystemIndex\" WHERE (System.FileName LIKE 'flow.launcher.sln%' " + "OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:'")] public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryShouldUseExpectedString( - string userSearchString, string expectedString) + string userSearchString, string expectedString) { // Given var queryConstructor = new QueryConstructor(new Settings()); @@ -145,18 +147,19 @@ namespace Flow.Launcher.Test.Plugins } [TestCase] - public void GivenTopLevelDirectorySearch_WhenIndexSearchNotRequired_ThenSearchMethodShouldContinueDirectoryInfoClassSearch() + public async Task GivenTopLevelDirectorySearch_WhenIndexSearchNotRequired_ThenSearchMethodShouldContinueDirectoryInfoClassSearch() { // Given var searchManager = new SearchManager(new Settings(), new PluginInitContext()); - + // When - var results = searchManager.TopLevelDirectorySearchBehaviourAsync( - MethodWindowsIndexSearchReturnsZeroResults, - MethodDirectoryInfoClassSearchReturnsTwoResults, - false, + var results = await searchManager.TopLevelDirectorySearchBehaviourAsync( + MethodWindowsIndexSearchReturnsZeroResultsAsync, + MethodDirectoryInfoClassSearchReturnsTwoResults, + false, new Query(), - "string not used"); + "string not used", + default); // Then Assert.IsTrue(results.Count == 2, @@ -165,18 +168,19 @@ namespace Flow.Launcher.Test.Plugins } [TestCase] - public void GivenTopLevelDirectorySearch_WhenIndexSearchNotRequired_ThenSearchMethodShouldNotContinueDirectoryInfoClassSearch() + public async Task GivenTopLevelDirectorySearch_WhenIndexSearchNotRequired_ThenSearchMethodShouldNotContinueDirectoryInfoClassSearch() { // Given var searchManager = new SearchManager(new Settings(), new PluginInitContext()); // When - var results = searchManager.TopLevelDirectorySearchBehaviourAsync( - MethodWindowsIndexSearchReturnsZeroResults, + var results = await searchManager.TopLevelDirectorySearchBehaviourAsync( + MethodWindowsIndexSearchReturnsZeroResultsAsync, MethodDirectoryInfoClassSearchReturnsTwoResults, true, new Query(), - "string not used"); + "string not used", + default); // Then Assert.IsTrue(results.Count == 0, @@ -223,7 +227,7 @@ namespace Flow.Launcher.Test.Plugins var query = new Query { ActionKeyword = "doc:", Search = "search term" }; var searchManager = new SearchManager(new Settings(), new PluginInitContext()); - + // When var result = searchManager.IsFileContentSearch(query.ActionKeyword); @@ -250,7 +254,7 @@ namespace Flow.Launcher.Test.Plugins $"Actual check result is {result} {Environment.NewLine}"); } - + [TestCase(@"C:\SomeFolder\SomeApp", true, @"C:\SomeFolder\")] [TestCase(@"C:\SomeFolder\SomeApp\SomeFile", true, @"C:\SomeFolder\SomeApp\")] [TestCase(@"C:\NonExistentFolder\SomeApp", false, "")] @@ -294,7 +298,7 @@ namespace Flow.Launcher.Test.Plugins [TestCase("c:\\SomeFolder\\>SomeName", "(System.FileName LIKE 'SomeName%' " + "OR CONTAINS(System.FileName,'\"SomeName*\"',1033)) AND " + "scope='file:c:\\SomeFolder'")] - public void GivenWindowsIndexSearch_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString(string path, string expectedString) + public void GivenWindowsIndexSearch_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString(string path, string expectedString) { // Given var queryConstructor = new QueryConstructor(new Settings()); @@ -308,7 +312,7 @@ namespace Flow.Launcher.Test.Plugins $"Actual string was: {resultString}{Environment.NewLine}"); } - [TestCase("c:\\somefolder\\>somefile","*somefile*")] + [TestCase("c:\\somefolder\\>somefile", "*somefile*")] [TestCase("c:\\somefolder\\somefile", "somefile*")] [TestCase("c:\\somefolder\\", "*")] public void GivenDirectoryInfoSearch_WhenSearchPatternHotKeyIsSearchAll_ThenSearchCriteriaShouldUseCriteriaString(string path, string expectedString) From d369108b0b17d05c300919d7f20e296c5322b731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 3 Jan 2021 20:40:18 +0800 Subject: [PATCH 094/158] Error handling for plugin update --- .../PluginsManager.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 880157a77..4debf5598 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -214,11 +214,19 @@ namespace Flow.Launcher.Plugin.PluginsManager Task.Run(async delegate { + Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), + Context.API.GetTranslation("plugin_pluginsmanager_please_wait")); + await Http.Download(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath).ConfigureAwait(false); + + Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), + Context.API.GetTranslation("plugin_pluginsmanager_download_success")); + Install(x.PluginNewUserPlugin, downloadToFilePath); Context.API.RestartApp(); - }); + }).ContinueWith(t => Log.Exception($"|PluginsManager|Update fail for {x.Name}", t.Exception.InnerException), + TaskContinuationOptions.OnlyOnFaulted); return true; } From 8fa1e2e33f4355766282ca6934e3a279b8a3b8be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 3 Jan 2021 20:49:14 +0800 Subject: [PATCH 095/158] add one more message --- .../Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 4debf5598..9cef704a6 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -225,8 +225,12 @@ namespace Flow.Launcher.Plugin.PluginsManager Install(x.PluginNewUserPlugin, downloadToFilePath); Context.API.RestartApp(); - }).ContinueWith(t => Log.Exception($"|PluginsManager|Update fail for {x.Name}", t.Exception.InnerException), - TaskContinuationOptions.OnlyOnFaulted); + }).ContinueWith(t => + { + Log.Exception($"|PluginsManager|Update fail for {x.Name}", t.Exception.InnerException); + Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"), + string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"), x.Name)); + }, TaskContinuationOptions.OnlyOnFaulted); return true; } From d35a3bb43d05cf19b1fd46ef3577376941b7ed44 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 4 Jan 2021 06:40:45 +1100 Subject: [PATCH 096/158] version bump for PluginsManager --- Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json index f5bbf3f6b..ef2c1255a 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json @@ -6,7 +6,7 @@ "Name": "Plugins Manager", "Description": "Management of installing, uninstalling or updating Flow Launcher plugins", "Author": "Jeremy Wu", - "Version": "1.4.0", + "Version": "1.4.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll", From 6287358275e01457fabf0e2482343836a80bb9dd Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 4 Jan 2021 06:41:05 +1100 Subject: [PATCH 097/158] update error message --- Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 9cef704a6..724ddf20d 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -227,7 +227,7 @@ namespace Flow.Launcher.Plugin.PluginsManager Context.API.RestartApp(); }).ContinueWith(t => { - Log.Exception($"|PluginsManager|Update fail for {x.Name}", t.Exception.InnerException); + Log.Exception("PluginsManager", $"Update failed for {x.Name}", t.Exception.InnerException, "RequestUpdate"); Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"), string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"), x.Name)); }, TaskContinuationOptions.OnlyOnFaulted); From a8dc1599d746d88c8ce28ac551abb0e02bf6a90d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 4 Jan 2021 10:28:33 +0800 Subject: [PATCH 098/158] Add Http Error Handling --- Flow.Launcher.Infrastructure/Http/Http.cs | 44 +++++++++++++++++------ 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 8e2832690..b796d807c 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -91,7 +91,7 @@ namespace Flow.Launcher.Infrastructure.Http /// /// Asynchrously get the result as string from url. - /// When supposing the result is long and large, try using GetStreamAsync to avoid reading as string + /// When supposing the result larger than 83kb, try using GetStreamAsync to avoid reading as string /// /// /// @@ -101,19 +101,33 @@ namespace Flow.Launcher.Infrastructure.Http return GetAsync(new Uri(url.Replace("#", "%23"))); } + /// + /// Asynchrously get the result as string from url. + /// When supposing the result larger than 83kb, try using GetStreamAsync to avoid reading as string + /// + /// + /// public static async Task GetAsync([NotNull] Uri url) { Log.Debug($"|Http.Get|Url <{url}>"); - using var response = await client.GetAsync(url); - var content = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) + try { - return content; + using var response = await client.GetAsync(url); + var content = await response.Content.ReadAsStringAsync(); + if (response.StatusCode == HttpStatusCode.OK) + { + return content; + } + else + { + throw new HttpRequestException( + $"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>"); + } } - else + catch (HttpRequestException e) { - throw new HttpRequestException( - $"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>"); + Log.Exception("Infrastructure.Http", "Http Request Error", e, "GetAsync"); + throw; } } @@ -124,9 +138,17 @@ namespace Flow.Launcher.Infrastructure.Http /// public static async Task GetStreamAsync([NotNull] string url) { - Log.Debug($"|Http.Get|Url <{url}>"); - var response = await client.GetAsync(url); - return await response.Content.ReadAsStreamAsync(); + try + { + Log.Debug($"|Http.Get|Url <{url}>"); + var response = await client.GetAsync(url); + return await response.Content.ReadAsStreamAsync(); + } + catch (HttpRequestException e) + { + Log.Exception("Infrastructure.Http", "Http Request Error", e, "GetStreamAsync"); + throw; + } } } } From bc63b9ebfa100426176603ba6c7009b72dd3502e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 4 Jan 2021 10:31:05 +0800 Subject: [PATCH 099/158] Change Download to DownloadAsync Add error handling for download as well --- Flow.Launcher.Infrastructure/Http/Http.cs | 22 +++++++++++++------ .../PluginsManager.cs | 4 ++-- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index b796d807c..78fa099c9 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -75,17 +75,25 @@ namespace Flow.Launcher.Infrastructure.Http }; } - public static async Task Download([NotNull] string url, [NotNull] string filePath) + public static async Task DownloadAsync([NotNull] string url, [NotNull] string filePath) { - using var response = await client.GetAsync(url); - if (response.StatusCode == HttpStatusCode.OK) + try { - await using var fileStream = new FileStream(filePath, FileMode.CreateNew); - await response.Content.CopyToAsync(fileStream); + using var response = await client.GetAsync(url); + if (response.StatusCode == HttpStatusCode.OK) + { + await using var fileStream = new FileStream(filePath, FileMode.CreateNew); + await response.Content.CopyToAsync(fileStream); + } + else + { + throw new HttpRequestException($"Error code <{response.StatusCode}> returned from <{url}>"); + } } - else + catch (HttpRequestException e) { - throw new HttpRequestException($"Error code <{response.StatusCode}> returned from <{url}>"); + Log.Exception("Infrastructure.Http", "Http Request Error", e, "DownloadAsync"); + throw; } } diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 724ddf20d..68df5bc1f 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -127,7 +127,7 @@ namespace Flow.Launcher.Plugin.PluginsManager Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), Context.API.GetTranslation("plugin_pluginsmanager_please_wait")); - await Http.Download(plugin.UrlDownload, filePath).ConfigureAwait(false); + await Http.DownloadAsync(plugin.UrlDownload, filePath).ConfigureAwait(false); Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), Context.API.GetTranslation("plugin_pluginsmanager_download_success")); @@ -217,7 +217,7 @@ namespace Flow.Launcher.Plugin.PluginsManager Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), Context.API.GetTranslation("plugin_pluginsmanager_please_wait")); - await Http.Download(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath).ConfigureAwait(false); + await Http.DownloadAsync(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath).ConfigureAwait(false); Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), Context.API.GetTranslation("plugin_pluginsmanager_download_success")); From fb640b68c8e5a49e105e0666ce77e057c1a43735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 4 Jan 2021 11:05:59 +0800 Subject: [PATCH 100/158] Use ValueTask instead of Task --- Flow.Launcher/ViewModel/ResultViewModel.cs | 25 ++++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/Flow.Launcher/ViewModel/ResultViewModel.cs b/Flow.Launcher/ViewModel/ResultViewModel.cs index 00a0e1ae5..20d32890b 100644 --- a/Flow.Launcher/ViewModel/ResultViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultViewModel.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Media; @@ -12,7 +13,7 @@ namespace Flow.Launcher.ViewModel { public class ResultViewModel : BaseModel { - public class LazyAsync : Lazy> + public class LazyAsync : Lazy> { private T defaultValue; @@ -23,21 +24,27 @@ namespace Flow.Launcher.ViewModel { if (!IsValueCreated) { - base.Value.ContinueWith(_ => - { - _updateCallback(); - }); + _ = Exercute(); return defaultValue; } - + if (!base.Value.IsCompleted || base.Value.IsFaulted) return defaultValue; return base.Value.Result; + + // If none of the variables captured by the local function are captured by other lambdas + // , the compiler can avoid heap allocations. + async ValueTask Exercute() + { + await base.Value.ConfigureAwait(false); + _updateCallback(); + } + } } - public LazyAsync(Func> factory, T defaultValue, Action updateCallback) : base(factory) + public LazyAsync(Func> factory, T defaultValue, Action updateCallback) : base(factory) { if (defaultValue != null) { @@ -55,7 +62,7 @@ namespace Flow.Launcher.ViewModel Result = result; Image = new LazyAsync( - SetImage, + SetImage, ImageLoader.DefaultImage, () => { @@ -82,7 +89,7 @@ namespace Flow.Launcher.ViewModel public LazyAsync Image { get; set; } - private async Task SetImage() + private async ValueTask SetImage() { var imagePath = Result.IcoPath; if (string.IsNullOrEmpty(imagePath) && Result.Icon != null) From 7967844cf381563c362f2d91bcac73342b3f5237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 4 Jan 2021 11:10:02 +0800 Subject: [PATCH 101/158] make defaultValue readonly and edit comment --- Flow.Launcher/ViewModel/ResultViewModel.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher/ViewModel/ResultViewModel.cs b/Flow.Launcher/ViewModel/ResultViewModel.cs index 20d32890b..d1b03f911 100644 --- a/Flow.Launcher/ViewModel/ResultViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultViewModel.cs @@ -15,7 +15,7 @@ namespace Flow.Launcher.ViewModel { public class LazyAsync : Lazy> { - private T defaultValue; + private readonly T defaultValue; private readonly Action _updateCallback; public new T Value @@ -24,7 +24,7 @@ namespace Flow.Launcher.ViewModel { if (!IsValueCreated) { - _ = Exercute(); + _ = Exercute(); // manually use callback strategy return defaultValue; } @@ -34,8 +34,8 @@ namespace Flow.Launcher.ViewModel return base.Value.Result; - // If none of the variables captured by the local function are captured by other lambdas - // , the compiler can avoid heap allocations. + // If none of the variables captured by the local function are captured by other lambdas, + // the compiler can avoid heap allocations. async ValueTask Exercute() { await base.Value.ConfigureAwait(false); From fc015245b3392664f4b630a6a10a68b85d05bc2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 4 Jan 2021 16:22:48 +0800 Subject: [PATCH 102/158] Change the way checking successfuly --- Flow.Launcher/ViewModel/ResultViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/ResultViewModel.cs b/Flow.Launcher/ViewModel/ResultViewModel.cs index d1b03f911..9eece1a97 100644 --- a/Flow.Launcher/ViewModel/ResultViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultViewModel.cs @@ -29,7 +29,7 @@ namespace Flow.Launcher.ViewModel return defaultValue; } - if (!base.Value.IsCompleted || base.Value.IsFaulted) + if (!base.Value.IsCompletedSuccessfully) return defaultValue; return base.Value.Result; From 6703e0d137b3fcd03c8fdb16644df7fa5645b24b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 4 Jan 2021 16:31:48 +0800 Subject: [PATCH 103/158] Return default image when loading UWP icon fail --- Flow.Launcher/ViewModel/ResultViewModel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/ViewModel/ResultViewModel.cs b/Flow.Launcher/ViewModel/ResultViewModel.cs index 9eece1a97..4c65f2b9f 100644 --- a/Flow.Launcher/ViewModel/ResultViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultViewModel.cs @@ -68,7 +68,7 @@ namespace Flow.Launcher.ViewModel { OnPropertyChanged(nameof(Image)); }); - } + } Settings = settings; } @@ -101,7 +101,7 @@ namespace Flow.Launcher.ViewModel catch (Exception e) { Log.Exception($"|ResultViewModel.Image|IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", e); - imagePath = Constant.MissingImgIcon; + return ImageLoader.DefaultImage; } } From e790e9474e8fab545325eaa685314ea99821d470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 5 Jan 2021 16:11:38 +0800 Subject: [PATCH 104/158] Add Plugin Priority Settings --- .../UserSettings/PluginSettings.cs | 5 +- Flow.Launcher.Plugin/PluginMetadata.cs | 5 +- Flow.Launcher/PriorityChangeWindow.xaml | 43 ++++++++++++ Flow.Launcher/PriorityChangeWindow.xaml.cs | 69 +++++++++++++++++++ Flow.Launcher/SettingWindow.xaml | 8 ++- Flow.Launcher/SettingWindow.xaml.cs | 12 +++- Flow.Launcher/ViewModel/MainViewModel.cs | 6 +- Flow.Launcher/ViewModel/PluginViewModel.cs | 7 ++ .../ViewModel/SettingWindowViewModel.cs | 1 + .../Flow.Launcher.Plugin.Calculator/Main.cs | 2 +- 10 files changed, 149 insertions(+), 9 deletions(-) create mode 100644 Flow.Launcher/PriorityChangeWindow.xaml create mode 100644 Flow.Launcher/PriorityChangeWindow.xaml.cs diff --git a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs index ccd9beb86..29bc11480 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs @@ -31,6 +31,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings metadata.ActionKeyword = settings.ActionKeywords[0]; } metadata.Disabled = settings.Disabled; + metadata.Priority = settings.Priority; } else { @@ -40,7 +41,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings Name = metadata.Name, Version = metadata.Version, ActionKeywords = metadata.ActionKeywords, - Disabled = metadata.Disabled + Disabled = metadata.Disabled, + Priority = metadata.Priority }; } } @@ -52,6 +54,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings public string Name { get; set; } public string Version { get; set; } public List ActionKeywords { get; set; } // a reference of the action keywords from plugin manager + public int Priority { get; set; } /// /// Used only to save the state of the plugin in settings diff --git a/Flow.Launcher.Plugin/PluginMetadata.cs b/Flow.Launcher.Plugin/PluginMetadata.cs index d81b442e2..8bcbcd231 100644 --- a/Flow.Launcher.Plugin/PluginMetadata.cs +++ b/Flow.Launcher.Plugin/PluginMetadata.cs @@ -37,11 +37,14 @@ namespace Flow.Launcher.Plugin public List ActionKeywords { get; set; } public string IcoPath { get; set;} - + public override string ToString() { return Name; } + [JsonIgnore] + public int Priority { get; set; } + /// /// Init time include both plugin load time and init time diff --git a/Flow.Launcher/PriorityChangeWindow.xaml b/Flow.Launcher/PriorityChangeWindow.xaml new file mode 100644 index 000000000..51509f1ab --- /dev/null +++ b/Flow.Launcher/PriorityChangeWindow.xaml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Flow.Launcher/PriorityChangeWindow.xaml.cs b/Flow.Launcher/PriorityChangeWindow.xaml.cs new file mode 100644 index 000000000..01c6454c9 --- /dev/null +++ b/Flow.Launcher/PriorityChangeWindow.xaml.cs @@ -0,0 +1,69 @@ +using Flow.Launcher.Core.Plugin; +using Flow.Launcher.Core.Resource; +using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Plugin; +using Flow.Launcher.ViewModel; +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace Flow.Launcher +{ + /// + /// PriorityChangeWindow.xaml 的交互逻辑 + /// + public partial class PriorityChangeWindow : Window + { + private readonly PluginPair plugin; + private Settings settings; + private readonly Internationalization translater = InternationalizationManager.Instance; + private readonly PluginViewModel pluginViewModel; + + public PriorityChangeWindow(string pluginId, Settings settings, PluginViewModel pluginViewModel) + { + InitializeComponent(); + plugin = PluginManager.GetPluginForId(pluginId); + this.settings = settings; + this.pluginViewModel = pluginViewModel; + if (plugin == null) + { + MessageBox.Show(translater.GetTranslation("cannotFindSpecifiedPlugin")); + Close(); + } + } + + private void BtnCancel_OnClick(object sender, RoutedEventArgs e) + { + Close(); + } + + private void btnDone_OnClick(object sender, RoutedEventArgs e) + { + if (int.TryParse(tbAction.Text.Trim(), out var newPriority)) + { + pluginViewModel.ChangePriority(newPriority); + Close(); + } + else + { + string msg = "Please provide an valid integer";// translater.GetTranslation("newActionKeywordsHasBeenAssigned"); + MessageBox.Show(msg); + } + + } + + private void PriorityChangeWindow_Loaded(object sender, RoutedEventArgs e) + { + OldPriority.Text = pluginViewModel.Priority.ToString(); + tbAction.Focus(); + } + } +} diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index e47f0e779..b000db20e 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -172,10 +172,14 @@ - + + + Margin="5 0 0 0"/> PluginPair.Metadata.InitTime.ToString() + "ms"; public string QueryTime => PluginPair.Metadata.AvgQueryTime + "ms"; public string ActionKeywordsText => string.Join(Query.ActionKeywordSeperater, PluginPair.Metadata.ActionKeywords); + public int Priority => PluginPair.Metadata.Priority; public void ChangeActionKeyword(string newActionKeyword, string oldActionKeyword) { @@ -34,6 +35,12 @@ namespace Flow.Launcher.ViewModel OnPropertyChanged(nameof(ActionKeywordsText)); } + public void ChangePriority(int newPriority) + { + PluginPair.Metadata.Priority = newPriority; + OnPropertyChanged(nameof(Priority)); + } + public bool IsActionKeywordRegistered(string newActionKeyword) => PluginManager.ActionKeywordRegistered(newActionKeyword); } } diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index c122f8037..4ebf898ea 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -88,6 +88,7 @@ namespace Flow.Launcher.ViewModel var id = vm.PluginPair.Metadata.ID; Settings.PluginSettings.Plugins[id].Disabled = vm.PluginPair.Metadata.Disabled; + Settings.PluginSettings.Plugins[id].Priority = vm.Priority; } PluginManager.Save(); diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs index 949911229..5b23ceacc 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs @@ -91,7 +91,7 @@ namespace Flow.Launcher.Plugin.Caculator }; } } - catch + catch (Exception) { // ignored } From a2f741b376d5c00da6ad51e0d800e5f3ccf24d5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 6 Jan 2021 09:59:20 +0800 Subject: [PATCH 105/158] change bitmap scaling mode to Fant (High Quality) to make the search icon looks better. --- Flow.Launcher/MainWindow.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 07bb96339..b6782cb60 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -92,7 +92,7 @@ - + Date: Wed, 6 Jan 2021 10:34:08 +0800 Subject: [PATCH 106/158] Use SVG for search icon --- Flow.Launcher.Infrastructure/Constant.cs | 2 +- Flow.Launcher/Flow.Launcher.csproj | 1 + Flow.Launcher/Images/mainsearch.svg | 10 ++++++++++ Flow.Launcher/MainWindow.xaml | 9 +++++---- Flow.Launcher/ViewModel/MainViewModel.cs | 2 +- 5 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 Flow.Launcher/Images/mainsearch.svg diff --git a/Flow.Launcher.Infrastructure/Constant.cs b/Flow.Launcher.Infrastructure/Constant.cs index 3dba35f8d..de6ed1b29 100644 --- a/Flow.Launcher.Infrastructure/Constant.cs +++ b/Flow.Launcher.Infrastructure/Constant.cs @@ -30,7 +30,7 @@ namespace Flow.Launcher.Infrastructure public static string PythonPath; - public static readonly string QueryTextBoxIconImagePath = $"{ProgramDirectory}\\Images\\mainsearch.png"; + public static readonly string QueryTextBoxIconImagePath = $"{ProgramDirectory}\\Images\\mainsearch.svg"; public const string DefaultTheme = "Darker"; diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 39b2087aa..0f8e6a767 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -82,6 +82,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Flow.Launcher/Images/mainsearch.svg b/Flow.Launcher/Images/mainsearch.svg new file mode 100644 index 000000000..5d28abdb3 --- /dev/null +++ b/Flow.Launcher/Images/mainsearch.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index b6782cb60..c9db58385 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -6,6 +6,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:converters="clr-namespace:Flow.Launcher.Converters" + xmlns:svgc="http://sharpvectors.codeplex.com/svgc/" mc:Ignorable="d" Title="Flow Launcher" Topmost="True" @@ -68,8 +69,8 @@ Margin="18,0,56,0"> - - + + @@ -81,7 +82,7 @@ AllowDrop="True" Visibility="Visible" Background="Transparent" - Margin="18,0,56,0"> + Margin="18,1,0,1" HorizontalAlignment="Left" Width="660"> @@ -92,7 +93,7 @@ - + ImageLoader.Load(Constant.QueryTextBoxIconImagePath); + public string Image => Constant.QueryTextBoxIconImagePath; #endregion From 0dd6a982ad93affc9cc2d3aae8dd7a4ba31bc176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 6 Jan 2021 15:51:52 +0800 Subject: [PATCH 107/158] change setting window to svg as well --- Flow.Launcher/SettingWindow.xaml | 3 ++- Flow.Launcher/ViewModel/SettingWindowViewModel.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index e47f0e779..121b062ee 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -8,6 +8,7 @@ xmlns:userSettings="clr-namespace:Flow.Launcher.Infrastructure.UserSettings;assembly=Flow.Launcher.Infrastructure" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:ui="http://schemas.modernwpf.com/2019" + xmlns:svgc="http://sharpvectors.codeplex.com/svgc/" x:Class="Flow.Launcher.SettingWindow" mc:Ignorable="d" Icon="Images\app.png" @@ -252,7 +253,7 @@ Text="{DynamicResource hiThere}" IsReadOnly="True" Style="{DynamicResource QueryBoxStyle}" Margin="18 0 56 0" /> - + diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index c122f8037..c43167e09 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -438,7 +438,7 @@ namespace Flow.Launcher.ViewModel } } - public ImageSource ThemeImage => ImageLoader.Load(Constant.QueryTextBoxIconImagePath); + public string ThemeImage => Constant.QueryTextBoxIconImagePath; #endregion From 3bfd900f054687dee90a9c9670ff55e096000116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 6 Jan 2021 16:10:52 +0800 Subject: [PATCH 108/158] revert change for oneway for mutli binding --- Flow.Launcher/MainWindow.xaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index c9db58385..4373c0f0c 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -69,8 +69,8 @@ Margin="18,0,56,0"> - - + + @@ -93,7 +93,8 @@ - + Date: Wed, 6 Jan 2021 16:46:38 +0800 Subject: [PATCH 109/158] remove unintended change and change the binding mode to default --- Flow.Launcher/MainWindow.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 4373c0f0c..998bc3e9a 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -82,7 +82,7 @@ AllowDrop="True" Visibility="Visible" Background="Transparent" - Margin="18,1,0,1" HorizontalAlignment="Left" Width="660"> + Margin="18,0,56,0" HorizontalAlignment="Left" Width="660"> @@ -93,7 +93,7 @@ - Date: Wed, 6 Jan 2021 17:00:16 +0800 Subject: [PATCH 110/158] change autogenerated summary to english --- Flow.Launcher/PriorityChangeWindow.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/PriorityChangeWindow.xaml.cs b/Flow.Launcher/PriorityChangeWindow.xaml.cs index 01c6454c9..f04ad110c 100644 --- a/Flow.Launcher/PriorityChangeWindow.xaml.cs +++ b/Flow.Launcher/PriorityChangeWindow.xaml.cs @@ -18,7 +18,7 @@ using System.Windows.Shapes; namespace Flow.Launcher { /// - /// PriorityChangeWindow.xaml 的交互逻辑 + /// Interaction Logic of PriorityChangeWindow.xaml /// public partial class PriorityChangeWindow : Window { From 4973f2d979b2da3e6a66245b828105e6154676c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 6 Jan 2021 17:05:10 +0800 Subject: [PATCH 111/158] remove png file --- Flow.Launcher/Images/mainsearch.png | Bin 63073 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Flow.Launcher/Images/mainsearch.png diff --git a/Flow.Launcher/Images/mainsearch.png b/Flow.Launcher/Images/mainsearch.png deleted file mode 100644 index ac5492fa62dfa1d7bf207c60823b2f9fa6f4b622..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63073 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 From 833eccc25a9ff09f7ec4bbdbecd26904bbb7ee9f Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 6 Jan 2021 20:09:41 +1100 Subject: [PATCH 112/158] archive mainsearch.png --- Doc/mainsearch.png | Bin 0 -> 63073 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Doc/mainsearch.png diff --git a/Doc/mainsearch.png b/Doc/mainsearch.png new file mode 100644 index 0000000000000000000000000000000000000000..ac5492fa62dfa1d7bf207c60823b2f9fa6f4b622 GIT binary patch literal 63073 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 literal 0 HcmV?d00001 From e85b766f5781560facc2f3ccb89266f7bab70730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 6 Jan 2021 17:17:24 +0800 Subject: [PATCH 113/158] remove extra width --- Flow.Launcher/MainWindow.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 998bc3e9a..a196f369f 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -82,7 +82,7 @@ AllowDrop="True" Visibility="Visible" Background="Transparent" - Margin="18,0,56,0" HorizontalAlignment="Left" Width="660"> + Margin="18,0,56,0" HorizontalAlignment="Left"> From b2fcd98f2c4fa66ef211522a3a5f0f525d8dd4af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 6 Jan 2021 17:18:21 +0800 Subject: [PATCH 114/158] remove extra horizontalalignment --- Flow.Launcher/MainWindow.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index a196f369f..a2cfe569d 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -82,7 +82,7 @@ AllowDrop="True" Visibility="Visible" Background="Transparent" - Margin="18,0,56,0" HorizontalAlignment="Left"> + Margin="18,0,56,0"> From 8569b44247cbc066b2916c3ea1371a5a44751746 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 6 Jan 2021 20:23:38 +1100 Subject: [PATCH 115/158] search preview consistent --- Flow.Launcher/SettingWindow.xaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 121b062ee..e467d9a64 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -253,7 +253,8 @@ Text="{DynamicResource hiThere}" IsReadOnly="True" Style="{DynamicResource QueryBoxStyle}" Margin="18 0 56 0" /> - + From 182cdd199a564349df5ed146200514c8b2a80631 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 6 Jan 2021 20:24:36 +1100 Subject: [PATCH 116/158] fix typo --- Flow.Launcher/Languages/en.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index adb49b65d..6ee28e3ba 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -72,7 +72,7 @@ Are you sure you want to delete {0} plugin hotkey? Query window shadow effect Shadow effect has a substantial usage of GPU. - Not recommended if you computer performance is limited. + Not recommended if your computer performance is limited. HTTP Proxy From 6f43d4fdf85d0426fba0a89caf7167ac853f3d82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 6 Jan 2021 17:51:55 +0800 Subject: [PATCH 117/158] remove extra space --- Flow.Launcher.Plugin/PluginMetadata.cs | 1 + Flow.Launcher/PriorityChangeWindow.xaml.cs | 2 +- Flow.Launcher/SettingWindow.xaml.cs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Plugin/PluginMetadata.cs b/Flow.Launcher.Plugin/PluginMetadata.cs index 8bcbcd231..9eed86025 100644 --- a/Flow.Launcher.Plugin/PluginMetadata.cs +++ b/Flow.Launcher.Plugin/PluginMetadata.cs @@ -42,6 +42,7 @@ namespace Flow.Launcher.Plugin { return Name; } + [JsonIgnore] public int Priority { get; set; } diff --git a/Flow.Launcher/PriorityChangeWindow.xaml.cs b/Flow.Launcher/PriorityChangeWindow.xaml.cs index f04ad110c..0c1dad1ed 100644 --- a/Flow.Launcher/PriorityChangeWindow.xaml.cs +++ b/Flow.Launcher/PriorityChangeWindow.xaml.cs @@ -66,4 +66,4 @@ namespace Flow.Launcher tbAction.Focus(); } } -} +} \ No newline at end of file diff --git a/Flow.Launcher/SettingWindow.xaml.cs b/Flow.Launcher/SettingWindow.xaml.cs index 2402ccd24..a922b4d67 100644 --- a/Flow.Launcher/SettingWindow.xaml.cs +++ b/Flow.Launcher/SettingWindow.xaml.cs @@ -292,4 +292,4 @@ namespace Flow.Launcher } } -} +} \ No newline at end of file From 3ba1bf6ab0157ac48d13f62e2c85a74a4195b750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 6 Jan 2021 18:00:45 +0800 Subject: [PATCH 118/158] Use l18n instead of static string --- Flow.Launcher/Languages/en.xaml | 6 ++++++ Flow.Launcher/PriorityChangeWindow.xaml | 7 ++++--- Flow.Launcher/PriorityChangeWindow.xaml.cs | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index adb49b65d..a275a2c16 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -43,6 +43,8 @@ Action keyword: Current action keyword: New action keyword: + Current Priority: + New Priority: Plugin Directory Author Init time: @@ -104,6 +106,10 @@ Release Notes + + Higher the number, the closer the result will be to the top. If you want the results to be lower than any other plugin's, provide a negative number + Please provide an valid integer for Priority! + Old Action Keyword New Action Keyword diff --git a/Flow.Launcher/PriorityChangeWindow.xaml b/Flow.Launcher/PriorityChangeWindow.xaml index 51509f1ab..54e5c02cc 100644 --- a/Flow.Launcher/PriorityChangeWindow.xaml +++ b/Flow.Launcher/PriorityChangeWindow.xaml @@ -19,18 +19,19 @@ + HorizontalAlignment="Left" Text="{DynamicResource currentPriority}" /> + HorizontalAlignment="Left" Text="{DynamicResource newPriority}" /> + Text="{DynamicResource priority_tips}" TextWrapping="Wrap" + Margin="0,0,20,0"/> /// /// - public static async Task GetStreamAsync([NotNull] string url) + public static async Task GetStreamAsync([NotNull] string url, CancellationToken token = default) { Log.Debug($"|Http.Get|Url <{url}>"); - var response = await client.GetAsync(url); + var response = await client.GetAsync(url, token); + if (token.IsCancellationRequested) + return Stream.Null; return await response.Content.ReadAsStreamAsync(); } } From a4edbc2cb9a97f7a82b51fca4f0b3fe9c5faf642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 7 Jan 2021 11:09:08 +0800 Subject: [PATCH 131/158] Move WebSearch to Async model --- .../Flow.Launcher.Plugin.WebSearch/Main.cs | 84 +++++++++---------- .../SuggestionSources/Baidu.cs | 5 +- .../SuggestionSources/Bing.cs | 7 +- .../SuggestionSources/Google.cs | 5 +- .../SuggestionSources/SuggestionSource.cs | 3 +- 5 files changed, 52 insertions(+), 52 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs index 3c4d4c67d..a573674ec 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using System.Windows.Controls; @@ -13,14 +14,12 @@ using Flow.Launcher.Plugin.SharedCommands; namespace Flow.Launcher.Plugin.WebSearch { - public class Main : IPlugin, ISettingProvider, IPluginI18n, ISavable, IResultUpdated + public class Main : IAsyncPlugin, ISettingProvider, IPluginI18n, ISavable, IResultUpdated { private PluginInitContext _context; private readonly Settings _settings; private readonly SettingsViewModel _viewModel; - private CancellationTokenSource _updateSource; - private CancellationToken _updateToken; internal const string Images = "Images"; internal static string DefaultImagesDirectory; @@ -33,7 +32,7 @@ namespace Flow.Launcher.Plugin.WebSearch _viewModel.Save(); } - public List Query(Query query) + public async Task> QueryAsync(Query query, CancellationToken token) { if (FilesFolders.IsLocationPathString(query.Search)) return new List(); @@ -41,11 +40,7 @@ namespace Flow.Launcher.Plugin.WebSearch var searchSourceList = new List(); var results = new List(); - _updateSource?.Cancel(); - _updateSource = new CancellationTokenSource(); - _updateToken = _updateSource.Token; - - _settings.SearchSources.Where(o => (o.ActionKeyword == query.ActionKeyword || o.ActionKeyword == SearchSourceGlobalPluginWildCardSign) + _settings.SearchSources.Where(o => (o.ActionKeyword == query.ActionKeyword || o.ActionKeyword == SearchSourceGlobalPluginWildCardSign) && o.Enabled) .ToList() .ForEach(x => searchSourceList.Add(x)); @@ -94,49 +89,45 @@ namespace Flow.Launcher.Plugin.WebSearch }; results.Add(result); - ResultsUpdated?.Invoke(this, new ResultUpdatedEventArgs - { - Results = results, - Query = query - }); - - UpdateResultsFromSuggestion(results, keyword, subtitle, searchSource, query); } + + ResultsUpdated?.Invoke(this, new ResultUpdatedEventArgs + { + Results = results, + Query = query + }); + + await UpdateResultsFromSuggestionAsync(results, keyword, subtitle, searchSource, query, token).ConfigureAwait(false); } } return results; } - private void UpdateResultsFromSuggestion(List results, string keyword, string subtitle, - SearchSource searchSource, Query query) + private async Task UpdateResultsFromSuggestionAsync(List results, string keyword, string subtitle, + SearchSource searchSource, Query query, CancellationToken token) { if (_settings.EnableSuggestion) { - const int waittime = 300; - var task = Task.Run(async () => - { - var suggestions = await Suggestions(keyword, subtitle, searchSource); - results.AddRange(suggestions); - }, _updateToken); + var suggestions = await SuggestionsAsync(keyword, subtitle, searchSource, token).ConfigureAwait(false); + results.AddRange(suggestions); - if (!task.Wait(waittime)) + token.ThrowIfCancellationRequested(); + + ResultsUpdated?.Invoke(this, new ResultUpdatedEventArgs { - task.ContinueWith(_ => ResultsUpdated?.Invoke(this, new ResultUpdatedEventArgs - { - Results = results, - Query = query - }), _updateToken); - } + Results = results, + Query = query + }); } } - private async Task> Suggestions(string keyword, string subtitle, SearchSource searchSource) + private async Task> SuggestionsAsync(string keyword, string subtitle, SearchSource searchSource, CancellationToken token) { var source = _settings.SelectedSuggestion; if (source != null) { - var suggestions = await source.Suggestions(keyword); + var suggestions = await source.Suggestions(keyword, token); var resultsFromSuggestion = suggestions.Select(o => new Result { Title = o, @@ -169,19 +160,24 @@ namespace Flow.Launcher.Plugin.WebSearch _settings = _viewModel.Settings; } - public void Init(PluginInitContext context) + public Task InitAsync(PluginInitContext context) { - _context = context; - var pluginDirectory = _context.CurrentPluginMetadata.PluginDirectory; - var bundledImagesDirectory = Path.Combine(pluginDirectory, Images); - - // Default images directory is in the WebSearch's application folder - DefaultImagesDirectory = Path.Combine(pluginDirectory, Images); - Helper.ValidateDataDirectory(bundledImagesDirectory, DefaultImagesDirectory); + return Task.Run(Init); - // Custom images directory is in the WebSearch's data location folder - var name = Path.GetFileNameWithoutExtension(_context.CurrentPluginMetadata.ExecuteFileName); - CustomImagesDirectory = Path.Combine(DataLocation.PluginSettingsDirectory, name, "CustomIcons"); + void Init() + { + _context = context; + var pluginDirectory = _context.CurrentPluginMetadata.PluginDirectory; + var bundledImagesDirectory = Path.Combine(pluginDirectory, Images); + + // Default images directory is in the WebSearch's application folder + DefaultImagesDirectory = Path.Combine(pluginDirectory, Images); + Helper.ValidateDataDirectory(bundledImagesDirectory, DefaultImagesDirectory); + + // Custom images directory is in the WebSearch's data location folder + var name = Path.GetFileNameWithoutExtension(_context.CurrentPluginMetadata.ExecuteFileName); + CustomImagesDirectory = Path.Combine(DataLocation.PluginSettingsDirectory, name, "CustomIcons"); + }; } #region ISettingProvider Members diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs index 6772acf82..0c6d95b4f 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs @@ -9,6 +9,7 @@ using Newtonsoft.Json.Linq; using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Infrastructure.Logger; using System.Net.Http; +using System.Threading; namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources { @@ -16,14 +17,14 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources { private readonly Regex _reg = new Regex("window.baidu.sug\\((.*)\\)"); - public override async Task> Suggestions(string query) + public override async Task> Suggestions(string query, CancellationToken token) { string result; try { const string api = "http://suggestion.baidu.com/su?json=1&wd="; - result = await Http.GetAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false); + result = await Http.GetAsync(api + Uri.EscapeUriString(query), token).ConfigureAwait(false); } catch (HttpRequestException e) { diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs index 9c4746711..991e45267 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs @@ -9,19 +9,20 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Text.Json; using System.Linq; +using System.Threading; namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources { class Bing : SuggestionSource { - public override async Task> Suggestions(string query) + public override async Task> Suggestions(string query, CancellationToken token) { Stream resultStream; try { const string api = "https://api.bing.com/qsonhs.aspx?q="; - resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false); + resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query), token).ConfigureAwait(false); } catch (HttpRequestException e) { @@ -29,7 +30,7 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources return new List(); } - if (resultStream.Length == 0) return new List(); + if (resultStream.Length == 0) return new List(); // this handles the cancellation JsonElement json; try diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs index 5b9538091..d034679af 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs @@ -8,18 +8,19 @@ using Newtonsoft.Json.Linq; using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Infrastructure.Logger; using System.Net.Http; +using System.Threading; namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources { public class Google : SuggestionSource { - public override async Task> Suggestions(string query) + public override async Task> Suggestions(string query, CancellationToken token) { string result; try { const string api = "https://www.google.com/complete/search?output=chrome&q="; - result = await Http.GetAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false); + result = await Http.GetAsync(api + Uri.EscapeUriString(query), token).ConfigureAwait(false); } catch (HttpRequestException e) { diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/SuggestionSource.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/SuggestionSource.cs index d6d89415f..bf444a2f7 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/SuggestionSource.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/SuggestionSource.cs @@ -1,10 +1,11 @@ using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources { public abstract class SuggestionSource { - public abstract Task> Suggestions(string query); + public abstract Task> Suggestions(string query, CancellationToken token); } } \ No newline at end of file From f72b716fb4a3b9de8dab6a336825b38bdc5ae338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 7 Jan 2021 11:20:14 +0800 Subject: [PATCH 132/158] use string.empty instead of null --- Flow.Launcher.Infrastructure/Http/Http.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 173be8422..4abf0e832 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -113,7 +113,7 @@ namespace Flow.Launcher.Infrastructure.Http Log.Debug($"|Http.Get|Url <{url}>"); using var response = await client.GetAsync(url, token); if (token.IsCancellationRequested) - return null; + return string.Empty; var content = await response.Content.ReadAsStringAsync(); if (response.StatusCode == HttpStatusCode.OK) { From e1f715e6d607455bfbdd78f0e626ae1a5089f33f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 7 Jan 2021 16:52:05 +0800 Subject: [PATCH 133/158] change commnet --- Flow.Launcher.Infrastructure/UserSettings/Settings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index bcfe298a9..769237bcb 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -102,7 +102,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings public LastQueryMode LastQueryMode { get; set; } = LastQueryMode.Selected; - // Order defaults to 0 or -1, so 1 will let this property appear last + // This needs to be loaded last by staying at the bottom public PluginsSettings PluginSettings { get; set; } = new PluginsSettings(); } From 7501a7e7e7ee597a39838ffd57ccc7f959083b62 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 7 Jan 2021 20:40:22 +1100 Subject: [PATCH 134/158] version bump --- SolutionAssemblyInfo.cs | 6 +++--- appveyor.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/SolutionAssemblyInfo.cs b/SolutionAssemblyInfo.cs index ccbfef5d0..afd76b5d7 100644 --- a/SolutionAssemblyInfo.cs +++ b/SolutionAssemblyInfo.cs @@ -16,6 +16,6 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("1.6.0")] -[assembly: AssemblyFileVersion("1.6.0")] -[assembly: AssemblyInformationalVersion("1.6.0")] \ No newline at end of file +[assembly: AssemblyVersion("1.7.0")] +[assembly: AssemblyFileVersion("1.7.0")] +[assembly: AssemblyInformationalVersion("1.7.0")] \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index 7d1da7f3f..2c2f43b66 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: '1.6.0.{build}' +version: '1.7.0.{build}' init: - ps: | From a6609d6b2c86cc167ae15501494434294cb64dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 7 Jan 2021 21:37:45 +0800 Subject: [PATCH 135/158] Add Cancellationtoken for downloadasync Remove extra action in http for cancellation due to it will throw TaskCancelledExcpetion internally --- Flow.Launcher.Infrastructure/Http/Http.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 4212a1ab2..5af50bc6d 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -76,11 +76,11 @@ namespace Flow.Launcher.Infrastructure.Http }; } - public static async Task DownloadAsync([NotNull] string url, [NotNull] string filePath) + public static async Task DownloadAsync([NotNull] string url, [NotNull] string filePath, CancellationToken token = default) { try { - using var response = await client.GetAsync(url); + using var response = await client.GetAsync(url, token); if (response.StatusCode == HttpStatusCode.OK) { await using var fileStream = new FileStream(filePath, FileMode.CreateNew); @@ -120,8 +120,6 @@ namespace Flow.Launcher.Infrastructure.Http { Log.Debug($"|Http.Get|Url <{url}>"); using var response = await client.GetAsync(url, token); - if (token.IsCancellationRequested) - return string.Empty; var content = await response.Content.ReadAsStringAsync(); if (response.StatusCode == HttpStatusCode.OK) { @@ -143,8 +141,6 @@ namespace Flow.Launcher.Infrastructure.Http { Log.Debug($"|Http.Get|Url <{url}>"); var response = await client.GetAsync(url, token); - if (token.IsCancellationRequested) - return Stream.Null; return await response.Content.ReadAsStreamAsync(); } } From 8a5f98a6a3b4b73e302c39b2c77534fa32174151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 7 Jan 2021 21:38:21 +0800 Subject: [PATCH 136/158] Manually handling TaskCancelledException in search suggestion to aviod stunt in debugging --- .../Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs | 4 ++++ .../Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs | 4 ++++ .../SuggestionSources/Google.cs | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs index 0c6d95b4f..dcc2b9eef 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs @@ -26,6 +26,10 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources const string api = "http://suggestion.baidu.com/su?json=1&wd="; result = await Http.GetAsync(api + Uri.EscapeUriString(query), token).ConfigureAwait(false); } + catch (TaskCanceledException) + { + return null; + } catch (HttpRequestException e) { Log.Exception("|Baidu.Suggestions|Can't get suggestion from baidu", e); diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs index 991e45267..47bd016c1 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs @@ -24,6 +24,10 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources const string api = "https://api.bing.com/qsonhs.aspx?q="; resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query), token).ConfigureAwait(false); } + catch (TaskCanceledException) + { + return null; + } catch (HttpRequestException e) { Log.Exception("|Bing.Suggestions|Can't get suggestion from Bing", e); diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs index d034679af..1182013b6 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs @@ -22,6 +22,10 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources const string api = "https://www.google.com/complete/search?output=chrome&q="; result = await Http.GetAsync(api + Uri.EscapeUriString(query), token).ConfigureAwait(false); } + catch (TaskCanceledException) + { + return null; + } catch (HttpRequestException e) { Log.Exception("|Google.Suggestions|Can't get suggestion from google", e); From 86a9cf31c5e67b61d9cb5fb74ba49e02f3b389f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 7 Jan 2021 21:41:05 +0800 Subject: [PATCH 137/158] Optimize Websearch code --- .../Flow.Launcher.Plugin.WebSearch/Main.cs | 122 +++++++++--------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs index a573674ec..f76e28112 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs @@ -40,65 +40,63 @@ namespace Flow.Launcher.Plugin.WebSearch var searchSourceList = new List(); var results = new List(); - _settings.SearchSources.Where(o => (o.ActionKeyword == query.ActionKeyword || o.ActionKeyword == SearchSourceGlobalPluginWildCardSign) - && o.Enabled) - .ToList() - .ForEach(x => searchSourceList.Add(x)); - - if (searchSourceList.Any()) + foreach (SearchSource searchSource in _settings.SearchSources.Where(o => (o.ActionKeyword == query.ActionKeyword || + o.ActionKeyword == SearchSourceGlobalPluginWildCardSign) + && o.Enabled)) { - foreach (SearchSource searchSource in searchSourceList) + string keyword = string.Empty; + keyword = searchSource.ActionKeyword == SearchSourceGlobalPluginWildCardSign ? query.ToString() : query.Search; + var title = keyword; + string subtitle = _context.API.GetTranslation("flowlauncher_plugin_websearch_search") + " " + searchSource.Title; + + if (string.IsNullOrEmpty(keyword)) { - string keyword = string.Empty; - keyword = searchSource.ActionKeyword == SearchSourceGlobalPluginWildCardSign ? query.ToString() : query.Search; - var title = keyword; - string subtitle = _context.API.GetTranslation("flowlauncher_plugin_websearch_search") + " " + searchSource.Title; - - if (string.IsNullOrEmpty(keyword)) + var result = new Result { - var result = new Result - { - Title = subtitle, - SubTitle = string.Empty, - IcoPath = searchSource.IconPath - }; - results.Add(result); - } - else - { - var result = new Result - { - Title = title, - SubTitle = subtitle, - Score = 6, - IcoPath = searchSource.IconPath, - ActionKeywordAssigned = searchSource.ActionKeyword == SearchSourceGlobalPluginWildCardSign ? string.Empty : searchSource.ActionKeyword, - Action = c => - { - if (_settings.OpenInNewBrowser) - { - searchSource.Url.Replace("{q}", Uri.EscapeDataString(keyword)).NewBrowserWindow(_settings.BrowserPath); - } - else - { - searchSource.Url.Replace("{q}", Uri.EscapeDataString(keyword)).NewTabInBrowser(_settings.BrowserPath); - } - - return true; - } - }; - - results.Add(result); - } - - ResultsUpdated?.Invoke(this, new ResultUpdatedEventArgs - { - Results = results, - Query = query - }); - - await UpdateResultsFromSuggestionAsync(results, keyword, subtitle, searchSource, query, token).ConfigureAwait(false); + Title = subtitle, + SubTitle = string.Empty, + IcoPath = searchSource.IconPath + }; + results.Add(result); } + else + { + var result = new Result + { + Title = title, + SubTitle = subtitle, + Score = 6, + IcoPath = searchSource.IconPath, + ActionKeywordAssigned = searchSource.ActionKeyword == SearchSourceGlobalPluginWildCardSign ? string.Empty : searchSource.ActionKeyword, + Action = c => + { + if (_settings.OpenInNewBrowser) + { + searchSource.Url.Replace("{q}", Uri.EscapeDataString(keyword)).NewBrowserWindow(_settings.BrowserPath); + } + else + { + searchSource.Url.Replace("{q}", Uri.EscapeDataString(keyword)).NewTabInBrowser(_settings.BrowserPath); + } + + return true; + } + }; + + results.Add(result); + } + + ResultsUpdated?.Invoke(this, new ResultUpdatedEventArgs + { + Results = results, + Query = query + }); + + await UpdateResultsFromSuggestionAsync(results, keyword, subtitle, searchSource, query, token).ConfigureAwait(false); + + if (token.IsCancellationRequested) + return null; + } return results; @@ -110,15 +108,13 @@ namespace Flow.Launcher.Plugin.WebSearch if (_settings.EnableSuggestion) { var suggestions = await SuggestionsAsync(keyword, subtitle, searchSource, token).ConfigureAwait(false); + if (token.IsCancellationRequested || !suggestions.Any()) + return; + + results.AddRange(suggestions); token.ThrowIfCancellationRequested(); - - ResultsUpdated?.Invoke(this, new ResultUpdatedEventArgs - { - Results = results, - Query = query - }); } } @@ -128,6 +124,10 @@ namespace Flow.Launcher.Plugin.WebSearch if (source != null) { var suggestions = await source.Suggestions(keyword, token); + + if (token.IsCancellationRequested) + return null; + var resultsFromSuggestion = suggestions.Select(o => new Result { Title = o, From 919d5d51ab5b8e3ffb15f10f74586e0d26681e2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 7 Jan 2021 21:47:31 +0800 Subject: [PATCH 138/158] add using for Bing search source --- .../Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs index 47bd016c1..e6b0438d1 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs @@ -39,7 +39,8 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources JsonElement json; try { - json = (await JsonDocument.ParseAsync(resultStream)).RootElement.GetProperty("AS"); + using (resultStream) + json = (await JsonDocument.ParseAsync(resultStream)).RootElement.GetProperty("AS"); } catch (JsonException e) { From 1003ce416093d2320892c75f50b2082a522cc2e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 7 Jan 2021 21:48:55 +0800 Subject: [PATCH 139/158] move using to more specific place in Google.cs --- .../SuggestionSources/Google.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs index 17153b9a7..5c784a702 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs @@ -32,24 +32,24 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources return new List(); } - using (resultStream) + + if (resultStream.Length == 0) return new List(); + JsonDocument json; + try { - if (resultStream.Length == 0) return new List(); - JsonDocument json; - try - { + using (resultStream) json = await JsonDocument.ParseAsync(resultStream); - } - catch (JsonException e) - { - Log.Exception("|Google.Suggestions|can't parse suggestions", e); - return new List(); - } - - var results = json?.RootElement.EnumerateArray().ElementAt(1); - - return results?.EnumerateArray().Select(o => o.GetString()).ToList() ?? new List(); } + catch (JsonException e) + { + Log.Exception("|Google.Suggestions|can't parse suggestions", e); + return new List(); + } + + var results = json?.RootElement.EnumerateArray().ElementAt(1); + + return results?.EnumerateArray().Select(o => o.GetString()).ToList() ?? new List(); + } public override string ToString() From c939924ec8ec2e28e6eccf7422ac7bf9eb5f3e32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 7 Jan 2021 21:54:09 +0800 Subject: [PATCH 140/158] optimize code in searchsuggestions --- .../SuggestionSources/Bing.cs | 16 +++++----------- .../SuggestionSources/Google.cs | 17 ++++++----------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs index e6b0438d1..81725c3f2 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs @@ -17,12 +17,15 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources { public override async Task> Suggestions(string query, CancellationToken token) { - Stream resultStream; + JsonElement json; try { const string api = "https://api.bing.com/qsonhs.aspx?q="; - resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query), token).ConfigureAwait(false); + using var resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query), token).ConfigureAwait(false); + if (resultStream.Length == 0) return new List(); // this handles the cancellation + json = (await JsonDocument.ParseAsync(resultStream, cancellationToken: token)).RootElement.GetProperty("AS"); + } catch (TaskCanceledException) { @@ -33,15 +36,6 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources Log.Exception("|Bing.Suggestions|Can't get suggestion from Bing", e); return new List(); } - - if (resultStream.Length == 0) return new List(); // this handles the cancellation - - JsonElement json; - try - { - using (resultStream) - json = (await JsonDocument.ParseAsync(resultStream)).RootElement.GetProperty("AS"); - } catch (JsonException e) { Log.Exception("|Bing.Suggestions|can't parse suggestions", e); diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs index 5c784a702..b150d26c1 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs @@ -16,11 +16,15 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources { public override async Task> Suggestions(string query, CancellationToken token) { - Stream resultStream; + JsonDocument json; + try { const string api = "https://www.google.com/complete/search?output=chrome&q="; - resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false); + using var resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false); + if (resultStream.Length == 0) return new List(); + json = await JsonDocument.ParseAsync(resultStream); + } catch (TaskCanceledException) { @@ -31,15 +35,6 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources Log.Exception("|Google.Suggestions|Can't get suggestion from google", e); return new List(); } - - - if (resultStream.Length == 0) return new List(); - JsonDocument json; - try - { - using (resultStream) - json = await JsonDocument.ParseAsync(resultStream); - } catch (JsonException e) { Log.Exception("|Google.Suggestions|can't parse suggestions", e); From 6d5ade405694903859546b5392d5e81f349159e6 Mon Sep 17 00:00:00 2001 From: kubalav Date: Fri, 8 Jan 2021 09:44:26 +0100 Subject: [PATCH 141/158] Plugins Manager Slovak translation --- .../Languages/sk.xaml | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/sk.xaml diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/sk.xaml new file mode 100644 index 000000000..21ec558a6 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/sk.xaml @@ -0,0 +1,39 @@ + + + + Sťahovanie pluginu + Čakajte, prosím… + Úspešne stiahnuté + {0} od {1} {2}{3}Chcete odinštalovať tento plugin? Po odinštalovaní sa Flow automaticky reštartuje. + {0} by {1} {2}{3}Chcete nainštalovať tento plugin? Po odinštalovaní sa Flow automaticky reštartuje. + Inštalovať plugin + Odinštalovať plugin + Inštalácia zlyhala: nepodarilo sa nájsť metadáta súboru plugin.json nového pluginu + Chyba inštalácie pluginu + Nastala chyba počas inštaláciu pluginu {0} + Nie je k dispozícii žiadna aktualizácia + Všetky pluginy sú aktuálne + {0} od {1} {2}{3}Chcete aktualizovať tento plugin? Po odinštalovaní sa Flow automaticky reštartuje. + Aktualizácia pluginu + Tento plugin má dostupnú aktualizáciu, chcete ju zobraziť? + Tento plugin je už nainštalovaný + + + + + Správca pluginov + Správa inštalácie, odinštalácie alebo aktualizácie pluginov programu Flow Launcher + + + Prejsť na webovú stránku + Prejsť na webovú stránku pluginu + Zobraziť zdrojový kód + Zobraziť zdrojový kód pluginu + Navrhnúť vylepšenie alebo nahlásiť chybu + Navrhnúť vylepšenie alebo nahlásiť chybu vývojárovi pluginu + Prejsť na repozitár pluginov spúšťača Flow + Prejsť na repozitár pluginov spúšťača Flow a zobraziť príspevky komunity + + \ No newline at end of file From 49560db7f64d7535ffc7dfba41a8470a09ed8257 Mon Sep 17 00:00:00 2001 From: Ioannis G Date: Sat, 9 Jan 2021 02:49:14 +0200 Subject: [PATCH 142/158] copy svg images to output from the main project --- Flow.Launcher/Flow.Launcher.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index c3b56b904..fddaaab6a 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -63,6 +63,9 @@ PreserveNewest + + PreserveNewest + From 92be6fd3ddd1024e320aa6d0b98ab1000fe7e5df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sat, 9 Jan 2021 12:01:08 +0800 Subject: [PATCH 143/158] Use Task.Yield to avoid using Parallel.For --- Flow.Launcher/ViewModel/MainViewModel.cs | 59 +++++++++++------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index da977f11f..c3e32731b 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -86,7 +86,7 @@ namespace Flow.Launcher.ViewModel { foreach (var pair in PluginManager.GetPluginsForInterface()) { - var plugin = (IResultUpdated)pair.Plugin; + var plugin = (IResultUpdated) pair.Plugin; plugin.ResultsUpdated += (s, e) => { Task.Run(() => @@ -113,25 +113,13 @@ namespace Flow.Launcher.ViewModel } }); - SelectNextItemCommand = new RelayCommand(_ => - { - SelectedResults.SelectNextResult(); - }); + SelectNextItemCommand = new RelayCommand(_ => { SelectedResults.SelectNextResult(); }); - SelectPrevItemCommand = new RelayCommand(_ => - { - SelectedResults.SelectPrevResult(); - }); + SelectPrevItemCommand = new RelayCommand(_ => { SelectedResults.SelectPrevResult(); }); - SelectNextPageCommand = new RelayCommand(_ => - { - SelectedResults.SelectNextPage(); - }); + SelectNextPageCommand = new RelayCommand(_ => { SelectedResults.SelectNextPage(); }); - SelectPrevPageCommand = new RelayCommand(_ => - { - SelectedResults.SelectPrevPage(); - }); + SelectPrevPageCommand = new RelayCommand(_ => { SelectedResults.SelectPrevPage(); }); SelectFirstResultCommand = new RelayCommand(_ => SelectedResults.SelectFirstResult()); @@ -209,6 +197,7 @@ namespace Flow.Launcher.ViewModel public ResultsViewModel History { get; private set; } private string _queryText; + public string QueryText { get { return _queryText; } @@ -229,10 +218,12 @@ namespace Flow.Launcher.ViewModel QueryTextCursorMovedToEnd = true; QueryText = queryText; } + public bool LastQuerySelected { get; set; } public bool QueryTextCursorMovedToEnd { get; set; } private ResultsViewModel _selectedResults; + private ResultsViewModel SelectedResults { get { return _selectedResults; } @@ -264,6 +255,7 @@ namespace Flow.Launcher.ViewModel QueryText = string.Empty; } } + _selectedResults.Visbility = Visibility.Visible; } } @@ -324,7 +316,7 @@ namespace Flow.Launcher.ViewModel var filtered = results.Where ( r => StringMatcher.FuzzySearch(query, r.Title).IsSearchPrecisionScoreMet() - || StringMatcher.FuzzySearch(query, r.SubTitle).IsSearchPrecisionScoreMet() + || StringMatcher.FuzzySearch(query, r.SubTitle).IsSearchPrecisionScoreMet() ).ToList(); ContextMenu.AddResults(filtered, id); } @@ -351,7 +343,7 @@ namespace Flow.Launcher.ViewModel Title = string.Format(title, h.Query), SubTitle = string.Format(time, h.ExecutedDateTime), IcoPath = "Images\\history.png", - OriginQuery = new Query { RawQuery = h.Query }, + OriginQuery = new Query {RawQuery = h.Query}, Action = _ => { SelectedResults = Results; @@ -397,7 +389,8 @@ namespace Flow.Launcher.ViewModel _lastQuery = query; Task.Delay(200, currentCancellationToken).ContinueWith(_ => - { // start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet + { + // start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet if (currentUpdateSource == _updateSource && _isQueryRunning) { ProgressBarVisibility = Visibility.Visible; @@ -410,17 +403,14 @@ namespace Flow.Launcher.ViewModel // so looping will stop once it was cancelled Task[] tasks = new Task[plugins.Count]; - var parallelOptions = new ParallelOptions { CancellationToken = currentCancellationToken }; try { - Parallel.For(0, plugins.Count, parallelOptions, i => + for (var i = 0; i < plugins.Count; i++) { if (!plugins[i].Metadata.Disabled) - { tasks[i] = QueryTask(plugins[i], query, currentCancellationToken); - } else tasks[i] = Task.CompletedTask; // Avoid Null - }); + } // Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first await Task.WhenAll(tasks); @@ -434,20 +424,25 @@ namespace Flow.Launcher.ViewModel // until the end of all querying _isQueryRunning = false; if (!currentCancellationToken.IsCancellationRequested) - { // update to hidden if this is still the current query + { + // update to hidden if this is still the current query ProgressBarVisibility = Visibility.Hidden; } // Local Function async Task QueryTask(PluginPair plugin, Query query, CancellationToken token) { + // Since it is wrapped within a Task.Run, the synchronous context is null + // Task.Yield will force it to run in ThreadPool + await Task.Yield(); + var results = await PluginManager.QueryForPlugin(plugin, query, token); if (!currentCancellationToken.IsCancellationRequested) UpdateResultView(results, plugin.Metadata, query); } - - }, currentCancellationToken).ContinueWith(t => Log.Exception("|MainViewModel|Plugins Query Exceptions", t.Exception), - TaskContinuationOptions.OnlyOnFaulted); + }, currentCancellationToken).ContinueWith( + t => Log.Exception("|MainViewModel|Plugins Query Exceptions", t.Exception), + TaskContinuationOptions.OnlyOnFaulted); } } else @@ -514,6 +509,7 @@ namespace Flow.Launcher.ViewModel } }; } + return menu; } @@ -559,6 +555,7 @@ namespace Flow.Launcher.ViewModel var selected = SelectedResults == History; return selected; } + #region Hotkey private void SetHotkey(string hotkeyStr, EventHandler action) @@ -577,7 +574,8 @@ namespace Flow.Launcher.ViewModel catch (Exception) { string errorMsg = - string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr); + string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), + hotkeyStr); MessageBox.Show(errorMsg); } } @@ -627,7 +625,6 @@ namespace Flow.Launcher.ViewModel { if (!ShouldIgnoreHotkeys()) { - if (_settings.LastQueryMode == LastQueryMode.Empty) { ChangeQueryText(string.Empty); From 16148d59d4577d3db2efefdd3d9ccb9b6dbd25f7 Mon Sep 17 00:00:00 2001 From: kubalav Date: Sat, 9 Jan 2021 13:24:18 +0100 Subject: [PATCH 144/158] Fix Plugins Manager key name in Slovak translation --- Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/sk.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/sk.xaml index 21ec558a6..211f2b430 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/sk.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/sk.xaml @@ -27,7 +27,7 @@ Správa inštalácie, odinštalácie alebo aktualizácie pluginov programu Flow Launcher - Prejsť na webovú stránku + Prejsť na webovú stránku Prejsť na webovú stránku pluginu Zobraziť zdrojový kód Zobraziť zdrojový kód pluginu From 15a5bbabb8f8dbbcbde05c745c32c50ae34289c1 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 10 Jan 2021 13:17:40 +1100 Subject: [PATCH 145/158] version bump for PluginsManager --- Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json index ef2c1255a..60ab7ab18 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json @@ -6,7 +6,7 @@ "Name": "Plugins Manager", "Description": "Management of installing, uninstalling or updating Flow Launcher plugins", "Author": "Jeremy Wu", - "Version": "1.4.1", + "Version": "1.5.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll", From db0fcb21713f92c18263ca829c9250a2fba9df00 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 10 Jan 2021 20:59:49 +1100 Subject: [PATCH 146/158] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 654877f3a..1056ec48e 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be - Support of wide range of plugins. - Fully portable. +[![image](https://user-images.githubusercontent.com/26427004/104119722-9033c600-5385-11eb-9d57-4c376862fd36.png) **SOFTPEDIA EDITOR'S PICK**](https://www.softpedia.com/get/System/Launchers-Shutdown-Tools/Flow-Launcher.shtml) + ## Running Flow Launcher | [Windows 7 and up](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest) | From b97834d9bcfbe3e11bfdad97be38c4ac98f34778 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 12 Jan 2021 07:30:01 +1100 Subject: [PATCH 147/158] update message + window location --- Flow.Launcher.Plugin/PluginMetadata.cs | 1 - Flow.Launcher/Languages/en.xaml | 2 +- Flow.Launcher/PriorityChangeWindow.xaml | 3 ++- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Plugin/PluginMetadata.cs b/Flow.Launcher.Plugin/PluginMetadata.cs index d470b5556..e8f5cf744 100644 --- a/Flow.Launcher.Plugin/PluginMetadata.cs +++ b/Flow.Launcher.Plugin/PluginMetadata.cs @@ -45,7 +45,6 @@ namespace Flow.Launcher.Plugin [JsonIgnore] public int Priority { get; set; } - /// /// Init time include both plugin load time and init time /// diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 234fe5091..1e0e6a7e0 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -107,7 +107,7 @@ Release Notes - Higher the number, the closer the result will be to the top. If you want the results to be lower than any other plugin's, provide a negative number + Greater the number, the higher the result will be ranked. Try setting it as 5. If you want the results to be lower than any other plugin's, provide a negative number Please provide an valid integer for Priority! diff --git a/Flow.Launcher/PriorityChangeWindow.xaml b/Flow.Launcher/PriorityChangeWindow.xaml index 54e5c02cc..68b5a49b7 100644 --- a/Flow.Launcher/PriorityChangeWindow.xaml +++ b/Flow.Launcher/PriorityChangeWindow.xaml @@ -6,12 +6,13 @@ xmlns:local="clr-namespace:Flow.Launcher" Loaded="PriorityChangeWindow_Loaded" mc:Ignorable="d" + WindowStartupLocation="CenterScreen" Title="PriorityChangeWindow" Height="250" Width="300"> - + From b1642cc079c5a6490d305c349b0493d71457b78b Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 12 Jan 2021 07:31:13 +1100 Subject: [PATCH 148/158] priority score variable for clarity and seperation from original score --- Flow.Launcher/ViewModel/MainViewModel.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index af472cf06..40006ecbf 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -687,7 +687,8 @@ namespace Flow.Launcher.ViewModel } else { - result.Score += _userSelectedRecord.GetSelectedCount(result) * 5 + metadata.Priority * 50; + var priorityScore = metadata.Priority * 50 + result.Score += _userSelectedRecord.GetSelectedCount(result) * 5 + priorityScore; } } From b9158e55664a9bfd199fad9d42d26cca12ece1a9 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 12 Jan 2021 07:36:23 +1100 Subject: [PATCH 149/158] update typo --- Flow.Launcher/ViewModel/MainViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 40006ecbf..9b274d6e7 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -687,7 +687,7 @@ namespace Flow.Launcher.ViewModel } else { - var priorityScore = metadata.Priority * 50 + var priorityScore = metadata.Priority * 50; result.Score += _userSelectedRecord.GetSelectedCount(result) * 5 + priorityScore; } } From fc536aa1ed1760c409b2974c3849ccfba62e2bf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 13 Jan 2021 09:39:08 +0800 Subject: [PATCH 150/158] make priority score higher --- Flow.Launcher/ViewModel/MainViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 9b274d6e7..e83e28c33 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -687,7 +687,7 @@ namespace Flow.Launcher.ViewModel } else { - var priorityScore = metadata.Priority * 50; + var priorityScore = metadata.Priority * 150; result.Score += _userSelectedRecord.GetSelectedCount(result) * 5 + priorityScore; } } From ca7a8c2ef347dbf9b9a2006d049e81430e1a8d29 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 13 Jan 2021 19:39:54 +1100 Subject: [PATCH 151/158] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1056ec48e..ac8611298 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be - Support of wide range of plugins. - Fully portable. -[![image](https://user-images.githubusercontent.com/26427004/104119722-9033c600-5385-11eb-9d57-4c376862fd36.png) **SOFTPEDIA EDITOR'S PICK**](https://www.softpedia.com/get/System/Launchers-Shutdown-Tools/Flow-Launcher.shtml) +[ **SOFTPEDIA EDITOR'S PICK**](https://www.softpedia.com/get/System/Launchers-Shutdown-Tools/Flow-Launcher.shtml) ## Running Flow Launcher From 763b51858fc094eb158b98ef023f9ccb24d8d44a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 14 Jan 2021 12:24:41 +0800 Subject: [PATCH 152/158] Add comment in IPublic, IAsyncPlugin, IReloadable, IAsyncReloadable --- Flow.Launcher.Plugin/IAsyncPlugin.cs | 15 +++++++++++++ Flow.Launcher.Plugin/IPlugin.cs | 22 ++++++++++++++++++- .../Interfaces/IAsyncReloadable.cs | 11 ++++++++++ .../Interfaces/IReloadable.cs | 6 ++++- 4 files changed, 52 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Plugin/IAsyncPlugin.cs b/Flow.Launcher.Plugin/IAsyncPlugin.cs index 36f098e7d..3e1a086bc 100644 --- a/Flow.Launcher.Plugin/IAsyncPlugin.cs +++ b/Flow.Launcher.Plugin/IAsyncPlugin.cs @@ -4,9 +4,24 @@ using System.Threading.Tasks; namespace Flow.Launcher.Plugin { + /// + /// Asynchronous Plugin Model for Flow Launcher + /// public interface IAsyncPlugin { + /// + /// Asynchronous Querying + /// + /// Query to search + /// Cancel when querying job is obsolete + /// Task> QueryAsync(Query query, CancellationToken token); + + /// + /// Initialize plugin asynchrously (will still wait finish to continue) + /// + /// + /// Task InitAsync(PluginInitContext context); } } \ No newline at end of file diff --git a/Flow.Launcher.Plugin/IPlugin.cs b/Flow.Launcher.Plugin/IPlugin.cs index 4cc6d8d40..41c61a608 100644 --- a/Flow.Launcher.Plugin/IPlugin.cs +++ b/Flow.Launcher.Plugin/IPlugin.cs @@ -2,10 +2,30 @@ namespace Flow.Launcher.Plugin { + /// + /// Synchronous Plugin Model for Flow Launcher + /// + /// If you assume that Querying or Init method require IO transmission + /// or CPU Intense Job (performing better with cancellation), please try IAsyncPlugin interface + /// + /// public interface IPlugin { + /// + /// Querying when user's search changes + /// + /// This method will be called within a Task.Run, + /// so please avoid synchrously wait for long. + /// + /// + /// Query to search + /// List Query(Query query); - + + /// + /// Initialize plugin + /// + /// void Init(PluginInitContext context); } } \ No newline at end of file diff --git a/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs b/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs index 9c922f667..434d83646 100644 --- a/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs +++ b/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs @@ -2,6 +2,17 @@ namespace Flow.Launcher.Plugin { + /// + /// This interface is to indicate and allow plugins to asyncronously reload their + /// in memory data cache or other mediums when user makes a new change + /// that is not immediately captured. For example, for BrowserBookmark and Program + /// plugin does not automatically detect when a user added a new bookmark or program, + /// so this interface's function is exposed to allow user manually do the reloading after + /// those new additions. + /// + /// The command that allows user to manual reload is exposed via Plugin.Sys, and + /// it will call the plugins that have implemented this interface. + /// public interface IAsyncReloadable { Task ReloadDataAsync(); diff --git a/Flow.Launcher.Plugin/Interfaces/IReloadable.cs b/Flow.Launcher.Plugin/Interfaces/IReloadable.cs index 29b3c15c9..d9160b0ea 100644 --- a/Flow.Launcher.Plugin/Interfaces/IReloadable.cs +++ b/Flow.Launcher.Plugin/Interfaces/IReloadable.cs @@ -1,7 +1,7 @@ namespace Flow.Launcher.Plugin { /// - /// This interface is to indicate and allow plugins to reload their + /// This interface is to indicate and allow plugins to synchronously reload their /// in memory data cache or other mediums when user makes a new change /// that is not immediately captured. For example, for BrowserBookmark and Program /// plugin does not automatically detect when a user added a new bookmark or program, @@ -10,6 +10,10 @@ /// /// The command that allows user to manual reload is exposed via Plugin.Sys, and /// it will call the plugins that have implemented this interface. + /// + /// + /// If requiring reloading data asynchrouly, please try IAsyncReloadable + /// /// public interface IReloadable { From 723265a865afa75f7f250ab9f93668298e093d7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Fri, 15 Jan 2021 10:37:13 +0800 Subject: [PATCH 153/158] fix TopMostRecord and UserSelectedRecord due to System.Text.Json cannot parse private field in version 3.1 --- .../Storage/JsonStorage.cs | 4 +-- Flow.Launcher/Storage/TopMostRecord.cs | 26 ++++++++++++++----- Flow.Launcher/Storage/UserSelectedRecord.cs | 16 +++++++++++- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs index 268dc20b8..f0e4a79fc 100644 --- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs @@ -9,7 +9,7 @@ namespace Flow.Launcher.Infrastructure.Storage /// /// Serialize object using json format. /// - public class JsonStrorage + public class JsonStrorage where T : new() { private readonly JsonSerializerOptions _serializerSettings; private T _data; @@ -76,7 +76,7 @@ namespace Flow.Launcher.Infrastructure.Storage BackupOriginFile(); } - _data = JsonSerializer.Deserialize("{}", _serializerSettings); + _data = new T(); Save(); } diff --git a/Flow.Launcher/Storage/TopMostRecord.cs b/Flow.Launcher/Storage/TopMostRecord.cs index 09e69f6d8..c92ef4956 100644 --- a/Flow.Launcher/Storage/TopMostRecord.cs +++ b/Flow.Launcher/Storage/TopMostRecord.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.Json; +using System.Text.Json.Serialization; using Flow.Launcher.Plugin; namespace Flow.Launcher.Storage @@ -8,20 +9,24 @@ namespace Flow.Launcher.Storage // todo this class is not thread safe.... but used from multiple threads. public class TopMostRecord { - private Dictionary records = new Dictionary(); + /// + /// You should not directly access this field + /// + /// It is public due to System.Text.Json limitation in version 3.1 + /// + /// + /// TODO: Set it to private + public Dictionary records { get; set; } = new Dictionary(); internal bool IsTopMost(Result result) { - if (records.Count == 0) + if (records.Count == 0 || !records.ContainsKey(result.OriginQuery.RawQuery)) { return false; } - // since this dictionary should be very small (or empty) going over it should be pretty fast. - return records.Any(o => o.Value.Title == result.Title - && o.Value.SubTitle == result.SubTitle - && o.Value.PluginID == result.PluginID - && o.Key == result.OriginQuery.RawQuery); + // since this dictionary should be very small (or empty) going over it should be pretty fast. + return records[result.OriginQuery.RawQuery].Equals(result); } internal void Remove(Result result) @@ -53,5 +58,12 @@ namespace Flow.Launcher.Storage public string Title { get; set; } public string SubTitle { get; set; } public string PluginID { get; set; } + + public bool Equals(Result r) + { + return Title == r.Title + && SubTitle == r.SubTitle + && PluginID == r.PluginID; + } } } diff --git a/Flow.Launcher/Storage/UserSelectedRecord.cs b/Flow.Launcher/Storage/UserSelectedRecord.cs index c7ffe1a1d..b97138eb9 100644 --- a/Flow.Launcher/Storage/UserSelectedRecord.cs +++ b/Flow.Launcher/Storage/UserSelectedRecord.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Text.Json.Serialization; using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Plugin; @@ -6,7 +7,20 @@ namespace Flow.Launcher.Storage { public class UserSelectedRecord { - private Dictionary records = new Dictionary(); + /// + /// You should not directly access this field + /// + /// It is public due to System.Text.Json limitation in version 3.1 + /// + /// + /// TODO: Set it to private + [JsonPropertyName("records")] + public Dictionary records { get; set; } + + public UserSelectedRecord() + { + records = new Dictionary(); + } public void Add(Result result) { From 6655a8f168c9d1ae0586b8d52f3ce91fd3f9e321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Fri, 15 Jan 2021 15:56:28 +0800 Subject: [PATCH 154/158] optimize code --- Flow.Launcher/Storage/UserSelectedRecord.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/Storage/UserSelectedRecord.cs b/Flow.Launcher/Storage/UserSelectedRecord.cs index b97138eb9..bc7a2da73 100644 --- a/Flow.Launcher/Storage/UserSelectedRecord.cs +++ b/Flow.Launcher/Storage/UserSelectedRecord.cs @@ -25,9 +25,9 @@ namespace Flow.Launcher.Storage public void Add(Result result) { var key = result.ToString(); - if (records.TryGetValue(key, out int value)) + if (records.ContainsKey(key)) { - records[key] = value + 1; + records[key]++; } else { From 114c12bdaf694dd2b5f98fc59b14ff1d1b84c0ad Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 17 Jan 2021 18:47:19 +1100 Subject: [PATCH 155/158] formatting and description --- Flow.Launcher.Core/Plugin/PluginManager.cs | 2 +- Flow.Launcher.Plugin/IAsyncPlugin.cs | 4 ++++ Flow.Launcher.Plugin/IPlugin.cs | 4 ++-- Flow.Launcher.Plugin/Interfaces/IReloadable.cs | 2 +- Flow.Launcher/ViewModel/MainViewModel.cs | 9 +++++++-- .../Search/DirectoryInfo/DirectoryInfoSearch.cs | 1 - 6 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 5a5ba1971..26167e945 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -320,4 +320,4 @@ namespace Flow.Launcher.Core.Plugin } } } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Plugin/IAsyncPlugin.cs b/Flow.Launcher.Plugin/IAsyncPlugin.cs index 3e1a086bc..c6f11aead 100644 --- a/Flow.Launcher.Plugin/IAsyncPlugin.cs +++ b/Flow.Launcher.Plugin/IAsyncPlugin.cs @@ -12,6 +12,10 @@ namespace Flow.Launcher.Plugin /// /// Asynchronous Querying /// + /// + /// If the Querying or Init method requires high IO transmission + /// or performing CPU intense jobs (performing better with cancellation), please use this IAsyncPlugin interface + /// /// Query to search /// Cancel when querying job is obsolete /// diff --git a/Flow.Launcher.Plugin/IPlugin.cs b/Flow.Launcher.Plugin/IPlugin.cs index 41c61a608..208f3021b 100644 --- a/Flow.Launcher.Plugin/IPlugin.cs +++ b/Flow.Launcher.Plugin/IPlugin.cs @@ -5,8 +5,8 @@ namespace Flow.Launcher.Plugin /// /// Synchronous Plugin Model for Flow Launcher /// - /// If you assume that Querying or Init method require IO transmission - /// or CPU Intense Job (performing better with cancellation), please try IAsyncPlugin interface + /// If the Querying or Init method requires high IO transmission + /// or performaing CPU intense jobs (performing better with cancellation), please try the IAsyncPlugin interface /// /// public interface IPlugin diff --git a/Flow.Launcher.Plugin/Interfaces/IReloadable.cs b/Flow.Launcher.Plugin/Interfaces/IReloadable.cs index d9160b0ea..31611519c 100644 --- a/Flow.Launcher.Plugin/Interfaces/IReloadable.cs +++ b/Flow.Launcher.Plugin/Interfaces/IReloadable.cs @@ -12,7 +12,7 @@ /// it will call the plugins that have implemented this interface. /// /// - /// If requiring reloading data asynchrouly, please try IAsyncReloadable + /// If requiring reloading data asynchronously, please use the IAsyncReloadable interface /// /// public interface IReloadable diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index bcb257204..a062f59dc 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -408,8 +408,13 @@ namespace Flow.Launcher.ViewModel for (var i = 0; i < plugins.Count; i++) { if (!plugins[i].Metadata.Disabled) + { tasks[i] = QueryTask(plugins[i], query, currentCancellationToken); - else tasks[i] = Task.CompletedTask; // Avoid Null + } + else + { + tasks[i] = Task.CompletedTask; // Avoid Null + } } // Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first @@ -429,7 +434,7 @@ namespace Flow.Launcher.ViewModel ProgressBarVisibility = Visibility.Hidden; } - // Local Function + // Local function async Task QueryTask(PluginPair plugin, Query query, CancellationToken token) { // Since it is wrapped within a Task.Run, the synchronous context is null diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs index 3253b7a7b..88d7d6927 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Windows; namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo { From d741a98112a50b81cf4551037b1d6bad6c5b0be7 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 17 Jan 2021 18:47:43 +1100 Subject: [PATCH 156/158] fixed last index time during init --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 954c238a9..c283faa83 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -97,22 +97,31 @@ namespace Flow.Launcher.Plugin.Program Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload uwps <{_uwps.Length}>"); }); + bool indexedWinApps = false; + bool indexedUWPApps = false; var a = Task.Run(() => { if (IsStartupIndexProgramsRequired || !_win32s.Any()) + { Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|Win32Program index cost", IndexWin32Programs); + indexedWinApps = true; + } }); var b = Task.Run(() => { if (IsStartupIndexProgramsRequired || !_uwps.Any()) + { Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|Win32Program index cost", IndexUwpPrograms); + indexedUWPApps = true; + } }); await Task.WhenAll(a, b); - _settings.LastIndexTime = DateTime.Today; + if (indexedWinApps && indexedUWPApps) + _settings.LastIndexTime = DateTime.Today; } public static void IndexWin32Programs() From 969265c24fb617f018ddf38285fb61ec1ac2274b Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 17 Jan 2021 19:05:28 +1100 Subject: [PATCH 157/158] fix formatting --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 12 +++++++----- Plugins/Flow.Launcher.Plugin.Sys/Main.cs | 3 +-- .../SuggestionSources/Bing.cs | 6 +++++- .../SuggestionSources/Google.cs | 6 +++++- .../SuggestionSources/SuggestionSource.cs | 2 +- 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index c283faa83..d7413874b 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -202,12 +202,14 @@ namespace Flow.Launcher.Plugin.Program return; if (_uwps.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)) - _uwps.Where(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier).FirstOrDefault().Enabled = - false; + _uwps.Where(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier) + .FirstOrDefault() + .Enabled = false; if (_win32s.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)) - _win32s.Where(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier).FirstOrDefault().Enabled = - false; + _win32s.Where(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier) + .FirstOrDefault() + .Enabled = false; _settings.DisabledProgramSources .Add( @@ -241,4 +243,4 @@ namespace Flow.Launcher.Plugin.Program await IndexPrograms(); } } -} \ No newline at end of file +} diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs index 7ebab91a1..624fe05bc 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs @@ -170,8 +170,7 @@ namespace Flow.Launcher.Plugin.Sys // http://www.pinvoke.net/default.aspx/shell32/SHEmptyRecycleBin.html // FYI, couldn't find documentation for this but if the recycle bin is already empty, it will return -2147418113 (0x8000FFFF (E_UNEXPECTED)) // 0 for nothing - var result = SHEmptyRecycleBin(new WindowInteropHelper(Application.Current.MainWindow).Handle, - 0); + var result = SHEmptyRecycleBin(new WindowInteropHelper(Application.Current.MainWindow).Handle, 0); if (result != (uint) HRESULT.S_OK && result != (uint) 0x8000FFFF) { MessageBox.Show($"Error emptying recycle bin, error code: {result}\n" + diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs index 81725c3f2..ffde2fda2 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs @@ -22,8 +22,12 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources try { const string api = "https://api.bing.com/qsonhs.aspx?q="; + using var resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query), token).ConfigureAwait(false); - if (resultStream.Length == 0) return new List(); // this handles the cancellation + + if (resultStream.Length == 0) + return new List(); // this handles the cancellation + json = (await JsonDocument.ParseAsync(resultStream, cancellationToken: token)).RootElement.GetProperty("AS"); } diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs index b150d26c1..c33ebd7e1 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs @@ -21,8 +21,12 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources try { const string api = "https://www.google.com/complete/search?output=chrome&q="; + using var resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false); - if (resultStream.Length == 0) return new List(); + + if (resultStream.Length == 0) + return new List(); + json = await JsonDocument.ParseAsync(resultStream); } diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/SuggestionSource.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/SuggestionSource.cs index bf444a2f7..c58e61141 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/SuggestionSource.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/SuggestionSource.cs @@ -8,4 +8,4 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources { public abstract Task> Suggestions(string query, CancellationToken token); } -} \ No newline at end of file +} From 539f4bf4c46ca574a50dbb3f924b1b5160da7084 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 17 Jan 2021 19:10:26 +1100 Subject: [PATCH 158/158] add eol --- Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs | 2 +- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 2 +- Flow.Launcher.Plugin/IAsyncPlugin.cs | 2 +- Flow.Launcher.Plugin/IPlugin.cs | 2 +- Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs index 1a1b17539..273698b86 100644 --- a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs @@ -54,4 +54,4 @@ namespace Flow.Launcher.Core.Plugin return referencedPluginPackageDependencyResolver.ResolveAssemblyToPath(assemblyName) != null; } } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index b18c07e3c..fcf178445 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -183,4 +183,4 @@ namespace Flow.Launcher.Core.Plugin }); } } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Plugin/IAsyncPlugin.cs b/Flow.Launcher.Plugin/IAsyncPlugin.cs index c6f11aead..b0b41cc22 100644 --- a/Flow.Launcher.Plugin/IAsyncPlugin.cs +++ b/Flow.Launcher.Plugin/IAsyncPlugin.cs @@ -28,4 +28,4 @@ namespace Flow.Launcher.Plugin /// Task InitAsync(PluginInitContext context); } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Plugin/IPlugin.cs b/Flow.Launcher.Plugin/IPlugin.cs index 208f3021b..203dc9af7 100644 --- a/Flow.Launcher.Plugin/IPlugin.cs +++ b/Flow.Launcher.Plugin/IPlugin.cs @@ -28,4 +28,4 @@ namespace Flow.Launcher.Plugin /// void Init(PluginInitContext context); } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs b/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs index 434d83646..fc4ac4715 100644 --- a/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs +++ b/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs @@ -17,4 +17,4 @@ namespace Flow.Launcher.Plugin { Task ReloadDataAsync(); } -} \ No newline at end of file +}