Skip to content

Commit 3121b94

Browse files
committed
SSR work
1 parent fa34c8e commit 3121b94

File tree

14 files changed

+190
-211
lines changed

14 files changed

+190
-211
lines changed

src/auth/auth.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { FirebaseAuth, User } from '@firebase/auth-types';
22
import { FirebaseOptions } from '@firebase/app-types';
3-
import { Injectable, NgZone, Inject, Optional } from '@angular/core';
3+
import { Injectable, Inject, Optional, NgZone } from '@angular/core';
44
import { Observable } from 'rxjs/Observable';
55
import { observeOn } from 'rxjs/operator/observeOn';
6-
import { ZoneScheduler } from 'angularfire2';
76

8-
import { FirebaseAppConfig, FirebaseAppName, firebaseAppFactory } from 'angularfire2';
7+
import { FirebaseAppConfig, FirebaseAppName, firebaseAppFactory, FirebaseZoneScheduler } from 'angularfire2';
98

109
import 'rxjs/add/operator/switchMap';
1110
import 'rxjs/add/observable/of';
@@ -36,19 +35,25 @@ export class AngularFireAuth {
3635
const app = firebaseAppFactory(config, name);
3736
this.auth = app.auth!();
3837

39-
const authState$ = new Observable(subscriber => {
40-
const unsubscribe = this.auth.onAuthStateChanged(subscriber);
41-
return { unsubscribe };
38+
const authStateZone = new NgZone({});
39+
this.authState = authStateZone.runOutsideAngular(() => {
40+
const authState$ = new Observable(subscriber => {
41+
const unsubscribe = this.auth.onAuthStateChanged(subscriber);
42+
return { unsubscribe };
43+
});
44+
return observeOn.call(authState$, new FirebaseZoneScheduler(authStateZone));
4245
});
43-
this.authState = observeOn.call(authState$, new ZoneScheduler(Zone.current));
4446

45-
const idToken$ = new Observable<User|null>(subscriber => {
46-
const unsubscribe = this.auth.onIdTokenChanged(subscriber);
47-
return { unsubscribe };
48-
}).switchMap(user => {
49-
return user ? Observable.fromPromise(user.getIdToken()) : Observable.of(null)
47+
const idTokenZone = new NgZone({});
48+
this.idToken = idTokenZone.runOutsideAngular(() => {
49+
const idToken$ = new Observable(subscriber => {
50+
const unsubscribe = this.auth.onIdTokenChanged(subscriber);
51+
return { unsubscribe };
52+
}).switchMap((user:User|null) => {
53+
return user ? Observable.fromPromise(user.getIdToken()) : Observable.of(null)
54+
});
55+
return observeOn.call(idToken$, new FirebaseZoneScheduler(idTokenZone));
5056
});
51-
this.idToken = observeOn.call(idToken$, new ZoneScheduler(Zone.current));
5257
}
5358

5459
}

src/core/angularfire2.ts

Lines changed: 9 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,27 @@
1-
import { Injectable, InjectionToken, NgModule } from '@angular/core';
1+
import { InjectionToken, NgZone } from '@angular/core';
22
import { Subscription } from 'rxjs/Subscription';
33
import { Scheduler } from 'rxjs/Scheduler';
44
import { queue } from 'rxjs/scheduler/queue';
55

66
import firebase from '@firebase/app';
7-
import { FirebaseApp as FBApp, FirebaseOptions } from '@firebase/app-types';
8-
import { FirebaseAuth } from '@firebase/auth-types';
9-
import { FirebaseDatabase } from '@firebase/database-types';
10-
import { FirebaseMessaging } from '@firebase/messaging-types';
11-
import { FirebaseStorage } from '@firebase/storage-types';
12-
import { FirebaseFirestore } from '@firebase/firestore-types';
7+
import { FirebaseApp, FirebaseOptions } from '@firebase/app-types';
138

14-
export function firebaseAppFactory(config: FirebaseOptions, name?: string): FBApp {
9+
export function firebaseAppFactory(config: FirebaseOptions, name?: string): FirebaseApp {
1510
const appName = name || '[DEFAULT]';
1611
const existingApp = firebase.apps.filter(app => app.name == appName)[0];
1712
return existingApp || firebase.initializeApp(config, appName);
1813
}
1914

20-
export class FirebaseApp implements FBApp {
21-
constructor() {
22-
console.warn('FirebaseApp will be depreciated in favor @firebase/auth-types/FirebaseApp');
23-
}
24-
name: string;
25-
options: {};
26-
auth: () => FirebaseAuth;
27-
database: () => FirebaseDatabase;
28-
messaging: () => FirebaseMessaging;
29-
storage: () => FirebaseStorage;
30-
delete: () => Promise<any>;
31-
firestore: () => FirebaseFirestore;
32-
}
33-
3415
export const FirebaseAppName = new InjectionToken<string>('angularfire2.appName');
3516
export const FirebaseAppConfig = new InjectionToken<FirebaseOptions>('angularfire2.config');
3617

37-
export const FirebaseAppProvider = {
38-
provide: FirebaseApp,
39-
useFactory: firebaseAppFactory,
40-
deps: [ FirebaseAppConfig, FirebaseAppName ]
41-
};
42-
43-
@NgModule({
44-
providers: [ FirebaseAppProvider ],
45-
})
46-
export class AngularFireModule {
47-
static initializeApp(config: FirebaseOptions, appName?: string) {
48-
console.warn('initializeApp will be depreciated in favor of the angularfire2/FirebaseAppConfig provider');
49-
return {
50-
ngModule: AngularFireModule,
51-
providers: [
52-
{ provide: FirebaseAppConfig, useValue: config },
53-
{ provide: FirebaseAppName, useValue: appName }
54-
]
55-
}
56-
}
57-
}
18+
// Put in database.ts when we dropped depreciated
19+
export const RealtimeDatabaseURL = new InjectionToken<string>('angularfire2.realtimeDatabaseURL');
5820

59-
/**
60-
* TODO: remove this scheduler once Rx has a more robust story for working
61-
* with zones.
62-
*/
63-
export class ZoneScheduler {
64-
65-
// TODO: Correctly add ambient zone typings instead of using any.
66-
constructor(public zone: any) {}
6721

22+
export class FirebaseZoneScheduler {
23+
constructor(public zone: NgZone) {}
6824
schedule(...args: any[]): Subscription {
69-
return <Subscription>this.zone.run(() => queue.schedule.apply(queue, args));
25+
return <Subscription>this.zone.runGuarded(function() { return queue.schedule.apply(queue, args)});
7026
}
71-
}
27+
}
Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,9 @@
11
import { NgModule } from '@angular/core';
2-
import { AngularFireModule } from 'angularfire2';
32
import { FirebaseApp } from '@firebase/app-types';
43
import { AngularFireDatabase } from './database';
54
import '@firebase/database';
65

7-
export function _getAngularFireDatabase(app: FirebaseApp) {
8-
return new AngularFireDatabase(app);
9-
}
10-
11-
export const AngularFireDatabaseProvider = {
12-
provide: AngularFireDatabase,
13-
useFactory: _getAngularFireDatabase,
14-
deps: [ FirebaseApp ]
15-
};
16-
17-
export const DATABASE_PROVIDERS = [
18-
AngularFireDatabaseProvider,
19-
];
20-
216
@NgModule({
22-
imports: [ AngularFireModule ],
23-
providers: [ DATABASE_PROVIDERS ]
7+
providers: [ AngularFireDatabase ]
248
})
259
export class AngularFireDatabaseModule { }

src/database-deprecated/database.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { FirebaseDatabase } from '@firebase/database-types';
2-
import { Inject, Injectable } from '@angular/core';
2+
import { Inject, Injectable, Optional } from '@angular/core';
33
import { FirebaseApp } from '@firebase/app-types';
44
import { FirebaseListFactory } from './firebase_list_factory';
55
import { FirebaseListObservable } from './firebase_list_observable';
66
import { FirebaseListFactoryOpts, FirebaseObjectFactoryOpts, PathReference } from './interfaces';
77
import { FirebaseObjectFactory } from './firebase_object_factory';
88
import { FirebaseObjectObservable } from './firebase_object_observable';
99
import * as utils from './utils';
10+
import { FirebaseOptions } from '@firebase/app-types';
11+
import { FirebaseAppConfig, FirebaseAppName, RealtimeDatabaseURL, firebaseAppFactory } from 'angularfire2';
1012

1113
@Injectable()
1214
export class AngularFireDatabase {
@@ -16,17 +18,22 @@ export class AngularFireDatabase {
1618
*/
1719
database: FirebaseDatabase;
1820

19-
constructor(public app: FirebaseApp) {
20-
this.database = app.database!();
21+
constructor(
22+
@Inject(FirebaseAppConfig) config:FirebaseOptions,
23+
@Optional() @Inject(FirebaseAppName) name:string,
24+
@Optional() @Inject(RealtimeDatabaseURL) databaseURL:string
25+
) {
26+
const app = firebaseAppFactory(config, name);
27+
this.database = app.database!(databaseURL);
2128
}
2229

2330
list(pathOrRef: PathReference, opts?:FirebaseListFactoryOpts):FirebaseListObservable<any[]> {
24-
const ref = utils.getRef(this.app, pathOrRef);
31+
const ref = utils.getRef(this.database, pathOrRef);
2532
return FirebaseListFactory(ref, opts);
2633
}
2734

2835
object(pathOrRef: PathReference, opts?:FirebaseObjectFactoryOpts):FirebaseObjectObservable<any> {
29-
const ref = utils.getRef(this.app, pathOrRef);
36+
const ref = utils.getRef(this.database, pathOrRef);
3037
return FirebaseObjectFactory(ref, opts);
3138
}
3239

src/database-deprecated/firebase_list_factory.ts

Lines changed: 73 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import { NgZone } from '@angular/core';
12
import * as database from '@firebase/database-types';
2-
import { ZoneScheduler } from 'angularfire2';
3+
import { FirebaseZoneScheduler } from 'angularfire2';
34
import * as utils from './utils';
45
import { AFUnwrappedDataSnapshot } from './interfaces';
56
import { FirebaseListObservable } from './firebase_list_observable';
@@ -109,84 +110,87 @@ export function FirebaseListFactory (
109110
*/
110111
function firebaseListObservable(ref: database.Reference | DatabaseQuery, {preserveSnapshot}: FirebaseListFactoryOpts = {}): FirebaseListObservable<any> {
111112

112-
const toValue = preserveSnapshot ? (snapshot => snapshot) : utils.unwrapMapFn;
113-
const toKey = preserveSnapshot ? (value => value.key) : (value => value.$key);
114-
115-
const listObs = new FirebaseListObservable(ref, (obs: Observer<any>) => {
116-
117-
// Keep track of callback handles for calling ref.off(event, handle)
118-
const handles: { event: string, handle: (a: DatabaseSnapshot, b?: string | null | undefined) => any }[] = [];
119-
let hasLoaded = false;
120-
let lastLoadedKey: string = null!;
121-
let array: DatabaseSnapshot[] = [];
122-
123-
// The list children are always added to, removed from and changed within
124-
// the array using the child_added/removed/changed events. The value event
125-
// is only used to determine when the initial load is complete.
113+
const zone = new NgZone({});
114+
return zone.runOutsideAngular(() => {
115+
const toValue = preserveSnapshot ? (snapshot => snapshot) : utils.unwrapMapFn;
116+
const toKey = preserveSnapshot ? (value => value.key) : (value => value.$key);
117+
118+
const listObs = new FirebaseListObservable(ref, (obs: Observer<any>) => {
119+
120+
// Keep track of callback handles for calling ref.off(event, handle)
121+
const handles: { event: string, handle: (a: DatabaseSnapshot, b?: string | null | undefined) => any }[] = [];
122+
let hasLoaded = false;
123+
let lastLoadedKey: string = null!;
124+
let array: DatabaseSnapshot[] = [];
125+
126+
// The list children are always added to, removed from and changed within
127+
// the array using the child_added/removed/changed events. The value event
128+
// is only used to determine when the initial load is complete.
129+
130+
ref.once('value', (snap: any) => {
131+
if (snap.exists()) {
132+
snap.forEach((child: any) => {
133+
lastLoadedKey = child.key;
134+
});
135+
if (array.find((child: any) => toKey(child) === lastLoadedKey)) {
136+
hasLoaded = true;
137+
obs.next(array);
138+
}
139+
} else {
140+
hasLoaded = true;
141+
obs.next(array);
142+
}
143+
}, err => {
144+
if (err) { obs.error(err); obs.complete(); }
145+
});
126146

127-
ref.once('value', (snap: any) => {
128-
if (snap.exists()) {
129-
snap.forEach((child: any) => {
130-
lastLoadedKey = child.key;
131-
});
132-
if (array.find((child: any) => toKey(child) === lastLoadedKey)) {
147+
const addFn = ref.on('child_added', (child: any, prevKey: string) => {
148+
array = onChildAdded(array, toValue(child), toKey, prevKey);
149+
if (hasLoaded) {
150+
obs.next(array);
151+
} else if (child.key === lastLoadedKey) {
133152
hasLoaded = true;
134153
obs.next(array);
135154
}
136-
} else {
137-
hasLoaded = true;
138-
obs.next(array);
139-
}
140-
}, err => {
141-
if (err) { obs.error(err); obs.complete(); }
142-
});
155+
}, err => {
156+
if (err) { obs.error(err); obs.complete(); }
157+
});
158+
handles.push({ event: 'child_added', handle: addFn });
143159

144-
const addFn = ref.on('child_added', (child: any, prevKey: string) => {
145-
array = onChildAdded(array, toValue(child), toKey, prevKey);
146-
if (hasLoaded) {
147-
obs.next(array);
148-
} else if (child.key === lastLoadedKey) {
149-
hasLoaded = true;
150-
obs.next(array);
151-
}
152-
}, err => {
153-
if (err) { obs.error(err); obs.complete(); }
154-
});
155-
handles.push({ event: 'child_added', handle: addFn });
160+
let remFn = ref.on('child_removed', (child: any) => {
161+
array = onChildRemoved(array, toValue(child), toKey);
162+
if (hasLoaded) {
163+
obs.next(array);
164+
}
165+
}, err => {
166+
if (err) { obs.error(err); obs.complete(); }
167+
});
168+
handles.push({ event: 'child_removed', handle: remFn });
156169

157-
let remFn = ref.on('child_removed', (child: any) => {
158-
array = onChildRemoved(array, toValue(child), toKey);
159-
if (hasLoaded) {
160-
obs.next(array);
161-
}
162-
}, err => {
163-
if (err) { obs.error(err); obs.complete(); }
164-
});
165-
handles.push({ event: 'child_removed', handle: remFn });
170+
let chgFn = ref.on('child_changed', (child: any, prevKey: string) => {
171+
array = onChildChanged(array, toValue(child), toKey, prevKey);
172+
if (hasLoaded) {
173+
obs.next(array);
174+
}
175+
}, err => {
176+
if (err) { obs.error(err); obs.complete(); }
177+
});
178+
handles.push({ event: 'child_changed', handle: chgFn });
179+
180+
return () => {
181+
// Loop through callback handles and dispose of each event with handle
182+
// The Firebase SDK requires the reference, event name, and callback to
183+
// properly unsubscribe, otherwise it can affect other subscriptions.
184+
handles.forEach(item => {
185+
ref.off(item.event, item.handle);
186+
});
187+
};
166188

167-
let chgFn = ref.on('child_changed', (child: any, prevKey: string) => {
168-
array = onChildChanged(array, toValue(child), toKey, prevKey);
169-
if (hasLoaded) {
170-
obs.next(array);
171-
}
172-
}, err => {
173-
if (err) { obs.error(err); obs.complete(); }
174189
});
175-
handles.push({ event: 'child_changed', handle: chgFn });
176-
177-
return () => {
178-
// Loop through callback handles and dispose of each event with handle
179-
// The Firebase SDK requires the reference, event name, and callback to
180-
// properly unsubscribe, otherwise it can affect other subscriptions.
181-
handles.forEach(item => {
182-
ref.off(item.event, item.handle);
183-
});
184-
};
185190

191+
// TODO: should be in the subscription zone instead
192+
return observeOn.call(listObs, new FirebaseZoneScheduler(zone));
186193
});
187-
188-
// TODO: should be in the subscription zone instead
189-
return observeOn.call(listObs, new ZoneScheduler(Zone.current));
190194
}
191195

192196
export function onChildAdded(arr:any[], child:any, toKey:(element:any)=>string, prevKey:string): any[] {

0 commit comments

Comments
 (0)