Browse Source

fix: lint

pull/133/head
Matthew Raymer 3 weeks ago
parent
commit
8d2dffb012
  1. 75
      src/components/QRScanner/QRScannerDialog.vue
  2. 60
      src/services/QRScanner/WebDialogQRScanner.ts

75
src/components/QRScanner/QRScannerDialog.vue

@ -67,7 +67,8 @@
<path <path
class="opacity-75" class="opacity-75"
fill="currentColor" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0
3.042 1.135 5.824 3 7.938l3-2.647z"
></path> ></path>
</svg> </svg>
<span>{{ initializationStatus }}</span> <span>{{ initializationStatus }}</span>
@ -137,7 +138,8 @@
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
stroke-width="2" stroke-width="2"
d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z" d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0
011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"
/> />
<path <path
stroke-linecap="round" stroke-linecap="round"
@ -163,15 +165,36 @@
</div> </div>
<!-- Error Banner --> <!-- Error Banner -->
<div v-if="error" class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4" role="alert"> <div
v-if="error"
class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4"
role="alert"
>
<strong class="font-bold">Camera Error:</strong> <strong class="font-bold">Camera Error:</strong>
<span class="block sm:inline">{{ error }}</span> <span class="block sm:inline">{{ error }}</span>
<ul class="mt-2 text-sm text-red-600 list-disc list-inside"> <ul class="mt-2 text-sm text-red-600 list-disc list-inside">
<li v-if="error.includes('No camera found')">Check if your device has a camera and it is enabled.</li> <li v-if="error.includes('No camera found')">
<li v-if="error.includes('denied')">Allow camera access in your browser settings and reload the page.</li> Check if your device has a camera and it is enabled.
<li v-if="error.includes('in use')">Close other applications that may be using the camera.</li> </li>
<li v-if="error.includes('HTTPS')">Ensure you are using a secure (HTTPS) connection.</li> <li v-if="error.includes('denied')">
<li v-if="!error.includes('No camera found') && !error.includes('denied') && !error.includes('in use') && !error.includes('HTTPS')">Try refreshing the page or using a different browser/device.</li> Allow camera access in your browser settings and reload the page.
</li>
<li v-if="error.includes('in use')">
Close other applications that may be using the camera.
</li>
<li v-if="error.includes('HTTPS')">
Ensure you are using a secure (HTTPS) connection.
</li>
<li
v-if="
!error.includes('No camera found') &&
!error.includes('denied') &&
!error.includes('in use') &&
!error.includes('HTTPS')
"
>
Try refreshing the page or using a different browser/device.
</li>
</ul> </ul>
</div> </div>
@ -305,7 +328,9 @@ export default class QRScannerDialog extends Vue {
// Check for video devices // Check for video devices
const devices = await navigator.mediaDevices.enumerateDevices(); const devices = await navigator.mediaDevices.enumerateDevices();
const videoDevices = devices.filter(device => device.kind === 'videoinput'); const videoDevices = devices.filter(
(device) => device.kind === "videoinput",
);
if (videoDevices.length === 0) { if (videoDevices.length === 0) {
throw new Error("No camera found on this device"); throw new Error("No camera found on this device");
@ -313,15 +338,17 @@ export default class QRScannerDialog extends Vue {
logger.log("Starting QR scanner initialization...", { logger.log("Starting QR scanner initialization...", {
mediaDevices: !!navigator.mediaDevices, mediaDevices: !!navigator.mediaDevices,
getUserMedia: !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia), getUserMedia: !!(
navigator.mediaDevices && navigator.mediaDevices.getUserMedia
),
videoDevices: videoDevices.length, videoDevices: videoDevices.length,
constraints: { constraints: {
video: { video: {
facingMode: this.preferredCamera, facingMode: this.preferredCamera,
width: { ideal: 1280 }, width: { ideal: 1280 },
height: { ideal: 720 } height: { ideal: 720 },
} },
} },
}); });
// Explicitly request camera permission first // Explicitly request camera permission first
@ -331,8 +358,8 @@ export default class QRScannerDialog extends Vue {
video: { video: {
facingMode: this.preferredCamera, facingMode: this.preferredCamera,
width: { ideal: 1280 }, width: { ideal: 1280 },
height: { ideal: 720 } height: { ideal: 720 },
} },
}); });
// Stop the test stream immediately // Stop the test stream immediately
@ -347,15 +374,24 @@ export default class QRScannerDialog extends Vue {
message: error.message, message: error.message,
}); });
if (error.name === "NotAllowedError" || error.name === "PermissionDeniedError") { if (
error.name === "NotAllowedError" ||
error.name === "PermissionDeniedError"
) {
throw new Error( throw new Error(
"Camera access denied. Please grant camera permission and try again.", "Camera access denied. Please grant camera permission and try again.",
); );
} else if (error.name === "NotFoundError" || error.name === "DevicesNotFoundError") { } else if (
error.name === "NotFoundError" ||
error.name === "DevicesNotFoundError"
) {
throw new Error( throw new Error(
"No camera found. Please ensure your device has a camera.", "No camera found. Please ensure your device has a camera.",
); );
} else if (error.name === "NotReadableError" || error.name === "TrackStartError") { } else if (
error.name === "NotReadableError" ||
error.name === "TrackStartError"
) {
throw new Error("Camera is in use by another application."); throw new Error("Camera is in use by another application.");
} else { } else {
throw new Error(`Camera error: ${error.message}`); throw new Error(`Camera error: ${error.message}`);
@ -371,7 +407,8 @@ export default class QRScannerDialog extends Vue {
this.cameraStatus = "Ready"; this.cameraStatus = "Ready";
logger.log("QR scanner initialized successfully"); logger.log("QR scanner initialized successfully");
} catch (error) { } catch (error) {
const wrappedError = error instanceof Error ? error : new Error(String(error)); const wrappedError =
error instanceof Error ? error : new Error(String(error));
this.error = wrappedError.message; this.error = wrappedError.message;
this.cameraStatus = "Error"; this.cameraStatus = "Error";
if (this.onError) { if (this.onError) {

60
src/services/QRScanner/WebDialogQRScanner.ts

@ -30,40 +30,54 @@ export class WebDialogQRScanner implements QRScannerService {
try { try {
// First check if we have any video devices // First check if we have any video devices
const devices = await navigator.mediaDevices.enumerateDevices(); const devices = await navigator.mediaDevices.enumerateDevices();
const videoDevices = devices.filter(device => device.kind === 'videoinput'); const videoDevices = devices.filter(
(device) => device.kind === "videoinput",
);
if (videoDevices.length === 0) { if (videoDevices.length === 0) {
logger.error('No video devices found'); logger.error("No video devices found");
throw new Error('No camera found on this device'); throw new Error("No camera found on this device");
} }
// Try to get a stream with specific constraints // Try to get a stream with specific constraints
const stream = await navigator.mediaDevices.getUserMedia({ const stream = await navigator.mediaDevices.getUserMedia({
video: { video: {
facingMode: 'environment', facingMode: "environment",
width: { ideal: 1280 }, width: { ideal: 1280 },
height: { ideal: 720 } height: { ideal: 720 },
} },
}); });
// Stop the test stream immediately // Stop the test stream immediately
stream.getTracks().forEach(track => track.stop()); stream.getTracks().forEach((track) => track.stop());
return true; return true;
} catch (error) { } catch (error) {
const wrappedError = error instanceof Error ? error : new Error(String(error)); const wrappedError =
logger.error('Error requesting camera permissions:', { error instanceof Error ? error : new Error(String(error));
logger.error("Error requesting camera permissions:", {
error: wrappedError.message, error: wrappedError.message,
stack: wrappedError.stack, stack: wrappedError.stack,
name: wrappedError.name name: wrappedError.name,
}); });
// Provide more specific error messages // Provide more specific error messages
if (wrappedError.name === 'NotFoundError' || wrappedError.name === 'DevicesNotFoundError') { if (
throw new Error('No camera found on this device'); wrappedError.name === "NotFoundError" ||
} else if (wrappedError.name === 'NotAllowedError' || wrappedError.name === 'PermissionDeniedError') { wrappedError.name === "DevicesNotFoundError"
throw new Error('Camera access denied. Please grant camera permission and try again'); ) {
} else if (wrappedError.name === 'NotReadableError' || wrappedError.name === 'TrackStartError') { throw new Error("No camera found on this device");
throw new Error('Camera is in use by another application'); } else if (
wrappedError.name === "NotAllowedError" ||
wrappedError.name === "PermissionDeniedError"
) {
throw new Error(
"Camera access denied. Please grant camera permission and try again",
);
} else if (
wrappedError.name === "NotReadableError" ||
wrappedError.name === "TrackStartError"
) {
throw new Error("Camera is in use by another application");
} else { } else {
throw new Error(`Camera error: ${wrappedError.message}`); throw new Error(`Camera error: ${wrappedError.message}`);
} }
@ -74,30 +88,32 @@ export class WebDialogQRScanner implements QRScannerService {
try { try {
// Check for secure context first // Check for secure context first
if (!window.isSecureContext) { if (!window.isSecureContext) {
logger.warn('Camera access requires HTTPS (secure context)'); logger.warn("Camera access requires HTTPS (secure context)");
return false; return false;
} }
// Check for camera API support // Check for camera API support
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
logger.warn('Camera API not supported in this browser'); logger.warn("Camera API not supported in this browser");
return false; return false;
} }
// Check if we have any video devices // Check if we have any video devices
const devices = await navigator.mediaDevices.enumerateDevices(); const devices = await navigator.mediaDevices.enumerateDevices();
const hasVideoDevices = devices.some(device => device.kind === 'videoinput'); const hasVideoDevices = devices.some(
(device) => device.kind === "videoinput",
);
if (!hasVideoDevices) { if (!hasVideoDevices) {
logger.warn('No video devices found'); logger.warn("No video devices found");
return false; return false;
} }
return true; return true;
} catch (error) { } catch (error) {
logger.error('Error checking camera support:', { logger.error("Error checking camera support:", {
error: error instanceof Error ? error.message : String(error), error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined stack: error instanceof Error ? error.stack : undefined,
}); });
return false; return false;
} }

Loading…
Cancel
Save