import { Component , OnDestroy , OnInit } from 'angular-ts-decorators' ; import autobind from 'autobind-decorator' ; import { BackupSync } from '../../../shared/backup-restore/backup-restore.interface' ; import { AndroidError , FailedScanError } from '../../../shared/errors/errors' ; import Globals from '../../../shared/global-shared.constants' ; import { PlatformService } from '../../../shared/global-shared.interface' ; import { LogService } from '../../../shared/log/log.service' ; import { StoreKey } from '../../../shared/store/store.enum' ; import { StoreService } from '../../../shared/store/store.service' ; import { UtilityService } from '../../../shared/utility/utility.service' ; import { AndroidAppHelperService } from '../shared/android-app-helper/android-app-helper.service' ; @ autobind @ Component ( { controllerAs : 'vm' , selector : 'appScan' , styles : [ require ( './android-app-scan.component.scss' ) ] , template : require ( './android-app-scan.component.html' ) } ) export class AndroidAppScanComponent implements OnInit , OnDestroy { Strings = require ( '../../../../../res/strings/en.json' ) ; $q : ng . IQService ; $timeout : ng . ITimeoutService ; appHelperSvc : AndroidAppHelperService ; logSvc : LogService ; platformSvc : PlatformService ; storeSvc : StoreService ; utilitySvc : UtilityService ; displayScanInterface = false ; invalidSyncId = false ; lightEnabled = false ; static $inject = [ '$q' , '$timeout' , 'AppHelperService' , 'LogService' , 'PlatformService' , 'StoreService' , 'UtilityService' ] ; constructor ( $q , $timeout , AppHelperSvc , LogSvc , PlatformSvc , StoreSvc , UtilitySvc ) { this . $q = $q ; this . $timeout = $timeout ; this . appHelperSvc = AppHelperSvc ; this . logSvc = LogSvc ; this . platformSvc = PlatformSvc ; this . storeSvc = StoreSvc ; this . utilitySvc = UtilitySvc ; } close ( ) { this . appHelperSvc . switchView ( ) ; } decodeQrCode ( qrCodeValue ) { let serviceUrl ; let syncId ; try { const syncInfo = JSON . parse ( qrCodeValue ) ; syncId = syncInfo . id ; serviceUrl = syncInfo . url ; } catch ( err ) { const arr = qrCodeValue . split ( Globals . QrCode . Delimiter ) ; [ syncId , serviceUrl ] = arr ; } const urlRegex = new RegExp ( `^${ Globals . URL . ValidUrlRegex }$` , 'i' ) ; if ( ! this . utilitySvc . syncIdIsValid ( syncId ) || ! urlRegex . test ( serviceUrl ) ) { throw new Error ( 'Invalid QR code' ) ; } return { id : syncId , url : serviceUrl } ; } disableLight ( ) { if ( ! this . lightEnabled ) { return this . $q . resolve ( ) ; } return this . $q < void > ( ( resolve , reject ) => { window . QRScanner . disableLight ( ( err ) => { if ( err ) { return reject ( new AndroidError ( err . _message ?? err . name ?? err . code ) ) ; } resolve ( ) ; } ) ; } ) ; } enableLight ( ) { return this . $q < void > ( ( resolve , reject ) => { window . QRScanner . enableLight ( ( err ) => { if ( err ) { return reject ( new AndroidError ( err . _message ?? err . name ?? err . code ) ) ; } resolve ( ) ; } ) ; } ) ; } ngOnDestroy ( ) { this . stopScanning ( ) ; } ngOnInit ( ) { this . startScanning ( ) ; } scanCompleted ( scannedSyncInfo ) { return this . $q . all ( [ this . appHelperSvc . updateServiceUrl ( scannedSyncInfo . url ) , this . storeSvc . set ( StoreKey . SyncId , scannedSyncInfo . id ) ] ) . then ( ( ) => this . appHelperSvc . switchView ( ) ) . then ( ( ) => this . appHelperSvc . focusOnElement ( '.active-login-form  input[name="txtPassword"]' ) ) ; } startScanning ( ) { this . lightEnabled = false ; this . invalidSyncId = false ; return this . $q < BackupSync > ( ( resolve , reject ) => { const waitForScan = ( ) => { this . displayScanInterface = true ; this . $timeout ( ( ) => { this . invalidSyncId = false ; } , 100 ) ; window . QRScanner . scan ( ( err , scannedText ) => { if ( err ) { return reject ( new AndroidError ( err . _message ?? err . name ?? err . code ) ) ; } window . QRScanner . pausePreview ( ( ) => { this . logSvc . logInfo ( `Scanned: ${ scannedText }` ) ; let syncInfo ; try { syncInfo = this . decodeQrCode ( scannedText ) ; } catch ( decodeQrCodeErr ) { this . invalidSyncId = true ; this . $timeout ( ( ) => window . QRScanner . resumePreview ( waitForScan ) , 3e3 ) ; return ; } this . $timeout ( ( ) => resolve ( syncInfo ) , 1e3 ) ; } ) ; } ) ; } ; window . QRScanner . prepare ( ( err , status ) => { if ( err ) { return reject ( new AndroidError ( err . _message ?? err . name ?? err . code ) ) ; } if ( status . authorized ) { window . QRScanner . show ( ( ) => waitForScan ( ) ) ; } else { reject ( new AndroidError ( 'Camera use not authorised' ) ) ; } } ) ; } ) . then ( this . scanCompleted ) . catch ( ( err ) => { this . appHelperSvc . switchView ( ) . then ( ( ) => { throw new FailedScanError ( undefined , err ) ; } ) ; } ) ; } stopScanning ( ) { this . displayScanInterface = false ; this . lightEnabled = false ; this . $timeout ( ( ) => this . disableLight ( ) . catch ( ( ) => { } ) . finally ( ( ) => window . QRScanner . hide ( ( ) => window . QRScanner . destroy ( ) ) ) , Globals . InterfaceReadyTimeout ) ; } toggleCameraLight ( switchOn ? ) { if ( switchOn !== undefined ) { return ( switchOn ? this . enableLight ( ) : this . disableLight ( ) ) . then ( ( ) => { this . lightEnabled = switchOn ; } ) ; } return this . $q ( ( resolve , reject ) => { window . QRScanner . getStatus ( ( status ) => { ( status . lightEnabled ? this . disableLight ( ) : this . enableLight ( ) ) . then ( ( ) => { this . lightEnabled = ! status . lightEnabled ; resolve ( ) ; } ) . catch ( reject ) ; } ) ; } ) ; } }