import { AfterViewInit, ChangeDetectionStrategy, Component, effect, OnDestroy, output, signal, } from '@angular/core'; import { CameraDevice, Html5Qrcode, Html5QrcodeScanType } from 'html5-qrcode'; import { ButtonDirective } from '@/shared/ui/button.directive'; const config = { fps: 10, qrbox: 250, supportedScanTypes: [Html5QrcodeScanType.SCAN_TYPE_CAMERA], }; type Camera = 'environment' | 'user'; @Component({ selector: 'app-scanner', templateUrl: './scanner.component.html', styleUrls: ['./scanner.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ButtonDirective], }) export class ScannerComponent implements AfterViewInit, OnDestroy { scanned = output(); close = output(); qrcodeScanner!: Html5Qrcode; devices = signal([]); activeCamera = signal( (localStorage.getItem('qrcodeScannerCamera') as Camera | null) ?? 'user' ); onSwitchCamera = effect(() => { localStorage.setItem('qrcodeScannerCamera', this.activeCamera()); }); ngAfterViewInit() { return this.setupScanner(); } ngOnDestroy() { return this.qrcodeScanner.stop(); } async setupScanner() { const devices = await Html5Qrcode.getCameras(); this.devices.set(devices); this.qrcodeScanner = new Html5Qrcode('scanner'); await this.qrcodeScanner.start( { facingMode: this.activeCamera() }, config, (decodedText) => this.onScanSuccess(decodedText), () => {} ); } onScanSuccess(decodedText: string) { this.scanned.emit(decodedText); } stopScanner() { this.close.emit(); } async switchCamera() { const devices = this.devices(); if (devices.length === 1) return; await this.qrcodeScanner.stop(); await this.qrcodeScanner.start( { facingMode: this.activeCamera() === 'environment' ? 'user' : 'environment' }, config, (decodedText) => this.onScanSuccess(decodedText), () => {} ); this.activeCamera.update((c) => (c === 'environment' ? 'user' : 'environment')); } }