@@ -21,6 +21,7 @@ import { calculateBackoffMillis, FirebaseError } from "@firebase/util";
2121import { ERROR_FACTORY , ErrorCode } from "../errors" ;
2222import { Storage } from "../storage/storage" ;
2323import { isBefore } from 'date-fns' ;
24+ import { VisibilityMonitor } from './visibility_monitor' ;
2425
2526const API_KEY_HEADER = 'X-Goog-Api-Key' ;
2627const INSTALLATIONS_AUTH_TOKEN_HEADER = 'X-Goog-Firebase-Installations-Auth' ;
@@ -40,6 +41,7 @@ export class RealtimeHandler {
4041 ) {
4142 this . httpRetriesRemaining = ORIGINAL_RETRIES ;
4243 this . setRetriesRemaining ( ) ;
44+ VisibilityMonitor . getInstance ( ) . on ( 'visible' , this . onVisibilityChange , this ) ;
4345 }
4446
4547 private observers : Set < ConfigUpdateObserver > = new Set < ConfigUpdateObserver > ( ) ;
@@ -48,6 +50,7 @@ export class RealtimeHandler {
4850 private controller ?: AbortController ;
4951 private reader : ReadableStreamDefaultReader | undefined ;
5052 private httpRetriesRemaining : number = ORIGINAL_RETRIES ;
53+ private isInBackground : boolean = false ;
5154
5255 private async setRetriesRemaining ( ) {
5356 // Retrieve number of remaining retries from last session. The minimum retry count being one.
@@ -101,9 +104,9 @@ export class RealtimeHandler {
101104 * and canceling the stream reader if they exist.
102105 */
103106 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 ;
107110 }
108111
109112 if ( this . reader ) {
@@ -244,15 +247,22 @@ export class RealtimeHandler {
244247 //await configAutoFetch.listenForNotifications();
245248 }
246249 } 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 {
247256 //there might have been a transient error so the client will retry the connection.
248257 console . error ( 'Exception connecting to real-time RC backend. Retrying the connection...:' , error ) ;
258+ }
249259 } finally {
250260 // Close HTTP connection and associated streams.
251261 this . closeRealtimeHttpConnection ( ) ;
252262 this . setIsHttpConnectionRunning ( false ) ;
253263
254264 // 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 ) ) ;
256266
257267 if ( connectionFailed ) {
258268 await this . updateBackoffMetadataWithLastFailedStreamConnectionTime ( new Date ( ) ) ;
@@ -279,9 +289,10 @@ export class RealtimeHandler {
279289 const hasActiveListeners = this . observers . size > 0 ;
280290 const isNotDisabled = ! this . isRealtimeDisabled ;
281291 const isNoConnectionActive = ! this . isConnectionActive ;
282- return hasActiveListeners && isNotDisabled && isNoConnectionActive ;
292+ const inForeground = ! this . isInBackground ;
293+ return hasActiveListeners && isNotDisabled && isNoConnectionActive && inForeground ;
283294 }
284-
295+
285296 private async makeRealtimeHttpConnection ( delayMillis : number ) : Promise < void > {
286297 if ( ! this . canEstablishStreamConnection ( ) ) {
287298 return ;
@@ -291,6 +302,9 @@ export class RealtimeHandler {
291302 setTimeout ( async ( ) => {
292303 await this . beginRealtimeHttpStream ( ) ;
293304 } , 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 ) ;
294308 }
295309 }
296310
@@ -308,4 +322,24 @@ export class RealtimeHandler {
308322 this . observers . add ( observer ) ;
309323 await this . beginRealtime ( ) ;
310324 }
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+ }
311345}
0 commit comments