@@ -71,13 +71,78 @@ export default function ProductSearch() {
7171 const [ pageLoading , setPageLoading ] = useState ( true ) ;
7272 const [ currentCameraIndex , setCurrentCameraIndex ] = useState ( 0 ) ;
7373 const [ availableCameras , setAvailableCameras ] = useState < MediaDeviceInfo [ ] > ( [ ] ) ;
74+ const [ filteredCameras , setFilteredCameras ] = useState < MediaDeviceInfo [ ] > ( [ ] ) ;
7475 const videoRef = useRef < HTMLVideoElement > ( null ) ;
7576 const cameraControlsRef = useRef < { stop : ( ) => void } | null > ( null ) ;
7677 const router = useRouter ( ) ;
7778 const { setNavigating } = useTopBar ( ) ;
7879
7980 const debouncedQuery = useDebounce ( query , 500 ) ; // Increased debounce delay
8081
82+ // Filter cameras to only include back, front, and ultrawide
83+ const filterCameras = async ( devices : MediaDeviceInfo [ ] ) : Promise < MediaDeviceInfo [ ] > => {
84+ const filtered : MediaDeviceInfo [ ] = [ ] ;
85+
86+ for ( const device of devices ) {
87+ try {
88+ // Get camera capabilities to determine facing mode
89+ const stream = await navigator . mediaDevices . getUserMedia ( {
90+ video : { deviceId : device . deviceId }
91+ } ) ;
92+
93+ const track = stream . getVideoTracks ( ) [ 0 ] ;
94+ const capabilities = track . getCapabilities ( ) ;
95+ const settings = track . getSettings ( ) ;
96+
97+ // Stop the stream immediately
98+ stream . getTracks ( ) . forEach ( track => track . stop ( ) ) ;
99+
100+ // Check for facing mode or camera type in label
101+ const label = device . label . toLowerCase ( ) ;
102+ const facingMode = settings . facingMode || capabilities . facingMode ;
103+
104+ // Include back camera (main camera)
105+ if ( facingMode === 'environment' ||
106+ label . includes ( 'back' ) ||
107+ label . includes ( 'rear' ) ||
108+ ( ! label . includes ( 'front' ) && ! label . includes ( 'selfie' ) && devices . indexOf ( device ) === 0 ) ) {
109+ filtered . push ( device ) ;
110+ }
111+ // Include front camera
112+ else if ( facingMode === 'user' ||
113+ label . includes ( 'front' ) ||
114+ label . includes ( 'selfie' ) ) {
115+ filtered . push ( device ) ;
116+ }
117+ // Include ultrawide camera
118+ else if ( label . includes ( 'ultra' ) ||
119+ label . includes ( 'wide' ) ||
120+ label . includes ( '0.5' ) ) {
121+ filtered . push ( device ) ;
122+ }
123+ } catch ( error ) {
124+ // If we can't get capabilities, use label-based filtering
125+ const label = device . label . toLowerCase ( ) ;
126+ if ( label . includes ( 'back' ) ||
127+ label . includes ( 'rear' ) ||
128+ label . includes ( 'front' ) ||
129+ label . includes ( 'selfie' ) ||
130+ label . includes ( 'ultra' ) ||
131+ label . includes ( 'wide' ) ||
132+ ( ! label . includes ( 'front' ) && devices . indexOf ( device ) === 0 ) ) {
133+ filtered . push ( device ) ;
134+ }
135+ }
136+ }
137+
138+ // If no cameras matched our criteria, fall back to first available camera
139+ if ( filtered . length === 0 && devices . length > 0 ) {
140+ filtered . push ( devices [ 0 ] ) ;
141+ }
142+
143+ return filtered ;
144+ } ;
145+
81146 // Auth state listener
82147 useEffect ( ( ) => {
83148 const unsub = onAuthStateChanged ( auth , ( user ) => {
@@ -133,8 +198,12 @@ export default function ProductSearch() {
133198 const devices = await BrowserMultiFormatReader . listVideoInputDevices ( ) ;
134199 setAvailableCameras ( devices ) ;
135200
136- if ( devices . length > 0 && videoRef . current && active ) {
137- const selectedDevice = devices [ currentCameraIndex ] || devices [ 0 ] ;
201+ // Filter cameras to only include desired types
202+ const filtered = await filterCameras ( devices ) ;
203+ setFilteredCameras ( filtered ) ;
204+
205+ if ( filtered . length > 0 && videoRef . current && active ) {
206+ const selectedDevice = filtered [ currentCameraIndex ] || filtered [ 0 ] ;
138207 const codeReader = new BrowserMultiFormatReader ( ) ;
139208 const controls = await codeReader . decodeFromVideoDevice (
140209 selectedDevice . deviceId ,
@@ -179,15 +248,15 @@ export default function ProductSearch() {
179248 } , [ ] ) ;
180249
181250 const flipCamera = ( ) => {
182- if ( availableCameras . length > 1 ) {
251+ if ( filteredCameras . length > 1 ) {
183252 // Stop current camera before switching
184253 if ( cameraControlsRef . current ) {
185254 cameraControlsRef . current . stop ( ) ;
186255 cameraControlsRef . current = null ;
187256 }
188257
189258 setCurrentCameraIndex ( ( prevIndex ) =>
190- ( prevIndex + 1 ) % availableCameras . length
259+ ( prevIndex + 1 ) % filteredCameras . length
191260 ) ;
192261 }
193262 } ;
@@ -325,7 +394,7 @@ export default function ProductSearch() {
325394 < video ref = { videoRef } className = "w-full h-auto" />
326395 </ div >
327396 { /* Flip Camera Button */ }
328- { availableCameras . length > 1 && (
397+ { filteredCameras . length > 1 && (
329398 < button
330399 onClick = { flipCamera }
331400 className = "absolute top-2 right-2 p-2 rounded-full shadow-lg transition-all duration-200 hover:scale-110 active:scale-95"
0 commit comments