import { AppsService } from './apps.service' ; import { Injectable } from '@angular/core' ; import * as firebase from 'firebase' ; import 'firebase/firestore' ; import { MAT_DIALOG_SCROLL_STRATEGY } from '@angular/material' ; import * as _ from 'lodash' ; import { Observable } from '@firebase/util' ; import { Subject , BehaviorSubject } from 'rxjs' ; import { AngularFirestoreDocument } from 'angularfire2/firestore' ; const DATETIME_REGEX = /(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+)|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d)|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d)/ ; @ Injectable ( ) export class DataService { constructor ( private apps ) { } private isCollection ( path = '' ) { return path . split ( '/' ) . length % 2 === 1 ; } private snapshotToData ( snap ) { return { id : snap . id , data : snap . data ( ) , path : snap . ref . path } ; } protected setWindowResult ( result , options ) { const win = window as any ; const windowResults = this . isCollection ( options . path ) ? _ . map ( result , item => item . data ) : result . data ; win . r = windowResults ; win . result = windowResults ; } async get ( options = { } ) { const ref = this . ref ( options ) ; console . time ( 'get' ) ; const docs = await ref . get ( ) ; console . timeEnd ( 'get' ) ; const result = this . isCollection ( options . path ) ? docs . docs . map ( this . snapshotToData ) : this . snapshotToData ( docs ) ; this . setWindowResult ( result , options ) ; return result ; } ref ( options = { } ) { options . path = options . path || '' ; const firestore = firebase . firestore ( this . apps . activeApp ) ; if ( this . isCollection ( options . path ) ) { let ref = firestore . collection ( options . path ) ; const query = options . query ? eval ( options . query ) : ref ; return query ; } else { return firestore . doc ( options . path ) ; } } async delete ( options = { } ) { return this . ref ( options ) . delete ( ) ; } deletePaths ( paths ) { const chunks = _ . chunk ( paths , 20 ) ; const subject = new BehaviorSubject < number > ( 0 ) ; let doneCount = 0 ; const doIt = async ( ) => { for ( const chunk of chunks ) { await Promise . all ( _ . map ( chunk , path => this . delete ( { path } ) . then ( ( ) => { doneCount ++ ; subject . next ( doneCount ) ; } ) ) ) ; } } ; doIt ( ) . then ( ( ) => subject . complete ( ) ) ; return subject . asObservable ( ) ; } singleRef ( path ) { const firestore = firebase . firestore ( this . apps . activeApp ) ; const ref = firestore . doc ( path ) ; return ref ; } edit ( path , doc , override = false ) { const ref = this . singleRef ( path ) ; const betterDoc = this . dateify ( doc ) ; if ( override ) { return ref . set ( betterDoc ) ; } else { return ref . update ( betterDoc ) ; } } dateify ( doc ) { if ( doc === '__del__' ) { return firebase . firestore . FieldValue . delete ( ) ; } if ( typeof doc === 'string' ) { if ( ! doc . match ( DATETIME_REGEX ) ) { return doc ; } const date = Date . parse ( doc ) ; return new Date ( date ) ; } if ( doc === null ) { return null ; } if ( Array . isArray ( doc ) ) { return _ . map ( doc , ( val , key ) => this . dateify ( val ) ) ; } if ( typeof doc === 'object' ) { return _ . mapValues ( doc , ( val , key ) => this . dateify ( val ) ) ; } return doc ; } editMultiple ( paths , doc , override = false ) { const chunks = _ . chunk ( paths , 20 ) ; const subject = new BehaviorSubject < number > ( 0 ) ; let doneCount = 0 ; const doIt = async ( ) => { for ( const chunk of chunks ) { await Promise . all ( _ . map ( chunk , path => this . edit ( path , doc , override ) . then ( ( ) => { doneCount ++ ; subject . next ( doneCount ) ; } ) . catch ( err => subject . error ( err ) ) ) ) ; } } ; doIt ( ) . then ( ( ) => subject . complete ( ) ) ; return subject . asObservable ( ) ; } create ( path , doc ) { const betterDoc = this . dateify ( doc ) ; const firestore = firebase . firestore ( this . apps . activeApp ) ; const collection = firestore . collection ( path ) ; const id = betterDoc . id ; const docRef = id ? collection . doc ( id . toString ( ) ) : collection . doc ( ) ; return docRef . set ( betterDoc ) ; } createMultiple ( path , docs ) { const chunks = _ . chunk ( docs , 20 ) ; const subject = new BehaviorSubject < number > ( 0 ) ; let doneCount = 0 ; const doIt = async ( ) => { for ( const chunk of chunks ) { await Promise . all ( _ . map ( chunk , doc => this . create ( path , doc ) . then ( ( ) => { doneCount ++ ; subject . next ( doneCount ) ; } ) . catch ( err => subject . error ( err ) ) ) ) ; } } ; doIt ( ) . then ( ( ) => subject . complete ( ) ) ; return subject . asObservable ( ) ; } }