@@ -82,12 +82,6 @@ export class OAuthToken implements Token {
8282 */
8383export type CredentialChangeListener = ( user : User ) => void ;
8484
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-
9185/**
9286 * Provides methods for getting the uid and token for the current user and
9387 * listening for changes.
@@ -107,7 +101,10 @@ export interface CredentialsProvider {
107101 * (sign-in / sign-out, token changes). It is immediately called once with the
108102 * initial user.
109103 */
110- setChangeListener ( changeListener : AsyncCredentialChangeListener ) : void ;
104+ setChangeListener (
105+ asyncQueue : AsyncQueue ,
106+ changeListener : CredentialChangeListener
107+ ) : void ;
111108
112109 /** Removes the previously-set change listener. */
113110 removeChangeListener ( ) : void ;
@@ -120,22 +117,25 @@ export class EmptyCredentialsProvider implements CredentialsProvider {
120117 * This isn't actually necessary since the UID never changes, but we use this
121118 * to verify the listen contract is adhered to in tests.
122119 */
123- private changeListener : AsyncCredentialChangeListener | null = null ;
120+ private changeListener : CredentialChangeListener | null = null ;
124121
125122 getToken ( ) : Promise < Token | null > {
126123 return Promise . resolve < Token | null > ( null ) ;
127124 }
128125
129126 invalidateToken ( ) : void { }
130127
131- setChangeListener ( changeListener : AsyncCredentialChangeListener ) : void {
128+ setChangeListener (
129+ asyncQueue : AsyncQueue ,
130+ changeListener : CredentialChangeListener
131+ ) : void {
132132 debugAssert (
133133 ! this . changeListener ,
134134 'Can only call setChangeListener() once.'
135135 ) ;
136136 this . changeListener = changeListener ;
137137 // Fire with initial user.
138- changeListener ( Promise . resolve ( User . UNAUTHENTICATED ) ) ;
138+ changeListener ( User . UNAUTHENTICATED ) ;
139139 }
140140
141141 removeChangeListener ( ) : void {
@@ -161,23 +161,25 @@ export class FirebaseCredentialsProvider implements CredentialsProvider {
161161 private tokenCounter = 0 ;
162162
163163 /** The listener registered with setChangeListener(). */
164- private changeListener : AsyncCredentialChangeListener | null = null ;
164+ private changeListener : CredentialChangeListener | null = null ;
165165
166166 private forceRefresh = false ;
167167
168168 private auth : FirebaseAuthInternal | null = null ;
169169
170- private authInitialized = new Deferred < void > ( ) ;
170+ private asyncQueue : AsyncQueue | null = null ;
171+
172+ private authListeners : Array < Deferred > = [ ] ;
171173
172174 constructor ( authProvider : Provider < FirebaseAuthInternalName > ) {
173175 this . tokenListener = ( ) => {
174176 this . tokenCounter ++ ;
175- this . authInitialized . resolve ( ) ;
177+ this . authListeners . forEach ( listener => listener . resolve ( ) ) ;
176178 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 ) ;
180181 }
182+ this . receivedInitialUser = true ;
181183 } ;
182184
183185 this . tokenCounter = 0 ;
@@ -186,15 +188,10 @@ export class FirebaseCredentialsProvider implements CredentialsProvider {
186188 logDebug ( 'FirebaseCredentialsProvider' , 'Auth detected' ) ;
187189 this . auth = auth ;
188190 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 ( ) ;
191194 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- ) ;
198195 }
199196 } ;
200197
@@ -216,6 +213,8 @@ export class FirebaseCredentialsProvider implements CredentialsProvider {
216213 }
217214 }
218215 } , 0 ) ;
216+
217+ this . awaitToken ( ) ;
219218 }
220219
221220 getToken ( ) : Promise < Token | null > {
@@ -263,16 +262,22 @@ export class FirebaseCredentialsProvider implements CredentialsProvider {
263262 this . forceRefresh = true ;
264263 }
265264
266- setChangeListener ( changeListener : AsyncCredentialChangeListener ) : void {
265+ setChangeListener (
266+ asyncQueue : AsyncQueue ,
267+ changeListener : CredentialChangeListener
268+ ) : void {
267269 debugAssert (
268270 ! this . changeListener ,
269271 'Can only call setChangeListener() once.'
270272 ) ;
271273 this . changeListener = changeListener ;
274+ this . asyncQueue = asyncQueue ;
272275
273276 // Fire the initial event
274277 if ( this . receivedInitialUser ) {
275- changeListener ( Promise . resolve ( this . currentUser ) ) ;
278+ asyncQueue . enqueueRetryable ( ( ) => {
279+ changeListener ( this . currentUser ) ;
280+ } ) ;
276281 }
277282 }
278283
@@ -296,6 +301,25 @@ export class FirebaseCredentialsProvider implements CredentialsProvider {
296301 ) ;
297302 return new User ( currentUid ) ;
298303 }
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+ }
299323}
300324
301325// Manual type definition for the subset of Gapi we use.
@@ -358,9 +382,12 @@ export class FirstPartyCredentialsProvider implements CredentialsProvider {
358382 ) ;
359383 }
360384
361- setChangeListener ( changeListener : AsyncCredentialChangeListener ) : void {
385+ setChangeListener (
386+ asyncQueue : AsyncQueue ,
387+ changeListener : CredentialChangeListener
388+ ) : void {
362389 // Fire with initial uid.
363- changeListener ( Promise . resolve ( User . FIRST_PARTY ) ) ;
390+ changeListener ( User . FIRST_PARTY ) ;
364391 }
365392
366393 removeChangeListener ( ) : void { }
0 commit comments