diff --git a/frontend/src/lib/components/CameraCapture.svelte b/frontend/src/lib/components/CameraCapture.svelte index 501b623..c4d8f56 100644 --- a/frontend/src/lib/components/CameraCapture.svelte +++ b/frontend/src/lib/components/CameraCapture.svelte @@ -5,9 +5,29 @@ interface Props { oncapture: (blob: Blob, type: 'photo' | 'video') => void; onclose: () => void; + /// Fired once, when the live preview is actually on screen — not when the camera was + /// merely requested. The caller uses it to dismiss whatever launched the camera. + /// + /// Gated on the preview rather than on `getUserMedia` resolving, because the two differ + /// exactly where it matters: if permission is denied, or the page is not a secure context, + /// this never fires and the caller's UI stays put behind the error panel — so "Schließen" + /// returns the guest to where they were instead of dropping them somewhere they never + /// asked to be. + onready?: () => void; } - let { oncapture, onclose }: Props = $props(); + let { oncapture, onclose, onready }: Props = $props(); + + /// `onready` is a one-shot. `startCamera` re-runs on every lens flip and photo/video switch, + /// and re-announcing "the camera is ready" mid-session would ask the caller to redo a + /// dismissal it has already done. + let announcedReady = false; + + function handlePreviewLive() { + if (announcedReady) return; + announcedReady = true; + onready?.(); + } let videoEl: HTMLVideoElement = $state()!; let canvasEl: HTMLCanvasElement = $state()!; @@ -157,7 +177,14 @@ } -
+ +
{#if error} @@ -197,6 +224,7 @@ autoplay playsinline muted + onloadedmetadata={handlePreviewLive} class="h-full w-full object-cover {facingMode === 'user' ? 'scale-x-[-1]' : ''}" > diff --git a/frontend/src/lib/components/UploadSheet.svelte b/frontend/src/lib/components/UploadSheet.svelte index 9b76e29..f230bd8 100644 --- a/frontend/src/lib/components/UploadSheet.svelte +++ b/frontend/src/lib/components/UploadSheet.svelte @@ -1,4 +1,5 @@ {#if showCamera} - + {/if}