import { Injectable } from '@angular/core' ; import { HttpClient } from '@angular/common/http' ; import { Database , IconTable } from './database' ; import { Icon } from './models/icon.model' ; import { Font } from './models/font.model' ; import { Alias } from './models/alias.model' ; import { Tag } from './models/tag.model' ; interface StringMap { [ key ] : string ; } const font = new Font ( ) . from ( { id : 'D051337E-BC7E-11E5-A4E9-842B2B6CFE1B' } as Font ) ; @ Injectable ( ) export class DatabaseService { constructor ( private http ) { } private db = new Database ( ) ; param font private async getHashesFromServer ( font ) { const res = await this . http . get < StringMap > ( `/api/font/${ font . id }/hash` ) . toPromise ( ) ; return res ; } private async getIconsFromServer ( font , ids ) { if ( ids . length === 0 ) { return [ ] ; } const res = await this . http . get < Icon [ ] > ( `/api/font/${ font . id }` , { params : { ids : ids . join ( ',' ) } } ) . toPromise ( ) ; return res . map ( icon => new Icon ( ) . from ( icon ) ) ; } private async getIconsByPage ( font , page , size ) { const res = await this . http . get < Icon [ ] > ( `/api/font/${ font . id }` , { params : { page : page . toString ( ) , size : size . toString ( ) } } ) . toPromise ( ) ; return res . map ( icon => new Icon ( ) . from ( icon ) ) ; } async asyncThrottledMap < T , U > ( maxCount , array , f ) { let inFlight = new Set < Promise < U > > ( ) ; const result = [ ] ; for ( let elem of array ) { if ( inFlight . size >= maxCount ) { await Promise . race ( inFlight ) ; } const origPromise = f ( elem ) ; const handledPromise = wrap ( origPromise ) ; result . push ( handledPromise ) ; } return Promise . all ( result ) ; async function wrap ( p ) { inFlight . add ( p ) ; const result = await p ; inFlight . delete ( p ) ; return result ; } } async sync ( ) { console . log ( 'start' ) ; const hashes = await this . getHashesFromServer ( font ) ; const hashIds = Object . keys ( hashes ) ; const localHashObj = await this . db . hashes . toArray ( ) ; const localHashes = { } ; localHashObj . forEach ( ( item ) => { localHashes [ item . id ] = item . hash ; } ) ; const localHashIds = Object . keys ( localHashes ) ; const modifiedIds = [ ] ; hashIds . forEach ( ( id ) => { if ( ! ( id in localHashes ) || localHashes [ id ] !== hashes [ id ] ) { modifiedIds . push ( id ) ; } } ) ; const removeIds = [ ] ; localHashIds . forEach ( ( id ) => { if ( ! ( id in hashes ) ) { removeIds . push ( id ) ; } } ) ; await this . db . icons . bulkDelete ( removeIds ) ; if ( modifiedIds . length < 500 ) { let i , j , chunkIds = [ ] , chunk = 100 ; for ( i = 0 , j = modifiedIds . length ; i < j ; i += chunk ) { chunkIds . push ( modifiedIds . slice ( i , i + chunk ) ) ; } const chunkPromises = chunkIds . map ( ( ids ) => { return this . getIconsFromServer ( font , ids ) ; } ) ; const results = await Promise . all ( chunkPromises ) ; results . forEach ( icons => { this . db . icons . bulkPut ( icons . map ( icon => ( { id : icon . fontIcon . id , idFull : icon . id , fontId : font . id , name : icon . name , data : icon . data , version : icon . version , codepoint : icon . codepoint , aliases : icon . aliases , tags : icon . tags } ) ) ) ; } ) ; } else { let size = 500 , pages = Math . ceil ( hashIds . length / size ) , chunkPromises = [ ] ; for ( let page = 1 ; page <= pages ; page ++ ) { chunkPromises . push ( this . getIconsByPage ( font , page , size ) ) ; } await this . asyncThrottledMap < Promise < Icon [ ] > , void > ( 2 , chunkPromises , ( p ) => p . then ( icons => { this . db . icons . bulkPut ( icons . map ( icon => ( { id : icon . fontIcon . id , idFull : icon . id , fontId : font . id , name : icon . name , data : icon . data , version : icon . version , codepoint : icon . codepoint , aliases : JSON . stringify ( icon . aliases ) , tags : JSON . stringify ( icon . tags ) } ) ) ) ; } ) ) ; } await this . db . hashes . bulkPut ( hashIds . map ( id => ( { id , hash : hashes [ id ] } ) ) ) ; await this . db . hashes . bulkDelete ( removeIds ) ; console . log ( 'done' ) ; } convert ( local ) { const icon = new Icon ( ) ; icon . id = local . idFull ; icon . name = local . name ; icon . data = local . data ; icon . version = local . version ; icon . codepoint = local . codepoint ; const aliases = local . aliases instanceof Array ? local . aliases : JSON . parse ( local . aliases ) ; icon . aliases = aliases . map ( alias => new Alias ( ) . from ( alias ) ) ; const tags = local . tags instanceof Array ? local . tags : JSON . parse ( local . tags ) ; icon . tags = tags . map ( tag => new Tag ( ) . from ( tag ) ) ; return icon ; } async getIconByName ( name ) { const local = await this . db . icons . where ( 'name' ) . equals ( name ) . first ( ) ; if ( ! local ) { return null ; } return this . convert ( local ) ; } async getIcons ( term ? ) { let icons ; if ( term ) { const safeTerm = term . toLowerCase ( ) . replace ( /[^a-z0-9-]/g , '' ) ; const reg = new RegExp ( `${ safeTerm }` ) ; icons = await this . db . icons . where ( 'fontId' ) . equals ( font . id ) . filter ( ( icon ) => { console . log ( JSON . parse ( icon . aliases ) ) ; return icon . name . match ( reg ) !== null ; } ) . sortBy ( 'name' ) ; } else { icons = await this . db . icons . where ( 'fontId' ) . equals ( font . id ) . sortBy ( 'name' ) ; } return icons . map ( icon => this . convert ( icon ) ) ; } async delete ( ) { await this . db . delete ( ) ; } }