@ -119,12 +119,21 @@
playsinline
muted
> < / video >
< button
class = "absolute bottom-4 left-1/2 -translate-x-1/2 bg-white text-slate-800 p-3 rounded-full text-2xl leading-none"
@ click = "capturePhoto"
>
< font -awesome icon = "camera" class = "w-[1em]" / >
< / button >
< div class = "absolute bottom-4 inset-x-0 flex items-center justify-center gap-4" >
< button
class = "bg-white text-slate-800 p-3 rounded-full text-2xl leading-none"
@ click = "capturePhoto"
>
< font -awesome icon = "camera" class = "w-[1em]" / >
< / button >
< button
v - if = "platformCapabilities.isMobile"
class = "bg-white text-slate-800 p-3 rounded-full text-2xl leading-none"
@ click = "rotateCamera"
>
< font -awesome icon = "rotate" class = "w-[1em]" / >
< / button >
< / div >
< / div >
< / div >
< div
@ -303,6 +312,9 @@ export default class ImageMethodDialog extends Vue {
/** Camera stream reference */
private cameraStream : MediaStream | null = null ;
/** Current camera facing mode */
private currentFacingMode : 'environment' | 'user' = 'environment' ;
private platformService = PlatformServiceFactory . getInstance ( ) ;
URL = window . URL || window . webkitURL ;
@ -478,7 +490,7 @@ export default class ImageMethodDialog extends Vue {
await this . $nextTick ( ) ;
const stream = await navigator . mediaDevices . getUserMedia ( {
video : { facingMode : "environment" } ,
video : { facingMode : this . currentFacingMode } ,
} ) ;
logger . debug ( "Camera access granted" ) ;
this . cameraStream = stream ;
@ -503,15 +515,17 @@ export default class ImageMethodDialog extends Vue {
let errorMessage =
error instanceof Error ? error . message : "Failed to access camera" ;
if (
error instanceof Error && (
error . name === "NotReadableError" ||
error . name === "TrackStartError"
) {
) ) {
errorMessage =
"Camera is in use by another application. Please close any other apps or browser tabs using the camera and try again." ;
} else if (
error instanceof Error && (
error . name === "NotAllowedError" ||
error . name === "PermissionDeniedError"
) {
) ) {
errorMessage =
"Camera access was denied. Please allow camera access in your browser settings." ;
}
@ -579,6 +593,42 @@ export default class ImageMethodDialog extends Vue {
}
}
async rotateCamera ( ) {
if ( this . platformCapabilities . isNativeApp ) {
try {
await this . platformService . rotateCamera ( ) ;
/ / T a k e a n e w p i c t u r e w i t h t h e r o t a t e d c a m e r a
const result = await this . platformService . takePicture ( ) ;
this . blob = result . blob ;
this . fileName = result . fileName ;
this . showRetry = true ;
} catch ( error ) {
logger . error ( "Error rotating camera:" , error ) ;
this . $notify (
{
group : "alert" ,
type : "danger" ,
title : "Error" ,
text : "Failed to rotate camera. Please try again." ,
} ,
5000 ,
) ;
}
} else {
/ / F o r w e b b r o w s e r s , t o g g l e b e t w e e n f r o n t a n d b a c k c a m e r a s
this . currentFacingMode = this . currentFacingMode === 'environment' ? 'user' : 'environment' ;
/ / S t o p c u r r e n t s t r e a m
if ( this . cameraStream ) {
this . cameraStream . getTracks ( ) . forEach ( track => track . stop ( ) ) ;
this . cameraStream = null ;
}
/ / S t a r t n e w s t r e a m w i t h u p d a t e d f a c i n g m o d e
await this . startCameraPreview ( ) ;
}
}
private createBlobURL ( blob : Blob ) : string {
return URL . createObjectURL ( blob ) ;
}