diff --git a/ElectronNET.API/Entities/NotificationAction.cs b/ElectronNET.API/Entities/NotificationAction.cs new file mode 100644 index 0000000..25bee11 --- /dev/null +++ b/ElectronNET.API/Entities/NotificationAction.cs @@ -0,0 +1,15 @@ +namespace ElectronNET.API.Entities +{ + public class NotificationAction + { + /// + /// The label for the given action. + /// + public string Text { get; set; } + + /// + /// The type of action, can be button. + /// + public string Type { get; set; } + } +} \ No newline at end of file diff --git a/ElectronNET.API/Entities/NotificationOptions.cs b/ElectronNET.API/Entities/NotificationOptions.cs index efa127f..f2fc91c 100644 --- a/ElectronNET.API/Entities/NotificationOptions.cs +++ b/ElectronNET.API/Entities/NotificationOptions.cs @@ -1,4 +1,7 @@ -namespace ElectronNET.API.Entities +using Newtonsoft.Json; +using System; + +namespace ElectronNET.API.Entities { public class NotificationOptions { @@ -44,6 +47,63 @@ /// public string Sound { get; set; } + /// + /// Actions to add to the notification. Please read the available actions and + /// limitations in the NotificationAction documentation + /// + public NotificationAction Actions { get; set; } + + /// + /// Emitted when the notification is shown to the user, + /// note this could be fired multiple times as a notification + /// can be shown multiple times through the Show() method. + /// + [JsonIgnore] + public Action OnShow { get; set; } + + [JsonProperty] + internal string ShowID { get; set; } + + /// + /// Emitted when the notification is clicked by the user. + /// + [JsonIgnore] + public Action OnClick { get; set; } + + [JsonProperty] + internal string ClickID { get; set; } + + /// + /// Emitted when the notification is closed by manual intervention from the user. + /// + /// This event is not guarunteed to be emitted in all cases where the notification is closed. + /// + [JsonIgnore] + public Action OnClose { get; set; } + + [JsonProperty] + internal string CloseID { get; set; } + + /// + /// macOS only: Emitted when the user clicks the “Reply” button on a notification with hasReply: true. + /// + /// The string the user entered into the inline reply field + /// + [JsonIgnore] + public Action OnReply { get; set; } + + [JsonProperty] + internal string ReplyID { get; set; } + + /// + /// macOS only - The index of the action that was activated + /// + [JsonIgnore] + public Action OnAction { get; set; } + + [JsonProperty] + internal string ActionID { get; set; } + public NotificationOptions(string title, string body) { Title = title; diff --git a/ElectronNET.API/Notification.cs b/ElectronNET.API/Notification.cs index ad396d0..b7fd081 100644 --- a/ElectronNET.API/Notification.cs +++ b/ElectronNET.API/Notification.cs @@ -2,6 +2,10 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; namespace ElectronNET.API { @@ -24,11 +28,105 @@ namespace ElectronNET.API } } + private static List _notificationOptions = new List(); + + /// + /// Create OS desktop notifications + /// + /// public void Show(NotificationOptions notificationOptions) { + GenerateIDsForDefinedActions(notificationOptions); + BridgeConnector.Socket.Emit("createNotification", JObject.FromObject(notificationOptions, _jsonSerializer)); } + private static void GenerateIDsForDefinedActions(NotificationOptions notificationOptions) + { + bool isActionDefined = false; + + if (notificationOptions.OnShow != null) + { + notificationOptions.ShowID = Guid.NewGuid().ToString(); + isActionDefined = true; + + BridgeConnector.Socket.Off("NotificationEventShow"); + BridgeConnector.Socket.On("NotificationEventShow", (id) => { + _notificationOptions.Single(x => x.ShowID == id.ToString()).OnShow(); + }); + } + + if (notificationOptions.OnClick != null) + { + notificationOptions.ClickID = Guid.NewGuid().ToString(); + isActionDefined = true; + + BridgeConnector.Socket.Off("NotificationEventClick"); + BridgeConnector.Socket.On("NotificationEventClick", (id) => { + _notificationOptions.Single(x => x.ClickID == id.ToString()).OnClick(); + }); + } + + if (notificationOptions.OnClose != null) + { + notificationOptions.CloseID = Guid.NewGuid().ToString(); + isActionDefined = true; + + BridgeConnector.Socket.Off("NotificationEventClose"); + BridgeConnector.Socket.On("NotificationEventClose", (id) => { + _notificationOptions.Single(x => x.CloseID == id.ToString()).OnClose(); + }); + } + + if (notificationOptions.OnReply != null) + { + notificationOptions.ReplyID = Guid.NewGuid().ToString(); + isActionDefined = true; + + BridgeConnector.Socket.Off("NotificationEventReply"); + BridgeConnector.Socket.On("NotificationEventReply", (args) => { + var arguments = ((JArray)args).ToObject(); + _notificationOptions.Single(x => x.ReplyID == arguments[0].ToString()).OnReply(arguments[1].ToString()); + }); + } + + if (notificationOptions.OnAction != null) + { + notificationOptions.ActionID = Guid.NewGuid().ToString(); + isActionDefined = true; + + BridgeConnector.Socket.Off("NotificationEventAction"); + BridgeConnector.Socket.On("NotificationEventAction", (args) => { + var arguments = ((JArray)args).ToObject(); + _notificationOptions.Single(x => x.ReplyID == arguments[0].ToString()).OnAction(arguments[1].ToString()); + }); + } + + if (isActionDefined) + { + _notificationOptions.Add(notificationOptions); + } + } + + /// + /// Whether or not desktop notifications are supported on the current system. + /// + /// + public Task IsSupportedAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("notificationIsSupportedComplete", (isSupported) => + { + BridgeConnector.Socket.Off("notificationIsSupportedComplete"); + taskCompletionSource.SetResult((bool)isSupported); + }); + + BridgeConnector.Socket.Emit("notificationIsSupported"); + + return taskCompletionSource.Task; + } + private JsonSerializer _jsonSerializer = new JsonSerializer() { ContractResolver = new CamelCasePropertyNamesContractResolver(), diff --git a/ElectronNET.Host/api/notification.js b/ElectronNET.Host/api/notification.js index 8ce83a7..020a153 100644 --- a/ElectronNET.Host/api/notification.js +++ b/ElectronNET.Host/api/notification.js @@ -1,10 +1,49 @@ "use strict"; exports.__esModule = true; var electron_1 = require("electron"); +var notifications = []; module.exports = function (socket) { socket.on('createNotification', function (options) { var notification = new electron_1.Notification(options); + var haveEvent = false; + if (options.showID) { + haveEvent = true; + notification.on('show', function () { + socket.emit('NotificationEventShow', options.showID); + }); + } + if (options.clickID) { + haveEvent = true; + notification.on('click', function () { + socket.emit('NotificationEventClick', options.clickID); + }); + } + if (options.closeID) { + haveEvent = true; + notification.on('close', function () { + socket.emit('NotificationEventClose', options.closeID); + }); + } + if (options.replyID) { + haveEvent = true; + notification.on('reply', function (event, value) { + socket.emit('NotificationEventReply', [options.replyID, value]); + }); + } + if (options.actionID) { + haveEvent = true; + notification.on('action', function (event, value) { + socket.emit('NotificationEventAction', [options.actionID, value]); + }); + } + if (haveEvent) { + notifications.push(notification); + } notification.show(); }); + socket.on('notificationIsSupported', function (options) { + var isSupported = electron_1.Notification.isSupported; + socket.emit('notificationIsSupportedComplete', isSupported); + }); }; //# sourceMappingURL=notification.js.map \ No newline at end of file diff --git a/ElectronNET.Host/api/notification.js.map b/ElectronNET.Host/api/notification.js.map index b7d3ab1..85c75b9 100644 --- a/ElectronNET.Host/api/notification.js.map +++ b/ElectronNET.Host/api/notification.js.map @@ -1 +1 @@ -{"version":3,"file":"notification.js","sourceRoot":"","sources":["notification.ts"],"names":[],"mappings":";;AAAA,qCAAwC;AAExC,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,oBAAoB,EAAE,UAAC,OAAO;QACpC,IAAM,YAAY,GAAG,IAAI,uBAAY,CAAC,OAAO,CAAC,CAAC;QAC/C,YAAY,CAAC,IAAI,EAAE,CAAC;IACxB,CAAC,CAAC,CAAC;AACP,CAAC,CAAA"} \ No newline at end of file +{"version":3,"file":"notification.js","sourceRoot":"","sources":["notification.ts"],"names":[],"mappings":";;AAAA,qCAAwC;AACxC,IAAM,aAAa,GAA4B,EAAE,CAAC;AAElD,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,oBAAoB,EAAE,UAAC,OAAO;QACpC,IAAM,YAAY,GAAG,IAAI,uBAAY,CAAC,OAAO,CAAC,CAAC;QAC/C,IAAI,SAAS,GAAG,KAAK,CAAC;QAEtB,EAAE,CAAA,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;YAChB,SAAS,GAAG,IAAI,CAAC;YACjB,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE;gBACpB,MAAM,CAAC,IAAI,CAAC,uBAAuB,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;YACzD,CAAC,CAAC,CAAC;QACP,CAAC;QAED,EAAE,CAAA,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;YACjB,SAAS,GAAG,IAAI,CAAC;YACjB,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE;gBACrB,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;YAC3D,CAAC,CAAC,CAAC;QACP,CAAC;QAED,EAAE,CAAA,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;YACjB,SAAS,GAAG,IAAI,CAAC;YACjB,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE;gBACrB,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;YAC3D,CAAC,CAAC,CAAC;QACP,CAAC;QAED,EAAE,CAAA,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;YACjB,SAAS,GAAG,IAAI,CAAC;YACjB,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,UAAC,KAAK,EAAE,KAAK;gBAClC,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;YACpE,CAAC,CAAC,CAAC;QACP,CAAC;QAED,EAAE,CAAA,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;YAClB,SAAS,GAAG,IAAI,CAAC;YACjB,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,UAAC,KAAK,EAAE,KAAK;gBACnC,MAAM,CAAC,IAAI,CAAC,yBAAyB,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;YACtE,CAAC,CAAC,CAAC;QACP,CAAC;QAED,EAAE,CAAA,CAAC,SAAS,CAAC,CAAC,CAAC;YACX,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACrC,CAAC;QAED,YAAY,CAAC,IAAI,EAAE,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,yBAAyB,EAAE,UAAC,OAAO;QACzC,IAAM,WAAW,GAAG,uBAAY,CAAC,WAAW,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,iCAAiC,EAAE,WAAW,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;AACP,CAAC,CAAA"} \ No newline at end of file diff --git a/ElectronNET.Host/api/notification.ts b/ElectronNET.Host/api/notification.ts index 2264373..b388a4e 100644 --- a/ElectronNET.Host/api/notification.ts +++ b/ElectronNET.Host/api/notification.ts @@ -1,8 +1,55 @@ import { Notification } from "electron"; +const notifications: Electron.Notification[] = []; module.exports = (socket: SocketIO.Server) => { socket.on('createNotification', (options) => { const notification = new Notification(options); + let haveEvent = false; + + if(options.showID) { + haveEvent = true; + notification.on('show', () => { + socket.emit('NotificationEventShow', options.showID); + }); + } + + if(options.clickID) { + haveEvent = true; + notification.on('click', () => { + socket.emit('NotificationEventClick', options.clickID); + }); + } + + if(options.closeID) { + haveEvent = true; + notification.on('close', () => { + socket.emit('NotificationEventClose', options.closeID); + }); + } + + if(options.replyID) { + haveEvent = true; + notification.on('reply', (event, value) => { + socket.emit('NotificationEventReply', [options.replyID, value]); + }); + } + + if(options.actionID) { + haveEvent = true; + notification.on('action', (event, value) => { + socket.emit('NotificationEventAction', [options.actionID, value]); + }); + } + + if(haveEvent) { + notifications.push(notification); + } + notification.show(); }); + + socket.on('notificationIsSupported', (options) => { + const isSupported = Notification.isSupported; + socket.emit('notificationIsSupportedComplete', isSupported); + }); } \ No newline at end of file diff --git a/ElectronNET.WebApp/Controllers/HomeController.cs b/ElectronNET.WebApp/Controllers/HomeController.cs index 12792a1..e939029 100644 --- a/ElectronNET.WebApp/Controllers/HomeController.cs +++ b/ElectronNET.WebApp/Controllers/HomeController.cs @@ -11,7 +11,13 @@ namespace ElectronNET.WebApp.Controllers { Electron.IpcMain.On("SayHello", async (args) => { - Electron.Notification.Show(new NotificationOptions("Hallo Robert", "Nachricht von ASP.NET Core App")); + Electron.Notification.Show(new NotificationOptions("Hallo Robert", "Nachricht von ASP.NET Core App") { + OnClick = async () => { await Electron.Dialog.ShowMessageBoxAsync(new MessageBoxOptions("Notification clicked")); }, + OnShow = async () => { await Electron.Dialog.ShowMessageBoxAsync(new MessageBoxOptions("Notification show")); }, + OnClose = async () => { await Electron.Dialog.ShowMessageBoxAsync(new MessageBoxOptions("Notification closed")); }, + OnAction = async (value) => { await Electron.Dialog.ShowMessageBoxAsync(new MessageBoxOptions("Notification Action")); }, + OnReply = async (value) => { await Electron.Dialog.ShowMessageBoxAsync(new MessageBoxOptions("Notification Reply")); } + }); Electron.IpcMain.Send(Electron.WindowManager.BrowserWindows.First(), "Goodbye", "Elephant!"); var currentBrowserWindow = Electron.WindowManager.BrowserWindows.First(); @@ -28,6 +34,8 @@ namespace ElectronNET.WebApp.Controllers Electron.IpcMain.On("GetPath", async (args) => { + Electron.Notification.Show(new NotificationOptions("test", "test2")); + var currentBrowserWindow = Electron.WindowManager.BrowserWindows.First(); string pathName = await Electron.App.GetPathAsync(PathName.pictures);