Skip to content

Commit 56333aa

Browse files
committed
feat: camera flip button
1 parent 9a1324d commit 56333aa

File tree

1 file changed

+54
-10
lines changed

1 file changed

+54
-10
lines changed

src/app/search/page.tsx

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ export default function ProductSearch() {
6969
const [showDropdown, setShowDropdown] = useState(false);
7070
const [firebaseUser, setFirebaseUser] = useState<FirebaseUser | null>(null);
7171
const [pageLoading, setPageLoading] = useState(true);
72+
const [currentCameraIndex, setCurrentCameraIndex] = useState(0);
73+
const [availableCameras, setAvailableCameras] = useState<MediaDeviceInfo[]>([]);
7274
const videoRef = useRef<HTMLVideoElement>(null);
7375
const router = useRouter();
7476
const { setNavigating } = useTopBar();
@@ -129,10 +131,13 @@ export default function ProductSearch() {
129131
const startCamera = async () => {
130132
try {
131133
const devices = await BrowserMultiFormatReader.listVideoInputDevices();
134+
setAvailableCameras(devices);
135+
132136
if (devices.length > 0 && videoRef.current && active) {
137+
const selectedDevice = devices[currentCameraIndex] || devices[0];
133138
const codeReader = new BrowserMultiFormatReader();
134139
controls = await codeReader.decodeFromVideoDevice(
135-
devices[0].deviceId,
140+
selectedDevice.deviceId,
136141
videoRef.current,
137142
(result, _err, c) => {
138143
if (result && active) {
@@ -156,7 +161,15 @@ export default function ProductSearch() {
156161
clearTimeout(timer);
157162
if (controls) controls.stop();
158163
};
159-
}, [router, pageLoading]);
164+
}, [router, pageLoading, currentCameraIndex]);
165+
166+
const flipCamera = () => {
167+
if (availableCameras.length > 1) {
168+
setCurrentCameraIndex((prevIndex) =>
169+
(prevIndex + 1) % availableCameras.length
170+
);
171+
}
172+
};
160173

161174
const handleManualSearch = () => {
162175
handleSearch(query);
@@ -280,14 +293,45 @@ export default function ProductSearch() {
280293
>
281294
Scan a Barcode
282295
</h1>
283-
<div
284-
className="rounded-lg overflow-hidden shadow-xl mb-4 w-full max-w-xs flex items-center justify-center aspect-video"
285-
style={{
286-
backgroundColor: colours.content.surface,
287-
border: `2px solid ${colours.card.border}`
288-
}}
289-
>
290-
<video ref={videoRef} className="w-full h-auto" />
296+
<div className="relative">
297+
<div
298+
className="rounded-lg overflow-hidden shadow-xl mb-4 w-full max-w-xs flex items-center justify-center aspect-video"
299+
style={{
300+
backgroundColor: colours.content.surface,
301+
border: `2px solid ${colours.card.border}`
302+
}}
303+
>
304+
<video ref={videoRef} className="w-full h-auto" />
305+
</div>
306+
{/* Flip Camera Button */}
307+
{availableCameras.length > 1 && (
308+
<button
309+
onClick={flipCamera}
310+
className="absolute top-2 right-2 p-2 rounded-full shadow-lg transition-all duration-200 hover:scale-110 active:scale-95"
311+
style={{
312+
backgroundColor: colours.content.surface,
313+
border: `1px solid ${colours.card.border}`,
314+
color: colours.text.primary
315+
}}
316+
title="Flip Camera"
317+
>
318+
<svg
319+
width="20"
320+
height="20"
321+
viewBox="0 0 24 24"
322+
fill="none"
323+
stroke="currentColor"
324+
strokeWidth="2"
325+
strokeLinecap="round"
326+
strokeLinejoin="round"
327+
>
328+
<path d="M3 6h4l2-2h6l2 2h4a1 1 0 0 1 1 1v11a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"/>
329+
<circle cx="12" cy="13" r="3"/>
330+
<path d="M16 6l2-2"/>
331+
<path d="M16 6l-2-2"/>
332+
</svg>
333+
</button>
334+
)}
291335
</div>
292336
</div>
293337
</ContentBox>

0 commit comments

Comments
 (0)