import localforage from 'localforage' ; import { processObserverList } from './facades' ; import { LocalForageWithObservablePrivateProps } from './LocalForageWithObservablePrivateProps' ; interface StorageEventPayload { name : string ; storeName : string ; key : string ; methodName : string ; valueChange : boolean ; success : boolean ; fail : boolean ; error : string ; ticks : number ; } const isSupported = typeof window !== 'undefined' && typeof window . addEventListener === 'function' && typeof window . removeEventListener === 'function' && typeof JSON !== 'undefined' && JSON . stringify && JSON . parse && localforage . supports ( localforage . LOCALSTORAGE ) ; const sysKeyPrefix = [ '_localforage_sys' , '_localforage_observable_sys' ] . join ( '/' ) ; const db = isSupported ? window . localStorage : null ; let inited = false ; export default class StorageEventObserver { private localforageInstance : LocalForageWithObservablePrivateProps | null ; constructor ( localforageInstance ) { this . localforageInstance = localforageInstance as LocalForageWithObservablePrivateProps ; this . _onStorageEventBinded = this . _onStorageEvent . bind ( this ) ; } setup ( ) { if ( ! isSupported || inited ) { return ; } window . addEventListener ( 'storage' , this . _onStorageEventBinded , false ) ; inited = true ; } destroy ( ) { this . localforageInstance = null ; if ( inited ) { window . removeEventListener ( 'storage' , this . _onStorageEventBinded , false ) ; inited = false ; } } _onStorageEventBinded : ( e : StorageEvent ) => Promise < void > ; _onStorageEvent ( e ) { if ( ! this . localforageInstance || e . key !== sysKeyPrefix || ! e . newValue ) { return ; } try { const payload = JSON . parse ( e . newValue ) as LocalForageObservableChange & { name : string ; storeName : string ; } ; if ( ! payload ) { return ; } const dbInfo = this . localforageInstance . _dbInfo ; if ( dbInfo . name !== payload . name || dbInfo . storeName !== payload . storeName ) { return ; } return this . localforageInstance . ready ( ) . then ( ( ) => { const changeArgs = { key : payload . key , methodName : payload . methodName , oldValue : null , newValue : null , success : payload . success , fail : payload . fail , error : payload . error , valueChange : payload . valueChange , crossTabNotification : 'StorageEvent' , originalEvent : e , } as LocalForageObservableChange ; if ( payload . methodName === 'setItem' && payload . success ) { return this . localforageInstance ! . getItem ( payload . key ) . then ( ( newValue ) => { changeArgs . newValue = newValue ; return changeArgs ; } ) ; } return changeArgs ; } ) . then ( ( changeArgs ) => { if ( changeArgs . valueChange ) { processObserverList ( this . localforageInstance ! . _observables . changeDetection , changeArgs ) ; } processObserverList ( this . localforageInstance ! . _observables . callDetection , changeArgs ) ; } ) ; } catch ( ex ) { return Promise . reject ( ex ) ; } } publish ( changeArgs ) { if ( ! isSupported || ! db ) { return ; } const dbInfo = this . localforageInstance ! . _dbInfo ; let errorString ; try { if ( changeArgs . error ) { errorString = JSON . stringify ( changeArgs . error ) ; } } catch ( ex ) { } const payload = { name : dbInfo . name , storeName : dbInfo . storeName , key : changeArgs . key , methodName : changeArgs . methodName , valueChange : changeArgs . valueChange , success : changeArgs . success , fail : changeArgs . fail , error : errorString , ticks : + new Date ( ) , } as StorageEventPayload ; const value = JSON . stringify ( payload ) ; db . setItem ( sysKeyPrefix , value ) ; } }