@@ -82,12 +82,6 @@ export class OAuthToken implements Token {
82
82
*/
83
83
export type CredentialChangeListener = ( user : User ) => void ;
84
84
85
- /**
86
- * A Listener for credential change events. The listener blocks until a user
87
- * is available.
88
- */
89
- export type AsyncCredentialChangeListener = ( user : Promise < User > ) => void ;
90
-
91
85
/**
92
86
* Provides methods for getting the uid and token for the current user and
93
87
* listening for changes.
@@ -107,7 +101,10 @@ export interface CredentialsProvider {
107
101
* (sign-in / sign-out, token changes). It is immediately called once with the
108
102
* initial user.
109
103
*/
110
- setChangeListener ( changeListener : AsyncCredentialChangeListener ) : void ;
104
+ setChangeListener (
105
+ asyncQueue : AsyncQueue ,
106
+ changeListener : CredentialChangeListener
107
+ ) : void ;
111
108
112
109
/** Removes the previously-set change listener. */
113
110
removeChangeListener ( ) : void ;
@@ -120,22 +117,25 @@ export class EmptyCredentialsProvider implements CredentialsProvider {
120
117
* This isn't actually necessary since the UID never changes, but we use this
121
118
* to verify the listen contract is adhered to in tests.
122
119
*/
123
- private changeListener : AsyncCredentialChangeListener | null = null ;
120
+ private changeListener : CredentialChangeListener | null = null ;
124
121
125
122
getToken ( ) : Promise < Token | null > {
126
123
return Promise . resolve < Token | null > ( null ) ;
127
124
}
128
125
129
126
invalidateToken ( ) : void { }
130
127
131
- setChangeListener ( changeListener : AsyncCredentialChangeListener ) : void {
128
+ setChangeListener (
129
+ asyncQueue : AsyncQueue ,
130
+ changeListener : CredentialChangeListener
131
+ ) : void {
132
132
debugAssert (
133
133
! this . changeListener ,
134
134
'Can only call setChangeListener() once.'
135
135
) ;
136
136
this . changeListener = changeListener ;
137
137
// Fire with initial user.
138
- changeListener ( Promise . resolve ( User . UNAUTHENTICATED ) ) ;
138
+ changeListener ( User . UNAUTHENTICATED ) ;
139
139
}
140
140
141
141
removeChangeListener ( ) : void {
@@ -161,23 +161,25 @@ export class FirebaseCredentialsProvider implements CredentialsProvider {
161
161
private tokenCounter = 0 ;
162
162
163
163
/** The listener registered with setChangeListener(). */
164
- private changeListener : AsyncCredentialChangeListener | null = null ;
164
+ private changeListener : CredentialChangeListener | null = null ;
165
165
166
166
private forceRefresh = false ;
167
167
168
168
private auth : FirebaseAuthInternal | null = null ;
169
169
170
- private authInitialized = new Deferred < void > ( ) ;
170
+ private asyncQueue : AsyncQueue | null = null ;
171
+
172
+ private authListeners : Array < Deferred > = [ ] ;
171
173
172
174
constructor ( authProvider : Provider < FirebaseAuthInternalName > ) {
173
175
this . tokenListener = ( ) => {
174
176
this . tokenCounter ++ ;
175
- this . authInitialized . resolve ( ) ;
177
+ this . authListeners . forEach ( listener => listener . resolve ( ) ) ;
176
178
this . currentUser = this . getUser ( ) ;
177
- this . receivedInitialUser = true ;
178
- if ( this . changeListener ) {
179
- this . changeListener ( Promise . resolve ( this . currentUser ) ) ;
179
+ if ( this . receivedInitialUser && this . changeListener ) {
180
+ this . changeListener ( this . currentUser ) ;
180
181
}
182
+ this . receivedInitialUser = true ;
181
183
} ;
182
184
183
185
this . tokenCounter = 0 ;
@@ -186,15 +188,10 @@ export class FirebaseCredentialsProvider implements CredentialsProvider {
186
188
logDebug ( 'FirebaseCredentialsProvider' , 'Auth detected' ) ;
187
189
this . auth = auth ;
188
190
this . receivedInitialUser = false ;
189
- // tokenListener/changeListener can be removed by removeChangeListener()
190
- if ( this . tokenListener && this . changeListener ) {
191
+ // tokenListener can be removed by removeChangeListener()
192
+ if ( this . tokenListener ) {
193
+ this . awaitToken ( ) ;
191
194
this . auth . addAuthTokenListener ( this . tokenListener ) ;
192
- // Block the change listener until auth becomes available
193
- this . changeListener (
194
- new Promise < User > ( resolve => {
195
- this . authInitialized . promise . then ( ( ) => resolve ( this . getUser ( ) ) ) ;
196
- } )
197
- ) ;
198
195
}
199
196
} ;
200
197
@@ -216,6 +213,8 @@ export class FirebaseCredentialsProvider implements CredentialsProvider {
216
213
}
217
214
}
218
215
} , 0 ) ;
216
+
217
+ this . awaitToken ( ) ;
219
218
}
220
219
221
220
getToken ( ) : Promise < Token | null > {
@@ -263,16 +262,22 @@ export class FirebaseCredentialsProvider implements CredentialsProvider {
263
262
this . forceRefresh = true ;
264
263
}
265
264
266
- setChangeListener ( changeListener : AsyncCredentialChangeListener ) : void {
265
+ setChangeListener (
266
+ asyncQueue : AsyncQueue ,
267
+ changeListener : CredentialChangeListener
268
+ ) : void {
267
269
debugAssert (
268
270
! this . changeListener ,
269
271
'Can only call setChangeListener() once.'
270
272
) ;
271
273
this . changeListener = changeListener ;
274
+ this . asyncQueue = asyncQueue ;
272
275
273
276
// Fire the initial event
274
277
if ( this . receivedInitialUser ) {
275
- changeListener ( Promise . resolve ( this . currentUser ) ) ;
278
+ asyncQueue . enqueueRetryable ( ( ) => {
279
+ changeListener ( this . currentUser ) ;
280
+ } ) ;
276
281
}
277
282
}
278
283
@@ -296,6 +301,25 @@ export class FirebaseCredentialsProvider implements CredentialsProvider {
296
301
) ;
297
302
return new User ( currentUid ) ;
298
303
}
304
+
305
+ /**
306
+ * Blocks the AsyncQueue until the next user is available. This is invoked
307
+ * on SDK start to wait for the first user token (or `null` if Auth is not yet
308
+ * loaded). If Auth is loaded after Firestore, `awaitToken()` is also used to
309
+ * block Firestore until auth is fully initialized.
310
+ */
311
+ private awaitToken ( ) : void {
312
+ if ( this . asyncQueue ) {
313
+ this . authListeners . push ( new Deferred < void > ( ) ) ;
314
+ this . asyncQueue . enqueueRetryable ( async ( ) => {
315
+ console . log ( 'Blocking queue' ) ;
316
+ await Promise . all ( this . authListeners . map ( deferred => deferred . promise ) ) ;
317
+ if ( this . changeListener ) {
318
+ this . changeListener ( this . currentUser ) ;
319
+ }
320
+ } ) ;
321
+ }
322
+ }
299
323
}
300
324
301
325
// Manual type definition for the subset of Gapi we use.
@@ -358,9 +382,12 @@ export class FirstPartyCredentialsProvider implements CredentialsProvider {
358
382
) ;
359
383
}
360
384
361
- setChangeListener ( changeListener : AsyncCredentialChangeListener ) : void {
385
+ setChangeListener (
386
+ asyncQueue : AsyncQueue ,
387
+ changeListener : CredentialChangeListener
388
+ ) : void {
362
389
// Fire with initial uid.
363
- changeListener ( Promise . resolve ( User . FIRST_PARTY ) ) ;
390
+ changeListener ( User . FIRST_PARTY ) ;
364
391
}
365
392
366
393
removeChangeListener ( ) : void { }
0 commit comments