forked from jsnbuchanan/crowd-funder-for-time-pwa
Mobile native to use web camera
- Ensure consistent UI experience for uploading photos across mobile web and native
This commit is contained in:
@@ -379,16 +379,16 @@ export default class ImageMethodDialog extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open(setImageFn: (arg: string) => void, claimType: string, crop?: boolean) {
|
open(setImageFn: (arg: string) => void, claimType: string, crop?: boolean) {
|
||||||
|
logger.debug("ImageMethodDialog.open called");
|
||||||
this.claimType = claimType;
|
this.claimType = claimType;
|
||||||
this.crop = !!crop;
|
this.crop = !!crop;
|
||||||
this.imageCallback = setImageFn;
|
this.imageCallback = setImageFn;
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
this.currentFacingMode = this.defaultCameraMode as 'environment' | 'user';
|
this.currentFacingMode = this.defaultCameraMode as 'environment' | 'user';
|
||||||
|
|
||||||
// Start camera preview immediately if not on mobile
|
// Start camera preview immediately
|
||||||
if (!this.platformCapabilities.isNativeApp) {
|
logger.debug("Starting camera preview from open()");
|
||||||
this.startCameraPreview();
|
this.startCameraPreview();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async uploadImageFile(event: Event) {
|
async uploadImageFile(event: Event) {
|
||||||
@@ -457,44 +457,19 @@ export default class ImageMethodDialog extends Vue {
|
|||||||
logger.debug("startCameraPreview called");
|
logger.debug("startCameraPreview called");
|
||||||
logger.debug("Current showCameraPreview state:", this.showCameraPreview);
|
logger.debug("Current showCameraPreview state:", this.showCameraPreview);
|
||||||
logger.debug("Platform capabilities:", this.platformCapabilities);
|
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 {
|
try {
|
||||||
this.cameraState = "initializing";
|
this.cameraState = "initializing";
|
||||||
this.cameraStateMessage = "Requesting camera access...";
|
this.cameraStateMessage = "Requesting camera access...";
|
||||||
this.showCameraPreview = true;
|
this.showCameraPreview = true;
|
||||||
await this.$nextTick();
|
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({
|
const stream = await navigator.mediaDevices.getUserMedia({
|
||||||
video: { facingMode: this.currentFacingMode },
|
video: { facingMode: this.currentFacingMode },
|
||||||
});
|
});
|
||||||
@@ -511,10 +486,17 @@ export default class ImageMethodDialog extends Vue {
|
|||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
videoElement.onloadedmetadata = () => {
|
videoElement.onloadedmetadata = () => {
|
||||||
videoElement.play().then(() => {
|
videoElement.play().then(() => {
|
||||||
|
logger.debug("Video element started playing");
|
||||||
resolve(true);
|
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) {
|
} catch (error) {
|
||||||
logger.error("Error starting camera preview:", error);
|
logger.error("Error starting camera preview:", error);
|
||||||
@@ -538,6 +520,7 @@ export default class ImageMethodDialog extends Vue {
|
|||||||
this.cameraState = "error";
|
this.cameraState = "error";
|
||||||
this.cameraStateMessage = errorMessage;
|
this.cameraStateMessage = errorMessage;
|
||||||
this.error = errorMessage;
|
this.error = errorMessage;
|
||||||
|
this.showCameraPreview = false;
|
||||||
this.$notify(
|
this.$notify(
|
||||||
{
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
@@ -547,7 +530,6 @@ export default class ImageMethodDialog extends Vue {
|
|||||||
},
|
},
|
||||||
5000,
|
5000,
|
||||||
);
|
);
|
||||||
this.showCameraPreview = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -600,39 +582,17 @@ export default class ImageMethodDialog extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async rotateCamera() {
|
async rotateCamera() {
|
||||||
if (this.platformCapabilities.isNativeApp) {
|
// Toggle between front and back cameras
|
||||||
try {
|
this.currentFacingMode = this.currentFacingMode === 'environment' ? 'user' : 'environment';
|
||||||
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
|
// Stop current stream
|
||||||
if (this.cameraStream) {
|
if (this.cameraStream) {
|
||||||
this.cameraStream.getTracks().forEach(track => track.stop());
|
this.cameraStream.getTracks().forEach(track => track.stop());
|
||||||
this.cameraStream = null;
|
this.cameraStream = null;
|
||||||
}
|
|
||||||
|
|
||||||
// Start new stream with updated facing mode
|
|
||||||
await this.startCameraPreview();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start new stream with updated facing mode
|
||||||
|
await this.startCameraPreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
private createBlobURL(blob: Blob): string {
|
private createBlobURL(blob: Blob): string {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<ImageMethodDialog ref="imageDialog" />
|
<ImageMethodDialog ref="imageDialog" default-camera-mode="environment" />
|
||||||
|
|
||||||
<input
|
<input
|
||||||
v-model="agentDid"
|
v-model="agentDid"
|
||||||
|
|||||||
Reference in New Issue
Block a user