aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2025-03-14 09:50:02 -0700
committerAdam Soltys <[email protected]>2025-03-14 09:50:02 -0700
commitb33110e18d7b492ebde32f82d0676fd90614b2e5 (patch)
tree4586c0dc8cb571fdc1b19c1d5729f1921439e41e
parent4d458fafd70411e3826bf6c6b8257e24a54b34c9 (diff)
downloadcoinos-ui-b33110e18d7b492ebde32f82d0676fd90614b2e5.tar.gz
coinos-ui-b33110e18d7b492ebde32f82d0676fd90614b2e5.zip
default to back camera for ios, remember camera
-rw-r--r--src/lib/store.ts1
-rw-r--r--src/routes/(app)/scan/+page.svelte86
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>