Skip to content

Commit

Permalink
Fix: hotkeys didn't work on Blazor Server hosting model
Browse files Browse the repository at this point in the history
  • Loading branch information
jsakamoto committed May 13, 2024
1 parent 356a929 commit 4c63e5a
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 43 deletions.
32 changes: 17 additions & 15 deletions BlazingStory/Components/BlazingStoryApp.razor
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@

<div class="default-background init-level-@(this._InitLevel)"></div>

<CascadingValue Value="this._ServiceProvider">
<CascadingValue TValue="IServiceProvider" Value="this._ServiceScope?.ServiceProvider">

<StoriesRazorDetector Assemblies="this.Assemblies" StoriesStore="this._StoriesStore" />

Expand Down Expand Up @@ -132,7 +132,7 @@

private int _InitLevel = 0;

private IServiceProvider? _ServiceProvider;
private AsyncServiceScope? _ServiceScope;

private readonly JSModule _JSModule;

Expand Down Expand Up @@ -161,33 +161,34 @@

protected override void OnInitialized()
{
this._ServiceProvider = this.ConfigureServices();
this._ServiceScope = this.ConfigureServices();
}

private IServiceProvider ConfigureServices()
private AsyncServiceScope ConfigureServices()
{
var services = new ServiceCollection()
.AddSingleton<IJSRuntime>(_ => this.JSRuntime)
.AddSingleton<ILoggerFactory>(_ => LoggerFactory)
.AddSingleton(typeof(ILogger<>), typeof(Logger<>))
.AddSingleton<HttpClient>(_ => this.GlobalServices.GetRequiredService<HttpClient>())
.AddSingleton<NavigationManager>(_ => this.NavigationManager)
.AddHotKeys2()
.AddSingleton<HelperScript>()
.AddSingleton<CommandService>()
.AddSingleton<NavigationService>()
.AddSingleton<AddonsStore>(_ => this._AddonsStore)
.AddSingleton<BlazingStoryOptions>(_ => this._Options)
.AddSingleton<WebAssets>()
.AddSingleton<GlobalServiceProvider>(_ => new(this.GlobalServices))
.AddHotKeys2()
.AddScoped<HelperScript>()
.AddScoped<CommandService>()
.AddScoped<NavigationService>()
.AddScoped<AddonsStore>(_ => this._AddonsStore)
.AddScoped<BlazingStoryOptions>(_ => this._Options)
.AddScoped<WebAssets>()
.AddTransient<ComponentActionLogs>();

if (OperatingSystem.IsBrowser())
services.AddSingleton<IXmlDocComment, XmlDocCommentForWasm>();
else
services.AddSingleton<IXmlDocComment, XmlDocCommentForServer>();

return services.BuildServiceProvider();
var serviceProvider = services.BuildServiceProvider();
return serviceProvider.CreateAsyncScope();
}

protected override async Task OnParametersSetAsync()
Expand Down Expand Up @@ -235,11 +236,11 @@

private async ValueTask UpdatePreferesColorSchemeAsync()
{
if (this._ServiceProvider is null) throw new InvalidOperationException("The service provider is not initialized.");
if (!this._ServiceScope.HasValue) throw new InvalidOperationException("The service provider is not initialized.");

if (this.AvailableColorSchemes == AvailableColorSchemes.Both)
{
var helperScript = this._ServiceProvider.GetRequiredService<HelperScript>();
var helperScript = this._ServiceScope.Value.ServiceProvider.GetRequiredService<HelperScript>();
var colorScheme = await helperScript.GetLocalStorageItemAsync("ColorScheme", defaultValue: "system");
if (colorScheme != "dark" && colorScheme != "light")
{
Expand Down Expand Up @@ -269,6 +270,7 @@
{
await this._PreferesColorSchemeChangeSubscriber.DisposeIfConnectedAsync("dispose");
this._RefThis.Dispose();
await _JSModule.DisposeAsync();
await this._JSModule.DisposeAsync();
if (this._ServiceScope.HasValue) await _ServiceScope.Value.DisposeAsync();
}
}
26 changes: 3 additions & 23 deletions BlazingStory/Internals/Services/Command/CommandService.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
using BlazingStory.Internals.Extensions;
using BlazingStory.Internals.Utils;
using BlazingStory.Internals.Utils;
using Microsoft.Extensions.Logging;
using Toolbelt.Blazor.HotKeys2;

namespace BlazingStory.Internals.Services.Command;

internal class CommandService : IAsyncDisposable
{
private readonly HotKeys _HotKeys;

private readonly HotKeysContext _HotKeysContext;

private readonly ILogger<CommandService> _Logger;
Expand All @@ -19,11 +16,9 @@ internal class CommandService : IAsyncDisposable

public CommandService(HotKeys hotKeys, HelperScript helperScript, ILogger<CommandService> logger)
{
this.Commands = new CommandSet<CommandType>(this.CommandStateKeyName, helperScript, logger);
this._HotKeys = hotKeys;
this._Logger = logger;
this._HotKeysContext = this._HotKeys.CreateContext();
this._HotKeys.KeyDown += this.HotKeys_OnKeyDown;
this._HotKeysContext = hotKeys.CreateContext();
this.Commands = new CommandSet<CommandType>(this.CommandStateKeyName, this._HotKeysContext, helperScript, logger);
}

public Command? this[CommandType type] => this.Commands[type];
Expand All @@ -40,24 +35,9 @@ public IDisposable Subscribe(CommandType type, ValueTaskCallback callBack)
return command.Subscribe(callBack);
}

private void HotKeys_OnKeyDown(object? sender, HotKeyDownEventArgs args)
{
if (args.SrcElementTagName is "TEXTAREA" or "INPUT") return;
var commad = this.Commands
.Select(entry => entry.Command)
.FirstOrDefault(cmd => cmd.HotKey != null && cmd.HotKey.Code == args.Code && cmd.HotKey.Modifiers == args.Modifiers);
if (commad == null) return;

// TODO: args.PreventDefault works only on Blazor WebAssembly.
args.PreventDefault = true;

commad.InvokeAsync().AndLogException(this._Logger);
}

public async ValueTask DisposeAsync()
{
this.Commands.Dispose();
this._HotKeys.KeyDown -= this.HotKeys_OnKeyDown;
await this._HotKeysContext.DisposeAsync();
}
}
16 changes: 11 additions & 5 deletions BlazingStory/Internals/Services/Command/CommandSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using BlazingStory.Internals.Extensions;
using BlazingStory.Internals.Utils;
using Microsoft.Extensions.Logging;
using Toolbelt.Blazor.HotKeys2;

namespace BlazingStory.Internals.Services.Command;

Expand All @@ -11,6 +12,8 @@ internal class CommandSet<TKey> : IDisposable, IEnumerable<(TKey Type, Command C
{
private readonly string _StorageKey;

private readonly HotKeysContext _HotKeysContext;

private readonly HelperScript _HelperScript;

private readonly ILogger _Logger;
Expand All @@ -21,9 +24,10 @@ internal class CommandSet<TKey> : IDisposable, IEnumerable<(TKey Type, Command C

public Command? this[TKey type] => this._Commands[(object)type] as Command;

internal CommandSet(string storageKey, HelperScript helperScript, ILogger logger)
internal CommandSet(string storageKey, HotKeysContext hotKeysContext, HelperScript helperScript, ILogger logger)
{
this._StorageKey = storageKey;
this._HotKeysContext = hotKeysContext;
this._HelperScript = helperScript;
this._Logger = logger;
}
Expand All @@ -34,11 +38,13 @@ internal async ValueTask EnsureInitializedAsync(Func<IEnumerable<(TKey Type, Com
this._Initialized = true;

var commandStates = await this._HelperScript.LoadObjectFromLocalStorageAsync(this._StorageKey, new Dictionary<TKey, CommandState>());
foreach (var cmdEntry in getCommandEntries())
foreach (var (type, command) in getCommandEntries())
{
if (commandStates.TryGetValue(cmdEntry.Type, out var state)) state.Apply(cmdEntry.Command);
cmdEntry.Command.StateChanged += this.Command_StateChanged;
this._Commands.Add(cmdEntry.Type, cmdEntry.Command);
if (commandStates.TryGetValue(type, out var state)) state.Apply(command);
command.StateChanged += this.Command_StateChanged;
this._Commands.Add(type, command);

if (command.HotKey != null) this._HotKeysContext.Add(command.HotKey.Modifiers, command.HotKey.Code, command.InvokeAsync);
}
}

Expand Down

0 comments on commit 4c63e5a

Please sign in to comment.