fix(qr): improve QR scanner implementation and error handling

- Implement robust QR scanner factory with platform detection
- Add proper camera permissions to Android manifest
- Improve error handling and logging across scanner implementations
- Add continuous scanning mode for Capacitor/MLKit scanner
- Enhance UI feedback during scanning process
- Fix build configuration for proper platform detection
- Clean up resources properly in scanner components
- Add TypeScript improvements and error wrapping

The changes include:
- Adding CAMERA permission to AndroidManifest.xml
- Setting proper build flags (__IS_MOBILE__, __USE_QR_READER__)
- Implementing continuous scanning mode for better UX
- Adding proper cleanup of scanner resources
- Improving error handling and type safety
- Enhancing UI with loading states and error messages
This commit is contained in:
Matthew Raymer
2025-04-22 10:00:37 +00:00
parent 3e87b2ecda
commit 70dfbe44fa
10 changed files with 548 additions and 451 deletions

View File

@@ -10,13 +10,16 @@ import { logger } from "@/utils/logger";
export class CapacitorQRScanner implements QRScannerService {
private scanListener: ScanListener | null = null;
private isScanning = false;
private listenerHandles: Array<() => Promise<void>> = [];
async checkPermissions(): Promise<boolean> {
try {
const { camera } = await BarcodeScanner.checkPermissions();
return camera === "granted";
} catch (error) {
logger.error("Error checking camera permissions:", error);
const wrappedError =
error instanceof Error ? error : new Error(String(error));
logger.error("Error checking camera permissions:", wrappedError);
return false;
}
}
@@ -32,7 +35,9 @@ export class CapacitorQRScanner implements QRScannerService {
const { camera } = await BarcodeScanner.requestPermissions();
return camera === "granted";
} catch (error) {
logger.error("Error requesting camera permissions:", error);
const wrappedError =
error instanceof Error ? error : new Error(String(error));
logger.error("Error requesting camera permissions:", wrappedError);
return false;
}
}
@@ -42,7 +47,9 @@ export class CapacitorQRScanner implements QRScannerService {
const { supported } = await BarcodeScanner.isSupported();
return supported;
} catch (error) {
logger.error("Error checking scanner support:", error);
const wrappedError =
error instanceof Error ? error : new Error(String(error));
logger.error("Error checking scanner support:", wrappedError);
return false;
}
}
@@ -79,17 +86,23 @@ export class CapacitorQRScanner implements QRScannerService {
};
logger.log("Scanner options:", scanOptions);
const result = await BarcodeScanner.scan(scanOptions);
logger.log("Scan result:", result);
if (result.barcodes.length > 0) {
this.scanListener?.onScan(result.barcodes[0].rawValue);
}
// Add listener for barcode scans
const handle = await BarcodeScanner.addListener('barcodeScanned', (result) => {
if (this.scanListener) {
this.scanListener.onScan(result.barcode.rawValue);
}
});
this.listenerHandles.push(handle.remove);
// Start continuous scanning
await BarcodeScanner.startScan(scanOptions);
} catch (error) {
logger.error("Error during QR scan:", error);
this.scanListener?.onError?.(error as Error);
} finally {
this.isScanning = false;
const wrappedError =
error instanceof Error ? error : new Error(String(error));
logger.error("Error during QR scan:", wrappedError);
this.scanListener?.onError?.(wrappedError);
throw wrappedError;
}
}
@@ -100,10 +113,14 @@ export class CapacitorQRScanner implements QRScannerService {
try {
await BarcodeScanner.stopScan();
this.isScanning = false;
} catch (error) {
logger.error("Error stopping QR scan:", error);
this.scanListener?.onError?.(error as Error);
const wrappedError =
error instanceof Error ? error : new Error(String(error));
logger.error("Error stopping QR scan:", wrappedError);
this.scanListener?.onError?.(wrappedError);
throw wrappedError;
} finally {
this.isScanning = false;
}
}
@@ -112,7 +129,19 @@ export class CapacitorQRScanner implements QRScannerService {
}
async cleanup(): Promise<void> {
await this.stopScan();
this.scanListener = null;
try {
await this.stopScan();
for (const handle of this.listenerHandles) {
await handle();
}
} catch (error) {
const wrappedError =
error instanceof Error ? error : new Error(String(error));
logger.error("Error during cleanup:", wrappedError);
throw wrappedError;
} finally {
this.listenerHandles = [];
this.scanListener = null;
}
}
}