import { CameraView, useCameraPermissions, type BarcodeScanningResult } from 'expo-camera'; import { router } from 'expo-router'; import { useEffect, useRef, useState } from 'react'; import { Linking, StyleSheet, Text, View } from 'react-native'; import { parseProfileLink } from '@musicshare/shared'; import { Screen } from '@/components/Screen'; import { Body, Button, Title } from '@/components/ui'; import { FOSS_BUILD } from '@/lib/build'; import { decodeQrFromJpeg, pickSnapshotSize } from '@/lib/qrSnapshot'; import { useSession } from '@/lib/session'; import { colors, radius, space } from '@/lib/theme'; /** Scan a friend's musicshare QR code and open their profile (with its Follow button). */ export default function ScanScreen() { const { me } = useSession(); const [permission, requestPermission] = useCameraPermissions(); const [message, setMessage] = useState(null); // The camera reports the same code many times a second; act on the first. const handled = useRef(false); const camera = useRef(null); const [snapshotSize, setSnapshotSize] = useState(); const onCode = (data: string) => { if (handled.current) return; const username = parseProfileLink(data); if (!username) { setMessage('That’s not a musicshare code.'); return; } if (username === me?.username) { setMessage('That’s your own code. Show it to a friend instead!'); return; } handled.current = true; router.replace(`/u/${username}`); }; // The snapshot loop calls whatever the current handler is. const latest = useRef(onCode); useEffect(() => { latest.current = onCode; }); const granted = Boolean(permission?.granted); // F-Droid builds have no ML Kit: take a small snapshot every moment and decode it in JS. useEffect(() => { if (!FOSS_BUILD || !granted) return; let stopped = false; let timer: ReturnType; const tick = async () => { if (stopped || handled.current) return; try { const photo = await camera.current?.takePictureAsync({ base64: true, quality: 0.5, skipProcessing: true, shutterSound: false }); const data = photo?.base64 ? decodeQrFromJpeg(photo.base64) : null; if (data) latest.current(data); } catch { // camera not ready yet, or a blurry frame; try again } if (!stopped) timer = setTimeout(tick, 600); }; timer = setTimeout(tick, 800); return () => { stopped = true; clearTimeout(timer); }; }, [granted]); if (!permission) return ; if (!permission.granted) { return ( Scan a friend’s code musicshare needs the camera to scan QR codes. It’s only used while this screen is open. {permission.canAskAgain ? (