diff options
| author | Adam Soltys <[email protected]> | 2025-03-14 09:50:02 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2025-03-14 09:50:02 -0700 |
| commit | 530ae84792a22ef4b0a355286d4beefad2031c31 (patch) | |
| tree | 4586c0dc8cb571fdc1b19c1d5729f1921439e41e | |
| parent | e63706cf4faecfe8c99034c20e34bbb69879daf9 (diff) | |
| download | coinos-ui-530ae84792a22ef4b0a355286d4beefad2031c31.tar.gz coinos-ui-530ae84792a22ef4b0a355286d4beefad2031c31.zip | |
default to back camera for ios, remember camera
| -rw-r--r-- | src/lib/store.ts | 1 | ||||
| -rw-r--r-- | src/routes/(app)/scan/+page.svelte | 86 |
2 files changed, 52 insertions, 35 deletions
diff --git a/src/lib/store.ts b/src/lib/store.ts index 9b57b05d..c7dc9970 100644 --- a/src/lib/store.ts +++ b/src/lib/store.ts @@ -64,6 +64,7 @@ export const passwordPrompt = writable(); export const eventToSign = writable(); export const pubkey = persistLocal("pubkey"); export const signer = persistLocal("signer"); +export const camera = persistLocal("camera"); export const selectSigner = writable(); export const pin = persistLocal("pin"); export const rate = writable(); diff --git a/src/routes/(app)/scan/+page.svelte b/src/routes/(app)/scan/+page.svelte index aab7bcaa..80047487 100644 --- a/src/routes/(app)/scan/+page.svelte +++ b/src/routes/(app)/scan/+page.svelte @@ -3,6 +3,7 @@ import { goto } from "$app/navigation"; import { onMount, onDestroy } from "svelte"; import { BarcodeDetector } from "barcode-detector/ponyfill"; + import { camera } from "$lib/store"; let resize = () => { if (resizing) clearTimeout(resizing); @@ -14,7 +15,6 @@ let barcodeDetector; let scanningFrameId; let cameras = []; - let selectedCamera = ""; let detectionResult = null; // will hold the detected QR code value // Enumerate available video input devices @@ -22,20 +22,36 @@ try { const devices = await navigator.mediaDevices.enumerateDevices(); cameras = devices.filter((device) => device.kind === "videoinput"); - if (!selectedCamera && cameras.length > 0) { - // Prefer a rear-facing camera if available - const envCamera = cameras.find( - (camera) => - camera.label.toLowerCase().includes("back") || - camera.label.toLowerCase().includes("env"), - ); - selectedCamera = envCamera ? envCamera.deviceId : cameras[0].deviceId; + + // Check if we're on an iOS device + const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent); + + if (!$camera && cameras.length > 0) { + if (isIOS) { + // Prefer a camera labeled "back camera" (without additional qualifiers) + const backCamera = cameras.find( + (camera) => + camera.label.toLowerCase().includes("back") && + !camera.label.toLowerCase().includes("dual") && + !camera.label.toLowerCase().includes("wide"), + ); + $camera = backCamera + ? backCamera.deviceId + : cameras[0].deviceId; + } else { + // For non-iOS, default to a camera with the environment facing mode + const envCamera = cameras.find( + (camera) => + camera.label.toLowerCase().includes("back") || + camera.label.toLowerCase().includes("env"), + ); + $camera = envCamera ? envCamera.deviceId : cameras[0].deviceId; + } } } catch (err) { console.error("Error enumerating devices:", err); } } - // Start the camera stream for a given deviceId async function startCamera(deviceId) { stopCameraStream(); @@ -106,8 +122,8 @@ // Handle change of camera selection async function onCameraChange(event) { - selectedCamera = event.target.value; - await startCamera(selectedCamera); + $camera = event.target.value; + await startCamera($camera); videoEl.addEventListener("loadeddata", () => { canvasEl.width = videoEl.videoWidth; canvasEl.height = videoEl.videoHeight; @@ -118,7 +134,7 @@ // Provide a way to restart scanning async function restart() { detectionResult = null; - await startCamera(selectedCamera); + await startCamera($camera); videoEl.addEventListener("loadeddata", () => { canvasEl.width = videoEl.videoWidth; canvasEl.height = videoEl.videoHeight; @@ -129,7 +145,7 @@ let initialize = async () => { barcodeDetector = new BarcodeDetector({ formats: ["qr_code"] }); await getCameraDevices(); - await startCamera(selectedCamera); + await startCamera($camera); videoEl.addEventListener("loadeddata", () => { canvasEl.width = videoEl.videoWidth; canvasEl.height = videoEl.videoHeight; @@ -146,27 +162,27 @@ <svelte:window onresize={resize} /> <div class="flex w-full mb-4 p-4"> <div class="mx-auto rounded-3xl space-y-5"> - <video - bind:this={videoEl} - class="border-4 rounded-3xl border-black max-h-[calc(100vh*0.7)]" - ></video> - <canvas bind:this={canvasEl} style="display: none;"></canvas> - <div class="flex justify-center"> - <button class="btn !w-auto" onclick={back}>Cancel</button> - </div> - <div> - <select - id="cameraSelect" - bind:value={selectedCamera} - onchange={onCameraChange} - > - {#each cameras as camera} - <option value={camera.deviceId}> - {camera.label || `Camera ${camera.deviceId}`} - </option> - {/each} - </select> - </div> + <video + bind:this={videoEl} + class="border-4 rounded-3xl border-black max-h-[calc(100vh*0.7)]" + ></video> + <canvas bind:this={canvasEl} style="display: none;"></canvas> + <div class="flex justify-center"> + <button class="btn !w-auto" onclick={back}>Cancel</button> + </div> + <div> + <select + id="cameraSelect" + bind:value={$camera} + onchange={onCameraChange} + > + {#each cameras as camera} + <option value={camera.deviceId}> + {camera.label || `Camera ${camera.deviceId}`} + </option> + {/each} + </select> + </div> </div> </div> |
