Browse Source

Fix: image retry in mobile app displays error

pull/142/head
Jose Olarte III 3 days ago
parent
commit
ff95c001f4
  1. 89
      src/components/ImageMethodDialog.vue

89
src/components/ImageMethodDialog.vue

@ -20,7 +20,7 @@
<!-- FEEDBACK: Show if camera preview is not visible after mounting --> <!-- FEEDBACK: Show if camera preview is not visible after mounting -->
<div <div
v-if="!showCameraPreview && !blob && isRegistered" v-if="!showCameraPreview && !blob && isRegistered && !isRetrying"
class="bg-red-100 text-red-700 border border-red-400 rounded px-4 py-3 my-4 text-sm" class="bg-red-100 text-red-700 border border-red-400 rounded px-4 py-3 my-4 text-sm"
> >
<strong>Camera preview not started.</strong> <strong>Camera preview not started.</strong>
@ -328,6 +328,9 @@ export default class ImageMethodDialog extends Vue {
/** Whether to show camera preview */ /** Whether to show camera preview */
showCameraPreview = false; showCameraPreview = false;
/** Whether currently retrying camera preview */
isRetrying = false;
/** Camera stream reference */ /** Camera stream reference */
private cameraStream: MediaStream | null = null; private cameraStream: MediaStream | null = null;
@ -589,6 +592,7 @@ export default class ImageMethodDialog extends Vue {
} }
this.blob = undefined; this.blob = undefined;
this.showCameraPreview = false; this.showCameraPreview = false;
this.isRetrying = false;
} }
async startCameraPreview() { async startCameraPreview() {
@ -600,6 +604,13 @@ export default class ImageMethodDialog extends Vue {
"getUserMedia available:", "getUserMedia available:",
!!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia), !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia),
); );
logger.debug("Current facing mode:", this.currentFacingMode);
// If camera is already active, don't restart it
if (this.cameraState === "active" && this.cameraStream) {
logger.debug("Camera is already active, skipping restart");
return;
}
try { try {
this.cameraState = "initializing"; this.cameraState = "initializing";
@ -611,10 +622,17 @@ export default class ImageMethodDialog extends Vue {
throw new Error("Camera API not available in this browser"); throw new Error("Camera API not available in this browser");
} }
logger.debug(
"Requesting camera stream with facingMode:",
this.currentFacingMode,
);
const stream = await navigator.mediaDevices.getUserMedia({ const stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: this.currentFacingMode }, video: { facingMode: this.currentFacingMode },
}); });
logger.debug("Camera access granted"); logger.debug(
"Camera access granted, stream tracks:",
stream.getTracks().map((t) => ({ kind: t.kind, label: t.label })),
);
this.cameraStream = stream; this.cameraStream = stream;
this.cameraState = "active"; this.cameraState = "active";
this.cameraStateMessage = "Camera is active"; this.cameraStateMessage = "Camera is active";
@ -623,20 +641,26 @@ export default class ImageMethodDialog extends Vue {
const videoElement = this.$refs.videoElement as HTMLVideoElement; const videoElement = this.$refs.videoElement as HTMLVideoElement;
if (videoElement) { if (videoElement) {
logger.debug("Setting video element srcObject");
videoElement.srcObject = stream; videoElement.srcObject = stream;
await new Promise((resolve) => { await new Promise((resolve, reject) => {
videoElement.onloadedmetadata = () => { videoElement.onloadedmetadata = () => {
logger.debug("Video metadata loaded, starting playback");
videoElement videoElement
.play() .play()
.then(() => { .then(() => {
logger.debug("Video element started playing"); logger.debug("Video element started playing successfully");
resolve(true); resolve(true);
}) })
.catch((error) => { .catch((error) => {
logger.error("Error playing video:", error); logger.error("Error playing video:", error);
throw error; reject(error);
}); });
}; };
videoElement.onerror = (error) => {
logger.error("Video element error:", error);
reject(new Error("Video element error"));
};
}); });
} else { } else {
logger.error("Video element not found"); logger.error("Video element not found");
@ -672,14 +696,31 @@ export default class ImageMethodDialog extends Vue {
} }
stopCameraPreview() { stopCameraPreview() {
logger.debug("stopCameraPreview called");
if (this.cameraStream) { if (this.cameraStream) {
this.cameraStream.getTracks().forEach((track) => track.stop()); logger.debug("Stopping camera stream tracks");
this.cameraStream.getTracks().forEach((track) => {
track.stop();
logger.debug(`Stopped track: ${track.kind} - ${track.label}`);
});
this.cameraStream = null; this.cameraStream = null;
} }
// Clear video element srcObject to ensure cleanup
const videoElement = this.$refs.videoElement as HTMLVideoElement;
if (videoElement) {
videoElement.srcObject = null;
logger.debug("Cleared video element srcObject");
}
this.showCameraPreview = false; this.showCameraPreview = false;
this.cameraState = "off"; this.cameraState = "off";
this.cameraStateMessage = "Camera stopped"; this.cameraStateMessage = "Camera stopped";
this.error = null; this.error = null;
// Don't reset isRetrying here - let the calling method handle it
logger.debug("Camera preview stopped and cleaned up");
} }
async capturePhoto() { async capturePhoto() {
@ -746,10 +787,42 @@ export default class ImageMethodDialog extends Vue {
} }
async retryImage() { async retryImage() {
// Set retry flag FIRST to prevent error message flash
this.isRetrying = true;
this.blob = undefined; this.blob = undefined;
if (!this.platformCapabilities.isNativeApp) { this.showRetry = true;
await this.startCameraPreview();
// Stop camera stream but keep showCameraPreview true to prevent error flash
if (this.cameraStream) {
logger.debug("Stopping camera stream tracks");
this.cameraStream.getTracks().forEach((track) => {
track.stop();
logger.debug(`Stopped track: ${track.kind} - ${track.label}`);
});
this.cameraStream = null;
} }
// Clear video element srcObject to ensure cleanup
const videoElement = this.$refs.videoElement as HTMLVideoElement;
if (videoElement) {
videoElement.srcObject = null;
logger.debug("Cleared video element srcObject");
}
this.cameraState = "off";
this.cameraStateMessage = "Camera stopped";
this.error = null;
// Add a small delay to ensure cleanup is complete
await new Promise((resolve) => setTimeout(resolve, 100));
// For native apps (iOS/Android), we need to restart the camera preview
// For web browsers, we also restart the camera preview
await this.startCameraPreview();
// Reset retry flag after camera is started
this.isRetrying = false;
} }
async uploadImage() { async uploadImage() {

Loading…
Cancel
Save