mirror of
				https://github.com/lantean-code/qbtmud.git
				synced 2025-11-04 05:53:22 +00:00 
			
		
		
		
	Ensure keyboard events can be scoped to dialogs only.
This commit is contained in:
		@@ -1,10 +1,10 @@
 | 
				
			|||||||
@inherits SubmittableDialog
 | 
					<MudDialog>
 | 
				
			||||||
 | 
					 | 
				
			||||||
<MudDialog>
 | 
					 | 
				
			||||||
    <DialogContent>
 | 
					    <DialogContent>
 | 
				
			||||||
        <MudGrid>
 | 
					        <MudGrid>
 | 
				
			||||||
            <MudItem xs="12">
 | 
					            <MudItem xs="12">
 | 
				
			||||||
                <MudTextField @ref="UrlsTextField" Label="Urls" Lines="10" @bind-Value="Urls" Variant="Variant.Outlined" AutoFocus="true" />
 | 
					                <MudFocusTrap>
 | 
				
			||||||
 | 
					                    <MudTextField @ref="UrlsTextField" Label="Urls" Lines="10" @bind-Value="Urls" Variant="Variant.Outlined" AutoFocus="true" />
 | 
				
			||||||
 | 
					                </MudFocusTrap>
 | 
				
			||||||
            </MudItem>
 | 
					            </MudItem>
 | 
				
			||||||
        </MudGrid>
 | 
					        </MudGrid>
 | 
				
			||||||
        <AddTorrentOptions @ref="TorrentOptions" ShowCookieOption="false" />
 | 
					        <AddTorrentOptions @ref="TorrentOptions" ShowCookieOption="false" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,21 @@
 | 
				
			|||||||
using Lantean.QBTMudBlade.Models;
 | 
					using Lantean.QBTMudBlade.Models;
 | 
				
			||||||
 | 
					using Lantean.QBTMudBlade.Services;
 | 
				
			||||||
using Microsoft.AspNetCore.Components;
 | 
					using Microsoft.AspNetCore.Components;
 | 
				
			||||||
using MudBlazor;
 | 
					using MudBlazor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Lantean.QBTMudBlade.Components.Dialogs
 | 
					namespace Lantean.QBTMudBlade.Components.Dialogs
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public partial class AddTorrentLinkDialog
 | 
					    public partial class AddTorrentLinkDialog : IAsyncDisposable
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        private bool _disposedValue;
 | 
				
			||||||
 | 
					        private readonly KeyboardEvent _ctrlEnterKey = new KeyboardEvent("Enter")
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            CtrlKey = true,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Inject]
 | 
				
			||||||
 | 
					        protected IKeyboardService KeyboardService { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -15,6 +25,19 @@ namespace Lantean.QBTMudBlade.Components.Dialogs
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        protected AddTorrentOptions TorrentOptions { get; set; } = default!;
 | 
					        protected AddTorrentOptions TorrentOptions { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected override async Task OnAfterRenderAsync(bool firstRender)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (firstRender)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                await KeyboardService.RegisterKeypressEvent(_ctrlEnterKey, k =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    Submit();
 | 
				
			||||||
 | 
					                    return Task.CompletedTask;
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                await KeyboardService.Focus();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected void Cancel()
 | 
					        protected void Cancel()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            MudDialog.Cancel();
 | 
					            MudDialog.Cancel();
 | 
				
			||||||
@@ -31,19 +54,25 @@ namespace Lantean.QBTMudBlade.Components.Dialogs
 | 
				
			|||||||
            MudDialog.Close(DialogResult.Ok(options));
 | 
					            MudDialog.Close(DialogResult.Ok(options));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected override Task Submit(KeyboardEvent keyboardEvent)
 | 
					        protected virtual async ValueTask DisposeAsync(bool disposing)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Submit();
 | 
					            if (!_disposedValue)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (disposing)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    await KeyboardService.UnregisterKeypressEvent(_ctrlEnterKey);
 | 
				
			||||||
 | 
					                    await KeyboardService.UnFocus();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return Task.CompletedTask;
 | 
					                _disposedValue = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected override async Task OnAfterRenderAsync(bool firstRender)
 | 
					        public async ValueTask DisposeAsync()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (firstRender && UrlsTextField is not null)
 | 
					            // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
 | 
				
			||||||
            {
 | 
					            await DisposeAsync(disposing: true);
 | 
				
			||||||
                await UrlsTextField.FocusAsync();
 | 
					            GC.SuppressFinalize(this);
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -16,6 +16,7 @@ namespace Lantean.QBTMudBlade.Components.Dialogs
 | 
				
			|||||||
            if (firstRender)
 | 
					            if (firstRender)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await KeyboardService.RegisterKeypressEvent("Enter", k => Submit(k));
 | 
					                await KeyboardService.RegisterKeypressEvent("Enter", k => Submit(k));
 | 
				
			||||||
 | 
					                await KeyboardService.Focus();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -28,6 +29,7 @@ namespace Lantean.QBTMudBlade.Components.Dialogs
 | 
				
			|||||||
                if (disposing)
 | 
					                if (disposing)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await KeyboardService.UnregisterKeypressEvent("Enter");
 | 
					                    await KeyboardService.UnregisterKeypressEvent("Enter");
 | 
				
			||||||
 | 
					                    await KeyboardService.UnFocus();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                _disposedValue = true;
 | 
					                _disposedValue = true;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -161,9 +161,12 @@ namespace Lantean.QBTMudBlade.Components
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        protected async Task Remove()
 | 
					        protected async Task Remove()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            await DialogService.InvokeDeleteTorrentDialog(ApiClient, Hashes.ToArray());
 | 
					            var deleted = await DialogService.InvokeDeleteTorrentDialog(ApiClient, Hashes.ToArray());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            NavigationManager.NavigateTo("/");
 | 
					            if (deleted)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                NavigationManager.NavigateTo("/");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected async Task SetLocation()
 | 
					        protected async Task SetLocation()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -95,11 +95,11 @@ namespace Lantean.QBTMudBlade.Helpers
 | 
				
			|||||||
                options.DownloadFirstAndLastPiecesFirst);
 | 
					                options.DownloadFirstAndLastPiecesFirst);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static async Task InvokeDeleteTorrentDialog(this IDialogService dialogService, IApiClient apiClient, params string[] hashes)
 | 
					        public static async Task<bool> InvokeDeleteTorrentDialog(this IDialogService dialogService, IApiClient apiClient, params string[] hashes)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (hashes.Length == 0)
 | 
					            if (hashes.Length == 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return;
 | 
					                return false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var parameters = new DialogParameters
 | 
					            var parameters = new DialogParameters
 | 
				
			||||||
@@ -111,10 +111,12 @@ namespace Lantean.QBTMudBlade.Helpers
 | 
				
			|||||||
            var dialogResult = await reference.Result;
 | 
					            var dialogResult = await reference.Result;
 | 
				
			||||||
            if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
 | 
					            if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return;
 | 
					                return false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await apiClient.DeleteTorrents(hashes, (bool)dialogResult.Data);
 | 
					            await apiClient.DeleteTorrents(hashes, (bool)dialogResult.Data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static async Task InvokeRenameFilesDialog(this IDialogService dialogService, IApiClient apiClient, string hash)
 | 
					        public static async Task InvokeRenameFilesDialog(this IDialogService dialogService, IApiClient apiClient, string hash)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,7 +42,7 @@ namespace Lantean.QBTMudBlade
 | 
				
			|||||||
            builder.Services.AddSingleton<IDataManager, DataManager>();
 | 
					            builder.Services.AddSingleton<IDataManager, DataManager>();
 | 
				
			||||||
            builder.Services.AddBlazoredLocalStorage();
 | 
					            builder.Services.AddBlazoredLocalStorage();
 | 
				
			||||||
            builder.Services.AddSingleton<IClipboardService, ClipboardService>();
 | 
					            builder.Services.AddSingleton<IClipboardService, ClipboardService>();
 | 
				
			||||||
            builder.Services.AddSingleton<IKeyboardService, KeyboardService>();
 | 
					            builder.Services.AddTransient<IKeyboardService, KeyboardService>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if DEBUG
 | 
					#if DEBUG
 | 
				
			||||||
            builder.Logging.SetMinimumLevel(LogLevel.Information);
 | 
					            builder.Logging.SetMinimumLevel(LogLevel.Information);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,8 @@ namespace Lantean.QBTMudBlade.Services
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public interface IKeyboardService
 | 
					    public interface IKeyboardService
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        Task Focus();
 | 
				
			||||||
 | 
					        Task UnFocus();
 | 
				
			||||||
        Task RegisterKeypressEvent(KeyboardEvent criteria, Func<KeyboardEvent, Task> onKeyPress);
 | 
					        Task RegisterKeypressEvent(KeyboardEvent criteria, Func<KeyboardEvent, Task> onKeyPress);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Task UnregisterKeypressEvent(KeyboardEvent criteria);
 | 
					        Task UnregisterKeypressEvent(KeyboardEvent criteria);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,12 +4,12 @@ using System.Collections.Concurrent;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Lantean.QBTMudBlade.Services
 | 
					namespace Lantean.QBTMudBlade.Services
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class KeyboardService : IKeyboardService
 | 
					    public class KeyboardService : IKeyboardService, IAsyncDisposable
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly IJSRuntime _jSRuntime;
 | 
					        private readonly IJSRuntime _jSRuntime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private DotNetObjectReference<KeyboardService>? _dotNetObjectReference;
 | 
					        private DotNetObjectReference<KeyboardService>? _dotNetObjectReference;
 | 
				
			||||||
 | 
					        private bool _disposedValue;
 | 
				
			||||||
        private readonly ConcurrentDictionary<string, Func<KeyboardEvent, Task>> _keyboardHandlers = new();
 | 
					        private readonly ConcurrentDictionary<string, Func<KeyboardEvent, Task>> _keyboardHandlers = new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public KeyboardService(IJSRuntime jSRuntime)
 | 
					        public KeyboardService(IJSRuntime jSRuntime)
 | 
				
			||||||
@@ -46,5 +46,41 @@ namespace Lantean.QBTMudBlade.Services
 | 
				
			|||||||
            await _jSRuntime.InvokeVoidAsync("qbt.unregisterKeypressEvent", criteria, GetObjectReference());
 | 
					            await _jSRuntime.InvokeVoidAsync("qbt.unregisterKeypressEvent", criteria, GetObjectReference());
 | 
				
			||||||
            _keyboardHandlers.Remove(criteria, out var _);
 | 
					            _keyboardHandlers.Remove(criteria, out var _);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task Focus()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            await _jSRuntime.InvokeVoidAsync("qbt.keyPressFocusInstance", GetObjectReference());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task UnFocus()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            await _jSRuntime.InvokeVoidAsync("qbt.keyPressUnFocusInstance", GetObjectReference());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected async virtual ValueTask DisposeAsync(bool disposing)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (!_disposedValue)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (disposing)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    await UnFocus();
 | 
				
			||||||
 | 
					                    foreach (var key in _keyboardHandlers.Keys)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        await _jSRuntime.InvokeVoidAsync("qbt.unregisterKeypressEvent", key, GetObjectReference());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    _keyboardHandlers.Clear();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                _disposedValue = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async ValueTask DisposeAsync()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
 | 
				
			||||||
 | 
					            await DisposeAsync(disposing: true);
 | 
				
			||||||
 | 
					            GC.SuppressFinalize(this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -103,21 +103,20 @@ window.qbt.clearSelection = () => {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let supportedEvents = new Map();
 | 
					let supportedEvents = new Map();
 | 
				
			||||||
 | 
					let focusInstance = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
document.addEventListener('keyup', event => {
 | 
					document.addEventListener('keyup', event => {
 | 
				
			||||||
    const key = getKey(event);
 | 
					    const key = getKey(event);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    console.log(key);
 | 
					 | 
				
			||||||
    console.log(event);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const references = supportedEvents.get(key);
 | 
					    const references = supportedEvents.get(key);
 | 
				
			||||||
    if (!references) {
 | 
					    if (!references) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    console.log(references);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    references.forEach(dotNetObjectReference => {
 | 
					    references.forEach(dotNetObjectReference => {
 | 
				
			||||||
 | 
					        if (focusInstance && dotNetObjectReference !== focusInstance) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        dotNetObjectReference.invokeMethodAsync('HandleKeyPressEvent', {
 | 
					        dotNetObjectReference.invokeMethodAsync('HandleKeyPressEvent', {
 | 
				
			||||||
            key: event.key,
 | 
					            key: event.key,
 | 
				
			||||||
            code: event.code,
 | 
					            code: event.code,
 | 
				
			||||||
@@ -143,8 +142,6 @@ window.qbt.registerKeypressEvent = (keyboardEventArgs, dotNetObjectReference) =>
 | 
				
			|||||||
        references.set(dotNetObjectReference._id, dotNetObjectReference);
 | 
					        references.set(dotNetObjectReference._id, dotNetObjectReference);
 | 
				
			||||||
        supportedEvents.set(key, references);
 | 
					        supportedEvents.set(key, references);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.qbt.unregisterKeypressEvent = (keyboardEventArgs, dotNetObjectReference) => {
 | 
					window.qbt.unregisterKeypressEvent = (keyboardEventArgs, dotNetObjectReference) => {
 | 
				
			||||||
@@ -158,6 +155,14 @@ window.qbt.unregisterKeypressEvent = (keyboardEventArgs, dotNetObjectReference)
 | 
				
			|||||||
    references.delete(dotNetObjectReference._id);
 | 
					    references.delete(dotNetObjectReference._id);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					window.qbt.keyPressFocusInstance = dotNetObjectReference => {
 | 
				
			||||||
 | 
					    focusInstance = dotNetObjectReference;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					window.qbt.keyPressUnFocusInstance = dotNetObjectReference => {
 | 
				
			||||||
 | 
					    focusInstance = null;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getKey(keyboardEvent) {
 | 
					function getKey(keyboardEvent) {
 | 
				
			||||||
    return keyboardEvent.key + (keyboardEvent.ctrlKey ? '1' : '0') + (keyboardEvent.shiftKey ? '1' : '0') + (keyboardEvent.altKey ? '1' : '0') + (keyboardEvent.metaKey ? '1' : '0') + (keyboardEvent.repeat ? '1' : '0');
 | 
					    return keyboardEvent.key + (keyboardEvent.ctrlKey ? '1' : '0') + (keyboardEvent.shiftKey ? '1' : '0') + (keyboardEvent.altKey ? '1' : '0') + (keyboardEvent.metaKey ? '1' : '0') + (keyboardEvent.repeat ? '1' : '0');
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user