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> {
|
async onInit(promise: Promise<void>, attempt = 1): Promise<void> {
|
||||||
if (this.isNativePlatform) {
|
if (this.isNativePlatform) return;
|
||||||
logger.log("Skipping web scanner initialization on native platform");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isInitializing = true;
|
this.isInitializing = true;
|
||||||
this.error = null;
|
this.error = null;
|
||||||
this.initializationStatus = "Checking camera access...";
|
this.initializationStatus = "Checking camera access...";
|
||||||
|
|
||||||
try {
|
let timeoutHit = false;
|
||||||
// First check if mediaDevices API is available
|
const timeout = setTimeout(() => {
|
||||||
if (!navigator.mediaDevices) {
|
timeoutHit = true;
|
||||||
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;
|
|
||||||
this.isInitializing = false;
|
this.isInitializing = false;
|
||||||
this.cameraStatus = "Ready";
|
this.cameraStatus = "Ready (timeout fallback)";
|
||||||
logger.log("QR scanner initialized successfully");
|
this.initializationStatus = "Camera ready (fallback)";
|
||||||
|
// Optionally log a warning or show a subtle message
|
||||||
|
}, 4000); // 4 seconds
|
||||||
|
|
||||||
|
try {
|
||||||
|
await promise;
|
||||||
|
if (!timeoutHit) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
this.isInitializing = false;
|
||||||
|
this.cameraStatus = "Ready";
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
clearTimeout(timeout);
|
||||||
if (attempt < 3) {
|
if (attempt < 3) {
|
||||||
// Retry after a short delay
|
// Retry after a short delay
|
||||||
setTimeout(() => this.onInit(promise, attempt + 1), 1500);
|
setTimeout(() => this.onInit(promise, attempt + 1), 1500);
|
||||||
|
|||||||
Reference in New Issue
Block a user