Skip to content

Commit

Permalink
Fix: Disposing immediately after "CreateContext" will not work.
Browse files Browse the repository at this point in the history
  • Loading branch information
jsakamoto committed Apr 13, 2024
1 parent f580055 commit ae5bd3b
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 43 deletions.
74 changes: 32 additions & 42 deletions HotKeys2/HotKeysContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public partial class HotKeysContext : IDisposable

private readonly ILogger _Logger;

private bool _IsDisposed = false;

/// <summary>
/// Initialize a new instance of the HotKeysContext class.
/// </summary>
Expand Down Expand Up @@ -306,7 +308,9 @@ public HotKeysContext Add(ModKey modifiers, Key key, Func<HotKeyEntryByKey, Task
/// <returns>This context.</returns>
private HotKeysContext AddInternal(ModKey modifiers, Key key, Func<HotKeyEntryByKey, ValueTask> action, IHandleEvent? ownerOfAction, HotKeyOptions options)
{
lock (this.Keys) this.Keys.Add(this.Register(new HotKeyEntryByKey(this._Logger, modifiers, key, action, ownerOfAction, options)));
var hotkeyEntry = new HotKeyEntryByKey(this._Logger, modifiers, key, action, ownerOfAction, options);
lock (this.Keys) this.Keys.Add(hotkeyEntry);
var _ = this.RegisterAsync(hotkeyEntry);
return this;
}

Expand Down Expand Up @@ -589,59 +593,44 @@ public HotKeysContext Add(ModCode modifiers, Code code, Func<HotKeyEntryByCode,
/// <returns>This context.</returns>
private HotKeysContext AddInternal(ModCode modifiers, Code code, Func<HotKeyEntryByCode, ValueTask> action, IHandleEvent? ownerOfAction, HotKeyOptions options)
{
lock (this.Keys) this.Keys.Add(this.Register(new HotKeyEntryByCode(this._Logger, modifiers, code, action, ownerOfAction, options)));
var hotkeyEntry = new HotKeyEntryByCode(this._Logger, modifiers, code, action, ownerOfAction, options);
lock (this.Keys) this.Keys.Add(hotkeyEntry);
var _ = this.RegisterAsync(hotkeyEntry);
return this;
}

// ===============================================================================================


private HotKeyEntry Register(HotKeyEntry hotKeyEntry)
private async ValueTask RegisterAsync(HotKeyEntry hotKeyEntry)
{
this._AttachTask.ContinueWith(t =>
{
if (t.IsCompleted && !t.IsFaulted)
{
return t.Result.InvokeAsync<int>(
"Toolbelt.Blazor.HotKeys2.register",
hotKeyEntry._ObjectRef, hotKeyEntry.Mode, hotKeyEntry._Modifiers, hotKeyEntry._KeyEntry, hotKeyEntry.Exclude, hotKeyEntry.ExcludeSelector).AsTask();
}
else
{
var tcs = new TaskCompletionSource<int>();
tcs.TrySetException(t.Exception?.InnerExceptions ?? new[] { new Exception() }.AsEnumerable());
return tcs.Task;
}
})
.Unwrap()
.ContinueWith(t =>
await this.InvokeJsSafeAsync(async () =>
{
if (!t.IsCanceled && !t.IsFaulted) { hotKeyEntry.Id = t.Result; }
var module = await this._AttachTask;
if (this._IsDisposed) return;

hotKeyEntry.Id = await module.InvokeAsync<int>(
"Toolbelt.Blazor.HotKeys2.register",
hotKeyEntry._ObjectRef, hotKeyEntry.Mode, hotKeyEntry._Modifiers, hotKeyEntry._KeyEntry, hotKeyEntry.Exclude, hotKeyEntry.ExcludeSelector);
});
return hotKeyEntry;
}

private void Unregister(HotKeyEntry hotKeyEntry)
private async ValueTask UnregisterAsync(HotKeyEntry hotKeyEntry)
{
if (hotKeyEntry.Id == -1) return;

this._AttachTask.ContinueWith(t =>
{
if (t.IsCompleted && !t.IsFaulted)
{
return t.Result.InvokeVoidAsync("Toolbelt.Blazor.HotKeys2.unregister", hotKeyEntry.Id).AsTask();
}
else
{
var tcs = new TaskCompletionSource<int>();
tcs.TrySetException(t.Exception?.InnerExceptions ?? new[] { new Exception() }.AsEnumerable());
return tcs.Task as Task;
}
})
.ContinueWith(t =>
await this.InvokeJsSafeAsync(async () =>
{
hotKeyEntry.Dispose();
var module = await this._AttachTask;
await module.InvokeVoidAsync("Toolbelt.Blazor.HotKeys2.unregister", hotKeyEntry.Id);
});

await this.InvokeJsSafeAsync(() => { hotKeyEntry.Dispose(); return ValueTask.CompletedTask; });
}

private async ValueTask InvokeJsSafeAsync(Func<ValueTask> action)
{
try { await action(); }
catch (JSDisconnectedException) { } // Ignore this exception because it is thrown when the user navigates to another page.
catch (Exception ex) { this._Logger.LogError(ex, ex.Message); }
}

private const string _AMBIGUOUS_PARAMETER_EXCEPTION_MESSAGE = "Specified parameters are ambiguous to identify the single hotkey entry that should be removed.";
Expand Down Expand Up @@ -744,7 +733,7 @@ public HotKeysContext Remove(Func<IEnumerable<HotKeyEntry>, IEnumerable<HotKeyEn
var entries = filter.Invoke(this.Keys).ToArray();
foreach (var entry in entries)
{
this.Unregister(entry);
var _ = this.UnregisterAsync(entry);
lock (this.Keys) this.Keys.Remove(entry);
}
return this;
Expand All @@ -755,9 +744,10 @@ public HotKeysContext Remove(Func<IEnumerable<HotKeyEntry>, IEnumerable<HotKeyEn
/// </summary>
public void Dispose()
{
this._IsDisposed = true;
foreach (var entry in this.Keys)
{
this.Unregister(entry);
var _ = this.UnregisterAsync(entry);
}
this.Keys.Clear();
}
Expand Down
2 changes: 2 additions & 0 deletions HotKeys2/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export var Toolbelt;
return id;
};
HotKeys2.unregister = (id) => {
if (id === -1)
return;
hotKeyEntries.delete(id);
};
const convertToKeyNameMap = {
Expand Down
1 change: 1 addition & 0 deletions HotKeys2/script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
}

export const unregister = (id: number): void => {
if (id === -1) return;
hotKeyEntries.delete(id);
}

Expand Down
2 changes: 1 addition & 1 deletion HotKeys2/wwwroot/script.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit ae5bd3b

Please sign in to comment.