import { Component , HostListener , NgZone , ViewChild } from '@angular/core' ; import { Device } from '@ionic-native/device' ; import { File } from '@ionic-native/file' ; import { FirebaseAnalytics } from '@ionic-native/firebase-analytics' ; import { InAppBrowser } from '@ionic-native/in-app-browser/ngx' ; import { NativeAudio } from '@ionic-native/native-audio' ; import { SocialSharing } from '@ionic-native/social-sharing' ; import { Promise as BluebirdPromise } from 'bluebird' ; import { ActionSheetController , AlertController , ModalController , NavController , NavParams , Platform } from 'ionic-angular' ; import { Subscription } from 'rxjs' ; import { KeyboardInputComponent } from '../../components/keyboard-input/keyboard-input' ; import { requestModelDeleteScan , requestModelPutScanSessions , requestModelUpdateScanSession } from '../../models/request.model' ; import { responseModel , responseModelPutScanAck } from '../../models/response.model' ; import { ScanSessionModel } from '../../models/scan-session.model' ; import { ScanModel } from '../../models/scan.model' ; import { ScanProvider } from '../../providers/scan' ; import { ScanSessionsStorage } from '../../providers/scan-sessions-storage' ; import { ServerProvider } from '../../providers/server' ; import { Utils } from '../../providers/utils' ; import { SettingsPage } from '../settings/settings' ; import { Config } from './../../providers/config' ; import { Settings } from './../../providers/settings' ; import { CSVExportOptionsPage } from './csv-export-options/csv-export-options' ; import { EditScanSessionPage } from './edit-scan-session/edit-scan-session' ; import { OcrPage } from './ocr/ocr' ; import { SelectScanningModePage } from './select-scanning-mode/select-scanning-mode' ; /**
 * This page is used to display the list of the barcodes of a specific
 * ScanSession.
 * It also intereacts with the ScanProvider to allow adding more barcodes.
 * In addition it contains a keyboard-input component to provide a way to enter
 * barcodes with the keyboard (both native and OTG)
 */ @ Component ( { selector : 'page-scan-session' , templateUrl : 'scan-session.html' } ) export class ScanSessionPage { @ ViewChild ( KeyboardInputComponent ) keyboardInput : KeyboardInputComponent ; public scanSession : ScanSessionModel ; public repeatingStatus : 'paused' | 'repeating' | 'stopped' = 'stopped' ; private isNewSession = false ; private repeatInterval = Config . DEFAULT_REPEAT_INVERVAL ; private repeatAllTimeout = null ; private next = - 1 ; private responseSubscription : Subscription = null ; private scanProviderSubscription : Subscription = null ; private skipAlreadySent = false ; private selectedOutputProfileIndex : number ; private enableBeep = true ; private unregisterBackButton = null ; private resumeSubscription : Subscription = null ; private pauseSubscription : Subscription = null ; private isPaused : boolean = false ; constructor ( public navParams , public actionSheetCtrl , public alertCtrl , public serverProvider , public navCtrl , public scanSessionsStorage , public modalCtrl , private firebaseAnalytics , public settings , public socialSharing , public nativeAudio , public ngZone , public device , private utils , public scanProvider , public platform , private iab , private file ) { this . scanSession = navParams . get ( 'scanSession' ) ; if ( ! this . scanSession ) { this . isNewSession = true ; } if ( this . resumeSubscription != null ) { this . resumeSubscription . unsubscribe ( ) ; this . resumeSubscription = null ; this . pauseSubscription . unsubscribe ( ) ; this . pauseSubscription = null ; } this . resumeSubscription = this . platform . resume . subscribe ( ( ) => { setTimeout ( ( ) => { this . isPaused = false ; } , 2000 ) ; } ) ; this . pauseSubscription = this . platform . pause . subscribe ( ( ) => { this . isPaused = true ; } ) ; } @ HostListener ( 'window:keyup' , [ '$event' ] ) keyEvent ( event ) { this . ngZone . run ( ( ) => { if ( event . keyCode == 13 && this . keyboardInput . value . length > 0 ) { this . onEnterClick ( ) ; } } ) ; } ionViewDidEnter ( ) { this . firebaseAnalytics . setCurrentScreen ( "ScanSessionPage" ) ; this . responseSubscription = this . serverProvider . onMessage ( ) . subscribe ( message => { if ( message . action == responseModel . ACTION_PUT_SCAN_ACK ) { let response = message ; if ( this . scanSession . id == response . scanSessionId ) { let len = this . scanSession . scannings . length ; for ( let i = ( len - 1 ) ; i >= 0 ; i -- ) { if ( this . scanSession . scannings [ i ] . id == response . scanId ) { this . scanSession . scannings [ i ] . ack = true ; if ( response . serverUUID && this . scanSession . syncedWith . indexOf ( response . serverUUID ) == - 1 ) this . scanSession . syncedWith . push ( response . serverUUID ) ; if ( response . outputBlocks && response . outputBlocks . length != 0 ) { this . scanSession . scannings [ i ] . outputBlocks = response . outputBlocks ; this . scanSession . scannings [ i ] . displayValue = ScanModel . ToString ( this . scanSession . scannings [ i ] ) ; } } } } } } ) ; this . unregisterBackButton = this . platform . registerBackButtonAction ( ( ) => { if ( ! this . isPaused ) this . navCtrl . pop ( ) ; } , 0 ) ; } ionViewDidLoad ( ) { if ( this . isNewSession ) { this . scan ( ) ; } else { BluebirdPromise . join ( this . settings . getNoRunnings ( ) , this . settings . getSoundFeedbackOrDialogShown ( ) , this . settings . getEnableBeep ( ) , async ( runnings , soundFeedbackOrDialogShown , enableBeep ) => { this . enableBeep = enableBeep ; if ( ! soundFeedbackOrDialogShown && runnings >= Config . NO_RUNNINGS_BEFORE_SHOW_SOUND_FEEDBACK_OR_DIALOG ) { this . alertCtrl . create ( { title : await this . utils . text ( 'beepSoundDialogTitle' ) , message : await this . utils . text ( 'beepSoundDialogMessage' ) , buttons : [ { text : await this . utils . text ( 'beepSoundDialogOpenSettingsButton' ) , handler : ( ) => { this . navCtrl . push ( SettingsPage ) ; } } , { text : await this . utils . text ( 'beepSoundDialogShowLaterButton' ) , role : 'cancel' , handler : ( ) => { } } , { text : await this . utils . text ( 'beepSoundDialogUnderstandButton' ) , role : 'cancel' , handler : ( ) => { this . settings . setSoundFeedbackOrDialogShown ( true ) ; } } ] } ) . present ( ) ; } } ) ; } } ionViewDidLeave ( ) { if ( this . responseSubscription != null && this . responseSubscription ) { this . responseSubscription . unsubscribe ( ) ; } if ( this . scanProviderSubscription && this . scanProviderSubscription != null ) { this . scanProviderSubscription . unsubscribe ( ) ; } if ( this . unregisterBackButton != null ) { this . unregisterBackButton ( ) ; this . unregisterBackButton = null ; } } async ionViewWillEnter ( ) { this . selectedOutputProfileIndex = await this . settings . getSelectedOutputProfile ( ) ; } scan ( ) { let selectScanningModeModal = this . modalCtrl . create ( SelectScanningModePage , { scanSession : this . scanSession } ) ; selectScanningModeModal . onDidDismiss ( data => { if ( ! data || data . cancelled ) { if ( this . isNewSession ) { this . navCtrl . pop ( ) ; } return ; } let scanMode = data . scanMode ; this . scanSession = data . scanSession ; this . selectedOutputProfileIndex = data . selectedOutputProfileIndex ; if ( this . scanProviderSubscription != null ) { this . scanProviderSubscription . unsubscribe ( ) ; } this . scanProviderSubscription = this . scanProviderSubscription = this . scanProvider . scan ( scanMode , this . selectedOutputProfileIndex , this . scanSession , this . keyboardInput ) . subscribe ( scan => { this . saveAndSendScan ( scan ) ; this . isNewSession = false ; } , err => { console . log ( 'err' ) ; if ( ! this . isNewSession && this . scanSession . scannings . length == 0 ) { this . navCtrl . pop ( ) ; } } , ( ) => { if ( this . isNewSession ) { if ( this . scanSession . scannings . length == 0 ) { this . navCtrl . pop ( ) ; } } } ) ; } ) ; selectScanningModeModal . present ( ) ; } keyboardInputTouchStart ( event ) { if ( ! this . keyboardInput . isFocussed ( ) ) { event . preventDefault ( ) ; event . stopPropagation ( ) ; } if ( this . scanProviderSubscription && this . scanProviderSubscription != null ) { if ( this . scanProvider . acqusitionMode == 'manual' ) { if ( this . scanProvider . awaitingForBarcode ) { this . keyboardInput . focus ( ) ; } return ; } else { this . scanProviderSubscription . unsubscribe ( ) ; } } this . scanProviderSubscription = this . scanProvider . scan ( SelectScanningModePage . SCAN_MODE_ENTER_MAUALLY , this . selectedOutputProfileIndex , this . scanSession , this . keyboardInput ) . subscribe ( scan => this . saveAndSendScan ( scan ) , err => { if ( ! this . isNewSession && this . scanSession . scannings . length == 0 ) { this . navCtrl . pop ( ) ; } } , ( ) => { this . scanProviderSubscription . unsubscribe ( ) ; this . scanProviderSubscription = null ; } ) ; } saveAndSendScan ( scan ) { this . firebaseAnalytics . logEvent ( 'scan' , { } ) ; this . scanSession . scannings . unshift ( scan ) ; this . save ( ) ; this . sendPutScan ( scan ) ; } async onItemClicked ( scan , scanIndex ) { if ( scan . ack == true ) { this . alertCtrl . create ( { title : await this . utils . text ( 'alreadyReceivedScanDialogTitle' ) , message : await this . utils . text ( 'alreadyReceivedScanDialogMessage' ) , buttons : [ { text : await this . utils . text ( 'alreadyReceivedScanCancelButton' ) , role : 'cancel' , handler : ( ) => { } } , { text : await this . utils . text ( 'alreadyReceivedScanDialogSendAgainButton' ) , handler : ( ) => { this . repeat ( scan ) ; } } ] } ) . present ( ) ; } else { this . repeat ( scan ) ; } this . firebaseAnalytics . logEvent ( 'repeat' , { } ) ; } async onItemPressed ( scan , scanIndex ) { let buttons = [ ] ; buttons . push ( { text : await this . utils . text ( 'scanDeleteOnlySmartphoneDeleteButton' ) , icon : 'trash' , role : 'destructive' , handler : async ( ) => { this . firebaseAnalytics . logEvent ( 'delete' , { } ) ; this . alertCtrl . create ( { title : await this . utils . text ( 'scanDeleteOnlySmartphoneDialogTitle' ) , message : await this . utils . text ( 'scanDeleteOnlySmartphoneDialogMessage' ) , buttons : [ { text : await this . utils . text ( 'scanDeleteOnlySmartphoneDialogCancelButton' ) , role : 'cancel' } , { text : await this . utils . text ( 'scanDeleteOnlySmartphoneDialogDeleteButton' ) , handler : ( ) => { this . scanSession . scannings . splice ( scanIndex , 1 ) ; this . save ( ) ; this . sendDeleteScan ( scan ) ; if ( this . scanSession . scannings . length == 0 ) { } } } ] } ) . present ( ) ; } } ) ; let scanString = ScanModel . ToString ( scan ) ; buttons . push ( { text : await this . utils . text ( 'shareButton' ) , icon : 'share' , handler : ( ) => { this . firebaseAnalytics . logEvent ( 'share' , { } ) ; this . socialSharing . share ( scanString , "" , "" , "" ) ; } } ) ; if ( scanString . toLocaleLowerCase ( ) . trim ( ) . startsWith ( 'http' ) ) { buttons . push ( { text : await this . utils . text ( 'openInBrowserButton' ) , icon : 'open' , handler : ( ) => { this . firebaseAnalytics . logEvent ( 'open_browser' , { } ) ; this . iab . create ( scanString , '_system' ) ; } } ) ; } buttons . push ( { text : await this . utils . text ( 'repeatHereButton' ) , icon : 'refresh' , handler : ( ) => { this . firebaseAnalytics . logEvent ( 'repeatAll' , { } ) ; if ( this . repeatingStatus == 'stopped' ) { this . repeatAll ( scanIndex ) ; } } } ) ; buttons . push ( { text : await this . utils . text ( 'cancelButton' ) , role : 'cancel' } ) ; let actionSheet = this . actionSheetCtrl . create ( { buttons : buttons } ) ; actionSheet . present ( ) ; } async onEditClick ( ) { if ( ! this . serverProvider . isConnected ( ) ) { this . alertCtrl . create ( { title : await this . utils . text ( 'actionOnlyOnlineDialogTitle' ) , message : await this . utils . text ( 'actionOnlyOnlineDialogMessage' ) , buttons : [ { text : await this . utils . text ( 'actionOnlyOnlineDialogCloseButton' ) , role : 'cancel' , } ] } ) . present ( ) ; return ; } this . firebaseAnalytics . logEvent ( 'edit_scan' , { } ) ; let editModal = this . modalCtrl . create ( EditScanSessionPage , this . scanSession ) ; editModal . onDidDismiss ( scanSession => { this . scanSession = scanSession ; this . sendUpdateScanSession ( this . scanSession ) ; this . save ( ) ; } ) ; editModal . present ( ) ; } onAddClicked ( ) { this . scan ( ) ; } save ( ) { this . scanSessionsStorage . updateScanSession ( this . scanSession ) ; } sendPutScan ( scan , sendKeystrokes = true ) { let scanSession = { ... this . scanSession } ; scanSession . scannings = [ scan ] ; let wsRequest = new requestModelPutScanSessions ( ) . fromObject ( { scanSessions : [ scanSession ] , sendKeystrokes : sendKeystrokes , deviceId : this . device . uuid , } ) ; this . serverProvider . send ( wsRequest ) ; } sendDeleteScan ( scan ) { let wsRequest = new requestModelDeleteScan ( ) . fromObject ( { scan : scan , scanSessionId : this . scanSession . id , } ) ; this . serverProvider . send ( wsRequest ) ; } sendUpdateScanSession ( updatedScanSession ) { let request = new requestModelUpdateScanSession ( ) . fromObject ( { scanSessionId : this . scanSession . id , scanSessionName : this . scanSession . name , scanSessionDate : updatedScanSession . date } ) ; this . serverProvider . send ( request ) ; } repeat ( scan , setRepeated = true ) { if ( this . skipAlreadySent && scan . ack ) { return ; } if ( setRepeated ) { scan . repeated = true ; } this . sendPutScan ( scan ) ; if ( this . enableBeep ) { this . nativeAudio . play ( 'beep' ) ; } } async onRepeatAllClick ( ) { this . alertCtrl . create ( { title : await this . utils . text ( 'sendBarcodeAgainDialogTitle' ) , inputs : [ { type : 'checkbox' , label : await this . utils . text ( 'sendBarcodeAgainDialogMessage' ) , value : 'skipAlreadySent' , checked : true } ] , buttons : [ { text : await this . utils . text ( 'sendBarcodeAgainDialogCancelButton' ) , role : 'cancel' , handler : data => { } } , { text : 'Send' , handler : data => { this . firebaseAnalytics . logEvent ( 'repeatAll' , { } ) ; this . skipAlreadySent = ( data == 'skipAlreadySent' ) ; this . settings . getRepeatInterval ( ) . then ( repeatInterval => { if ( repeatInterval != null ) { this . repeatInterval = repeatInterval ; } if ( this . repeatingStatus != 'repeating' && this . repeatingStatus != 'paused' ) { let startFrom = this . scanSession . scannings . length - 1 ; this . repeatAll ( startFrom ) ; } } ) ; } } ] } ) . present ( ) ; } onPauseRepeatingClick ( ) { this . repeatingStatus = 'paused' ; if ( this . repeatAllTimeout ) clearTimeout ( this . repeatAllTimeout ) ; setTimeout ( ( ) => { this . save ( ) ; } , 1500 ) ; } onResumeRepeatingClick ( ) { this . repeatingStatus = 'repeating' ; this . repeatAll ( this . next ) ; } stopRepeatingClick ( ) { if ( this . repeatAllTimeout ) clearTimeout ( this . repeatAllTimeout ) ; this . repeatAllTimeout = null ; this . next = - 1 ; this . repeatingStatus = 'stopped' ; this . scanSession . scannings . forEach ( scan => scan . repeated = false ) ; this . skipAlreadySent = false ; setTimeout ( ( ) => { this . save ( ) ; } , 1500 ) ; } onEnterClick ( event = null ) { this . keyboardInput . submit ( ) ; } private repeatAll ( startFrom = - 1 ) { if ( startFrom < 0 || startFrom >= this . scanSession . scannings . length ) { this . stopRepeatingClick ( ) ; return ; } this . repeatingStatus = 'repeating' ; let scan = this . scanSession . scannings [ startFrom ] ; this . repeat ( scan ) ; this . next = startFrom - 1 ; if ( this . repeatAllTimeout ) clearTimeout ( this . repeatAllTimeout ) ; this . repeatAllTimeout = setTimeout ( ( ) => { if ( this . next >= 0 ) { this . repeatAll ( this . next ) ; } else { this . stopRepeatingClick ( ) ; } } , this . repeatInterval ) ; } onShareClick ( ) { let editModal = this . modalCtrl . create ( CSVExportOptionsPage , this . scanSession ) ; editModal . onDidDismiss ( csv => { let base64CSV = null ; try { base64CSV = btoa ( csv ) ; if ( ! base64CSV ) { this . onShareClickFallBack ( csv ) ; return ; } } catch ( error ) { this . onShareClickFallBack ( csv ) ; } if ( this . utils . isAndroid ( ) ) { this . socialSharing . share ( null , this . scanSession . name , "data:text/csv;base64," + base64CSV , null ) ; } else { this . socialSharing . share ( null , this . scanSession . name , "data:text/csv;df:" + this . scanSession . name + ".csv;base64," + base64CSV , null ) ; } } ) ; editModal . present ( ) ; } async onShareClickFallBack ( csv ) { const fileName = this . scanSession . name + '.csv' ; try { await this . file . removeFile ( this . file . cacheDirectory , fileName ) ; } catch { } let result = await this . file . writeFile ( this . file . cacheDirectory , fileName , csv ) ; if ( result && result . nativeURL ) { this . socialSharing . share ( null , this . scanSession . name , result . nativeURL , null ) ; } else { this . alertCtrl . create ( { title : await this . utils . text ( 'exportErrorDialogButton' ) , message : await this . utils . text ( 'exportErrorDialogMessage' , { "config.email_support" : Config . EMAIL_SUPPORT } ) } ) . present ( ) ; } } onOCRClick ( ) { let ocrPage = this . modalCtrl . create ( OcrPage ) ; ocrPage . onDidDismiss ( text => { if ( ! text ) return ; this . keyboardInput . value += text ; this . keyboardInput . focus ( ) ; } ) ; ocrPage . present ( ) ; } }