From e4db9fb056f7963d5568d1239a72d256425a8397 Mon Sep 17 00:00:00 2001 From: Jeff Cross Date: Mon, 18 Jul 2016 17:37:00 -0700 Subject: [PATCH] fix(auth): add scheduler to schedule onAuth events through Angular zone Closes #354 --- src/auth/auth.spec.ts | 23 +++++++++++++++++++++++ src/auth/firebase_sdk_auth_backend.ts | 13 ++++++++++--- src/utils.ts | 19 ++++++++++++++++++- test/e2e/auth/firebase_auth_example.ts | 7 ++++++- tsconfig.json | 3 ++- 5 files changed, 59 insertions(+), 6 deletions(-) diff --git a/src/auth/auth.spec.ts b/src/auth/auth.spec.ts index a9beedad7..c02cfc557 100644 --- a/src/auth/auth.spec.ts +++ b/src/auth/auth.spec.ts @@ -87,6 +87,29 @@ const AngularFireAuthState = { } }; +describe('Zones', () => { + it('should call operators and subscriber in the same zone as when service was initialized', (done) => { + // Initialize the app outside of the zone, to mimick real life behavior. + var app = initializeApp(COMMON_CONFIG, 'zoneapp'); + + let ngZone = Zone.current.fork({ + name: 'ngZone' + }); + ngZone.run(() => { + var afAuth = new AngularFireAuth(new FirebaseSdkAuthBackend(app), window.location); + afAuth + .take(1) + .do(_ => { + expect(Zone.current.name).toBe('ngZone'); + }) + .subscribe(() => { + expect(Zone.current.name).toBe('ngZone'); + done() + }, done.fail); + }); + }); +}); + describe('FirebaseAuth', () => { let app: firebase.app.App; let authData: any; diff --git a/src/auth/firebase_sdk_auth_backend.ts b/src/auth/firebase_sdk_auth_backend.ts index 096bcc441..fa9730ac6 100644 --- a/src/auth/firebase_sdk_auth_backend.ts +++ b/src/auth/firebase_sdk_auth_backend.ts @@ -2,7 +2,7 @@ import { Injectable, Inject } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import { Observer } from 'rxjs/Observer'; import { FirebaseApp } from '../tokens'; -import { isPresent } from '../utils'; +import { isPresent, ZoneScheduler } from '../utils'; import { auth } from 'firebase'; import { authDataToAuthState, @@ -22,6 +22,7 @@ const { import 'rxjs/add/operator/map'; import 'rxjs/add/observable/fromPromise'; +import 'rxjs/add/operator/observeOn'; @Injectable() export class FirebaseSdkAuthBackend extends AuthBackend { @@ -42,14 +43,20 @@ export class FirebaseSdkAuthBackend extends AuthBackend { } onAuth(): Observable { - // TODO: assumes this will accept an RxJS observer return Observable.create((observer: Observer) => { return this._fbAuth.onAuthStateChanged(observer); }) .map((user: firebase.User) => { if (!user) return null; return authDataToAuthState(user); - }); + }) + /** + * TODO: since the auth service automatically subscribes to this before + * any user, it will run in the Angular zone, instead of the subscription + * zone. The auth service should be refactored to capture the subscription + * zone and not use a ReplaySubject. + **/ + .observeOn(new ZoneScheduler(Zone.current)); } unauth(): void { diff --git a/src/utils.ts b/src/utils.ts index a3ecc20c4..08d566bc9 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,3 +1,6 @@ +import { Subscription } from 'rxjs/Subscription'; +import { QueueScheduler } from 'rxjs/scheduler/QueueScheduler'; +import { Scheduler } from 'rxjs/Scheduler'; import { AFUnwrappedDataSnapshot} from './interfaces'; export function isPresent(obj: any): boolean { @@ -75,4 +78,18 @@ export function stripLeadingSlash(value: string): string { } else { return value; } -} \ No newline at end of file +} + +/** + * TODO: remove this scheduler once Rx has a more robust story for working + * with zones. + */ +export class ZoneScheduler extends QueueScheduler { + constructor(public zone: Zone) { + super(); + } + + schedule(...args): Subscription { + return this.zone.run(() => super.schedule.apply(this, args)); + } +} diff --git a/test/e2e/auth/firebase_auth_example.ts b/test/e2e/auth/firebase_auth_example.ts index 408354ef8..4bc3403e9 100644 --- a/test/e2e/auth/firebase_auth_example.ts +++ b/test/e2e/auth/firebase_auth_example.ts @@ -11,6 +11,8 @@ import { FirebaseApp } from '../../../dist/angularfire2'; +declare var Zone: any; + enableProdMode(); // TODO fix imports and tsconfig @@ -111,7 +113,10 @@ class App { auth: null // makes easier to convert to json }) }) - .subscribe(user => this.user = user); + .subscribe(user => { + console.log('zone', Zone.current.name); + this.user = user + }); } signInAnonymously() { diff --git a/tsconfig.json b/tsconfig.json index 273762e49..17bad3458 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,6 +25,7 @@ "src/auth/auth.spec.ts", "src/auth/auth_backend.spec.ts", "typings/index.d.ts", - "manual_typings/firebase3/firebase3.d.ts" + "manual_typings/firebase3/firebase3.d.ts", + "node_modules/zone.js/dist/zone.js.d.ts" ] }