Merge Dev

This commit is contained in:
弘韬 张 2020-12-31 13:06:31 +08:00
commit 12b19cf122
36 changed files with 578 additions and 508 deletions

View file

@ -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<List<GithubRelease>>(json);
var releases = await System.Text.Json.JsonSerializer.DeserializeAsync<List<GithubRelease>>(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);

View file

@ -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";
}
}

View file

@ -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();
/// <summary>
/// Update the Address of the Proxy to modify the client Proxy
/// </summary>
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<string> Get([NotNull] string url, string encoding = "UTF-8")
/// <summary>
/// Asynchrously get the result as string from url.
/// When supposing the result is long and large, try using GetStreamAsync to avoid reading as string
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static Task<string> 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<string> 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}>");
}
}
/// <summary>
/// Asynchrously get the result as stream from url.
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static async Task<Stream> GetStreamAsync([NotNull] string url)
{
Log.Debug($"|Http.Get|Url <{url}>");
var response = await client.GetAsync(url);
return await response.Content.ReadAsStreamAsync();
}
}
}
}

View file

@ -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);
}
}
}

View file

@ -14,10 +14,10 @@
</PropertyGroup>
<PropertyGroup>
<Version>1.3.0</Version>
<PackageVersion>1.3.0</PackageVersion>
<AssemblyVersion>1.3.0</AssemblyVersion>
<FileVersion>1.3.0</FileVersion>
<Version>1.3.1</Version>
<PackageVersion>1.3.1</PackageVersion>
<AssemblyVersion>1.3.1</AssemblyVersion>
<FileVersion>1.3.1</FileVersion>
<PackageId>Flow.Launcher.Plugin</PackageId>
<Authors>Flow-Launcher</Authors>
<PackageLicenseExpression>MIT</PackageLicenseExpression>

View file

@ -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}

View file

@ -429,8 +429,8 @@
<TextBlock Grid.Row="0" Grid.ColumnSpan="2" Text="{Binding ActivatedTimes, Mode=OneWay}" FontSize="12" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="{DynamicResource website}"/>
<TextBlock Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left">
<Hyperlink NavigateUri="{Binding Github, Mode=OneWay}" RequestNavigate="OnRequestNavigate">
<Run Text="{Binding Github, Mode=OneWay}" />
<Hyperlink NavigateUri="{Binding Website, Mode=OneWay}" RequestNavigate="OnRequestNavigate">
<Run Text="{Binding Website, Mode=OneWay}" />
</Hyperlink>
</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Version" />

View file

@ -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<PluginViewModel> 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);

View file

@ -1,101 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<ProjectGuid>{F35190AA-4758-4D9E-A193-E3BDF6AD3567}</ProjectGuid>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Flow.Launcher.Plugin.Color</RootNamespace>
<AssemblyName>Flow.Launcher.Plugin.Color</AssemblyName>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.Color\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\Output\Release\Plugins\Flow.Launcher.Plugin.Color\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<None Include="Images\color.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="Languages\en.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Languages\zh-cn.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Languages\zh-tw.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Languages\de.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Languages\pl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Languages\tr.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -1,8 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_color_plugin_name">Farben</system:String>
<system:String x:Key="flowlauncher_plugin_color_plugin_description">Stellt eine HEX-Farben Vorschau bereit. (Versuche #000 in Flow Launcher)</system:String>
</ResourceDictionary>

View file

@ -1,8 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_color_plugin_name">Colors</system:String>
<system:String x:Key="flowlauncher_plugin_color_plugin_description">Allows to preview colors using hex values.(Try #000 in Flow Launcher)</system:String>
</ResourceDictionary>

View file

@ -1,8 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_color_plugin_name">Kolory</system:String>
<system:String x:Key="flowlauncher_plugin_color_plugin_description">Podgląd kolorów po wpisaniu ich kodu szesnastkowego. (Spróbuj wpisać #000 w oknie Flow Launchera)</system:String>
</ResourceDictionary>

View file

@ -1,8 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_color_plugin_name">Farby</system:String>
<system:String x:Key="flowlauncher_plugin_color_plugin_description">Zobrazuje náhľad farieb v HEX formáte. (Skúste #000 vo Flow Launcheri)</system:String>
</ResourceDictionary>

View file

@ -1,8 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_color_plugin_name">Renkler</system:String>
<system:String x:Key="flowlauncher_plugin_color_plugin_description">Hex kodunu girdiğiniz renkleri görüntülemeye yarar.(#000 yazmayı deneyin)</system:String>
</ResourceDictionary>

View file

@ -1,8 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_color_plugin_name">颜色</system:String>
<system:String x:Key="flowlauncher_plugin_color_plugin_description">提供在Flow Launcher查询hex颜色。(尝试在Flow Launcher中输入#000)</system:String>
</ResourceDictionary>

View file

@ -1,7 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_color_plugin_name">顏色</system:String>
<system:String x:Key="flowlauncher_plugin_color_plugin_description">提供在 Flow Launcher 查詢 hex 顏色。(試著在 Flow Launcher 中輸入 #000)</system:String>
</ResourceDictionary>

View file

@ -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<Result> Query(Query query)
{
var raw = query.Search;
if (!IsAvailable(raw)) return new List<Result>(0);
try
{
var cached = Find(raw);
if (cached.Length == 0)
{
var path = CreateImage(raw);
return new List<Result>
{
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<Result>(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");
}
}
}

View file

@ -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"
}

View file

@ -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<Result> LoadContextMenus(Result selectedResult)
{
// Open website
// Go to source code
// Report an issue?
// Request a feature?
return new List<Result>();
var pluginManifestInfo = selectedResult.ContextData as UserPlugin;
return new List<Result>
{
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;
}
}
};
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

View file

@ -11,6 +11,13 @@
<system:String x:Key="plugin_pluginsmanager_install_title">Plugin Install</system:String>
<system:String x:Key="plugin_pluginsmanager_uninstall_title">Plugin Uninstall</system:String>
<system:String x:Key="plugin_pluginsmanager_install_errormetadatafile">Install failed: unable to find the plugin.json metadata file from the new plugin</system:String>
<system:String x:Key="plugin_pluginsmanager_update_noresult_title">No update available</system:String>
<system:String x:Key="plugin_pluginsmanager_update_noresult_subtitle">All plugins are up to date</system:String>
<system:String x:Key="plugin_pluginsmanager_update_prompt">{0} by {1} {2}{3}Would you like to update this plugin? After the update Flow will automatically restart.</system:String>
<system:String x:Key="plugin_pluginsmanager_update_title">Plugin Update</system:String>
<system:String x:Key="plugin_pluginsmanager_update_exists">This plugin has an update, would you like to see it?</system:String>
<system:String x:Key="plugin_pluginsmanager_update_alreadyexists">This plugin is already installed</system:String>
<!--Controls-->
<!--Plugin Infos-->
@ -18,5 +25,13 @@
<system:String x:Key="plugin_pluginsmanager_plugin_description">Management of installing, uninstalling or updating Flow Launcher plugins</system:String>
<!--Context menu items-->
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_openwebsite_title">Open website</system:String>
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_openwebsite_subtitle">Visit the plugin's website</system:String>
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_gotosourcecode_title">See source code</system:String>
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_gotosourcecode_subtitle">See the plugin's source code</system:String>
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_newissue_title">Suggest an enhancement or submit an issue</system:String>
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_newissue_subtitle">Suggest an enhancement or submit an issue to the plugin developer</system:String>
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_pluginsmanifest_title">Go to Flow's plugins repository</system:String>
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_pluginsmanifest_subtitle">Visit the PluginsManifest repository to see comunity-made plugin submissions</system:String>
</ResourceDictionary>

View file

@ -1,9 +1,12 @@
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin.PluginsManager.ViewModels;
using Flow.Launcher.Plugin.PluginsManager.Views;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using Flow.Launcher.Infrastructure;
using System;
using System.Threading.Tasks;
namespace Flow.Launcher.Plugin.PluginsManager
{
@ -17,6 +20,10 @@ namespace Flow.Launcher.Plugin.PluginsManager
private IContextMenu contextMenu;
internal PluginsManager pluginManager;
private DateTime lastUpdateTime = 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<Result> 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()

View file

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

View file

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Flow.Launcher.Plugin.PluginsManager.Models
{
public class UserPlugin

View file

@ -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<Result> GetDefaultHotKeys()
{
return new List<Result>()
{
new Result()
{
Title = Settings.HotKeyInstall,
IcoPath = icoPath,
Action = _ =>
{
Context.API.ChangeQuery("pm install ");
return false;
}
},
new Result()
{
Title = Settings.HotkeyUninstall,
IcoPath = icoPath,
Action = _ =>
{
Context.API.ChangeQuery("pm uninstall ");
return false;
}
},
new Result()
{
Title = Settings.HotkeyUpdate,
IcoPath = icoPath,
Action = _ =>
{
Context.API.ChangeQuery("pm update ");
return false;
}
}
};
}
internal 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<Result> 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<Result>
{
new Result
{
Title = Context.API.GetTranslation("plugin_pluginsmanager_update_noresult_title"),
SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_update_noresult_subtitle"),
IcoPath = icoPath
}
};
var results = resultsForUpdate
.Select(x =>
new Result
{
Title = $"{x.Name} by {x.Author}",
SubTitle = $"Update from version {x.CurrentVersion} to {x.NewVersion}",
IcoPath = x.IcoPath,
Action = e =>
{
string message = string.Format(
Context.API.GetTranslation("plugin_pluginsmanager_update_prompt"),
x.Name, x.Author,
Environment.NewLine, Environment.NewLine);
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<Result> Search(List<Result> results, string searchName)
internal List<Result> Search(IEnumerable<Result> 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<Result> RequestInstallOrUpdate(string searchName)
{
var searchNameWithoutKeyword = searchName.Replace(Settings.HotKeyInstall, string.Empty).Trim();
var results =
pluginsManifest
.UserPlugins
.Select(x =>
new Result
{
Title = $"{x.Name} by {x.Author}",
SubTitle = x.Description,
IcoPath = icoPath,
Action = e =>
.UserPlugins
.Select(x =>
new Result
{
Application.Current.MainWindow.Hide();
InstallOrUpdate(x);
Title = $"{x.Name} by {x.Author}",
SubTitle = x.Description,
IcoPath = icoPath,
Action = e =>
{
Application.Current.MainWindow.Hide();
_ = InstallOrUpdate(x); // 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<Result> 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<Result> 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<Result>
{
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<Result>();
}
}
}

View file

@ -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";
}
}

View file

@ -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",

View file

@ -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<string>();

View file

@ -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<string>();
;
}
if (string.IsNullOrEmpty(result)) return new List<string>();
JContainer json;

View file

@ -45,7 +45,7 @@ Windows may complain about security due to code not being signed, this will be c
- Open flow's search window: <kbd>Alt</kbd>+<kbd>Space</kbd> is the default hotkey.
- Open context menu: <kbd>Ctrl</kbd>+<kbd>O</kbd>/<kbd>Shift</kbd>+<kbd>Enter</kbd>.
- Cancel/Return to previous screen: <kbd>Esc</kbd>.
- 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-<VersionOfYourFlowLauncher>\UserData`

View file

@ -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
}

View file

@ -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")]
[assembly: AssemblyVersion("1.6.0")]
[assembly: AssemblyFileVersion("1.6.0")]
[assembly: AssemblyInformationalVersion("1.6.0")]

View file

@ -1,4 +1,4 @@
version: '1.5.0.{build}'
version: '1.6.0.{build}'
init:
- ps: |