@implements IAsyncDisposable @inject IJSRuntime JS @if (IsOpen) {
} @code { [Parameter] public bool IsOpen { get; set; } [Parameter] public EventCallback IsOpenChanged { get; set; } [Parameter] public string Title { get; set; } = "QR-Code scannen"; [Parameter] public string? Subtitle { get; set; } [Parameter] public EventCallback OnScanned { get; set; } private readonly string _elementId = $"qr-reader-{Guid.NewGuid():N}"; private DotNetObjectReference? _selfRef; private bool _scannerActive; private bool _wasOpen; private bool isStarting; private string? cameraError; private string manualText = ""; protected override async Task OnAfterRenderAsync(bool firstRender) { if (IsOpen && !_wasOpen) { _wasOpen = true; await StartScanner(); } else if (!IsOpen && _wasOpen) { _wasOpen = false; await StopScanner(); } } private async Task StartScanner() { cameraError = null; isStarting = true; manualText = ""; StateHasChanged(); try { _selfRef ??= DotNetObjectReference.Create(this); await JS.InvokeVoidAsync("qrScanner.start", _elementId, _selfRef); _scannerActive = true; } catch (JSException ex) { cameraError = ExtractMessage(ex.Message); } catch (Exception ex) { cameraError = ex.Message; } finally { isStarting = false; StateHasChanged(); } } private static string ExtractMessage(string raw) { if (raw.Contains("NotAllowedError", StringComparison.OrdinalIgnoreCase) || raw.Contains("Permission", StringComparison.OrdinalIgnoreCase)) return "Kamerazugriff wurde verweigert. Bitte Berechtigung im Browser erlauben."; if (raw.Contains("NotFoundError", StringComparison.OrdinalIgnoreCase) || raw.Contains("Keine Kamera", StringComparison.OrdinalIgnoreCase)) return "Keine Kamera gefunden."; var idx = raw.IndexOf('\n'); return idx > 0 ? raw[..idx] : raw; } private async Task StopScanner() { if (!_scannerActive) return; _scannerActive = false; try { await JS.InvokeVoidAsync("qrScanner.stop"); } catch { } } [JSInvokable] public async Task OnQrScanned(string text) { if (string.IsNullOrWhiteSpace(text)) return; await Close(); await OnScanned.InvokeAsync(text.Trim()); } private async Task SubmitManual() { if (string.IsNullOrWhiteSpace(manualText)) return; var text = manualText.Trim(); await Close(); await OnScanned.InvokeAsync(text); } private async Task OnManualKeyDown(KeyboardEventArgs e) { if (e.Key == "Enter") await SubmitManual(); } private async Task Cancel() => await Close(); private async Task Close() { await StopScanner(); _wasOpen = false; if (IsOpen) { IsOpen = false; await IsOpenChanged.InvokeAsync(false); } } public async ValueTask DisposeAsync() { await StopScanner(); _selfRef?.Dispose(); } }