|
@ -67,8 +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 |
|
|
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" |
|
|
3.042 1.135 5.824 3 7.938l3-2.647z" |
|
|
></path> |
|
|
></path> |
|
|
</svg> |
|
|
</svg> |
|
|
<span>{{ initializationStatus }}</span> |
|
|
<span>{{ initializationStatus }}</span> |
|
@ -164,6 +164,40 @@ |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</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" |
|
|
|
|
|
> |
|
|
|
|
|
<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> |
|
|
|
|
|
</ul> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
<!-- Footer --> |
|
|
<!-- Footer --> |
|
|
<div class="p-4 border-t border-gray-200"> |
|
|
<div class="p-4 border-t border-gray-200"> |
|
|
<div class="flex flex-col space-y-4"> |
|
|
<div class="flex flex-col space-y-4"> |
|
@ -275,7 +309,8 @@ export default class QRScannerDialog extends Vue { |
|
|
|
|
|
|
|
|
async onInit(promise: Promise<void>): Promise<void> { |
|
|
async onInit(promise: Promise<void>): Promise<void> { |
|
|
if (this.isNativePlatform) { |
|
|
if (this.isNativePlatform) { |
|
|
logger.log("Skipping web scanner initialization on native platform"); |
|
|
logger.log("Closing QR dialog on native platform"); |
|
|
|
|
|
this.$nextTick(() => this.close()); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -291,14 +326,28 @@ export default class QRScannerDialog extends Vue { |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Check for video devices |
|
|
|
|
|
const devices = await navigator.mediaDevices.enumerateDevices(); |
|
|
|
|
|
const videoDevices = devices.filter( |
|
|
|
|
|
(device) => device.kind === "videoinput", |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
if (videoDevices.length === 0) { |
|
|
|
|
|
throw new Error("No camera found on this device"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
logger.log("Starting QR scanner initialization...", { |
|
|
logger.log("Starting QR scanner initialization...", { |
|
|
mediaDevices: !!navigator.mediaDevices, |
|
|
mediaDevices: !!navigator.mediaDevices, |
|
|
getUserMedia: !!( |
|
|
getUserMedia: !!( |
|
|
navigator.mediaDevices && navigator.mediaDevices.getUserMedia |
|
|
navigator.mediaDevices && navigator.mediaDevices.getUserMedia |
|
|
), |
|
|
), |
|
|
|
|
|
videoDevices: videoDevices.length, |
|
|
constraints: { |
|
|
constraints: { |
|
|
video: true, |
|
|
video: { |
|
|
facingMode: this.preferredCamera, |
|
|
facingMode: this.preferredCamera, |
|
|
|
|
|
width: { ideal: 1280 }, |
|
|
|
|
|
height: { ideal: 720 }, |
|
|
|
|
|
}, |
|
|
}, |
|
|
}, |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
@ -308,6 +357,8 @@ export default class QRScannerDialog extends Vue { |
|
|
const stream = await navigator.mediaDevices.getUserMedia({ |
|
|
const stream = await navigator.mediaDevices.getUserMedia({ |
|
|
video: { |
|
|
video: { |
|
|
facingMode: this.preferredCamera, |
|
|
facingMode: this.preferredCamera, |
|
|
|
|
|
width: { ideal: 1280 }, |
|
|
|
|
|
height: { ideal: 720 }, |
|
|
}, |
|
|
}, |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
@ -350,7 +401,7 @@ export default class QRScannerDialog extends Vue { |
|
|
// Now initialize the QR scanner |
|
|
// Now initialize the QR scanner |
|
|
this.initializationStatus = "Starting QR scanner..."; |
|
|
this.initializationStatus = "Starting QR scanner..."; |
|
|
logger.log("Initializing QR scanner..."); |
|
|
logger.log("Initializing QR scanner..."); |
|
|
// await promise; // <-- comment this out for debugging |
|
|
await promise; |
|
|
|
|
|
|
|
|
this.isInitializing = false; |
|
|
this.isInitializing = false; |
|
|
this.cameraStatus = "Ready"; |
|
|
this.cameraStatus = "Ready"; |
|
@ -401,8 +452,8 @@ export default class QRScannerDialog extends Vue { |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// Handle both promise and non-promise results |
|
|
// Use instanceof Promise for type narrowing |
|
|
if (result && typeof result.then === "function") { |
|
|
if (result instanceof Promise) { |
|
|
result |
|
|
result |
|
|
.then(processResult) |
|
|
.then(processResult) |
|
|
.catch((error: Error) => this.handleError(error)) |
|
|
.catch((error: Error) => this.handleError(error)) |
|
|