@@ -21,6 +21,7 @@ import { calculateBackoffMillis, FirebaseError } from "@firebase/util";
21
21
import { ERROR_FACTORY , ErrorCode } from "../errors" ;
22
22
import { Storage } from "../storage/storage" ;
23
23
import { isBefore } from 'date-fns' ;
24
+ import { VisibilityMonitor } from './visibility_monitor' ;
24
25
25
26
const API_KEY_HEADER = 'X-Goog-Api-Key' ;
26
27
const INSTALLATIONS_AUTH_TOKEN_HEADER = 'X-Goog-Firebase-Installations-Auth' ;
@@ -40,6 +41,7 @@ export class RealtimeHandler {
40
41
) {
41
42
this . httpRetriesRemaining = ORIGINAL_RETRIES ;
42
43
this . setRetriesRemaining ( ) ;
44
+ VisibilityMonitor . getInstance ( ) . on ( 'visible' , this . onVisibilityChange , this ) ;
43
45
}
44
46
45
47
private observers : Set < ConfigUpdateObserver > = new Set < ConfigUpdateObserver > ( ) ;
@@ -48,6 +50,7 @@ export class RealtimeHandler {
48
50
private controller ?: AbortController ;
49
51
private reader : ReadableStreamDefaultReader | undefined ;
50
52
private httpRetriesRemaining : number = ORIGINAL_RETRIES ;
53
+ private isInBackground : boolean = false ;
51
54
52
55
private async setRetriesRemaining ( ) {
53
56
// Retrieve number of remaining retries from last session. The minimum retry count being one.
@@ -101,9 +104,9 @@ export class RealtimeHandler {
101
104
* and canceling the stream reader if they exist.
102
105
*/
103
106
private closeRealtimeHttpConnection ( ) : void {
104
- if ( this . controller ) {
105
- this . controller . abort ( ) ;
106
- this . controller = undefined ;
107
+ if ( this . controller && ! this . isInBackground ) {
108
+ this . controller . abort ( ) ;
109
+ this . controller = undefined ;
107
110
}
108
111
109
112
if ( this . reader ) {
@@ -244,15 +247,22 @@ export class RealtimeHandler {
244
247
//await configAutoFetch.listenForNotifications();
245
248
}
246
249
} catch ( error ) {
250
+ if ( this . isInBackground ) {
251
+ // It's possible the app was backgrounded while the connection was open, which
252
+ // threw an exception trying to read the response. No real error here, so treat
253
+ // this as a success, even if we haven't read a 200 response code yet.
254
+ this . resetRetryCount ( ) ;
255
+ } else {
247
256
//there might have been a transient error so the client will retry the connection.
248
257
console . error ( 'Exception connecting to real-time RC backend. Retrying the connection...:' , error ) ;
258
+ }
249
259
} finally {
250
260
// Close HTTP connection and associated streams.
251
261
this . closeRealtimeHttpConnection ( ) ;
252
262
this . setIsHttpConnectionRunning ( false ) ;
253
263
254
264
// Update backoff metadata if the connection failed in the foreground.
255
- const connectionFailed = responseCode == null || this . isStatusCodeRetryable ( responseCode ) ;
265
+ const connectionFailed = ! this . isInBackground && ( responseCode == null || this . isStatusCodeRetryable ( responseCode ) ) ;
256
266
257
267
if ( connectionFailed ) {
258
268
await this . updateBackoffMetadataWithLastFailedStreamConnectionTime ( new Date ( ) ) ;
@@ -279,9 +289,10 @@ export class RealtimeHandler {
279
289
const hasActiveListeners = this . observers . size > 0 ;
280
290
const isNotDisabled = ! this . isRealtimeDisabled ;
281
291
const isNoConnectionActive = ! this . isConnectionActive ;
282
- return hasActiveListeners && isNotDisabled && isNoConnectionActive ;
292
+ const inForeground = ! this . isInBackground ;
293
+ return hasActiveListeners && isNotDisabled && isNoConnectionActive && inForeground ;
283
294
}
284
-
295
+
285
296
private async makeRealtimeHttpConnection ( delayMillis : number ) : Promise < void > {
286
297
if ( ! this . canEstablishStreamConnection ( ) ) {
287
298
return ;
@@ -291,6 +302,9 @@ export class RealtimeHandler {
291
302
setTimeout ( async ( ) => {
292
303
await this . beginRealtimeHttpStream ( ) ;
293
304
} , delayMillis ) ;
305
+ } else if ( ! this . isInBackground ) {
306
+ const error = ERROR_FACTORY . create ( ErrorCode . CONFIG_UPDATE_STREAM_ERROR , { originalErrorMessage : 'Unable to connect to the server. Check your connection and try again.' } ) ;
307
+ this . propagateError ( error ) ;
294
308
}
295
309
}
296
310
@@ -308,4 +322,24 @@ export class RealtimeHandler {
308
322
this . observers . add ( observer ) ;
309
323
await this . beginRealtime ( ) ;
310
324
}
325
+
326
+ private abortRealtimeConnection ( ) : void {
327
+ if ( this . controller ) {
328
+ this . controller . abort ( ) ;
329
+ this . controller = undefined ;
330
+ this . isConnectionActive = false ;
331
+ }
332
+ }
333
+
334
+ private onVisibilityChange ( visible : unknown ) {
335
+ const wasInBackground = this . isInBackground ;
336
+ this . isInBackground = ! visible ;
337
+ if ( wasInBackground !== this . isInBackground ) {
338
+ if ( this . isInBackground ) {
339
+ this . abortRealtimeConnection ( ) ;
340
+ } else {
341
+ this . beginRealtime ( ) ;
342
+ }
343
+ }
344
+ }
311
345
}
0 commit comments