From efc720e47f923534827dd0aeffcc8a90d8c8441c Mon Sep 17 00:00:00 2001 From: Jose Olarte III Date: Tue, 27 May 2025 17:46:19 +0800 Subject: [PATCH] Mobile native to use web camera - Ensure consistent UI experience for uploading photos across mobile web and native --- src/components/ImageMethodDialog.vue | 96 ++++++++-------------------- src/views/NewEditProjectView.vue | 2 +- 2 files changed, 29 insertions(+), 69 deletions(-) diff --git a/src/components/ImageMethodDialog.vue b/src/components/ImageMethodDialog.vue index 91029963..6598946f 100644 --- a/src/components/ImageMethodDialog.vue +++ b/src/components/ImageMethodDialog.vue @@ -379,16 +379,16 @@ export default class ImageMethodDialog extends Vue { } open(setImageFn: (arg: string) => void, claimType: string, crop?: boolean) { + logger.debug("ImageMethodDialog.open called"); this.claimType = claimType; this.crop = !!crop; this.imageCallback = setImageFn; this.visible = true; this.currentFacingMode = this.defaultCameraMode as 'environment' | 'user'; - // Start camera preview immediately if not on mobile - if (!this.platformCapabilities.isNativeApp) { - this.startCameraPreview(); - } + // Start camera preview immediately + logger.debug("Starting camera preview from open()"); + this.startCameraPreview(); } async uploadImageFile(event: Event) { @@ -457,44 +457,19 @@ export default class ImageMethodDialog extends Vue { logger.debug("startCameraPreview called"); 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)); - if (this.platformCapabilities.isNativeApp) { - logger.debug("Using platform service for mobile device"); - this.cameraState = "initializing"; - this.cameraStateMessage = "Using platform camera service..."; - try { - const result = await this.platformService.takePicture(); - this.blob = result.blob; - this.fileName = result.fileName; - this.cameraState = "ready"; - this.cameraStateMessage = "Photo captured successfully"; - } catch (error) { - logger.error("Error taking picture:", error); - this.cameraState = "error"; - this.cameraStateMessage = - error instanceof Error ? error.message : "Failed to take picture"; - this.error = - error instanceof Error ? error.message : "Failed to take picture"; - this.$notify( - { - group: "alert", - type: "danger", - title: "Error", - text: "Failed to take picture. Please try again.", - }, - 5000, - ); - } - return; - } - - logger.debug("Starting camera preview for desktop browser"); try { this.cameraState = "initializing"; this.cameraStateMessage = "Requesting camera access..."; this.showCameraPreview = true; await this.$nextTick(); + if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { + throw new Error("Camera API not available in this browser"); + } + const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: this.currentFacingMode }, }); @@ -511,10 +486,17 @@ export default class ImageMethodDialog extends Vue { 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; }); }; }); + } else { + logger.error("Video element not found"); + throw new Error("Video element not found"); } } catch (error) { logger.error("Error starting camera preview:", error); @@ -538,6 +520,7 @@ export default class ImageMethodDialog extends Vue { this.cameraState = "error"; this.cameraStateMessage = errorMessage; this.error = errorMessage; + this.showCameraPreview = false; this.$notify( { group: "alert", @@ -547,7 +530,6 @@ export default class ImageMethodDialog extends Vue { }, 5000, ); - this.showCameraPreview = false; } } @@ -600,39 +582,17 @@ export default class ImageMethodDialog extends Vue { } async rotateCamera() { - if (this.platformCapabilities.isNativeApp) { - try { - await this.platformService.rotateCamera(); - // Take a new picture with the rotated camera - 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 { - // For web browsers, toggle between front and back cameras - this.currentFacingMode = this.currentFacingMode === 'environment' ? 'user' : 'environment'; - - // Stop current stream - if (this.cameraStream) { - this.cameraStream.getTracks().forEach(track => track.stop()); - this.cameraStream = null; - } - - // Start new stream with updated facing mode - await this.startCameraPreview(); + // Toggle between front and back cameras + this.currentFacingMode = this.currentFacingMode === 'environment' ? 'user' : 'environment'; + + // Stop current stream + if (this.cameraStream) { + this.cameraStream.getTracks().forEach(track => track.stop()); + this.cameraStream = null; } + + // Start new stream with updated facing mode + await this.startCameraPreview(); } private createBlobURL(blob: Blob): string { diff --git a/src/views/NewEditProjectView.vue b/src/views/NewEditProjectView.vue index f5d90b25..9cb36a9f 100644 --- a/src/views/NewEditProjectView.vue +++ b/src/views/NewEditProjectView.vue @@ -50,7 +50,7 @@ /> - +