From 674fe9d1e2f33e41dd7c0201e4e1ff131f90e37c Mon Sep 17 00:00:00 2001 From: 3dtours Date: Mon, 7 Sep 2026 17:30:41 +0700 Subject: [PATCH] fix: refresh camera device list after permission grant useCameraDevices fetches once on mount. First-run flow grants the camera permission after the viewfinder mounted, so the device list stays empty until remount/restart and the UI shows 'Camera not available on this device'. Poll Camera.getAvailableCameraDevices() for up to 6s after permission is granted and prefer the found device. --- src/components/Viewfinder.tsx | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/components/Viewfinder.tsx b/src/components/Viewfinder.tsx index aeaedc7..eed4d05 100644 --- a/src/components/Viewfinder.tsx +++ b/src/components/Viewfinder.tsx @@ -1,6 +1,6 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import { View, Text, StyleSheet, Dimensions, TouchableOpacity } from 'react-native'; -import { Camera, useCameraDevice, useSkiaFrameProcessor } from 'react-native-vision-camera'; +import { Camera, useCameraDevice, useSkiaFrameProcessor, type CameraDevice } from 'react-native-vision-camera'; import { useSharedValue } from 'react-native-reanimated'; import { Canvas, @@ -55,7 +55,32 @@ export default function Viewfinder({ onRequestCameraPermission, cameraRef, }: ViewfinderProps) { - const device = useCameraDevice('back'); + const hookedDevice = useCameraDevice('back'); + // VisionCamera fetches the device list once when this component mounts. When the + // camera permission is granted *after* mount (first-run flow: GRANT button, then + // OS dialog), that list stays empty until remount/restart, so the camera would + // report "not available". Poll the native list briefly after permission is + // granted and prefer a freshly found device. + const [grantedDevice, setGrantedDevice] = useState(); + useEffect(() => { + if (!cameraPermissionGranted) return; + let stop = false; + const iv = setInterval(() => { + const back = Camera.getAvailableCameraDevices().find((d) => d.position === 'back'); + if (back && !stop) { + setGrantedDevice(back); + clearInterval(iv); + clearTimeout(to); + } + }, 250); + const to = setTimeout(() => clearInterval(iv), 6000); + return () => { + stop = true; + clearInterval(iv); + clearTimeout(to); + }; + }, [cameraPermissionGranted]); + const device = grantedDevice ?? hookedDevice; const skiaImage = useImage(libraryImageUri || ''); const customFont = useFont( require('../../assets/CourierPrime-Regular.ttf'),