Aanpassingen autoupdate & async code

This commit is contained in:
Fre
2020-07-08 11:16:00 +02:00
parent f1ceaa208e
commit 4e9de2abfa
12 changed files with 404 additions and 29 deletions

View File

@@ -407,6 +407,27 @@ namespace ElectronNET.API
/// which will be preferred over name by Electron.
/// </summary>
public string Name
{
[Obsolete("Use the asynchronous version NameAsync instead")]
get
{
return AsyncHelper.RunSync(async () => await NameAsync);
}
set
{
BridgeConnector.Socket.Emit("appSetName", value);
}
}
/// <summary>
/// A <see cref="string"/> property that indicates the current application's name, which is the name in the
/// application's package.json file.
///
/// Usually the name field of package.json is a short lowercase name, according to the npm modules spec. You
/// should usually also specify a productName field, which is your application's full capitalized name, and
/// which will be preferred over name by Electron.
/// </summary>
public Task<string> NameAsync
{
get
{
@@ -423,14 +444,34 @@ namespace ElectronNET.API
BridgeConnector.Socket.Emit("appGetName");
return taskCompletionSource.Task;
}).Result;
}
set
{
BridgeConnector.Socket.Emit("appSetName", value);
});
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public Task<string> GetName()
{
var taskCompletionSource = new TaskCompletionSource<string>();
BridgeConnector.Socket.On("appGetNameCompleted", (result) =>
{
BridgeConnector.Socket.Off("appGetNameCompleted");
taskCompletionSource.SetResult((string)result);
});
BridgeConnector.Socket.Emit("appGetName");
return taskCompletionSource.Task;
}
internal App()
{
CommandLine = new CommandLine();
@@ -1479,6 +1520,27 @@ namespace ElectronNET.API
/// is used.
/// </summary>
public string UserAgentFallback
{
[Obsolete("Use the asynchronous version UserAgentFallbackAsync instead")]
get
{
return AsyncHelper.RunSync(async () => await UserAgentFallbackAsync);
}
set
{
BridgeConnector.Socket.Emit("appSetUserAgentFallback", value);
}
}
/// <summary>
/// A <see cref="string"/> which is the user agent string Electron will use as a global fallback.
/// <para/>
/// This is the user agent that will be used when no user agent is set at the webContents or
/// session level. It is useful for ensuring that your entire app has the same user agent. Set to a
/// custom value as early as possible in your app's initialization to ensure that your overridden value
/// is used.
/// </summary>
public Task<string> UserAgentFallbackAsync
{
get
{
@@ -1487,19 +1549,15 @@ namespace ElectronNET.API
var taskCompletionSource = new TaskCompletionSource<string>();
BridgeConnector.Socket.On("appGetUserAgentFallbackCompleted", (result) =>
{
BridgeConnector.Socket.Off("appGetUserAgentFallbackCompleted");
taskCompletionSource.SetResult((string)result);
});
{
BridgeConnector.Socket.Off("appGetUserAgentFallbackCompleted");
taskCompletionSource.SetResult((string)result);
});
BridgeConnector.Socket.Emit("appGetUserAgentFallback");
return taskCompletionSource.Task;
}).Result;
}
set
{
BridgeConnector.Socket.Emit("appSetUserAgentFallback", value);
});
}
}

View File

@@ -0,0 +1,46 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ElectronNET.API
{
internal static class AsyncHelper
{
private static readonly TaskFactory _taskFactory = new
TaskFactory(CancellationToken.None,
TaskCreationOptions.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
=> _taskFactory
.StartNew(func)
.Unwrap()
.GetAwaiter()
.GetResult();
public static void RunSync(Func<Task> func)
=> _taskFactory
.StartNew(func)
.Unwrap()
.GetAwaiter()
.GetResult();
public static async Task<TResult> TimeoutAfter<TResult>(this Task<TResult> task, TimeSpan timeout)
{
using (var timeoutCancellationTokenSource = new CancellationTokenSource())
{
var completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token));
if (completedTask == task)
{
timeoutCancellationTokenSource.Cancel();
return await task; // Very important in order to propagate exceptions
}
else
{
throw new TimeoutException($"{nameof(TimeoutAfter)}: The operation has timed out after {timeout:mm\\:ss}");
}
}
}
}
}

View File

@@ -1,6 +1,12 @@
using ElectronNET.API.Entities;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
namespace ElectronNET.API
@@ -182,11 +188,48 @@ namespace ElectronNET.API
}
}
/// <summary>
/// The current application version
/// </summary>
public Task<SemVer> CurrentVersionAsync
{
get
{
return Task.Run<SemVer>(() =>
{
var taskCompletionSource = new TaskCompletionSource<SemVer>();
BridgeConnector.Socket.On("autoUpdater-currentVersion-get-reply", (result) =>
{
BridgeConnector.Socket.Off("autoUpdater-currentVersion-get-reply");
SemVer version = ((JObject)result).ToObject<SemVer>();
taskCompletionSource.SetResult(version);
});
BridgeConnector.Socket.Emit("autoUpdater-currentVersion-get");
return taskCompletionSource.Task;
});
}
}
/// <summary>
/// Get the update channel. Not applicable for GitHub.
/// Doesnt return channel from the update configuration, only if was previously set.
/// </summary>
[Obsolete("Use the asynchronous version ChannelAsync instead")]
public string Channel
{
get
{
return AsyncHelper.RunSync(async () => await ChannelAsync);
}
}
/// <summary>
/// Get the update channel. Not applicable for GitHub.
/// Doesnt return channel from the update configuration, only if was previously set.
/// </summary>
public Task<string> ChannelAsync
{
get
{
@@ -199,18 +242,52 @@ namespace ElectronNET.API
BridgeConnector.Socket.Off("autoUpdater-channel-get-reply");
taskCompletionSource.SetResult(result.ToString());
});
BridgeConnector.Socket.Emit("autoUpdater-channel-get");
return taskCompletionSource.Task;
}).Result;
});
}
}
/// <summary>
/// The request headers.
/// </summary>
public Task<Dictionary<string, string>> RequestHeadersAsync
{
get
{
return Task.Run(() =>
{
var taskCompletionSource = new TaskCompletionSource<Dictionary<string, string>>();
BridgeConnector.Socket.On("autoUpdater-requestHeaders-get-reply", (headers) =>
{
BridgeConnector.Socket.Off("autoUpdater-requestHeaders-get-reply");
Dictionary<string, string> result = ((JObject)headers).ToObject<Dictionary<string, string>>();
taskCompletionSource.SetResult(result);
});
BridgeConnector.Socket.Emit("autoUpdater-requestHeaders-get");
return taskCompletionSource.Task;
});
}
}
/// <summary>
/// Emitted when there is an error while updating.
/// The request headers.
/// </summary>
public event Action<string> OnError
public Dictionary<string, string> RequestHeaders
{
set
{
BridgeConnector.Socket.Emit("autoUpdater-requestHeaders-set", JObject.FromObject(value, _jsonSerializer));
}
}
/// <summary>
/// Emitted when there is an error while updating.
/// </summary>
public event Action<string> OnError
{
add
{
@@ -416,9 +493,26 @@ namespace ElectronNET.API
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("autoUpdaterCheckForUpdatesComplete" + guid, (updateCheckResult) =>
{
try
{
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesComplete" + guid);
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesError" + guid);
taskCompletionSource.SetResult(JObject.Parse(updateCheckResult.ToString()).ToObject<UpdateCheckResult>());
}
catch (Exception ex)
{
taskCompletionSource.SetException(ex);
}
});
BridgeConnector.Socket.On("autoUpdaterCheckForUpdatesError" + guid, (error) =>
{
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesComplete" + guid);
taskCompletionSource.SetResult(JObject.Parse(updateCheckResult.ToString()).ToObject<UpdateCheckResult>());
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesError" + guid);
string message = "An error occurred in CheckForUpdatesAsync";
if (error != null && !string.IsNullOrEmpty(error.ToString()))
message = JsonConvert.SerializeObject(error);
taskCompletionSource.SetException(new ElectronException(message));
});
BridgeConnector.Socket.Emit("autoUpdaterCheckForUpdates", guid);
@@ -438,9 +532,29 @@ namespace ElectronNET.API
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid, (updateCheckResult) =>
{
try
{
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid);
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesAndNotifyError" + guid);
if (updateCheckResult == null)
taskCompletionSource.SetResult(null);
else
taskCompletionSource.SetResult(JObject.Parse(updateCheckResult.ToString()).ToObject<UpdateCheckResult>());
}
catch (Exception ex)
{
taskCompletionSource.SetException(ex);
}
});
BridgeConnector.Socket.On("autoUpdaterCheckForUpdatesAndNotifyError" + guid, (error) =>
{
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid);
taskCompletionSource.SetResult(JObject.Parse(updateCheckResult.ToString()).ToObject<UpdateCheckResult>());
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesAndNotifyError" + guid);
string message = "An error occurred in autoUpdaterCheckForUpdatesAndNotify";
if (error != null)
message = JsonConvert.SerializeObject(error);
taskCompletionSource.SetException(new ElectronException(message));
});
BridgeConnector.Socket.Emit("autoUpdaterCheckForUpdatesAndNotify", guid);
@@ -501,5 +615,10 @@ namespace ElectronNET.API
return taskCompletionSource.Task;
}
private readonly JsonSerializer _jsonSerializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
}
}

View File

@@ -23,6 +23,14 @@ namespace ElectronNET.API
{
Console.WriteLine("BridgeConnector connected!");
});
_socket.On(Socket.EVENT_CONNECT_ERROR, (args) =>
{
Console.WriteLine("Socket error! {0}", args??"no args");
});
_socket.On(Socket.EVENT_DISCONNECT, (args) =>
{
Console.WriteLine("Socket Disconnect! {0}", args ?? "no args");
});
}
}
}

View File

@@ -40,6 +40,7 @@ This package contains the API to access the "native" electron API.</Description>
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="SocketIoClientDotNet" Version="1.0.5" />
<PackageReference Include="System.Drawing.Common" Version="4.7.0" />
</ItemGroup>

View File

@@ -0,0 +1,28 @@
using System;
namespace ElectronNET.API.Entities
{
/// <summary>
/// Electron Exception
/// </summary>
[Serializable]
public class ElectronException : Exception
{
/// <summary>
///
/// </summary>
public ElectronException()
{
}
/// <summary>
///
/// </summary>
/// <param name="error"></param>
public ElectronException(string error) : base(error)
{
}
}
}

View File

@@ -0,0 +1,59 @@
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
public class SemVer
{
/// <summary>
///
/// </summary>
public string Raw { get; set; }
/// <summary>
///
/// </summary>
public bool Loose { get; set; }
/// <summary>
///
/// </summary>
public SemVerOptions Options { get; set; }
/// <summary>
///
/// </summary>
public int Major { get; set; }
/// <summary>
///
/// </summary>
public int Minor { get; set; }
/// <summary>
///
/// </summary>
public int Patch { get; set; }
/// <summary>
///
/// </summary>
public string Version { get; set; }
/// <summary>
///
/// </summary>
public string[] Build { get; set; }
/// <summary>
///
/// </summary>
public string[] Prerelease { get; set; }
}
/// <summary>
///
/// </summary>
public class SemVerOptions {
/// <summary>
///
/// </summary>
public bool? Loose { get; set; }
/// <summary>
///
/// </summary>
public bool? IncludePrerelease { get; set; }
}
}

View File

@@ -1,4 +1,6 @@
namespace ElectronNET.API
using Quobject.SocketIoClientDotNet.Client;
namespace ElectronNET.API
{
/// <summary>
///
@@ -18,5 +20,10 @@
return !string.IsNullOrEmpty(BridgeSettings.SocketPort);
}
}
/// <summary>
///
/// </summary>
public static Socket Socket { get { return BridgeConnector.Socket; } }
}
}