@ -137,7 +137,7 @@ import {
BarcodeScanner ,
type ScanResult ,
} from "@capacitor-mlkit/barcode-scanning" ;
import { ref , type Ref , reactive } from "vue" ;
import { ref , reactive } from "vue" ;
/ / D e c l a r e g l o b a l c o n s t a n t s
declare const __USE_QR_READER__ : boolean ;
@ -241,7 +241,7 @@ export default class ContactQRScanShowView extends Vue {
private isCapturingPhoto = false ;
private appStateListener ? : { remove : ( ) => Promise < void > } ;
private scanListener : Ref < PluginListenerHandle | null > = ref ( null ) ;
private scanListener : PluginListenerHandle | null = null ;
private state = reactive < AppState > ( {
isProcessing : false ,
processingStatus : "" ,
@ -307,7 +307,17 @@ export default class ContactQRScanShowView extends Vue {
this . appStateListener = await App . addListener (
"appStateChange" ,
( state : AppStateChangeEvent ) => {
logger . log ( "App state changed:" , state ) ;
const stateInfo = {
isActive : state . isActive ,
timestamp : new Date ( ) . toISOString ( ) ,
cameraActive : this . cameraActive ,
scannerState : {
... this . state . scannerState ,
/ / C o n v e r t c o m p l e x o b j e c t s t o s t r i n g s t o a v o i d [ o b j e c t O b j e c t ]
error : this . state . scannerState . error ? . toString ( ) || null ,
} ,
} ;
logger . log ( "App state changed:" , JSON . stringify ( stateInfo , null , 2 ) ) ;
if ( ! state . isActive ) {
this . cleanupCamera ( ) ;
}
@ -316,15 +326,31 @@ export default class ContactQRScanShowView extends Vue {
/ / A d d p a u s e l i s t e n e r
await App . addListener ( "pause" , ( ) => {
logger . log ( "App paused" ) ;
const pauseInfo = {
timestamp : new Date ( ) . toISOString ( ) ,
cameraActive : this . cameraActive ,
scannerState : {
... this . state . scannerState ,
error : this . state . scannerState . error ? . toString ( ) || null ,
} ,
isProcessing : this . state . isProcessing ,
} ;
logger . log ( "App paused:" , JSON . stringify ( pauseInfo , null , 2 ) ) ;
this . cleanupCamera ( ) ;
} ) ;
/ / A d d r e s u m e l i s t e n e r
await App . addListener ( "resume" , ( ) => {
logger . log ( "App resumed" ) ;
/ / D o n ' t a u t o m a t i c a l l y r e i n i t i a l i z e c a m e r a o n r e s u m e
/ / L e t u s e r e x p l i c i t l y r e q u e s t c a m e r a a c c e s s a g a i n
const resumeInfo = {
timestamp : new Date ( ) . toISOString ( ) ,
cameraActive : this . cameraActive ,
scannerState : {
... this . state . scannerState ,
error : this . state . scannerState . error ? . toString ( ) || null ,
} ,
isProcessing : this . state . isProcessing ,
} ;
logger . log ( "App resumed:" , JSON . stringify ( resumeInfo , null , 2 ) ) ;
} ) ;
logger . log ( "App lifecycle listeners setup complete" ) ;
@ -436,41 +462,59 @@ export default class ContactQRScanShowView extends Vue {
try {
this . state . isProcessing = true ;
this . state . processingStatus = "Starting camera..." ;
logger . log ( "Opening mobile camera - starting initialization" ) ;
/ / C h e c k c u r r e n t p e r m i s s i o n s t a t u s
const status = await BarcodeScanner . checkPermissions ( ) ;
logger . log ( "Camera permission status:" , JSON . stringify ( status , null , 2 ) ) ;
if ( status . camera !== "granted" ) {
/ / R e q u e s t p e r m i s s i o n i f n o t g r a n t e d
logger . log ( "Requesting camera permissions..." ) ;
const permissionStatus = await BarcodeScanner . requestPermissions ( ) ;
if ( permissionStatus . camera !== "granted" ) {
throw new Error ( "Camera permission not granted" ) ;
}
logger . log (
"Camera permission granted:" ,
JSON . stringify ( permissionStatus , null , 2 ) ,
) ;
}
/ / S e t u p t h e l i s t e n e r b e f o r e s t a r t i n g t h e s c a n
/ / R e m o v e a n y e x i s t i n g l i s t e n e r f i r s t
try {
const listener = await BarcodeScanner . addListener (
if ( this . scanListener ) {
logger . log ( "Removing existing barcode listener" ) ;
await this . scanListener . remove ( ) ;
this . scanListener = null ;
}
} catch ( error ) {
logger . error ( "Error removing existing listener:" , error ) ;
/ / C o n t i n u e w i t h s e t u p e v e n i f r e m o v a l f a i l s
}
/ / S e t u p t h e l i s t e n e r b e f o r e s t a r t i n g t h e s c a n
logger . log ( "Setting up new barcode listener" ) ;
this . scanListener = await BarcodeScanner . addListener (
"barcodesScanned" ,
async ( result : ScanResult ) => {
logger . log (
"Barcode scan result received:" ,
JSON . stringify ( result , null , 2 ) ,
) ;
if ( result . barcodes && result . barcodes . length > 0 ) {
this . state . processingDetails = ` Processing QR code: ${ result . barcodes [ 0 ] . rawValue } ` ;
await this . handleScanResult ( result . barcodes [ 0 ] . rawValue ) ;
}
} ,
) ;
/ / O n l y s e t t h e l i s t e n e r i f w e s u c c e s s f u l l y g o t o n e
if ( listener ) {
this . scanListener . value = listener ;
}
} catch ( error ) {
logger . error ( "Error setting up barcode listener:" , error ) ;
throw new Error ( "Failed to initialize barcode scanner" ) ;
}
logger . log ( "Barcode listener setup complete" ) ;
/ / S t a r t t h e s c a n n e r
logger . log ( "Starting barcode scanner" ) ;
await BarcodeScanner . startScan ( ) ;
logger . log ( "Barcode scanner started successfully" ) ;
this . state . isProcessing = false ;
this . state . processingStatus = "" ;
} catch ( error ) {
@ -481,19 +525,37 @@ export default class ContactQRScanShowView extends Vue {
this . showError (
error instanceof Error ? error . message : "Failed to open camera" ,
) ;
/ / C l e a n u p o n e r r o r
try {
if ( this . scanListener ) {
await this . scanListener . remove ( ) ;
this . scanListener = null ;
}
} catch ( cleanupError ) {
logger . error ( "Error during cleanup:" , cleanupError ) ;
}
}
}
private async handleScanResult ( rawValue : string ) {
try {
this . state . isProcessing = true ;
this . state . processingStatus = "Processing QR code..." ;
this . state . processingDetails = ` Scanned value: ${ rawValue } ` ;
try {
/ / S t o p s c a n n i n g b e f o r e p r o c e s s i n g
await this . stopScanning ( ) ;
/ / P r o c e s s t h e s c a n r e s u l t
await this . onScanDetect ( { rawValue } ) ;
} catch ( error ) {
logger . error ( "Error handling scan result:" , error ) ;
this . showError ( "Failed to process scan result" ) ;
} finally {
this . state . isProcessing = false ;
this . state . processingStatus = "" ;
this . state . processingDetails = "" ;
}
}
@ -880,6 +942,13 @@ export default class ContactQRScanShowView extends Vue {
async stopScanning ( ) {
try {
/ / R e m o v e t h e l i s t e n e r f i r s t
if ( this . scanListener ) {
await this . scanListener . remove ( ) ;
this . scanListener = null ;
}
/ / S t o p t h e s c a n n e r
await BarcodeScanner . stopScan ( ) ;
this . state . scannerState . processingStatus = "Scan stopped" ;
this . state . scannerState . isProcessing = false ;
@ -889,6 +958,7 @@ export default class ContactQRScanShowView extends Vue {
error instanceof Error ? error . message : String ( error ) ;
this . state . scannerState . error = ` Error stopping scan: ${ errorMessage } ` ;
this . state . scannerState . isProcessing = false ;
logger . error ( "Error stopping scanner:" , error ) ;
}
}