diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 3369526e0..5f20fa29f 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 { @@ -38,11 +38,11 @@ namespace Flow.Launcher.Core if (!silentUpdate) api.ShowMsg("Please wait...", "Checking for new update"); - using var updateManager = await GitHubUpdateManager(GitHubRepository); + using var updateManager = await GitHubUpdateManager(GitHubRepository).ConfigureAwait(false); // UpdateApp CheckForUpdate will return value only if the app is squirrel installed - newUpdateInfo = await updateManager.CheckForUpdate().NonNull(); + newUpdateInfo = await updateManager.CheckForUpdate().NonNull().ConfigureAwait(false); var newReleaseVersion = Version.Parse(newUpdateInfo.FutureReleaseEntry.Version.ToString()); var currentVersion = Version.Parse(Constant.Version); @@ -53,16 +53,15 @@ namespace Flow.Launcher.Core { if (!silentUpdate) MessageBox.Show("You already have the latest Flow Launcher version"); - updateManager.Dispose(); return; } if (!silentUpdate) api.ShowMsg("Update found", "Updating..."); - await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply); + await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply).ConfigureAwait(false); - await updateManager.ApplyReleases(newUpdateInfo); + await updateManager.ApplyReleases(newUpdateInfo).ConfigureAwait(false); if (DataLocation.PortableDataLocationInUse()) { @@ -74,7 +73,7 @@ namespace Flow.Launcher.Core } else { - await updateManager.CreateUninstallerRegistryEntry(); + await updateManager.CreateUninstallerRegistryEntry().ConfigureAwait(false); } var newVersionTips = NewVersinoTips(newReleaseVersion.ToString()); @@ -97,13 +96,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; } } @@ -113,13 +112,13 @@ 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 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/"); - 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/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.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 1d5f240e1..8e2832690 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -6,6 +6,8 @@ using System.Threading.Tasks; using JetBrains.Annotations; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; +using System; +using System.ComponentModel; namespace Flow.Launcher.Infrastructure.Http { @@ -13,6 +15,14 @@ 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,58 +30,103 @@ namespace Flow.Launcher.Infrastructure.Http ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; + + client = new HttpClient(socketsHttpHandler, false); + client.DefaultRequestHeaders.Add("User-Agent", UserAgent); } - public static HttpProxy Proxy { private get; set; } - public static IWebProxy WebProxy() + private static HttpProxy proxy; + + public static HttpProxy Proxy { - if (Proxy != null && Proxy.Enabled && !string.IsNullOrEmpty(Proxy.Server)) + private get { return proxy; } + set { - if (string.IsNullOrEmpty(Proxy.UserName) || string.IsNullOrEmpty(Proxy.Password)) + proxy = value; + proxy.PropertyChanged += UpdateProxy; + } + } + + public static WebProxy WebProxy { get; } = new WebProxy(); + + /// + /// Update the Address of the Proxy to modify the client Proxy + /// + public static void UpdateProxy(ProxyProperty property) + { + (WebProxy.Address, WebProxy.Credentials) = property switch + { + ProxyProperty.Enabled => Proxy.Enabled switch { - var webProxy = new WebProxy(Proxy.Server, Proxy.Port); - return webProxy; - } - else - { - var webProxy = new WebProxy(Proxy.Server, Proxy.Port) + true => Proxy.UserName switch { - Credentials = new NetworkCredential(Proxy.UserName, Proxy.Password) - }; - return webProxy; - } + 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}"), 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() + }; + } + + public static async Task Download([NotNull] string url, [NotNull] string filePath) + { + 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 { - return WebRequest.GetSystemWebProxy(); + throw new HttpRequestException($"Error code <{response.StatusCode}> returned from <{url}>"); } } - public static void Download([NotNull] string url, [NotNull] string filePath) - { - var client = new WebClient { Proxy = WebProxy() }; - client.Headers.Add("user-agent", UserAgent); - client.DownloadFile(url, filePath); - } - - 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 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(); + return GetAsync(new Uri(url.Replace("#", "%23"))); + } - using var reader = new StreamReader(stream, Encoding.GetEncoding(encoding)); - var content = await reader.ReadToEndAsync(); - if (response.StatusCode != HttpStatusCode.OK) - throw new HttpRequestException($"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>"); - - return content; + 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; + } + else + { + throw new HttpRequestException( + $"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>"); + } + } + + /// + /// Asynchrously get the result as stream 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(); } } -} \ No newline at end of file +} 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 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 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/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..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 @@ -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); 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 da28583b1..000000000 Binary files a/Plugins/Flow.Launcher.Plugin.Color/Images/color.png and /dev/null differ 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" -} diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs index d26c1ead4..7bc357be4 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; @@ -9,21 +10,67 @@ 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) { - // 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.NewTabInBrowser(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.NewTabInBrowser(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.NewTabInBrowser(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.NewTabInBrowser("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 000000000..a9126cb9b Binary files /dev/null and b/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/request.png differ 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 000000000..8efbdaa48 Binary files /dev/null and b/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/sourcecode.png differ diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/website.png b/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/website.png new file mode 100644 index 000000000..f96ba15b2 Binary files /dev/null and b/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/website.png differ diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml index 7f8557c28..8d24c145c 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml @@ -11,6 +11,13 @@ 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 + This plugin has an update, would you like to see it? + This plugin is already installed + @@ -18,5 +25,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/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 58ec5005f..d700b9dfd 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -1,9 +1,12 @@ using Flow.Launcher.Infrastructure.Storage; -using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin.PluginsManager.ViewModels; using Flow.Launcher.Plugin.PluginsManager.Views; using System.Collections.Generic; +using System.Linq; using System.Windows.Controls; +using Flow.Launcher.Infrastructure; +using System; +using System.Threading.Tasks; namespace Flow.Launcher.Plugin.PluginsManager { @@ -17,6 +20,10 @@ namespace Flow.Launcher.Plugin.PluginsManager private IContextMenu contextMenu; + internal PluginsManager pluginManager; + + private DateTime lastUpdateTime = DateTime.MinValue; + public Control CreateSettingPanel() { return new PluginsManagerSettings(viewModel); @@ -27,7 +34,9 @@ namespace Flow.Launcher.Plugin.PluginsManager Context = context; viewModel = new SettingsViewModel(context); Settings = viewModel.Settings; - contextMenu = new ContextMenu(Context, Settings); + contextMenu = new ContextMenu(Context); + pluginManager = new PluginsManager(Context, Settings); + lastUpdateTime = DateTime.Now; } public List LoadContextMenus(Result selectedResult) @@ -39,13 +48,29 @@ namespace Flow.Launcher.Plugin.PluginsManager { var search = query.Search.ToLower(); - var pluginManager = new PluginsManager(Context, Settings); + if (string.IsNullOrWhiteSpace(search)) + return pluginManager.GetDefaultHotKeys(); - if (!string.IsNullOrEmpty(search) - && ($"{Settings.UninstallHotkey} ".StartsWith(search) || search.StartsWith($"{Settings.UninstallHotkey} "))) - return pluginManager.RequestUninstall(search); - - return pluginManager.RequestInstallOrUpdate(search); + if ((DateTime.Now - lastUpdateTime).TotalHours > 12) // 12 hours + { + Task.Run(async () => + { + await pluginManager.UpdateManifest(); + lastUpdateTime = DateTime.Now; + }); + } + + return search switch + { + var s when s.StartsWith(Settings.HotKeyInstall) => pluginManager.RequestInstallOrUpdate(s), + var s when s.StartsWith(Settings.HotkeyUninstall) => pluginManager.RequestUninstall(s), + var s when s.StartsWith(Settings.HotkeyUpdate) => pluginManager.RequestUpdate(s), + _ => pluginManager.GetDefaultHotKeys().Where(hotkey => + { + hotkey.Score = StringMatcher.FuzzySearch(search, hotkey.Title).Score; + return hotkey.Score > 0; + }).ToList() + }; } public void Save() diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs index 290221710..814e0764d 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 @@ -10,23 +10,20 @@ namespace Flow.Launcher.Plugin.PluginsManager.Models internal class PluginsManifest { internal List UserPlugins { get; private set; } + internal PluginsManifest() { - DownloadManifest(); + Task.Run(async () => await DownloadManifest()).Wait(); } - private void DownloadManifest() + internal async Task DownloadManifest() { - var json = string.Empty; try { - var t = Task.Run( - async () => - json = await Http.Get("https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher.PluginsManifest/main/plugins.json")); + await using var jsonStream = await Http.GetStreamAsync("https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher.PluginsManifest/main/plugins.json") + .ConfigureAwait(false); - t.Wait(); - - UserPlugins = JsonConvert.DeserializeObject>(json); + UserPlugins = await JsonSerializer.DeserializeAsync>(jsonStream).ConfigureAwait(false); } catch (Exception e) { @@ -34,7 +31,6 @@ namespace Flow.Launcher.Plugin.PluginsManager.Models UserPlugins = new List(); } - } } -} +} \ No newline at end of file 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 c141916aa..ac15618ca 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -7,18 +7,35 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using System.Windows; namespace Flow.Launcher.Plugin.PluginsManager { internal class PluginsManager { - private readonly PluginsManifest pluginsManifest; + private PluginsManifest pluginsManifest; private PluginInitContext Context { get; set; } private Settings Settings { get; set; } + private bool shouldHideWindow = true; + + private bool ShouldHideWindow + { + 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) @@ -27,47 +44,187 @@ namespace Flow.Launcher.Plugin.PluginsManager Context = context; Settings = settings; } - internal void InstallOrUpdate(UserPlugin plugin) + + internal async Task UpdateManifest() + { + 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 async Task InstallOrUpdate(UserPlugin plugin) { if (PluginExists(plugin.ID)) { - Context.API.ShowMsg("Plugin already installed"); + if (Context.API.GetAllPlugins() + .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"), + 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; } 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.ID}.zip"); + var filePath = Path.Combine(DataLocation.PluginsDirectory, $"{plugin.Name}-{plugin.Version}.zip"); 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); + 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")); + 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)); + Install(plugin, filePath); + Context.API.RestartApp(); } - internal void Update() + internal List RequestUpdate(string search) { - throw new NotImplementedException(); + 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.CompareTo(pluginFromManifest.Version) < 0 // if current version precedes manifest version + select + new + { + pluginFromManifest.Name, + pluginFromManifest.Author, + CurrentVersion = existingPlugin.Metadata.Version, + NewVersion = pluginFromManifest.Version, + existingPlugin.Metadata.IcoPath, + PluginExistingMetadata = existingPlugin.Metadata, + PluginNewUserPlugin = pluginFromManifest + }; + + if (!resultsForUpdate.Any()) + return new List + { + 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"); + + Task.Run(async delegate + { + await Http.Download(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath).ConfigureAwait(false); + Install(x.PluginNewUserPlugin, downloadToFilePath); + + Context.API.RestartApp(); + }); + + return true; + } + + return false; + } + }); + + return Search(results, uninstallSearch); } internal bool PluginExists(string id) @@ -75,51 +232,46 @@ 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) + internal List Search(IEnumerable results, string searchName) { if (string.IsNullOrEmpty(searchName)) - return results; + 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 searchNameWithoutKeyword = searchName.Replace(Settings.HotKeyInstall, string.Empty).Trim(); + var results = pluginsManifest - .UserPlugins - .Select(x => - new Result - { - Title = $"{x.Name} by {x.Author}", - SubTitle = x.Description, - IcoPath = icoPath, - Action = e => + .UserPlugins + .Select(x => + new Result { - Application.Current.MainWindow.Hide(); - InstallOrUpdate(x); + Title = $"{x.Name} by {x.Author}", + SubTitle = x.Description, + IcoPath = icoPath, + Action = e => + { + Application.Current.MainWindow.Hide(); + _ = InstallOrUpdate(x); // No need to wait + return ShouldHideWindow; + }, + ContextData = x + }); - return true; - } - }) - .ToList(); - - return Search(results, searchName); + return Search(results, searchNameWithoutKeyword); } private void Install(UserPlugin plugin, string downloadedFilePath) @@ -153,32 +305,82 @@ 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); - - Context.API.RestartApp(); } internal List RequestUninstall(string search) + { + var autocompletedResults = AutoCompleteReturnAllResults(search, + Settings.HotkeyUninstall, + "Uninstall", + "Select a plugin to uninstall"); + + if (autocompletedResults.Any()) + return autocompletedResults; + + 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); + + 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; + } + }); + + return Search(results, uninstallSearch); + } + + private void Uninstall(PluginMetadata plugin) + { + // 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) { if (!string.IsNullOrEmpty(search) - && Settings.UninstallHotkey.StartsWith(search) - && (Settings.UninstallHotkey != search || !search.StartsWith(Settings.UninstallHotkey))) + && hotkey.StartsWith(search) + && (hotkey != search || !search.StartsWith(hotkey))) { - return + return new List { new Result { - Title = "Uninstall", + Title = title, IcoPath = icoPath, - SubTitle = "Select a plugin to uninstall", + SubTitle = subtitle, Action = e => { Context - .API - .ChangeQuery($"{Context.CurrentPluginMetadata.ActionKeywords.FirstOrDefault()} {Settings.UninstallHotkey} "); + .API + .ChangeQuery( + $"{Context.CurrentPluginMetadata.ActionKeywords.FirstOrDefault()} {hotkey} "); return false; } @@ -186,42 +388,7 @@ namespace Flow.Launcher.Plugin.PluginsManager }; } - var uninstallSearch = search.Replace(Settings.UninstallHotkey, 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 => - { - Application.Current.MainWindow.Hide(); - Uninstall(x.Metadata); - - return true; - } - }) - .ToList(); - - return Search(results, uninstallSearch); - } - - 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(); - } + return new List(); } } } diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs index 0c647e6ae..9c5b0d29f 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs @@ -6,6 +6,9 @@ namespace Flow.Launcher.Plugin.PluginsManager { internal class Settings { - internal string UninstallHotkey { get; set; } = "uninstall"; + internal string HotKeyInstall { get; set; } = "install"; + internal string HotkeyUninstall { get; set; } = "uninstall"; + + internal string HotkeyUpdate { get; set; } = "update"; } } diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json index 0353fffcc..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.1.0", + "Version": "1.3.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll", 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; diff --git a/README.md b/README.md index f0c08d8a0..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 plugins: in the search window, type `wpm install/uninstall` + 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` 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 } 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: |