Skip to content

Commit 98b421c

Browse files
committed
feat: added startup options for UseElectron
1 parent 1d382c6 commit 98b421c

File tree

6 files changed

+191
-11
lines changed

6 files changed

+191
-11
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
namespace ElectronNET
2+
{
3+
using System;
4+
using System.Threading.Tasks;
5+
6+
/// <summary>
7+
/// Represents callbacks that are invoked during different phases of the Electron application
8+
/// lifetime. These callbacks allow consumers to hook into the startup sequence more
9+
/// granularly than the existing single callback. Callbacks return <see cref="Task"/>
10+
/// enabling asynchronous work to be awaited before the next phase of the Electron runtime
11+
/// commences.
12+
/// </summary>
13+
public class ElectronAppLifetimeEvents
14+
{
15+
/// <summary>
16+
/// Gets or sets the callback that is invoked once the Electron process and socket bridge
17+
/// have been established but before the Electron <c>ready</c> event has been
18+
/// acknowledged. Use this hook to register custom protocols or perform other
19+
/// initialization that must occur prior to the <c>ready</c> event.
20+
/// </summary>
21+
public Func<Task> OnBeforeReady { get; set; } = () => Task.CompletedTask;
22+
23+
/// <summary>
24+
/// Gets or sets the callback that is invoked when the Electron <c>ready</c> event is
25+
/// fired. Use this hook to create browser windows or perform post-ready initialization.
26+
/// </summary>
27+
public Func<Task> OnReady { get; set; } = () => Task.CompletedTask;
28+
29+
/// <summary>
30+
/// Gets or sets the callback that is invoked when the Electron process is about to quit.
31+
/// This maps to the <c>will-quit</c> event in Electron and can be used to perform
32+
/// graceful shutdown logic. The default implementation does nothing.
33+
/// </summary>
34+
public Func<Task> OnWillQuit { get; set; } = () => Task.CompletedTask;
35+
36+
/// <summary>
37+
/// Gets or sets the callback that is invoked when the Electron process has fully
38+
/// terminated. This can be used for cleanup tasks. The default implementation does
39+
/// nothing.
40+
/// </summary>
41+
public Func<Task> OnQuit { get; set; } = () => Task.CompletedTask;
42+
}
43+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace ElectronNET
2+
{
3+
/// <summary>
4+
/// Provides configuration options for Electron.NET. Consumers can assign
5+
/// an instance of <see cref="ElectronAppLifetimeEvents"/> to the <see cref="Events"/>
6+
/// property to hook into the Electron application lifecycle. Additional
7+
/// configuration properties can be added to this class in future versions without
8+
/// breaking existing consumers.
9+
/// </summary>
10+
public class ElectronNetOptions
11+
{
12+
/// <summary>
13+
/// Gets or sets the collection of lifecycle callbacks. The default value is
14+
/// an instance of <see cref="ElectronAppLifetimeEvents"/> with no-op
15+
/// implementations. Assigning a new instance or modifying individual
16+
/// callbacks allows consumers to customize the startup sequence.
17+
/// </summary>
18+
public ElectronAppLifetimeEvents Events { get; set; } = new ElectronAppLifetimeEvents();
19+
}
20+
}

src/ElectronNET.API/ElectronNetRuntime.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ static ElectronNetRuntime()
4949

5050
internal static Func<Task> OnAppReadyCallback { get; set; }
5151

52+
/// <summary>
53+
/// Global configuration options for the Electron.NET runtime, including
54+
/// lifecycle events that can be configured from the ASP.NET host builder.
55+
/// </summary>
56+
public static ElectronNetOptions Options { get; set; } = new ElectronNetOptions();
57+
5258
internal static SocketIoFacade GetSocket()
5359
{
5460
return RuntimeControllerCore?.Socket;

src/ElectronNET.AspNet/API/WebHostBuilderExtensions.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System;
44
using System.IO;
55
using System.Threading.Tasks;
6+
using ElectronNET;
67
using ElectronNET.AspNet;
78
using ElectronNET.AspNet.Runtime;
89
using ElectronNET.Runtime;
@@ -57,9 +58,39 @@ public static class WebHostBuilderExtensions
5758
/// }
5859
/// </code>
5960
/// </example>
61+
[Obsolete("This method is deprecated. Please use the overload with the configure parameter")]
6062
public static IWebHostBuilder UseElectron(this IWebHostBuilder builder, string[] args, Func<Task> onAppReadyCallback)
6163
{
62-
ElectronNetRuntime.OnAppReadyCallback = onAppReadyCallback;
64+
if (onAppReadyCallback == null)
65+
{
66+
throw new ArgumentNullException(nameof(onAppReadyCallback));
67+
}
68+
69+
// Backwards compatible overload – wraps the single callback into the new options model.
70+
return UseElectron(builder, args, options =>
71+
{
72+
options.Events.OnReady = onAppReadyCallback;
73+
});
74+
}
75+
76+
/// <summary>
77+
/// Adds Electron.NET support to the current ASP.NET Core web host with granular lifecycle
78+
/// configuration. The provided <see cref="ElectronNetOptions"/> allows registration of callbacks
79+
/// for different phases of the Electron runtime.
80+
/// </summary>
81+
public static IWebHostBuilder UseElectron(this IWebHostBuilder builder, string[] args, Action<ElectronNetOptions> configure)
82+
{
83+
if (configure == null)
84+
{
85+
throw new ArgumentNullException(nameof(configure));
86+
}
87+
88+
var options = new ElectronNetOptions();
89+
configure(options);
90+
ElectronNetRuntime.Options = options;
91+
92+
// Preserve behaviour of the original API by mapping OnReady to the legacy callback.
93+
ElectronNetRuntime.OnAppReadyCallback = options.Events?.OnReady;
6394

6495
var webPort = PortHelper.GetFreePort(ElectronNetRuntime.AspNetWebPort ?? ElectronNetRuntime.DefaultWebPort);
6596
ElectronNetRuntime.AspNetWebPort = webPort;

src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetBase.cs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{
33
using System;
44
using System.Threading.Tasks;
5+
using ElectronNET;
56
using ElectronNET.API;
67
using ElectronNET.Common;
78
using ElectronNET.Runtime.Controllers;
@@ -81,9 +82,22 @@ protected void HandleStopped()
8182
(this.aspNetLifetimeAdapter.IsNullOrStopped()))
8283
{
8384
this.TransitionState(LifetimeState.Stopped);
85+
86+
// Everything is fully stopped – fire the OnQuit callback.
87+
Task.Run(this.RunQuitCallback);
8488
}
8589
}
8690

91+
/// <summary>
92+
/// Invoked when ASP.NET lifetime enters Stopping (ApplicationStopping).
93+
/// We only trigger the OnWillQuit callback here; the actual state
94+
/// transition to Stopping is handled in <see cref="HandleStopped"/>.
95+
/// </summary>
96+
protected void HandleStopping()
97+
{
98+
Task.Run(this.RunWillQuitCallback);
99+
}
100+
87101
protected abstract override Task StopCore();
88102

89103
private void SocketBridge_Ready(object sender, EventArgs e)
@@ -108,10 +122,67 @@ private void AspNetLifetimeAdapter_Stopped(object sender, EventArgs e)
108122

109123
private void AspNetLifetimeAdapter_Stopping(object sender, EventArgs e)
110124
{
125+
this.HandleStopping();
126+
}
127+
128+
private async Task RunWillQuitCallback()
129+
{
130+
var events = ElectronNetRuntime.Options?.Events;
131+
var handler = events?.OnWillQuit;
132+
133+
if (handler == null)
134+
{
135+
return;
136+
}
137+
138+
try
139+
{
140+
await handler().ConfigureAwait(false);
141+
}
142+
catch (Exception ex)
143+
{
144+
Console.WriteLine("Exception while executing OnWillQuit callback.\n" + ex);
145+
// We are already stopping; no need to call this.Stop() here.
146+
}
147+
}
148+
149+
private async Task RunQuitCallback()
150+
{
151+
var events = ElectronNetRuntime.Options?.Events;
152+
var handler = events?.OnQuit;
153+
154+
if (handler == null)
155+
{
156+
return;
157+
}
158+
159+
try
160+
{
161+
await handler().ConfigureAwait(false);
162+
}
163+
catch (Exception ex)
164+
{
165+
Console.WriteLine("Exception while executing OnQuit callback.\n" + ex);
166+
}
111167
}
112168

113169
private async Task RunReadyCallback()
114170
{
171+
var events = ElectronNetRuntime.Options?.Events;
172+
if (events?.OnBeforeReady != null)
173+
{
174+
try
175+
{
176+
await events.OnBeforeReady().ConfigureAwait(false);
177+
}
178+
catch (Exception ex)
179+
{
180+
Console.WriteLine("Exception while executing OnBeforeReady callback. Stopping...\n" + ex);
181+
this.Stop();
182+
return;
183+
}
184+
}
185+
115186
if (ElectronNetRuntime.OnAppReadyCallback == null)
116187
{
117188
Console.WriteLine("Warning: Non OnReadyCallback provided in UseElectron() setup.");

src/ElectronNET.WebApp/Program.cs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
namespace ElectronNET.WebApp
77
{
8+
using System;
89
using System.Threading.Tasks;
910
using ElectronNET.API.Entities;
1011

@@ -23,20 +24,28 @@ public static IWebHostBuilder CreateWebHostBuilder(string[] args)
2324
.UseStartup<Startup>();
2425
}
2526

26-
public static async Task ElectronBootstrap()
27+
private static void ElectronBootstrap(ElectronNetOptions options)
2728
{
28-
//AddDevelopmentTests();
29-
30-
var browserWindow = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions
29+
options.Events = new()
3130
{
32-
Width = 1152,
33-
Height = 940,
34-
Show = false
35-
});
31+
OnBeforeReady = async () =>
32+
{
33+
Console.WriteLine("Firing before ready callback!");
34+
},
35+
OnReady = async () =>
36+
{
37+
var window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions
38+
{
39+
Width = 1152,
40+
Height = 940,
41+
Show = false
42+
});
3643

37-
await browserWindow.WebContents.Session.ClearCacheAsync();
44+
await window.WebContents.Session.ClearCacheAsync();
3845

39-
browserWindow.OnReadyToShow += () => browserWindow.Show();
46+
window.OnReadyToShow += window.Show;
47+
}
48+
};
4049
}
4150

4251
private static void AddDevelopmentTests()

0 commit comments

Comments
 (0)