|
|
@ -67,7 +67,8 @@ |
|
|
|
<path |
|
|
|
class="opacity-75" |
|
|
|
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> |
|
|
|
</svg> |
|
|
|
<span>{{ initializationStatus }}</span> |
|
|
@ -137,7 +138,8 @@ |
|
|
|
stroke-linecap="round" |
|
|
|
stroke-linejoin="round" |
|
|
|
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 |
|
|
|
stroke-linecap="round" |
|
|
@ -163,15 +165,36 @@ |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 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> |
|
|
|
<span class="block sm:inline">{{ error }}</span> |
|
|
|
<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('denied')">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> |
|
|
|
<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('denied')"> |
|
|
|
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> |
|
|
|
</div> |
|
|
|
|
|
|
@ -305,7 +328,9 @@ export default class QRScannerDialog extends Vue { |
|
|
|
|
|
|
|
// Check for video devices |
|
|
|
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) { |
|
|
|
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...", { |
|
|
|
mediaDevices: !!navigator.mediaDevices, |
|
|
|
getUserMedia: !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia), |
|
|
|
getUserMedia: !!( |
|
|
|
navigator.mediaDevices && navigator.mediaDevices.getUserMedia |
|
|
|
), |
|
|
|
videoDevices: videoDevices.length, |
|
|
|
constraints: { |
|
|
|
video: { |
|
|
|
facingMode: this.preferredCamera, |
|
|
|
width: { ideal: 1280 }, |
|
|
|
height: { ideal: 720 } |
|
|
|
} |
|
|
|
} |
|
|
|
height: { ideal: 720 }, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}); |
|
|
|
|
|
|
|
// Explicitly request camera permission first |
|
|
@ -331,8 +358,8 @@ export default class QRScannerDialog extends Vue { |
|
|
|
video: { |
|
|
|
facingMode: this.preferredCamera, |
|
|
|
width: { ideal: 1280 }, |
|
|
|
height: { ideal: 720 } |
|
|
|
} |
|
|
|
height: { ideal: 720 }, |
|
|
|
}, |
|
|
|
}); |
|
|
|
|
|
|
|
// Stop the test stream immediately |
|
|
@ -347,15 +374,24 @@ export default class QRScannerDialog extends Vue { |
|
|
|
message: error.message, |
|
|
|
}); |
|
|
|
|
|
|
|
if (error.name === "NotAllowedError" || error.name === "PermissionDeniedError") { |
|
|
|
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") { |
|
|
|
} 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") { |
|
|
|
} 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}`); |
|
|
@ -371,7 +407,8 @@ export default class QRScannerDialog extends Vue { |
|
|
|
this.cameraStatus = "Ready"; |
|
|
|
logger.log("QR scanner initialized successfully"); |
|
|
|
} 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.cameraStatus = "Error"; |
|
|
|
if (this.onError) { |
|
|
|