Browse Source

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
pull/133/head
Matthew Raymer 3 weeks ago
parent
commit
559f52e6d6
  1. 90
      src/components/QRScanner/QRScannerDialog.vue

90
src/components/QRScanner/QRScannerDialog.vue

@ -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...";
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}`);
}
}
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
// Now initialize the QR scanner
this.initializationStatus = "Starting QR scanner...";
logger.log("Initializing QR scanner...");
try {
await promise;
this.isInitializing = false;
this.cameraStatus = "Ready";
logger.log("QR scanner initialized successfully");
if (!timeoutHit) {
clearTimeout(timeout);
this.isInitializing = false;
this.cameraStatus = "Ready";
}
} catch (error) {
clearTimeout(timeout);
if (attempt < 3) {
// Retry after a short delay
setTimeout(() => this.onInit(promise, attempt + 1), 1500);

Loading…
Cancel
Save