|
|
@ -4,7 +4,9 @@ |
|
|
|
<div class="text-lg text-center font-bold relative"> |
|
|
|
<h1 id="ViewHeading" class="text-center font-bold"> |
|
|
|
<span v-if="uploading">Uploading Image…</span> |
|
|
|
<span v-else-if="blob">{{ crop ? 'Crop Image' : 'Preview Image' }}</span> |
|
|
|
<span v-else-if="blob">{{ |
|
|
|
crop ? "Crop Image" : "Preview Image" |
|
|
|
}}</span> |
|
|
|
<span v-else-if="showCameraPreview">Upload Image</span> |
|
|
|
<span v-else>Add Photo</span> |
|
|
|
</h1> |
|
|
@ -119,7 +121,9 @@ |
|
|
|
playsinline |
|
|
|
muted |
|
|
|
></video> |
|
|
|
<div class="absolute bottom-4 inset-x-0 flex items-center justify-center gap-4"> |
|
|
|
<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" |
|
|
@ -278,9 +282,9 @@ const inputImageFileNameRef = ref<Blob>(); |
|
|
|
}, |
|
|
|
defaultCameraMode: { |
|
|
|
type: String, |
|
|
|
default: 'environment', |
|
|
|
validator: (value: string) => ['environment', 'user'].includes(value) |
|
|
|
} |
|
|
|
default: "environment", |
|
|
|
validator: (value: string) => ["environment", "user"].includes(value), |
|
|
|
}, |
|
|
|
}, |
|
|
|
}) |
|
|
|
export default class ImageMethodDialog extends Vue { |
|
|
@ -323,7 +327,7 @@ export default class ImageMethodDialog extends Vue { |
|
|
|
private cameraStream: MediaStream | null = null; |
|
|
|
|
|
|
|
/** Current camera facing mode */ |
|
|
|
private currentFacingMode: 'environment' | 'user' = 'environment'; |
|
|
|
private currentFacingMode: "environment" | "user" = "environment"; |
|
|
|
|
|
|
|
private platformService = PlatformServiceFactory.getInstance(); |
|
|
|
URL = window.URL || window.webkitURL; |
|
|
@ -391,7 +395,7 @@ export default class ImageMethodDialog extends Vue { |
|
|
|
this.crop = !!crop; |
|
|
|
this.imageCallback = setImageFn; |
|
|
|
this.visible = true; |
|
|
|
this.currentFacingMode = this.defaultCameraMode as 'environment' | 'user'; |
|
|
|
this.currentFacingMode = this.defaultCameraMode as "environment" | "user"; |
|
|
|
|
|
|
|
// Start camera preview immediately |
|
|
|
logger.debug("Starting camera preview from open()"); |
|
|
@ -465,7 +469,10 @@ export default class ImageMethodDialog extends Vue { |
|
|
|
logger.debug("Current showCameraPreview state:", this.showCameraPreview); |
|
|
|
logger.debug("Platform capabilities:", this.platformCapabilities); |
|
|
|
logger.debug("MediaDevices available:", !!navigator.mediaDevices); |
|
|
|
logger.debug("getUserMedia available:", !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia)); |
|
|
|
logger.debug( |
|
|
|
"getUserMedia available:", |
|
|
|
!!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia), |
|
|
|
); |
|
|
|
|
|
|
|
try { |
|
|
|
this.cameraState = "initializing"; |
|
|
@ -492,13 +499,16 @@ export default class ImageMethodDialog extends Vue { |
|
|
|
videoElement.srcObject = stream; |
|
|
|
await new Promise((resolve) => { |
|
|
|
videoElement.onloadedmetadata = () => { |
|
|
|
videoElement.play().then(() => { |
|
|
|
logger.debug("Video element started playing"); |
|
|
|
resolve(true); |
|
|
|
}).catch(error => { |
|
|
|
logger.error("Error playing video:", error); |
|
|
|
throw error; |
|
|
|
}); |
|
|
|
videoElement |
|
|
|
.play() |
|
|
|
.then(() => { |
|
|
|
logger.debug("Video element started playing"); |
|
|
|
resolve(true); |
|
|
|
}) |
|
|
|
.catch((error) => { |
|
|
|
logger.error("Error playing video:", error); |
|
|
|
throw error; |
|
|
|
}); |
|
|
|
}; |
|
|
|
}); |
|
|
|
} else { |
|
|
@ -510,17 +520,16 @@ 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" |
|
|
|
)) { |
|
|
|
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" |
|
|
|
)) { |
|
|
|
error instanceof Error && |
|
|
|
(error.name === "NotAllowedError" || |
|
|
|
error.name === "PermissionDeniedError") |
|
|
|
) { |
|
|
|
errorMessage = |
|
|
|
"Camera access was denied. Please allow camera access in your browser settings."; |
|
|
|
} |
|
|
@ -590,11 +599,12 @@ export default class ImageMethodDialog extends Vue { |
|
|
|
|
|
|
|
async rotateCamera() { |
|
|
|
// Toggle between front and back cameras |
|
|
|
this.currentFacingMode = this.currentFacingMode === 'environment' ? 'user' : 'environment'; |
|
|
|
|
|
|
|
this.currentFacingMode = |
|
|
|
this.currentFacingMode === "environment" ? "user" : "environment"; |
|
|
|
|
|
|
|
// Stop current stream |
|
|
|
if (this.cameraStream) { |
|
|
|
this.cameraStream.getTracks().forEach(track => track.stop()); |
|
|
|
this.cameraStream.getTracks().forEach((track) => track.stop()); |
|
|
|
this.cameraStream = null; |
|
|
|
} |
|
|
|
|
|
|
|