implement full Notification-API

This commit is contained in:
Gregor Biswanger
2017-10-18 03:49:34 +02:00
parent 072a24d091
commit 7be3cbe524
7 changed files with 270 additions and 3 deletions

View File

@@ -0,0 +1,15 @@
namespace ElectronNET.API.Entities
{
public class NotificationAction
{
/// <summary>
/// The label for the given action.
/// </summary>
public string Text { get; set; }
/// <summary>
/// The type of action, can be button.
/// </summary>
public string Type { get; set; }
}
}

View File

@@ -1,4 +1,7 @@
namespace ElectronNET.API.Entities
using Newtonsoft.Json;
using System;
namespace ElectronNET.API.Entities
{
public class NotificationOptions
{
@@ -44,6 +47,63 @@
/// </summary>
public string Sound { get; set; }
/// <summary>
/// Actions to add to the notification. Please read the available actions and
/// limitations in the NotificationAction documentation
/// </summary>
public NotificationAction Actions { get; set; }
/// <summary>
/// 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.
/// </summary>
[JsonIgnore]
public Action OnShow { get; set; }
[JsonProperty]
internal string ShowID { get; set; }
/// <summary>
/// Emitted when the notification is clicked by the user.
/// </summary>
[JsonIgnore]
public Action OnClick { get; set; }
[JsonProperty]
internal string ClickID { get; set; }
/// <summary>
/// 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.
/// </summary>
[JsonIgnore]
public Action OnClose { get; set; }
[JsonProperty]
internal string CloseID { get; set; }
/// <summary>
/// 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
/// </summary>
[JsonIgnore]
public Action<string> OnReply { get; set; }
[JsonProperty]
internal string ReplyID { get; set; }
/// <summary>
/// macOS only - The index of the action that was activated
/// </summary>
[JsonIgnore]
public Action<string> OnAction { get; set; }
[JsonProperty]
internal string ActionID { get; set; }
public NotificationOptions(string title, string body)
{
Title = title;

View File

@@ -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> _notificationOptions = new List<NotificationOptions>();
/// <summary>
/// Create OS desktop notifications
/// </summary>
/// <param name="notificationOptions"></param>
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<string[]>();
_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<string[]>();
_notificationOptions.Single(x => x.ReplyID == arguments[0].ToString()).OnAction(arguments[1].ToString());
});
}
if (isActionDefined)
{
_notificationOptions.Add(notificationOptions);
}
}
/// <summary>
/// Whether or not desktop notifications are supported on the current system.
/// </summary>
/// <returns></returns>
public Task<bool> IsSupportedAsync()
{
var taskCompletionSource = new TaskCompletionSource<bool>();
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(),

View File

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

View File

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

View File

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

View File

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