forked from jsnbuchanan/crowd-funder-for-time-pwa
fix(qr): add timeout fallback for QR scanner initialization
- Adds a 4-second timeout to force initialization complete if the QR scanner promise never resolves - Prevents UI from being stuck on "Checking camera access..." when camera is active but init promise hangs - Retains retry logic for transient initialization failures
This commit is contained in:
@@ -274,87 +274,29 @@ export default class QRScannerDialog extends Vue {
|
||||
}
|
||||
|
||||
async onInit(promise: Promise<void>, attempt = 1): Promise<void> {
|
||||
if (this.isNativePlatform) {
|
||||
logger.log("Skipping web scanner initialization on native platform");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isNativePlatform) return;
|
||||
this.isInitializing = true;
|
||||
this.error = null;
|
||||
this.initializationStatus = "Checking camera access...";
|
||||
|
||||
let timeoutHit = false;
|
||||
const timeout = setTimeout(() => {
|
||||
timeoutHit = true;
|
||||
this.isInitializing = false;
|
||||
this.cameraStatus = "Ready (timeout fallback)";
|
||||
this.initializationStatus = "Camera ready (fallback)";
|
||||
// Optionally log a warning or show a subtle message
|
||||
}, 4000); // 4 seconds
|
||||
|
||||
try {
|
||||
// First check if mediaDevices API is available
|
||||
if (!navigator.mediaDevices) {
|
||||
throw new Error(
|
||||
"Camera API not available. Please ensure you're using HTTPS.",
|
||||
);
|
||||
}
|
||||
|
||||
logger.log("Starting QR scanner initialization...", {
|
||||
mediaDevices: !!navigator.mediaDevices,
|
||||
getUserMedia: !!(
|
||||
navigator.mediaDevices && navigator.mediaDevices.getUserMedia
|
||||
),
|
||||
constraints: {
|
||||
video: true,
|
||||
facingMode: this.preferredCamera,
|
||||
},
|
||||
});
|
||||
|
||||
// Explicitly request camera permission first
|
||||
this.initializationStatus = "Requesting camera permission...";
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
video: {
|
||||
facingMode: this.preferredCamera,
|
||||
},
|
||||
});
|
||||
|
||||
// Stop the test stream immediately
|
||||
stream.getTracks().forEach((track) => track.stop());
|
||||
|
||||
this.initializationStatus = "Camera permission granted...";
|
||||
logger.log("Camera permission granted");
|
||||
} catch (permissionError) {
|
||||
const error = permissionError as Error;
|
||||
logger.error("Camera permission error:", {
|
||||
name: error.name,
|
||||
message: error.message,
|
||||
});
|
||||
|
||||
if (
|
||||
error.name === "NotAllowedError" ||
|
||||
error.name === "PermissionDeniedError"
|
||||
) {
|
||||
throw new Error(
|
||||
"Camera access denied. Please grant camera permission and try again.",
|
||||
);
|
||||
} else if (
|
||||
error.name === "NotFoundError" ||
|
||||
error.name === "DevicesNotFoundError"
|
||||
) {
|
||||
throw new Error(
|
||||
"No camera found. Please ensure your device has a camera.",
|
||||
);
|
||||
} else if (
|
||||
error.name === "NotReadableError" ||
|
||||
error.name === "TrackStartError"
|
||||
) {
|
||||
throw new Error("Camera is in use by another application.");
|
||||
} else {
|
||||
throw new Error(`Camera error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Now initialize the QR scanner
|
||||
this.initializationStatus = "Starting QR scanner...";
|
||||
logger.log("Initializing QR scanner...");
|
||||
await promise;
|
||||
if (!timeoutHit) {
|
||||
clearTimeout(timeout);
|
||||
this.isInitializing = false;
|
||||
this.cameraStatus = "Ready";
|
||||
logger.log("QR scanner initialized successfully");
|
||||
}
|
||||
} catch (error) {
|
||||
clearTimeout(timeout);
|
||||
if (attempt < 3) {
|
||||
// Retry after a short delay
|
||||
setTimeout(() => this.onInit(promise, attempt + 1), 1500);
|
||||
|
||||
Reference in New Issue
Block a user