diff --git a/ElectronNET.API/Dialog.cs b/ElectronNET.API/Dialog.cs index d30eab9..8fe4686 100644 --- a/ElectronNET.API/Dialog.cs +++ b/ElectronNET.API/Dialog.cs @@ -31,7 +31,7 @@ namespace ElectronNET.API /// and a directory selector, so if you set properties to ['openFile', 'openDirectory'] /// on these platforms, a directory selector will be shown. /// - /// + /// The browserWindow argument allows the dialog to attach itself to a parent window, making it modal. /// /// An array of file paths chosen by the user public Task ShowOpenDialogAsync(BrowserWindow browserWindow, OpenDialogOptions options) @@ -53,6 +53,30 @@ namespace ElectronNET.API return taskCompletionSource.Task; } + /// + /// Dialog for save files. + /// + /// The browserWindow argument allows the dialog to attach itself to a parent window, making it modal. + /// + /// Returns String, the path of the file chosen by the user, if a callback is provided it returns an empty string. + public Task ShowSaveDialogAsync(BrowserWindow browserWindow, SaveDialogOptions options) + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("showSaveDialogComplete", (filename) => + { + BridgeConnector.Socket.Off("showSaveDialogComplete"); + + taskCompletionSource.SetResult(filename.ToString()); + }); + + BridgeConnector.Socket.Emit("showSaveDialog", + JObject.FromObject(browserWindow, _jsonSerializer), + JObject.FromObject(options, _jsonSerializer)); + + return taskCompletionSource.Task; + } + /// /// Shows a message box, it will block the process until the message box is closed. /// It returns the index of the clicked button. The browserWindow argument allows @@ -106,6 +130,58 @@ namespace ElectronNET.API return taskCompletionSource.Task; } + /// + /// Displays a modal dialog that shows an error message. + /// + /// This API can be called safely before the ready event the app module emits, + /// it is usually used to report errors in early stage of startup.If called + /// before the app readyevent on Linux, the message will be emitted to stderr, + /// and no GUI dialog will appear. + /// + /// The title to display in the error box. + /// The text content to display in the error box. + public void ShowErrorBox(string title, string content) + { + BridgeConnector.Socket.Emit("showErrorBox", title, content); + } + + /// + /// On macOS, this displays a modal dialog that shows a message and certificate information, + /// and gives the user the option of trusting/importing the certificate. If you provide a + /// browserWindow argument the dialog will be attached to the parent window, making it modal. + /// + /// + /// + public Task ShowCertificateTrustDialogAsync(CertificateTrustDialogOptions options) + { + return ShowCertificateTrustDialogAsync(null, options); + } + + /// + /// On macOS, this displays a modal dialog that shows a message and certificate information, + /// and gives the user the option of trusting/importing the certificate. If you provide a + /// browserWindow argument the dialog will be attached to the parent window, making it modal. + /// + /// + /// + /// + public Task ShowCertificateTrustDialogAsync(BrowserWindow browserWindow, CertificateTrustDialogOptions options) + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("showCertificateTrustDialogComplete", () => + { + BridgeConnector.Socket.Off("showCertificateTrustDialogComplete"); + taskCompletionSource.SetResult(null); + }); + + BridgeConnector.Socket.Emit("showCertificateTrustDialog", + JObject.FromObject(browserWindow, _jsonSerializer), + JObject.FromObject(options, _jsonSerializer)); + + return taskCompletionSource.Task; + } + private JsonSerializer _jsonSerializer = new JsonSerializer() { ContractResolver = new CamelCasePropertyNamesContractResolver(), diff --git a/ElectronNET.API/Entities/Certificate.cs b/ElectronNET.API/Entities/Certificate.cs new file mode 100644 index 0000000..957ce5b --- /dev/null +++ b/ElectronNET.API/Entities/Certificate.cs @@ -0,0 +1,55 @@ +namespace ElectronNET.API.Entities +{ + public class Certificate + { + /// + /// PEM encoded data + /// + public string Data { get; set; } + + /// + /// Fingerprint of the certificate + /// + public string Fingerprint { get; set; } + + /// + /// Issuer principal + /// + public CertificatePrincipal Issuer { get; set; } + + /// + /// Issuer certificate (if not self-signed) + /// + public Certificate IssuerCert { get; set; } + + /// + /// Issuer's Common Name + /// + public string IssuerName { get; set; } + + /// + /// Hex value represented string + /// + public string SerialNumber { get; set; } + + /// + /// Subject principal + /// + public CertificatePrincipal Subject { get; set; } + + /// + /// Subject's Common Name + /// + public string SubjectName { get; set; } + + /// + /// End date of the certificate being valid in seconds + /// + public int ValidExpiry { get; set; } + + /// + /// Start date of the certificate being valid in seconds + /// + public int ValidStart { get; set; } + } +} \ No newline at end of file diff --git a/ElectronNET.API/Entities/CertificatePrincipal.cs b/ElectronNET.API/Entities/CertificatePrincipal.cs new file mode 100644 index 0000000..8691c96 --- /dev/null +++ b/ElectronNET.API/Entities/CertificatePrincipal.cs @@ -0,0 +1,35 @@ +namespace ElectronNET.API.Entities +{ + public class CertificatePrincipal + { + /// + /// Common Name + /// + public string CommonName { get; set; } + + /// + /// Country or region + /// + public string Country { get; set; } + + /// + /// Locality + /// + public string Locality { get; set; } + + /// + /// Organization names + /// + public string[] Organizations { get; set; } + + /// + /// Organization Unit names + /// + public string[] OrganizationUnits { get; set; } + + /// + /// State or province + /// + public string State { get; set; } + } +} \ No newline at end of file diff --git a/ElectronNET.API/Entities/CertificateTrustDialogOptions.cs b/ElectronNET.API/Entities/CertificateTrustDialogOptions.cs new file mode 100644 index 0000000..ed5729d --- /dev/null +++ b/ElectronNET.API/Entities/CertificateTrustDialogOptions.cs @@ -0,0 +1,15 @@ +namespace ElectronNET.API.Entities +{ + public class CertificateTrustDialogOptions + { + /// + /// The certificate to trust/import. + /// + public Certificate Certificate { get; set; } + + /// + /// The message to display to the user. + /// + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/ElectronNET.API/Entities/SaveDialogOptions.cs b/ElectronNET.API/Entities/SaveDialogOptions.cs new file mode 100644 index 0000000..42c4954 --- /dev/null +++ b/ElectronNET.API/Entities/SaveDialogOptions.cs @@ -0,0 +1,52 @@ +using ElectronNET.API.Entities; + +namespace ElectronNET.API +{ + public class SaveDialogOptions + { + public string Title { get; set; } + + /// + /// Absolute directory path, absolute file path, or file name to use by default. + /// + public string DefaultPath { get; set; } + + /// + /// Custom label for the confirmation button, when left empty the default label will + /// be used. + /// + public string ButtonLabel { get; set; } + + /// + /// The filters specifies an array of file types that can be displayed or + /// selected when you want to limit the user to a specific type. For example: + /// + /// + /// + /// new FileFilter[] + /// { + /// new FileFiler { Name = "Images", Extensions = new string[] { "jpg", "png", "gif" } }, + /// new FileFiler { Name = "Movies", Extensions = new string[] { "mkv", "avi", "mp4" } }, + /// new FileFiler { Name = "Custom File Type", Extensions= new string[] {"as" } }, + /// new FileFiler { Name = "All Files", Extensions= new string[] { "*" } } + /// } + /// + /// + public FileFilter[] Filters { get; set; } + + /// + /// Message to display above text fields. + /// + public string Message { get; set; } + + /// + /// Custom label for the text displayed in front of the filename text field. + /// + public string NameFieldLabel { get; set; } + + /// + /// Show the tags input box, defaults to true. + /// + public bool ShowsTagField { get; set; } + } +} \ No newline at end of file diff --git a/ElectronNET.Host/api/dialog.js b/ElectronNET.Host/api/dialog.js index 67c9945..7f66ddf 100644 --- a/ElectronNET.Host/api/dialog.js +++ b/ElectronNET.Host/api/dialog.js @@ -21,5 +21,20 @@ module.exports = function (socket) { socket.emit('showOpenDialogComplete', filePaths || []); }); }); + socket.on('showSaveDialog', function (browserWindow, options) { + var window = electron_1.BrowserWindow.fromId(browserWindow.id); + electron_1.dialog.showSaveDialog(window, options, function (filename) { + socket.emit('showSaveDialogComplete', filename || ''); + }); + }); + socket.on('showErrorBox', function (title, content) { + electron_1.dialog.showErrorBox(title, content); + }); + socket.on('showCertificateTrustDialog', function (browserWindow, options) { + var window = electron_1.BrowserWindow.fromId(browserWindow.id); + electron_1.dialog.showCertificateTrustDialog(window, options, function () { + socket.emit('showCertificateTrustDialogComplete'); + }); + }); }; //# sourceMappingURL=dialog.js.map \ No newline at end of file diff --git a/ElectronNET.Host/api/dialog.js.map b/ElectronNET.Host/api/dialog.js.map index 489341c..62bdf50 100644 --- a/ElectronNET.Host/api/dialog.js.map +++ b/ElectronNET.Host/api/dialog.js.map @@ -1 +1 @@ -{"version":3,"file":"dialog.js","sourceRoot":"","sources":["dialog.ts"],"names":[],"mappings":";;AAAA,qCAAiD;AAEjD,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,UAAC,aAAa,EAAE,OAAO;QAC/C,EAAE,CAAA,CAAC,IAAI,IAAI,aAAa,CAAC,CAAC,CAAC;YACvB,IAAI,MAAM,GAAG,wBAAa,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;YAEpD,iBAAM,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,UAAC,QAAQ,EAAE,eAAe;gBAC7D,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC;YACvE,CAAC,CAAC,CAAC;QACP,CAAC;QAAC,IAAI,CAAC,CAAC;YACJ,iBAAM,CAAC,cAAc,CAAC,aAAa,EAAE,UAAC,QAAQ,EAAE,eAAe;gBAC3D,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC;YACvE,CAAC,CAAC,CAAC;QACP,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,UAAC,aAAa,EAAE,OAAO;QAC3C,IAAI,MAAM,GAAG,wBAAa,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QACpD,iBAAM,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,UAAC,SAAS;YAC7C,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC;QAC3D,CAAC,CAAC,CAAC;IACX,CAAC,CAAC,CAAC;AACP,CAAC,CAAA"} \ No newline at end of file +{"version":3,"file":"dialog.js","sourceRoot":"","sources":["dialog.ts"],"names":[],"mappings":";;AAAA,qCAAiD;AAEjD,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,UAAC,aAAa,EAAE,OAAO;QAC/C,EAAE,CAAC,CAAC,IAAI,IAAI,aAAa,CAAC,CAAC,CAAC;YACxB,IAAI,MAAM,GAAG,wBAAa,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;YAEpD,iBAAM,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,UAAC,QAAQ,EAAE,eAAe;gBAC7D,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC;YACvE,CAAC,CAAC,CAAC;QACP,CAAC;QAAC,IAAI,CAAC,CAAC;YACJ,iBAAM,CAAC,cAAc,CAAC,aAAa,EAAE,UAAC,QAAQ,EAAE,eAAe;gBAC3D,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC;YACvE,CAAC,CAAC,CAAC;QACP,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,UAAC,aAAa,EAAE,OAAO;QAC/C,IAAI,MAAM,GAAG,wBAAa,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QACpD,iBAAM,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,UAAC,SAAS;YAC7C,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC;QAC3D,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,UAAC,aAAa,EAAE,OAAO;QAC/C,IAAI,MAAM,GAAG,wBAAa,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QACpD,iBAAM,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,UAAC,QAAQ;YAC5C,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,QAAQ,IAAI,EAAE,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,cAAc,EAAE,UAAC,KAAK,EAAE,OAAO;QACrC,iBAAM,CAAC,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,4BAA4B,EAAE,UAAC,aAAa,EAAE,OAAO;QAC3D,IAAI,MAAM,GAAG,wBAAa,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QACpD,iBAAM,CAAC,0BAA0B,CAAC,MAAM,EAAE,OAAO,EAAE;YAC/C,MAAM,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACP,CAAC,CAAA"} \ No newline at end of file diff --git a/ElectronNET.Host/api/dialog.ts b/ElectronNET.Host/api/dialog.ts index ac108ef..6be3fa0 100644 --- a/ElectronNET.Host/api/dialog.ts +++ b/ElectronNET.Host/api/dialog.ts @@ -2,7 +2,7 @@ import { BrowserWindow, dialog } from "electron"; module.exports = (socket: SocketIO.Server) => { socket.on('showMessageBox', (browserWindow, options) => { - if("id" in browserWindow) { + if ("id" in browserWindow) { var window = BrowserWindow.fromId(browserWindow.id); dialog.showMessageBox(window, options, (response, checkboxChecked) => { @@ -16,9 +16,27 @@ module.exports = (socket: SocketIO.Server) => { }); socket.on('showOpenDialog', (browserWindow, options) => { - var window = BrowserWindow.fromId(browserWindow.id); - dialog.showOpenDialog(window, options, (filePaths) => { - socket.emit('showOpenDialogComplete', filePaths || []); - }); + var window = BrowserWindow.fromId(browserWindow.id); + dialog.showOpenDialog(window, options, (filePaths) => { + socket.emit('showOpenDialogComplete', filePaths || []); + }); + }); + + socket.on('showSaveDialog', (browserWindow, options) => { + var window = BrowserWindow.fromId(browserWindow.id); + dialog.showSaveDialog(window, options, (filename) => { + socket.emit('showSaveDialogComplete', filename || ''); + }); + }); + + socket.on('showErrorBox', (title, content) => { + dialog.showErrorBox(title, content); + }); + + socket.on('showCertificateTrustDialog', (browserWindow, options) => { + var window = BrowserWindow.fromId(browserWindow.id); + dialog.showCertificateTrustDialog(window, options, () => { + socket.emit('showCertificateTrustDialogComplete'); + }); }); } \ No newline at end of file