@@ -5,20 +5,37 @@ import { FIREBASE_APP_NAME, FIREBASE_OPTIONS, FirebaseAppConfig, FirebaseOptions
5
5
import { remoteConfig } from 'firebase/app' ;
6
6
import { isPlatformBrowser } from '@angular/common' ;
7
7
8
- export interface ConfigTemplate { [ key : string ] : string | number | boolean ; }
8
+ export interface ConfigTemplate {
9
+ [ key : string ] : string | number | boolean ;
10
+ }
9
11
10
12
export const SETTINGS = new InjectionToken < remoteConfig . Settings > ( 'angularfire2.remoteConfig.settings' ) ;
11
13
export const DEFAULTS = new InjectionToken < ConfigTemplate > ( 'angularfire2.remoteConfig.defaultConfig' ) ;
12
14
13
- export interface AngularFireRemoteConfig extends ɵPromiseProxy < remoteConfig . RemoteConfig > { }
15
+ export interface AngularFireRemoteConfig extends ɵPromiseProxy < remoteConfig . RemoteConfig > {
16
+ }
14
17
15
18
// TODO export as implements Partial<...> so minor doesn't break us
16
19
export class Value implements remoteConfig . Value {
17
- asBoolean ( ) { return [ '1' , 'true' , 't' , 'y' , 'yes' , 'on' ] . indexOf ( this . _value . toLowerCase ( ) ) > - 1 ; }
18
- asString ( ) { return this . _value ; }
19
- asNumber ( ) { return Number ( this . _value ) || 0 ; }
20
- getSource ( ) { return this . _source ; }
21
- constructor ( public _source : remoteConfig . ValueSource , public _value : string ) { }
20
+ asBoolean ( ) {
21
+ return [ '1' , 'true' , 't' , 'y' , 'yes' , 'on' ] . indexOf ( this . _value . toLowerCase ( ) ) > - 1 ;
22
+ }
23
+
24
+ asString ( ) {
25
+ return this . _value ;
26
+ }
27
+
28
+ asNumber ( ) {
29
+ return Number ( this . _value ) || 0 ;
30
+ }
31
+
32
+ getSource ( ) {
33
+ return this . _source ;
34
+ }
35
+
36
+ // tslint:disable-next-line:variable-name
37
+ constructor ( public _source : remoteConfig . ValueSource , public _value : string ) {
38
+ }
22
39
}
23
40
24
41
// SEMVER use ConstructorParameters when we can support Typescript 3.6
@@ -29,7 +46,7 @@ export class Parameter extends Value {
29
46
}
30
47
31
48
// If it's a Parameter array, test any, else test the individual Parameter
32
- const filterTest = ( fn : ( param : Parameter ) => boolean ) => filter < Parameter | Parameter [ ] > ( it => Array . isArray ( it ) ? it . some ( fn ) : fn ( it ) ) ;
49
+ const filterTest = ( fn : ( param : Parameter ) => boolean ) => filter < Parameter | Parameter [ ] > ( it => Array . isArray ( it ) ? it . some ( fn ) : fn ( it ) ) ;
33
50
34
51
// Allow the user to bypass the default values and wait till they get something from the server, even if it's a cached copy;
35
52
// if used in conjuntion with first() it will only fetch RC values from the server if they aren't cached locally
@@ -45,16 +62,17 @@ export class AngularFireRemoteConfig {
45
62
46
63
readonly changes : Observable < Parameter > ;
47
64
readonly parameters : Observable < Parameter [ ] > ;
48
- readonly numbers : Observable < { [ key : string ] : number | undefined } > & { [ key : string ] : Observable < number > } ;
49
- readonly booleans : Observable < { [ key : string ] : boolean | undefined } > & { [ key : string ] : Observable < boolean > } ;
50
- readonly strings : Observable < { [ key : string ] : string | undefined } > & { [ key : string ] : Observable < string | undefined > } ;
65
+ readonly numbers : Observable < { [ key : string ] : number | undefined } > & { [ key : string ] : Observable < number > } ;
66
+ readonly booleans : Observable < { [ key : string ] : boolean | undefined } > & { [ key : string ] : Observable < boolean > } ;
67
+ readonly strings : Observable < { [ key : string ] : string | undefined } > & { [ key : string ] : Observable < string | undefined > } ;
51
68
52
69
constructor (
53
70
@Inject ( FIREBASE_OPTIONS ) options : FirebaseOptions ,
54
- @Optional ( ) @Inject ( FIREBASE_APP_NAME ) nameOrConfig : string | FirebaseAppConfig | null | undefined ,
55
- @Optional ( ) @Inject ( SETTINGS ) settings : remoteConfig . Settings | null ,
56
- @Optional ( ) @Inject ( DEFAULTS ) defaultConfig : ConfigTemplate | null ,
71
+ @Optional ( ) @Inject ( FIREBASE_APP_NAME ) nameOrConfig : string | FirebaseAppConfig | null | undefined ,
72
+ @Optional ( ) @Inject ( SETTINGS ) settings : remoteConfig . Settings | null ,
73
+ @Optional ( ) @Inject ( DEFAULTS ) defaultConfig : ConfigTemplate | null ,
57
74
private zone : NgZone ,
75
+ // tslint:disable-next-line:ban-types
58
76
@Inject ( PLATFORM_ID ) platformId : Object
59
77
) {
60
78
@@ -66,8 +84,12 @@ export class AngularFireRemoteConfig {
66
84
map ( ( ) => ɵfirebaseAppFactory ( options , zone , nameOrConfig ) ) ,
67
85
map ( app => app . remoteConfig ( ) ) ,
68
86
tap ( rc => {
69
- if ( settings ) { rc . settings = settings ; }
70
- if ( defaultConfig ) { rc . defaultConfig = defaultConfig ; }
87
+ if ( settings ) {
88
+ rc . settings = settings ;
89
+ }
90
+ if ( defaultConfig ) {
91
+ rc . defaultConfig = defaultConfig ;
92
+ }
71
93
} ) ,
72
94
startWith ( undefined ) ,
73
95
shareReplay ( { bufferSize : 1 , refCount : false } )
@@ -77,16 +99,16 @@ export class AngularFireRemoteConfig {
77
99
filter < remoteConfig . RemoteConfig > ( rc => ! ! rc )
78
100
) ;
79
101
80
- const default$ : Observable < { [ key : string ] : remoteConfig . Value } > = of ( Object . keys ( defaultConfig || { } ) . reduce (
81
- ( c , k ) => ( { ...c , [ k ] : new Value ( 'default' , defaultConfig ! [ k ] . toString ( ) ) } ) , { }
102
+ const default$ : Observable < { [ key : string ] : remoteConfig . Value } > = of ( Object . keys ( defaultConfig || { } ) . reduce (
103
+ ( c , k ) => ( { ...c , [ k ] : new Value ( 'default' , defaultConfig [ k ] . toString ( ) ) } ) , { }
82
104
) ) ;
83
105
84
106
// we should filter out the defaults we provided to RC, since we have our own implementation
85
107
// that gives us a -1 for fetchTimeMillis (so filterFresh can filter them out)
86
- const filterOutDefaults = map < { [ key : string ] : remoteConfig . Value } , { [ key : string ] : remoteConfig . Value } > ( all =>
108
+ const filterOutDefaults = map < { [ key : string ] : remoteConfig . Value } , { [ key : string ] : remoteConfig . Value } > ( all =>
87
109
Object . keys ( all )
88
- . filter ( key => all [ key ] . getSource ( ) != 'default' )
89
- . reduce ( ( acc , key ) => ( { ...acc , [ key ] : all [ key ] } ) , { } )
110
+ . filter ( key => all [ key ] . getSource ( ) !== 'default' )
111
+ . reduce ( ( acc , key ) => ( { ...acc , [ key ] : all [ key ] } ) , { } )
90
112
) ;
91
113
92
114
const existing$ = loadedRemoteConfig$ . pipe (
@@ -120,29 +142,33 @@ export class AngularFireRemoteConfig {
120
142
) )
121
143
) ;
122
144
123
- this . strings = proxyAll ( this . parameters , 'strings' ) ;
145
+ this . strings = proxyAll ( this . parameters , 'strings' ) ;
124
146
this . booleans = proxyAll ( this . parameters , 'booleans' ) ;
125
- this . numbers = proxyAll ( this . parameters , 'numbers' ) ;
147
+ this . numbers = proxyAll ( this . parameters , 'numbers' ) ;
126
148
127
149
return ɵlazySDKProxy ( this , loadedRemoteConfig$ , zone ) ;
128
150
}
129
151
130
152
}
131
153
132
154
// I ditched loading the defaults into RC and a simple map for scan since we already have our own defaults implementation.
133
- // The idea here being that if they have a default that never loads from the server, they will be able to tell via fetchTimeMillis on the Parameter.
134
- // Also if it doesn't come from the server it won't emit again in .changes, due to the distinctUntilChanged, which we can simplify to === rather than deep comparison
135
- const scanToParametersArray = ( remoteConfig : Observable < remoteConfig . RemoteConfig | undefined > ) : OperatorFunction < { [ key : string ] : remoteConfig . Value } , Parameter [ ] > => pipe (
155
+ // The idea here being that if they have a default that never loads from the server, they will be able to tell via fetchTimeMillis
156
+ // on the Parameter. Also if it doesn't come from the server it won't emit again in .changes, due to the distinctUntilChanged,
157
+ // which we can simplify to === rather than deep comparison
158
+ const scanToParametersArray = (
159
+ remoteConfig : Observable < remoteConfig . RemoteConfig | undefined >
160
+ ) : OperatorFunction < { [ key : string ] : remoteConfig . Value } , Parameter [ ] > => pipe (
136
161
withLatestFrom ( remoteConfig ) ,
137
162
scan ( ( existing , [ all , rc ] ) => {
138
163
// SEMVER use "new Set" to unique once we're only targeting es6
139
- // at the scale we expect remote config to be at, we probably won't see a performance hit from this unoptimized uniqueness implementation
164
+ // at the scale we expect remote config to be at, we probably won't see a performance hit from this unoptimized uniqueness
165
+ // implementation.
140
166
// const allKeys = [...new Set([...existing.map(p => p.key), ...Object.keys(all)])];
141
167
const allKeys = [ ...existing . map ( p => p . key ) , ...Object . keys ( all ) ] . filter ( ( v , i , a ) => a . indexOf ( v ) === i ) ;
142
168
return allKeys . map ( key => {
143
169
const updatedValue = all [ key ] ;
144
170
return updatedValue ? new Parameter ( key , rc ? rc . fetchTimeMillis : - 1 , updatedValue . getSource ( ) , updatedValue . asString ( ) )
145
- : existing . find ( p => p . key === key ) ! ;
171
+ : existing . find ( p => p . key === key ) ;
146
172
} ) ;
147
173
} , [ ] as Array < Parameter > )
148
174
) ;
@@ -151,72 +177,97 @@ const AS_TO_FN = { strings: 'asString', numbers: 'asNumber', booleans: 'asBoolea
151
177
const STATIC_VALUES = { numbers : 0 , booleans : false , strings : undefined } ;
152
178
153
179
export const budget = < T > ( interval : number ) : MonoTypeOperatorFunction < T > => ( source : Observable < T > ) => new Observable < T > ( observer => {
154
- let timedOut = false ;
155
- // TODO use scheduler task rather than settimeout
156
- const timeout = setTimeout ( ( ) => {
157
- observer . complete ( ) ;
158
- timedOut = true ;
159
- } , interval ) ;
160
- return source . subscribe ( {
161
- next ( val ) { if ( ! timedOut ) { observer . next ( val ) ; } } ,
162
- error ( err ) { if ( ! timedOut ) { clearTimeout ( timeout ) ; observer . error ( err ) ; } } ,
163
- complete ( ) { if ( ! timedOut ) { clearTimeout ( timeout ) ; observer . complete ( ) ; } }
164
- } ) ;
180
+ let timedOut = false ;
181
+ // TODO use scheduler task rather than settimeout
182
+ const timeout = setTimeout ( ( ) => {
183
+ observer . complete ( ) ;
184
+ timedOut = true ;
185
+ } , interval ) ;
186
+ return source . subscribe ( {
187
+ next ( val ) {
188
+ if ( ! timedOut ) {
189
+ observer . next ( val ) ;
190
+ }
191
+ } ,
192
+ error ( err ) {
193
+ if ( ! timedOut ) {
194
+ clearTimeout ( timeout ) ;
195
+ observer . error ( err ) ;
196
+ }
197
+ } ,
198
+ complete ( ) {
199
+ if ( ! timedOut ) {
200
+ clearTimeout ( timeout ) ;
201
+ observer . complete ( ) ;
202
+ }
203
+ }
165
204
} ) ;
205
+ } ) ;
166
206
167
207
const typedMethod = ( it : any ) => {
168
208
switch ( typeof it ) {
169
- case 'string' : return 'asString' ;
170
- case 'boolean' : return 'asBoolean' ;
171
- case 'number' : return 'asNumber' ;
172
- default : return 'asString' ;
209
+ case 'string' :
210
+ return 'asString' ;
211
+ case 'boolean' :
212
+ return 'asBoolean' ;
213
+ case 'number' :
214
+ return 'asNumber' ;
215
+ default :
216
+ return 'asString' ;
173
217
}
174
218
} ;
175
219
176
- export function scanToObject ( ) : OperatorFunction < Parameter , { [ key : string ] : string | undefined } > ;
177
- export function scanToObject ( to : 'numbers' ) : OperatorFunction < Parameter , { [ key : string ] : number | undefined } > ;
178
- export function scanToObject ( to : 'booleans' ) : OperatorFunction < Parameter , { [ key : string ] : boolean | undefined } > ;
179
- export function scanToObject ( to : 'strings' ) : OperatorFunction < Parameter , { [ key : string ] : string | undefined } > ;
180
- export function scanToObject < T extends ConfigTemplate > ( template : T ) : OperatorFunction < Parameter , T & { [ key : string ] : string | undefined } > ;
181
- export function scanToObject < T extends ConfigTemplate > ( to : 'numbers' | 'booleans' | 'strings' | T = 'strings' ) {
220
+ export function scanToObject ( ) : OperatorFunction < Parameter , { [ key : string ] : string | undefined } > ;
221
+ export function scanToObject ( to : 'numbers' ) : OperatorFunction < Parameter , { [ key : string ] : number | undefined } > ;
222
+ export function scanToObject ( to : 'booleans' ) : OperatorFunction < Parameter , { [ key : string ] : boolean | undefined } > ;
223
+ // tslint:disable-next-line:unified-signatures
224
+ export function scanToObject ( to : 'strings' ) : OperatorFunction < Parameter , { [ key : string ] : string | undefined } > ;
225
+ export function scanToObject < T extends ConfigTemplate > ( template : T ) : OperatorFunction < Parameter , T & { [ key : string ] : string | undefined } > ;
226
+ export function scanToObject < T extends ConfigTemplate > ( to : 'numbers' | 'booleans' | 'strings' | T = 'strings' ) {
182
227
return pipe (
183
228
// TODO cleanup
184
229
scan (
185
- ( c , p : Parameter ) => ( { ...c , [ p . key ] : typeof to === 'object' ?
186
- p [ typedMethod ( to [ p . key ] ) ] ( ) :
187
- p [ AS_TO_FN [ to ] ] ( ) } ) ,
230
+ ( c , p : Parameter ) => ( {
231
+ ...c , [ p . key ] : typeof to === 'object' ?
232
+ p [ typedMethod ( to [ p . key ] ) ] ( ) :
233
+ p [ AS_TO_FN [ to ] ] ( )
234
+ } ) ,
188
235
typeof to === 'object' ?
189
- to as T & { [ key : string ] : string | undefined } :
190
- { } as { [ key : string ] : number | boolean | string }
236
+ to as T & { [ key : string ] : string | undefined } :
237
+ { } as { [ key : string ] : number | boolean | string }
191
238
) ,
192
239
debounceTime ( 1 ) ,
193
240
budget ( 10 ) ,
194
241
distinctUntilChanged ( ( a , b ) => JSON . stringify ( a ) === JSON . stringify ( b ) )
195
242
) ;
196
243
}
197
244
198
- export function mapToObject ( ) : OperatorFunction < Parameter [ ] , { [ key : string ] : string | undefined } > ;
199
- export function mapToObject ( to : 'numbers' ) : OperatorFunction < Parameter [ ] , { [ key : string ] : number | undefined } > ;
200
- export function mapToObject ( to : 'booleans' ) : OperatorFunction < Parameter [ ] , { [ key : string ] : boolean | undefined } > ;
201
- export function mapToObject ( to : 'strings' ) : OperatorFunction < Parameter [ ] , { [ key : string ] : string | undefined } > ;
202
- export function mapToObject < T extends ConfigTemplate > ( template : T ) : OperatorFunction < Parameter [ ] , T & { [ key : string ] : string | undefined } > ;
203
- export function mapToObject < T extends ConfigTemplate > ( to : 'numbers' | 'booleans' | 'strings' | T = 'strings' ) {
245
+ export function mapToObject ( ) : OperatorFunction < Parameter [ ] , { [ key : string ] : string | undefined } > ;
246
+ export function mapToObject ( to : 'numbers' ) : OperatorFunction < Parameter [ ] , { [ key : string ] : number | undefined } > ;
247
+ export function mapToObject ( to : 'booleans' ) : OperatorFunction < Parameter [ ] , { [ key : string ] : boolean | undefined } > ;
248
+ // tslint:disable-next-line:unified-signatures
249
+ export function mapToObject ( to : 'strings' ) : OperatorFunction < Parameter [ ] , { [ key : string ] : string | undefined } > ;
250
+ export function mapToObject < T extends ConfigTemplate > ( template : T ) :
251
+ OperatorFunction < Parameter [ ] , T & { [ key : string ] : string | undefined } > ;
252
+ export function mapToObject < T extends ConfigTemplate > ( to : 'numbers' | 'booleans' | 'strings' | T = 'strings' ) {
204
253
return pipe (
205
254
// TODO this is getting a little long, cleanup
206
255
map ( ( params : Parameter [ ] ) => params . reduce (
207
- ( c , p ) => ( { ...c , [ p . key ] : typeof to === 'object' ?
208
- p [ typedMethod ( to [ p . key ] ) ] ( ) :
209
- p [ AS_TO_FN [ to ] ] ( ) } ) ,
256
+ ( c , p ) => ( {
257
+ ...c , [ p . key ] : typeof to === 'object' ?
258
+ p [ typedMethod ( to [ p . key ] ) ] ( ) :
259
+ p [ AS_TO_FN [ to ] ] ( )
260
+ } ) ,
210
261
typeof to === 'object' ?
211
- to as T & { [ key : string ] : string | undefined } :
212
- { } as { [ key : string ] : number | boolean | string }
262
+ to as T & { [ key : string ] : string | undefined } :
263
+ { } as { [ key : string ] : number | boolean | string }
213
264
) ) ,
214
265
distinctUntilChanged ( ( a , b ) => JSON . stringify ( a ) === JSON . stringify ( b ) )
215
266
) ;
216
267
}
217
268
218
269
// TODO look into the types here, I don't like the anys
219
- const proxyAll = ( observable : Observable < Parameter [ ] > , as : 'numbers' | 'booleans' | 'strings' ) => new Proxy (
270
+ const proxyAll = ( observable : Observable < Parameter [ ] > , as : 'numbers' | 'booleans' | 'strings' ) => new Proxy (
220
271
observable . pipe ( mapToObject ( as as any ) ) , {
221
272
get : ( self , name : string ) => self [ name ] || observable . pipe (
222
273
map ( all => all . find ( p => p . key === name ) ) ,
0 commit comments