using ElectronNET.API.Entities; using ElectronNET.API.Serialization; using ElectronNET.Common; using System; using System.Collections.Generic; using System.Text.Json; using System.Threading.Tasks; // ReSharper disable InconsistentNaming namespace ElectronNET.API { /// /// Enable apps to automatically update themselves. Based on electron-updater. /// public sealed class AutoUpdater { /// /// Whether to automatically download an update when it is found. (Default is true) /// public bool AutoDownload { get { return Task.Run(() => { var tcs = new TaskCompletionSource(); BridgeConnector.Socket.Once("autoUpdater-autoDownload-get-reply", tcs.SetResult); BridgeConnector.Socket.Emit("autoUpdater-autoDownload-get"); return tcs.Task; }).Result; } set { BridgeConnector.Socket.Emit("autoUpdater-autoDownload-set", value); } } /// /// Whether to automatically install a downloaded update on app quit (if `QuitAndInstall` was not called before). /// /// Applicable only on Windows and Linux. /// public bool AutoInstallOnAppQuit { get { return Task.Run(() => { var tcs = new TaskCompletionSource(); BridgeConnector.Socket.Once("autoUpdater-autoInstallOnAppQuit-get-reply", tcs.SetResult); BridgeConnector.Socket.Emit("autoUpdater-autoInstallOnAppQuit-get"); return tcs.Task; }).Result; } set { BridgeConnector.Socket.Emit("autoUpdater-autoInstallOnAppQuit-set", value); } } /// /// *GitHub provider only.* Whether to allow update to pre-release versions. /// Defaults to "true" if application version contains prerelease components (e.g. "0.12.1-alpha.1", here "alpha" is a prerelease component), otherwise "false". /// /// If "true", downgrade will be allowed("allowDowngrade" will be set to "true"). /// public bool AllowPrerelease { get { return Task.Run(() => { var tcs = new TaskCompletionSource(); BridgeConnector.Socket.Once("autoUpdater-allowPrerelease-get-reply", tcs.SetResult); BridgeConnector.Socket.Emit("autoUpdater-allowPrerelease-get"); return tcs.Task; }).Result; } set { BridgeConnector.Socket.Emit("autoUpdater-allowPrerelease-set", value); } } /// /// *GitHub provider only.* /// Get all release notes (from current version to latest), not just the latest (Default is false). /// public bool FullChangelog { get { return Task.Run(() => { var tcs = new TaskCompletionSource(); BridgeConnector.Socket.Once("autoUpdater-fullChangelog-get-reply", tcs.SetResult); BridgeConnector.Socket.Emit("autoUpdater-fullChangelog-get"); return tcs.Task; }).Result; } set { BridgeConnector.Socket.Emit("autoUpdater-fullChangelog-set", value); } } /// /// Whether to allow version downgrade (when a user from the beta channel wants to go back to the stable channel). /// Taken in account only if channel differs (pre-release version component in terms of semantic versioning). /// Default is false. /// public bool AllowDowngrade { get { return Task.Run(() => { var tcs = new TaskCompletionSource(); BridgeConnector.Socket.Once("autoUpdater-allowDowngrade-get-reply", tcs.SetResult); BridgeConnector.Socket.Emit("autoUpdater-allowDowngrade-get"); return tcs.Task; }).Result; } set { BridgeConnector.Socket.Emit("autoUpdater-allowDowngrade-set", value); } } /// /// For test only. /// public string UpdateConfigPath { get { return Task.Run(() => { var tcs = new TaskCompletionSource(); BridgeConnector.Socket.Once("autoUpdater-updateConfigPath-get-reply", tcs.SetResult); BridgeConnector.Socket.Emit("autoUpdater-updateConfigPath-get"); return tcs.Task; }).Result; } } /// /// The current application version /// public Task CurrentVersionAsync { get { return Task.Run(() => { var tcs = new TaskCompletionSource(); BridgeConnector.Socket.Once("autoUpdater-currentVersion-get-reply", tcs.SetResult); BridgeConnector.Socket.Emit("autoUpdater-currentVersion-get"); return tcs.Task; }); } } /// /// Get the update channel. Not applicable for GitHub. /// Doesn’t return channel from the update configuration, only if was previously set. /// [Obsolete("Use the asynchronous version ChannelAsync instead")] public string Channel { get { return ChannelAsync.Result; } } /// /// Get the update channel. Not applicable for GitHub. /// Doesn’t return channel from the update configuration, only if was previously set. /// public Task ChannelAsync { get { return Task.Run(() => { var tcs = new TaskCompletionSource(); BridgeConnector.Socket.Once("autoUpdater-channel-get-reply", tcs.SetResult); BridgeConnector.Socket.Emit("autoUpdater-channel-get"); return tcs.Task; }); } } /// /// The request headers. /// public Task> RequestHeadersAsync { get { return Task.Run(() => { var tcs = new TaskCompletionSource>(); BridgeConnector.Socket.Once>("autoUpdater-requestHeaders-get-reply", tcs.SetResult); BridgeConnector.Socket.Emit("autoUpdater-requestHeaders-get"); return tcs.Task; }); } } /// /// The request headers. /// public Dictionary RequestHeaders { set { BridgeConnector.Socket.Emit("autoUpdater-requestHeaders-set", value); } } /// /// Emitted when there is an error while updating. /// public event Action OnError { add => ApiEventManager.AddEvent("autoUpdater-error", GetHashCode(), _error, value, (args) => args.ToString()); remove => ApiEventManager.RemoveEvent("autoUpdater-error", GetHashCode(), _error, value); } private event Action _error; /// /// Emitted when checking if an update has started. /// public event Action OnCheckingForUpdate { add => ApiEventManager.AddEvent("autoUpdater-checking-for-update", GetHashCode(), _checkingForUpdate, value); remove => ApiEventManager.RemoveEvent("autoUpdater-checking-for-update", GetHashCode(), _checkingForUpdate, value); } private event Action _checkingForUpdate; /// /// Emitted when there is an available update. /// The update is downloaded automatically if AutoDownload is true. /// public event Action OnUpdateAvailable { add => ApiEventManager.AddEvent("autoUpdater-update-available", GetHashCode(), _updateAvailable, value, (args) => args.Deserialize(ElectronJsonContext.Default.UpdateInfo)); remove => ApiEventManager.RemoveEvent("autoUpdater-update-available", GetHashCode(), _updateAvailable, value); } private event Action _updateAvailable; /// /// Emitted when there is no available update. /// public event Action OnUpdateNotAvailable { add => ApiEventManager.AddEvent("autoUpdater-update-not-available", GetHashCode(), _updateNotAvailable, value, (args) => args.Deserialize(ElectronJsonContext.Default.UpdateInfo)); remove => ApiEventManager.RemoveEvent("autoUpdater-update-not-available", GetHashCode(), _updateNotAvailable, value); } private event Action _updateNotAvailable; /// /// Emitted on download progress. /// public event Action OnDownloadProgress { add => ApiEventManager.AddEvent("autoUpdater-download-progress", GetHashCode(), _downloadProgress, value, (args) => args.Deserialize(ElectronJsonContext.Default.ProgressInfo)); remove => ApiEventManager.RemoveEvent("autoUpdater-download-progress", GetHashCode(), _downloadProgress, value); } private event Action _downloadProgress; /// /// Emitted on download complete. /// public event Action OnUpdateDownloaded { add => ApiEventManager.AddEvent("autoUpdater-update-downloaded", GetHashCode(), _updateDownloaded, value, (args) => args.Deserialize(ElectronJsonContext.Default.UpdateInfo)); remove => ApiEventManager.RemoveEvent("autoUpdater-update-downloaded", GetHashCode(), _updateDownloaded, value); } private event Action _updateDownloaded; private static AutoUpdater _autoUpdater; private static object _syncRoot = new object(); internal AutoUpdater() { } internal static AutoUpdater Instance { get { if (_autoUpdater == null) { lock (_syncRoot) { if (_autoUpdater == null) { _autoUpdater = new AutoUpdater(); } } } return _autoUpdater; } } /// /// Asks the server whether there is an update. /// /// public Task CheckForUpdatesAsync() { var taskCompletionSource = new TaskCompletionSource(); string guid = Guid.NewGuid().ToString(); BridgeConnector.Socket.Once("autoUpdaterCheckForUpdatesComplete" + guid, (result) => { try { BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesError" + guid); taskCompletionSource.SetResult(result); } catch (Exception ex) { taskCompletionSource.SetException(ex); } }); BridgeConnector.Socket.Once("autoUpdaterCheckForUpdatesError" + guid, (result) => { BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesComplete" + guid); string message = "An error occurred in CheckForUpdatesAsync"; if (!string.IsNullOrEmpty(result)) message = result; taskCompletionSource.SetException(new Exception(message)); }); BridgeConnector.Socket.Emit("autoUpdaterCheckForUpdates", guid); return taskCompletionSource.Task; } /// /// Asks the server whether there is an update. /// /// This will immediately download an update, then install when the app quits. /// /// public Task CheckForUpdatesAndNotifyAsync() { var taskCompletionSource = new TaskCompletionSource(); string guid = Guid.NewGuid().ToString(); BridgeConnector.Socket.Once("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid, (result) => { try { BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesAndNotifyError" + guid); taskCompletionSource.SetResult(result); } catch (Exception ex) { taskCompletionSource.SetException(ex); } }); BridgeConnector.Socket.Once("autoUpdaterCheckForUpdatesAndNotifyError" + guid, (result) => { BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid); string message = "An error occurred in autoUpdaterCheckForUpdatesAndNotify"; if (!string.IsNullOrEmpty(result)) message = result; taskCompletionSource.SetException(new Exception(message)); }); BridgeConnector.Socket.Emit("autoUpdaterCheckForUpdatesAndNotify", guid); return taskCompletionSource.Task; } /// /// Restarts the app and installs the update after it has been downloaded. /// It should only be called after `update-downloaded` has been emitted. /// /// Note: QuitAndInstall() will close all application windows first and only emit `before-quit` event on `app` after that. /// This is different from the normal quit event sequence. /// /// *windows-only* Runs the installer in silent mode. Defaults to `false`. /// Run the app after finish even on silent install. Not applicable for macOS. Ignored if `isSilent` is set to `false`. public void QuitAndInstall(bool isSilent = false, bool isForceRunAfter = false) { BridgeConnector.Socket.Emit("autoUpdaterQuitAndInstall", isSilent, isForceRunAfter); } /// /// Start downloading update manually. You can use this method if "AutoDownload" option is set to "false". /// /// Path to downloaded file. public Task DownloadUpdateAsync() { var tcs = new TaskCompletionSource(); string guid = Guid.NewGuid().ToString(); BridgeConnector.Socket.Once("autoUpdaterDownloadUpdateComplete" + guid, tcs.SetResult); BridgeConnector.Socket.Emit("autoUpdaterDownloadUpdate", guid); return tcs.Task; } /// /// Feed URL. /// /// Feed URL. public Task GetFeedURLAsync() { var tcs = new TaskCompletionSource(); string guid = Guid.NewGuid().ToString(); BridgeConnector.Socket.Once("autoUpdaterGetFeedURLComplete" + guid, tcs.SetResult); BridgeConnector.Socket.Emit("autoUpdaterGetFeedURL", guid); return tcs.Task; } } }