Browse Source

feat(qr-scanner): Add camera state management to CapacitorQRScanner

- Add camera state tracking and listener management
- Implement addCameraStateListener and removeCameraStateListener methods
- Add state transitions during scanning operations
- Improve error handling with state updates
- Add proper type imports for CameraState and CameraStateListener

This change ensures CapacitorQRScanner fully implements the QRScannerService
interface and provides proper camera state feedback to consumers. Camera state
is now tracked through the entire lifecycle of scanning operations, with
appropriate state transitions for initialization, active scanning, errors,
and cleanup.
pull/137/head
Matthew Raymer 2 weeks ago
parent
commit
835a270e65
  1. 2
      src/db-sql/migration.ts
  2. 6
      src/interfaces/absurd-sql.d.ts
  3. 1
      src/services/AbsurdSqlDatabaseService.ts
  4. 32
      src/services/QRScanner/CapacitorQRScanner.ts
  5. 7
      tsconfig.electron.json

2
src/db-sql/migration.ts

@ -129,7 +129,7 @@ export async function registerMigrations(): Promise<void> {
export async function runMigrations(
sqlExec: (
sql: string,
params?: SqlValue[],
params?: unknown[],
) => Promise<Array<QueryExecResult>>,
): Promise<void> {
await registerMigrations();

6
src/interfaces/absurd-sql.d.ts

@ -13,10 +13,10 @@ declare module "@jlongster/sql.js" {
}
interface AbsurdSqlDatabase {
exec: (sql: string, params?: SqlValue[]) => Promise<QueryExecResult[]>;
exec: (sql: string, params?: unknown[]) => Promise<QueryExecResult[]>;
run: (
sql: string,
params?: SqlValue[],
params?: unknown[],
) => Promise<{ changes: number; lastId?: number }>;
}
@ -49,7 +49,7 @@ declare module "absurd-sql/dist/indexeddb-main-thread" {
}
export interface SQLiteDatabase {
exec: (sql: string, params?: SqlValue[]) => Promise<QueryExecResult[]>;
exec: (sql: string, params?: unknown[]) => Promise<QueryExecResult[]>;
close: () => Promise<void>;
}

1
src/services/AbsurdSqlDatabaseService.ts

@ -1,4 +1,3 @@
// Remove inline declarations since they are now in src/types/absurd-sql.d.ts
import initSqlJs from "@jlongster/sql.js";
import { SQLiteFS } from "absurd-sql";
import IndexedDBBackend from "absurd-sql/dist/indexeddb-backend";

32
src/services/QRScanner/CapacitorQRScanner.ts

@ -4,7 +4,7 @@ import {
StartScanOptions,
LensFacing,
} from "@capacitor-mlkit/barcode-scanning";
import { QRScannerService, ScanListener, QRScannerOptions } from "./types";
import { QRScannerService, ScanListener, QRScannerOptions, CameraStateListener, CameraState } from "./types";
import { logger } from "@/utils/logger";
export class CapacitorQRScanner implements QRScannerService {
@ -12,6 +12,9 @@ export class CapacitorQRScanner implements QRScannerService {
private isScanning = false;
private listenerHandles: Array<() => Promise<void>> = [];
private cleanupPromise: Promise<void> | null = null;
private cameraStateListeners: Set<CameraStateListener> = new Set();
private currentState: CameraState = "off";
private currentStateMessage?: string;
async checkPermissions(): Promise<boolean> {
try {
@ -79,8 +82,11 @@ export class CapacitorQRScanner implements QRScannerService {
}
try {
this.updateCameraState("initializing", "Starting camera...");
// Ensure we have permissions before starting
if (!(await this.checkPermissions())) {
this.updateCameraState("permission_denied", "Camera permission denied");
logger.debug("Requesting camera permissions");
const granted = await this.requestPermissions();
if (!granted) {
@ -90,11 +96,13 @@ export class CapacitorQRScanner implements QRScannerService {
// Check if scanning is supported
if (!(await this.isSupported())) {
this.updateCameraState("error", "QR scanning not supported on this device");
throw new Error("QR scanning not supported on this device");
}
logger.info("Starting MLKit scanner");
this.isScanning = true;
this.updateCameraState("active", "Camera is active");
const scanOptions: StartScanOptions = {
formats: [BarcodeFormat.QrCode],
@ -126,6 +134,7 @@ export class CapacitorQRScanner implements QRScannerService {
stack: wrappedError.stack,
});
this.isScanning = false;
this.updateCameraState("error", wrappedError.message);
await this.cleanup();
this.scanListener?.onError?.(wrappedError);
throw wrappedError;
@ -140,6 +149,7 @@ export class CapacitorQRScanner implements QRScannerService {
try {
logger.debug("Stopping QR scanner");
this.updateCameraState("off", "Camera stopped");
await BarcodeScanner.stopScan();
logger.info("QR scanner stopped successfully");
} catch (error) {
@ -149,6 +159,7 @@ export class CapacitorQRScanner implements QRScannerService {
error: wrappedError.message,
stack: wrappedError.stack,
});
this.updateCameraState("error", wrappedError.message);
this.scanListener?.onError?.(wrappedError);
throw wrappedError;
} finally {
@ -207,4 +218,23 @@ export class CapacitorQRScanner implements QRScannerService {
// No-op for native scanner
callback(null);
}
addCameraStateListener(listener: CameraStateListener): void {
this.cameraStateListeners.add(listener);
// Immediately notify the new listener of current state
listener.onStateChange(this.currentState, this.currentStateMessage);
}
removeCameraStateListener(listener: CameraStateListener): void {
this.cameraStateListeners.delete(listener);
}
private updateCameraState(state: CameraState, message?: string): void {
this.currentState = state;
this.currentStateMessage = message;
// Notify all listeners of state change
for (const listener of this.cameraStateListeners) {
listener.onStateChange(state, message);
}
}
}

7
tsconfig.electron.json

@ -19,8 +19,9 @@
}
},
"include": [
"src/electron/**/*.ts",
"src/utils/**/*.ts",
"src/constants/**/*.ts"
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue"
]
}
Loading…
Cancel
Save