|
|
@ -159,7 +159,9 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
|
|
|
|
async isSupported(): Promise<boolean> { |
|
|
|
try { |
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] Checking browser support...`); |
|
|
|
logger.error( |
|
|
|
`[WebInlineQRScanner:${this.id}] Checking browser support...`, |
|
|
|
); |
|
|
|
// Check for secure context first
|
|
|
|
if (!window.isSecureContext) { |
|
|
|
logger.error( |
|
|
@ -279,51 +281,63 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
|
|
|
|
// Scan for QR code
|
|
|
|
const code = jsQR(imageData.data, imageData.width, imageData.height, { |
|
|
|
inversionAttempts: "attemptBoth", // Try both normal and inverted
|
|
|
|
inversionAttempts: "attemptBoth", // Try both normal and inverted
|
|
|
|
}); |
|
|
|
|
|
|
|
if (code) { |
|
|
|
// Check if the QR code is blurry by examining the location points
|
|
|
|
const { topRightCorner, topLeftCorner, bottomLeftCorner } = code.location; |
|
|
|
const { topRightCorner, topLeftCorner, bottomLeftCorner } = |
|
|
|
code.location; |
|
|
|
const width = Math.sqrt( |
|
|
|
Math.pow(topRightCorner.x - topLeftCorner.x, 2) + Math.pow(topRightCorner.y - topLeftCorner.y, 2) |
|
|
|
Math.pow(topRightCorner.x - topLeftCorner.x, 2) + |
|
|
|
Math.pow(topRightCorner.y - topLeftCorner.y, 2), |
|
|
|
); |
|
|
|
const height = Math.sqrt( |
|
|
|
Math.pow(bottomLeftCorner.x - topLeftCorner.x, 2) + Math.pow(bottomLeftCorner.y - topLeftCorner.y, 2) |
|
|
|
Math.pow(bottomLeftCorner.x - topLeftCorner.x, 2) + |
|
|
|
Math.pow(bottomLeftCorner.y - topLeftCorner.y, 2), |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
// Adjust minimum size based on canvas dimensions
|
|
|
|
const minSize = Math.min(this.canvas.width, this.canvas.height) * 0.1; // 10% of the smaller dimension
|
|
|
|
const isBlurry = width < minSize || height < minSize || |
|
|
|
!code.data || code.data.length === 0; |
|
|
|
const isBlurry = |
|
|
|
width < minSize || |
|
|
|
height < minSize || |
|
|
|
!code.data || |
|
|
|
code.data.length === 0; |
|
|
|
|
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] QR Code detected:`, { |
|
|
|
data: code.data, |
|
|
|
location: code.location, |
|
|
|
attempts: this.scanAttempts, |
|
|
|
isBlurry, |
|
|
|
dimensions: { |
|
|
|
width, |
|
|
|
height, |
|
|
|
dimensions: { |
|
|
|
width, |
|
|
|
height, |
|
|
|
minSize, |
|
|
|
canvasWidth: this.canvas.width, |
|
|
|
canvasHeight: this.canvas.height, |
|
|
|
relativeWidth: width / this.canvas.width, |
|
|
|
relativeHeight: height / this.canvas.height |
|
|
|
relativeHeight: height / this.canvas.height, |
|
|
|
}, |
|
|
|
corners: { |
|
|
|
topLeft: topLeftCorner, |
|
|
|
topRight: topRightCorner, |
|
|
|
bottomLeft: bottomLeftCorner |
|
|
|
} |
|
|
|
bottomLeft: bottomLeftCorner, |
|
|
|
}, |
|
|
|
}); |
|
|
|
|
|
|
|
if (isBlurry) { |
|
|
|
if (this.scanListener?.onError) { |
|
|
|
this.scanListener.onError(new Error("QR code detected but too blurry to read. Please hold the camera steady and ensure the QR code is well-lit.")); |
|
|
|
this.scanListener.onError( |
|
|
|
new Error( |
|
|
|
"QR code detected but too blurry to read. Please hold the camera steady and ensure the QR code is well-lit.", |
|
|
|
), |
|
|
|
); |
|
|
|
} |
|
|
|
// Continue scanning if QR code is blurry
|
|
|
|
this.animationFrameId = requestAnimationFrame(() => this.scanQRCode()); |
|
|
|
this.animationFrameId = requestAnimationFrame(() => |
|
|
|
this.scanQRCode(), |
|
|
|
); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
@ -342,17 +356,21 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
error: error instanceof Error ? error.message : String(error), |
|
|
|
stack: error instanceof Error ? error.stack : undefined, |
|
|
|
attempt: this.scanAttempts, |
|
|
|
videoState: this.video ? { |
|
|
|
readyState: this.video.readyState, |
|
|
|
paused: this.video.paused, |
|
|
|
ended: this.video.ended, |
|
|
|
width: this.video.videoWidth, |
|
|
|
height: this.video.videoHeight |
|
|
|
} : null, |
|
|
|
canvasState: this.canvas ? { |
|
|
|
width: this.canvas.width, |
|
|
|
height: this.canvas.height |
|
|
|
} : null |
|
|
|
videoState: this.video |
|
|
|
? { |
|
|
|
readyState: this.video.readyState, |
|
|
|
paused: this.video.paused, |
|
|
|
ended: this.video.ended, |
|
|
|
width: this.video.videoWidth, |
|
|
|
height: this.video.videoHeight, |
|
|
|
} |
|
|
|
: null, |
|
|
|
canvasState: this.canvas |
|
|
|
? { |
|
|
|
width: this.canvas.width, |
|
|
|
height: this.canvas.height, |
|
|
|
} |
|
|
|
: null, |
|
|
|
}); |
|
|
|
if (this.scanListener?.onError) { |
|
|
|
this.scanListener.onError( |
|
|
@ -375,7 +393,9 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] Starting scan`); |
|
|
|
|
|
|
|
// Get camera stream
|
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] Requesting camera stream...`); |
|
|
|
logger.error( |
|
|
|
`[WebInlineQRScanner:${this.id}] Requesting camera stream...`, |
|
|
|
); |
|
|
|
this.stream = await navigator.mediaDevices.getUserMedia({ |
|
|
|
video: { |
|
|
|
facingMode: "environment", |
|
|
@ -441,7 +461,9 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
if (this.animationFrameId !== null) { |
|
|
|
cancelAnimationFrame(this.animationFrameId); |
|
|
|
this.animationFrameId = null; |
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] Animation frame cancelled`); |
|
|
|
logger.error( |
|
|
|
`[WebInlineQRScanner:${this.id}] Animation frame cancelled`, |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
// Stop video
|
|
|
@ -490,7 +512,9 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
} |
|
|
|
|
|
|
|
onStream(callback: (stream: MediaStream | null) => void): void { |
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] Adding stream event listener`); |
|
|
|
logger.error( |
|
|
|
`[WebInlineQRScanner:${this.id}] Adding stream event listener`, |
|
|
|
); |
|
|
|
this.events.on("stream", callback); |
|
|
|
} |
|
|
|
|
|
|
|