You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
3.9 KiB
136 lines
3.9 KiB
import { createApp, App } from "vue";
|
|
import { QRScannerService, ScanListener, QRScannerOptions } from "./types";
|
|
import QRScannerDialog from "@/components/QRScanner/QRScannerDialog.vue";
|
|
import { logger } from "@/utils/logger";
|
|
|
|
export class WebDialogQRScanner implements QRScannerService {
|
|
private dialogInstance: App | null = null;
|
|
private dialogComponent: InstanceType<typeof QRScannerDialog> | null = null;
|
|
private scanListener: ScanListener | null = null;
|
|
private isScanning = false;
|
|
private container: HTMLElement | null = null;
|
|
|
|
constructor(private options?: QRScannerOptions) {}
|
|
|
|
async checkPermissions(): Promise<boolean> {
|
|
try {
|
|
const permissions = await navigator.permissions.query({
|
|
name: "camera" as PermissionName,
|
|
});
|
|
return permissions.state === "granted";
|
|
} catch (error) {
|
|
const wrappedError =
|
|
error instanceof Error ? error : new Error(String(error));
|
|
logger.error("Error checking camera permissions:", wrappedError);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async requestPermissions(): Promise<boolean> {
|
|
try {
|
|
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
|
|
stream.getTracks().forEach((track) => track.stop());
|
|
return true;
|
|
} catch (error) {
|
|
const wrappedError =
|
|
error instanceof Error ? error : new Error(String(error));
|
|
logger.error("Error requesting camera permissions:", wrappedError);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async isSupported(): Promise<boolean> {
|
|
return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
|
|
}
|
|
|
|
async startScan(): Promise<void> {
|
|
if (this.isScanning) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
this.isScanning = true;
|
|
|
|
// Create and mount dialog component
|
|
this.container = document.createElement("div");
|
|
document.body.appendChild(this.container);
|
|
|
|
this.dialogInstance = createApp(QRScannerDialog, {
|
|
onScan: (result: string) => {
|
|
if (this.scanListener) {
|
|
this.scanListener.onScan(result);
|
|
}
|
|
},
|
|
onError: (error: Error) => {
|
|
if (this.scanListener?.onError) {
|
|
this.scanListener.onError(error);
|
|
}
|
|
},
|
|
options: this.options,
|
|
});
|
|
|
|
this.dialogComponent = this.dialogInstance.mount(this.container).$refs
|
|
.dialog as InstanceType<typeof QRScannerDialog>;
|
|
} catch (error) {
|
|
this.isScanning = false;
|
|
const wrappedError =
|
|
error instanceof Error ? error : new Error(String(error));
|
|
if (this.scanListener?.onError) {
|
|
this.scanListener.onError(wrappedError);
|
|
}
|
|
logger.error("Error starting scan:", wrappedError);
|
|
this.cleanupContainer();
|
|
throw wrappedError;
|
|
}
|
|
}
|
|
|
|
async stopScan(): Promise<void> {
|
|
if (!this.isScanning) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
if (this.dialogComponent) {
|
|
await this.dialogComponent.close();
|
|
}
|
|
if (this.dialogInstance) {
|
|
this.dialogInstance.unmount();
|
|
}
|
|
} catch (error) {
|
|
const wrappedError =
|
|
error instanceof Error ? error : new Error(String(error));
|
|
logger.error("Error stopping scan:", wrappedError);
|
|
throw wrappedError;
|
|
} finally {
|
|
this.isScanning = false;
|
|
this.cleanupContainer();
|
|
}
|
|
}
|
|
|
|
addListener(listener: ScanListener): void {
|
|
this.scanListener = listener;
|
|
}
|
|
|
|
private cleanupContainer(): void {
|
|
if (this.container && this.container.parentNode) {
|
|
this.container.parentNode.removeChild(this.container);
|
|
}
|
|
this.container = null;
|
|
}
|
|
|
|
async cleanup(): Promise<void> {
|
|
try {
|
|
await this.stopScan();
|
|
} catch (error) {
|
|
const wrappedError =
|
|
error instanceof Error ? error : new Error(String(error));
|
|
logger.error("Error during cleanup:", wrappedError);
|
|
throw wrappedError;
|
|
} finally {
|
|
this.dialogComponent = null;
|
|
this.dialogInstance = null;
|
|
this.scanListener = null;
|
|
this.cleanupContainer();
|
|
}
|
|
}
|
|
}
|
|
|