@@ -80,7 +80,9 @@ export default function ProductSearch() {
8080
8181 // Filter cameras to only include back, front, and ultrawide
8282 const filterCameras = async ( devices : MediaDeviceInfo [ ] ) : Promise < MediaDeviceInfo [ ] > => {
83- const filtered : MediaDeviceInfo [ ] = [ ] ;
83+ const backCameras : MediaDeviceInfo [ ] = [ ] ;
84+ const frontCameras : MediaDeviceInfo [ ] = [ ] ;
85+ const ultrawideCameras : MediaDeviceInfo [ ] = [ ] ;
8486
8587 // First, try to get proper camera permissions to ensure device labels are available
8688 let permissionGranted = false ;
@@ -120,72 +122,98 @@ export default function ProductSearch() {
120122 const label = device . label . toLowerCase ( ) ;
121123 const facingMode = settings . facingMode || capabilities . facingMode ;
122124
123- // Include back camera (main camera)
125+ // Categorize cameras
124126 if ( facingMode === 'environment' ||
125127 label . includes ( 'back' ) ||
126128 label . includes ( 'rear' ) ||
127129 ( ! label . includes ( 'front' ) && ! label . includes ( 'selfie' ) && devices . indexOf ( device ) === 0 ) ) {
128- filtered . push ( device ) ;
130+ backCameras . push ( device ) ;
129131 }
130- // Include front camera
131132 else if ( facingMode === 'user' ||
132133 label . includes ( 'front' ) ||
133134 label . includes ( 'selfie' ) ) {
134- filtered . push ( device ) ;
135+ frontCameras . push ( device ) ;
135136 }
136- // Include ultrawide camera
137137 else if ( label . includes ( 'ultra' ) ||
138138 label . includes ( 'wide' ) ||
139139 label . includes ( '0.5' ) ) {
140- filtered . push ( device ) ;
140+ ultrawideCameras . push ( device ) ;
141141 }
142142 } else {
143143 // Fallback: use label-based filtering without stream access
144144 const label = device . label . toLowerCase ( ) ;
145145 if ( label . includes ( 'back' ) ||
146- label . includes ( 'rear' ) ||
147- label . includes ( 'front' ) ||
148- label . includes ( 'selfie' ) ||
149- label . includes ( 'ultra' ) ||
150- label . includes ( 'wide' ) ||
151- ( ! label . includes ( 'front' ) && devices . indexOf ( device ) === 0 ) ) {
152- filtered . push ( device ) ;
146+ label . includes ( 'rear' ) ||
147+ ( ! label . includes ( 'front' ) && ! label . includes ( 'selfie' ) && devices . indexOf ( device ) === 0 ) ) {
148+ backCameras . push ( device ) ;
149+ }
150+ else if ( label . includes ( 'front' ) ||
151+ label . includes ( 'selfie' ) ) {
152+ frontCameras . push ( device ) ;
153+ }
154+ else if ( label . includes ( 'ultra' ) ||
155+ label . includes ( 'wide' ) ||
156+ label . includes ( '0.5' ) ) {
157+ ultrawideCameras . push ( device ) ;
153158 }
154159 }
155160 } catch {
156161 // If we can't get capabilities, use label-based filtering
157162 const label = device . label . toLowerCase ( ) ;
158163 if ( label . includes ( 'back' ) ||
159- label . includes ( 'rear' ) ||
160- label . includes ( 'front' ) ||
161- label . includes ( 'selfie' ) ||
162- label . includes ( 'ultra' ) ||
163- label . includes ( 'wide' ) ||
164- ( ! label . includes ( 'front' ) && devices . indexOf ( device ) === 0 ) ) {
165- filtered . push ( device ) ;
164+ label . includes ( 'rear' ) ||
165+ ( ! label . includes ( 'front' ) && ! label . includes ( 'selfie' ) && devices . indexOf ( device ) === 0 ) ) {
166+ backCameras . push ( device ) ;
167+ }
168+ else if ( label . includes ( 'front' ) ||
169+ label . includes ( 'selfie' ) ) {
170+ frontCameras . push ( device ) ;
171+ }
172+ else if ( label . includes ( 'ultra' ) ||
173+ label . includes ( 'wide' ) ||
174+ label . includes ( '0.5' ) ) {
175+ ultrawideCameras . push ( device ) ;
166176 }
167177 }
168178 }
169179
170- // Mobile device fallback: if we have multiple devices but filtering returned few,
171- // and no permissions were granted, include more devices
172- if ( filtered . length < 2 && devices . length >= 2 && ! permissionGranted ) {
180+ // Build filtered array with back camera first, then front, then ultrawide
181+ const filtered : MediaDeviceInfo [ ] = [ ] ;
182+
183+ // Add back cameras first (prioritize main back camera)
184+ if ( backCameras . length > 0 ) {
185+ filtered . push ( backCameras [ 0 ] ) ; // Main back camera
186+ }
187+
188+ // Add front camera second
189+ if ( frontCameras . length > 0 ) {
190+ filtered . push ( frontCameras [ 0 ] ) ; // Main front camera
191+ }
192+
193+ // Add ultrawide camera third (if available)
194+ if ( ultrawideCameras . length > 0 ) {
195+ filtered . push ( ultrawideCameras [ 0 ] ) ; // First ultrawide camera
196+ }
197+
198+ // Mobile device fallback: if no cameras were categorized properly
199+ if ( filtered . length === 0 && devices . length >= 2 && ! permissionGranted ) {
173200 // On mobile, typically the first device is back camera, second is front
174- filtered . length = 0 ; // Clear filtered array
175201 filtered . push ( devices [ 0 ] ) ; // Back camera (usually)
176202 if ( devices . length > 1 ) {
177203 filtered . push ( devices [ 1 ] ) ; // Front camera (usually)
178204 }
179- // Add any additional cameras that might be ultrawide
205+
206+ // Only add one more camera if it might be ultrawide
180207 for ( let i = 2 ; i < devices . length ; i ++ ) {
181208 const label = devices [ i ] . label . toLowerCase ( ) ;
182209 if ( label . includes ( 'ultra' ) || label . includes ( 'wide' ) || label . includes ( '0.5' ) ) {
183210 filtered . push ( devices [ i ] ) ;
211+ break ; // Only add one additional camera
184212 }
185213 }
186214 }
187215
188- // If no cameras matched our criteria, fall back to first available camera
216+ // Final fallback: if no cameras matched our criteria, fall back to first available camera
189217 if ( filtered . length === 0 && devices . length > 0 ) {
190218 filtered . push ( devices [ 0 ] ) ;
191219 }
@@ -286,6 +314,13 @@ export default function ProductSearch() {
286314 } ;
287315 } , [ router , pageLoading , currentCameraIndex ] ) ;
288316
317+ // Separate effect to initialize camera index when cameras are first detected
318+ useEffect ( ( ) => {
319+ if ( filteredCameras . length > 0 && currentCameraIndex !== 0 ) {
320+ setCurrentCameraIndex ( 0 ) ; // Always start with back camera (index 0)
321+ }
322+ } , [ filteredCameras , currentCameraIndex ] ) ;
323+
289324 // Cleanup camera when component unmounts
290325 useEffect ( ( ) => {
291326 return ( ) => {
0 commit comments