forked from trent_larson/crowd-funder-for-time-pwa
Fix: image retry in mobile app displays error
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
|
||||
<!-- FEEDBACK: Show if camera preview is not visible after mounting -->
|
||||
<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"
|
||||
>
|
||||
<strong>Camera preview not started.</strong>
|
||||
@@ -328,6 +328,9 @@ export default class ImageMethodDialog extends Vue {
|
||||
/** Whether to show camera preview */
|
||||
showCameraPreview = false;
|
||||
|
||||
/** Whether currently retrying camera preview */
|
||||
isRetrying = false;
|
||||
|
||||
/** Camera stream reference */
|
||||
private cameraStream: MediaStream | null = null;
|
||||
|
||||
@@ -589,6 +592,7 @@ export default class ImageMethodDialog extends Vue {
|
||||
}
|
||||
this.blob = undefined;
|
||||
this.showCameraPreview = false;
|
||||
this.isRetrying = false;
|
||||
}
|
||||
|
||||
async startCameraPreview() {
|
||||
@@ -600,6 +604,13 @@ export default class ImageMethodDialog extends Vue {
|
||||
"getUserMedia available:",
|
||||
!!(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 {
|
||||
this.cameraState = "initializing";
|
||||
@@ -611,10 +622,17 @@ export default class ImageMethodDialog extends Vue {
|
||||
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({
|
||||
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.cameraState = "active";
|
||||
this.cameraStateMessage = "Camera is active";
|
||||
@@ -623,20 +641,26 @@ export default class ImageMethodDialog extends Vue {
|
||||
|
||||
const videoElement = this.$refs.videoElement as HTMLVideoElement;
|
||||
if (videoElement) {
|
||||
logger.debug("Setting video element srcObject");
|
||||
videoElement.srcObject = stream;
|
||||
await new Promise((resolve) => {
|
||||
await new Promise((resolve, reject) => {
|
||||
videoElement.onloadedmetadata = () => {
|
||||
logger.debug("Video metadata loaded, starting playback");
|
||||
videoElement
|
||||
.play()
|
||||
.then(() => {
|
||||
logger.debug("Video element started playing");
|
||||
logger.debug("Video element started playing successfully");
|
||||
resolve(true);
|
||||
})
|
||||
.catch((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 {
|
||||
logger.error("Video element not found");
|
||||
@@ -672,14 +696,31 @@ export default class ImageMethodDialog extends Vue {
|
||||
}
|
||||
|
||||
stopCameraPreview() {
|
||||
logger.debug("stopCameraPreview called");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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.cameraState = "off";
|
||||
this.cameraStateMessage = "Camera stopped";
|
||||
this.error = null;
|
||||
// Don't reset isRetrying here - let the calling method handle it
|
||||
|
||||
logger.debug("Camera preview stopped and cleaned up");
|
||||
}
|
||||
|
||||
async capturePhoto() {
|
||||
@@ -746,10 +787,42 @@ export default class ImageMethodDialog extends Vue {
|
||||
}
|
||||
|
||||
async retryImage() {
|
||||
// Set retry flag FIRST to prevent error message flash
|
||||
this.isRetrying = true;
|
||||
|
||||
this.blob = undefined;
|
||||
if (!this.platformCapabilities.isNativeApp) {
|
||||
await this.startCameraPreview();
|
||||
this.showRetry = true;
|
||||
|
||||
// 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() {
|
||||
|
||||
Reference in New Issue
Block a user