From 507f456ee49c6afbf6896ea6cb7d63bbd12c3244 Mon Sep 17 00:00:00 2001 From: Fewiel Date: Fri, 13 Mar 2026 21:06:18 +0100 Subject: [PATCH] feat: Find Spool via NFC scan --- .../Components/FindSpoolModal.razor | 157 ++++++++++++++++++ .../Pages/Spools/SpoolDetail.razor | 18 ++ .../Pages/Spools/SpoolList.razor | 24 +++ src/SpoolManager.Client/wwwroot/css/app.css | 15 ++ src/SpoolManager.Client/wwwroot/i18n/de.json | 9 +- src/SpoolManager.Client/wwwroot/i18n/en.json | 9 +- 6 files changed, 230 insertions(+), 2 deletions(-) create mode 100644 src/SpoolManager.Client/Components/FindSpoolModal.razor diff --git a/src/SpoolManager.Client/Components/FindSpoolModal.razor b/src/SpoolManager.Client/Components/FindSpoolModal.razor new file mode 100644 index 0000000..e263b64 --- /dev/null +++ b/src/SpoolManager.Client/Components/FindSpoolModal.razor @@ -0,0 +1,157 @@ +@implements IAsyncDisposable +@inject NfcService Nfc +@inject LocalizationService L + +@if (_visible) +{ + +} + +@code { + [Parameter, EditorRequired] public SpoolDto Spool { get; set; } = default!; + + private enum FindState { Idle, Scanning, Mismatch, Found, NoTag } + + private bool _visible; + private FindState _state = FindState.Idle; + private DotNetObjectReference? _ref; + private System.Timers.Timer? _resetTimer; + + public async Task OpenAsync() + { + if (string.IsNullOrWhiteSpace(Spool.RfidTagUid)) + { + _state = FindState.NoTag; + _visible = true; + StateHasChanged(); + return; + } + + _state = FindState.Scanning; + _visible = true; + StateHasChanged(); + + _ref ??= DotNetObjectReference.Create(this); + await Nfc.StartReadAsync(_ref); + } + + [JSInvokable] + public void OnScanStarted() { } + + [JSInvokable] + public async void OnTagRead(string json, string serialNumber) + { + _resetTimer?.Stop(); + + var uid = serialNumber.Trim().ToLowerInvariant().Replace(":", "").Replace("-", ""); + var expected = (Spool.RfidTagUid ?? "").Trim().ToLowerInvariant().Replace(":", "").Replace("-", ""); + + if (uid == expected) + { + _state = FindState.Found; + await InvokeAsync(StateHasChanged); + await Nfc.StopReadAsync(); + await Task.Delay(2000); + await CloseAsync(); + } + else + { + _state = FindState.Mismatch; + await InvokeAsync(StateHasChanged); + + _resetTimer = new System.Timers.Timer(2000); + _resetTimer.AutoReset = false; + _resetTimer.Elapsed += async (_, _) => + { + _state = FindState.Scanning; + await InvokeAsync(StateHasChanged); + }; + _resetTimer.Start(); + } + } + + [JSInvokable] + public void OnReadError(string message) + { + _state = FindState.Scanning; + InvokeAsync(StateHasChanged); + } + + [JSInvokable] + public void OnWriteSuccess() { } + + [JSInvokable] + public void OnWriteError(string message) { } + + private async Task CloseAsync() + { + _resetTimer?.Stop(); + await Nfc.StopReadAsync(); + _state = FindState.Idle; + _visible = false; + await InvokeAsync(StateHasChanged); + } + + public async ValueTask DisposeAsync() + { + _resetTimer?.Dispose(); + await Nfc.StopReadAsync(); + _ref?.Dispose(); + } +} diff --git a/src/SpoolManager.Client/Pages/Spools/SpoolDetail.razor b/src/SpoolManager.Client/Pages/Spools/SpoolDetail.razor index 7bc8014..1f59266 100644 --- a/src/SpoolManager.Client/Pages/Spools/SpoolDetail.razor +++ b/src/SpoolManager.Client/Pages/Spools/SpoolDetail.razor @@ -205,6 +205,12 @@ else @if (_writingNfc) { } @L["tag.nfc.write"] + @if (!string.IsNullOrWhiteSpace(_spool!.RfidTagUid)) + { + + } } @L["tag.download.bin"] @@ -228,11 +234,17 @@ else } +@if (_spool != null) +{ + +} + @code { [Parameter] public Guid Id { get; set; } private bool _loading = true; private SpoolDto? _spool; + private FindSpoolModal? _findModal; private bool _showRemainingForm; private decimal _remainingGrams; private decimal _remainingPct; @@ -274,6 +286,12 @@ else private async Task MarkReopenedAsync() { await Spools.MarkReopenedAsync(Id); _spool = await Spools.GetByIdAsync(Id); } private async Task MarkConsumedAsync() { await Spools.MarkConsumedAsync(Id); _spool = await Spools.GetByIdAsync(Id); } + private async Task FindSpoolAsync() + { + if (_findModal != null) + await _findModal.OpenAsync(); + } + private async Task WriteNfcAsync() { _writingNfc = true; diff --git a/src/SpoolManager.Client/Pages/Spools/SpoolList.razor b/src/SpoolManager.Client/Pages/Spools/SpoolList.razor index d6e34e6..fc96d96 100644 --- a/src/SpoolManager.Client/Pages/Spools/SpoolList.razor +++ b/src/SpoolManager.Client/Pages/Spools/SpoolList.razor @@ -135,6 +135,13 @@ else disabled="@(_nfcWriting && _nfcTarget?.Id == spool.Id)"> + @if (!string.IsNullOrWhiteSpace(spool.RfidTagUid)) + { + + } }