Skip to content

Commit b666a80

Browse files
authored
feat(core): Adding global caches to survive/warn on HMR, cleanup, etc. (#2661)
* Adding global instance caches to the modules, so they don't freak out when HMR is enabled (#2655). This takes on `globalThis` as a needed polyfill for many environments. * If injected settings for modules are changed after they are initialized you will receive a warning and the prior instance will be returned (ignoring the changes), this is especially important for HMR. If HMR is detected there will be an additional warning suggesting they do a full reload to see the changes. * Added a polyfill table and notes about why we version like we do * Adding more convoluted stuff to my sample app to flex AngularFire * Internal cleanup on AngularFireAnalytics * AngularFireAnalytics will now wait for UserTrackingService to detect the user before sending the screen_view event, if UserTrackingService has been provided * Adding a warning if the Auth Emulator is detected in conjunction with AngularFirestore and AngularFireDatabase as they will invalidate the emulated auth token before the dynamic import of `firebase/auth` is completed (#2656) * Warn if we absorbed an error keeping Firestore persistence from enabling * Logging sign_up and login events in UserTrackingService * Adding credential observer to AngularFireAuth
1 parent b00e14b commit b666a80

25 files changed

+501
-234
lines changed

README.md

+16
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ export class MyApp {
4141

4242
## Compatibility
4343

44+
### Angular and Firebase versions
45+
46+
AngularFire doesn't follow Angular's versioning as Firebase also has breaking changes throughout the year. Instead we try to maintain compatability with both Firebase and Angular majors for as long as possible, only breaking when we need to support a new major of one or the other. We've been forunate that the APIs we rely on have been fairly stable, but we understand this can lead to some confusion so we've created the following table for advice.
47+
4448
| Angular | Firebase | AngularFire |
4549
| --------|----------|--------------|
4650
| 11 | 7,8 | @next |
@@ -53,6 +57,18 @@ export class MyApp {
5357

5458
<sub>Version combinations not documented here __may__ work but are untested and you will see NPM peer warnings.</sub>
5559

60+
### Polyfills
61+
62+
Neither AngularFire or Firebase ship with polyfills & we tend to use modern ES features in our development. To have compatability across as wide-range of environments we suggest the following polyfills be added to your application:
63+
64+
| API | Environments | Suggested Polyfill | License |
65+
|-----|--------------|--------------------|---------|
66+
| Various ES5+ features | IE 11<br>Safari &lt; 10<br>Node &lt; 6.5 | [`core-js/stable`](https://github.com/zloirock/core-js#readme) | MIT |
67+
| `globalThis` | [most](https://caniuse.com/mdn-javascript_builtins_globalthis) | [`globalThis`](https://github.com/es-shims/globalThis#readme) | MIT |
68+
| `Proxy` | [IE 11<br>Safari &lt; 10](https://caniuse.com/proxy) | [`proxy-polyfill`](https://github.com/GoogleChrome/proxy-polyfill#readme) | Apache 2.0 |
69+
| `fetch` | [IE 11<br>Node<br>Safari &lt; 10.1<br>iOS &lt; 10.3](https://caniuse.com/fetch) | [`cross-fetch`](https://github.com/lquixada/cross-fetch#readme) | MIT |
70+
| `Headers` | [IE 11<br>Node<br>Safari &lt; 10.1<br>iOS Safari](https://caniuse.com/mdn-api_headers) | [`cross-fetch`](https://github.com/lquixada/cross-fetch#readme) | MIT |
71+
5672
## Resources
5773

5874
[Quickstart](docs/install-and-setup.md) - Get your first application up and running by following our quickstart guide.

sample/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"scripts": {
55
"ng": "ng",
66
"start": "ng serve",
7-
"started:emulated": "concurrently -n ng,firebase -c red,yellow \"ng serve -c emulated\" \"firebase emulators:start\"",
7+
"start:emulated": "concurrently -n ng,firebase -c red,yellow \"ng serve -c emulated\" \"firebase emulators:start\"",
88
"build": "ng build",
99
"test": "ng test",
1010
"lint": "ng lint",

sample/src/app/app.module.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,15 @@ import { HomeComponent } from './home/home.component';
3535
import { AuthComponent } from './auth/auth.component';
3636
import { MessagingComponent } from './messaging/messaging.component';
3737
import { FunctionsComponent } from './functions/functions.component';
38+
import { FirestoreOfflineComponent } from './firestore-offline/firestore-offline.component';
39+
import { FirestoreOfflineModule } from './firestore-offline/firestore-offline.module';
3840

3941
@NgModule({
4042
declarations: [
4143
AppComponent,
4244
StorageComponent,
4345
FirestoreComponent,
46+
FirestoreOfflineComponent,
4447
DatabaseComponent,
4548
RemoteConfigComponent,
4649
HomeComponent,
@@ -56,21 +59,22 @@ import { FunctionsComponent } from './functions/functions.component';
5659
AngularFireModule.initializeApp(environment.firebase),
5760
AngularFireStorageModule,
5861
AngularFireDatabaseModule,
59-
AngularFirestoreModule.enablePersistence({ synchronizeTabs: true }),
62+
AngularFirestoreModule,
6063
AngularFireAuthModule,
6164
AngularFireAuthGuardModule,
6265
AngularFireRemoteConfigModule,
6366
AngularFireMessagingModule,
6467
AngularFireAnalyticsModule,
6568
AngularFireFunctionsModule,
6669
AngularFirePerformanceModule,
67-
AngularFireAuthGuardModule
70+
AngularFireAuthGuardModule,
71+
FirestoreOfflineModule
6872
],
6973
providers: [
7074
UserTrackingService,
7175
ScreenTrackingService,
7276
PerformanceMonitoringService,
73-
{ provide: ANALYTICS_DEBUG_MODE, useValue: false },
77+
{ provide: ANALYTICS_DEBUG_MODE, useValue: true },
7478
{ provide: COLLECTION_ENABLED, useValue: true },
7579
{ provide: USE_AUTH_EMULATOR, useValue: environment.useEmulators ? ['localhost', 9099] : undefined },
7680
{ provide: USE_DATABASE_EMULATOR, useValue: environment.useEmulators ? ['localhost', 9000] : undefined },

sample/src/app/auth/auth.component.ts

+12-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Component, OnInit, OnDestroy, PLATFORM_ID } from '@angular/core';
22
import { AngularFireAuth } from '@angular/fire/auth';
33
import firebase from 'firebase/app';
44
import { Subscription } from 'rxjs';
5-
import { map, tap } from 'rxjs/operators';
5+
import { map } from 'rxjs/operators';
66
import { trace } from '@angular/fire/performance';
77
import { Inject } from '@angular/core';
88
import { isPlatformServer } from '@angular/common';
@@ -13,7 +13,9 @@ import { isPlatformServer } from '@angular/common';
1313
<p>
1414
Auth!
1515
{{ (auth.user | async)?.uid | json }}
16+
{{ (auth.credential | async)?.operationType | json }}
1617
<button (click)="login()" *ngIf="showLoginButton">Log in with Google</button>
18+
<button (click)="loginAnonymously()" *ngIf="showLoginButton">Log in anonymously</button>
1719
<button (click)="logout()" *ngIf="showLogoutButton">Log out</button>
1820
</p>
1921
`,
@@ -46,12 +48,19 @@ export class AuthComponent implements OnInit, OnDestroy {
4648
}
4749
}
4850

49-
login() {
50-
this.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider());
51+
async login() {
52+
const user = await this.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider());
53+
// TODO sign into offline app
54+
}
55+
56+
async loginAnonymously() {
57+
const user = await this.auth.signInAnonymously();
58+
// TODO sign into offline app
5159
}
5260

5361
logout() {
5462
this.auth.signOut();
63+
// TODO sign out of offline app
5564
}
5665

5766
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { FirestoreOfflineComponent } from './firestore-offline.component';
4+
5+
describe('FirestoreComponent', () => {
6+
let component: FirestoreOfflineComponent;
7+
let fixture: ComponentFixture<FirestoreOfflineComponent>;
8+
9+
beforeEach(waitForAsync(() => {
10+
TestBed.configureTestingModule({
11+
declarations: [ FirestoreOfflineComponent ]
12+
})
13+
.compileComponents();
14+
}));
15+
16+
beforeEach(() => {
17+
fixture = TestBed.createComponent(FirestoreOfflineComponent);
18+
component = fixture.componentInstance;
19+
fixture.detectChanges();
20+
});
21+
22+
it('should create', () => {
23+
expect(component).toBeTruthy();
24+
});
25+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Component, OnInit } from '@angular/core';
2+
import { AngularFirestore } from '@angular/fire/firestore';
3+
import { Observable } from 'rxjs';
4+
import { startWith, tap } from 'rxjs/operators';
5+
import { makeStateKey, TransferState } from '@angular/platform-browser';
6+
import { trace } from '@angular/fire/performance';
7+
import { AngularFirestoreOffline } from './firestore-offline.module';
8+
9+
@Component({
10+
selector: 'app-firestore-offline',
11+
template: `<p>
12+
Firestore Offline!
13+
{{ testDocValue$ | async | json }}
14+
{{ persistenceEnabled$ | async }}
15+
</p>`,
16+
styles: [``]
17+
})
18+
export class FirestoreOfflineComponent implements OnInit {
19+
20+
public readonly persistenceEnabled$: Observable<boolean>;
21+
public readonly testDocValue$: Observable<any>;
22+
23+
constructor(state: TransferState, firestore: AngularFirestoreOffline) {
24+
const doc = firestore.doc('test/1');
25+
const key = makeStateKey(doc.ref.path);
26+
const existing = state.get(key, undefined);
27+
this.testDocValue$ = firestore.doc('test/1').valueChanges().pipe(
28+
trace('firestore'),
29+
existing ? startWith(existing) : tap(it => state.set(key, it))
30+
);
31+
this.persistenceEnabled$ = firestore.persistenceEnabled$;
32+
}
33+
34+
ngOnInit(): void {
35+
}
36+
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Inject, Injectable, InjectionToken, NgModule, NgZone, Optional, PLATFORM_ID } from '@angular/core';
2+
import { FirebaseOptions, FIREBASE_OPTIONS } from '@angular/fire';
3+
import { USE_EMULATOR } from '@angular/fire/firestore';
4+
import { AngularFirestore, SETTINGS, Settings } from '@angular/fire/firestore';
5+
import { USE_EMULATOR as USE_AUTH_EMULATOR } from '@angular/fire/auth';
6+
7+
export const FIRESTORE_OFFLINE = new InjectionToken<AngularFirestore>('my.firestore');
8+
9+
@Injectable()
10+
export class AngularFirestoreOffline extends AngularFirestore {
11+
constructor(
12+
@Inject(FIREBASE_OPTIONS) options: FirebaseOptions,
13+
@Optional() @Inject(SETTINGS) settings: Settings | null,
14+
// tslint:disable-next-line:ban-types
15+
@Inject(PLATFORM_ID) platformId: Object,
16+
zone: NgZone,
17+
@Optional() @Inject(USE_EMULATOR) useEmulator: any,
18+
@Optional() @Inject(USE_AUTH_EMULATOR) useAuthEmulator: any,
19+
) {
20+
super(options, 'offline', true, settings, platformId, zone, { synchronizeTabs: true }, useEmulator, useAuthEmulator);
21+
}
22+
}
23+
24+
@NgModule({
25+
providers: [ AngularFirestoreOffline ]
26+
}) export class FirestoreOfflineModule {
27+
28+
}

sample/src/app/home/home.component.ts

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { FirebaseApp } from '@angular/fire';
88
{{ firebaseApp.name }}
99
<!-- TODO wrap the Zone issue <app-database></app-database> -->
1010
<app-firestore></app-firestore>
11+
<app-firestore-offline></app-firestore-offline>
1112
<app-storage></app-storage>
1213
<app-auth></app-auth>
1314
<app-remote-config></app-remote-config>

sample/yarn.lock

+23-23
Original file line numberDiff line numberDiff line change
@@ -2295,9 +2295,9 @@ acorn-walk@^7.1.1:
22952295
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
22962296

22972297
acorn@^6.4.1:
2298-
version "6.4.1"
2299-
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474"
2300-
integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==
2298+
version "6.4.2"
2299+
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
2300+
integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==
23012301

23022302
acorn@^7.1.1:
23032303
version "7.4.0"
@@ -2373,7 +2373,7 @@ [email protected]:
23732373
json-schema-traverse "^0.4.1"
23742374
uri-js "^4.2.2"
23752375

2376-
[email protected], ajv@^6.12.5:
2376+
[email protected], ajv@^6.10.2, ajv@^6.12.5:
23772377
version "6.12.6"
23782378
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
23792379
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -2383,7 +2383,7 @@ [email protected], ajv@^6.12.5:
23832383
json-schema-traverse "^0.4.1"
23842384
uri-js "^4.2.2"
23852385

2386-
ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.0:
2386+
ajv@^6.1.0, ajv@^6.12.0:
23872387
version "6.12.0"
23882388
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7"
23892389
integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==
@@ -2959,7 +2959,7 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.4.0:
29592959
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828"
29602960
integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==
29612961

2962-
bn.js@^5.1.1:
2962+
bn.js@^5.0.0, bn.js@^5.1.1:
29632963
version "5.1.3"
29642964
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b"
29652965
integrity sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==
@@ -3142,11 +3142,11 @@ browserify-des@^1.0.0:
31423142
safe-buffer "^5.1.2"
31433143

31443144
browserify-rsa@^4.0.0, browserify-rsa@^4.0.1:
3145-
version "4.0.1"
3146-
resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524"
3147-
integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=
3145+
version "4.1.0"
3146+
resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d"
3147+
integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==
31483148
dependencies:
3149-
bn.js "^4.1.0"
3149+
bn.js "^5.0.0"
31503150
randombytes "^2.0.1"
31513151

31523152
browserify-sign@^4.0.0:
@@ -11265,15 +11265,15 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2:
1126511265
source-map-url "^0.4.0"
1126611266
urix "^0.1.0"
1126711267

11268-
[email protected], source-map-support@^0.5.17, source-map-support@~0.5.19:
11268+
[email protected], source-map-support@^0.5.17, source-map-support@~0.5.12, source-map-support@~0.5.19:
1126911269
version "0.5.19"
1127011270
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
1127111271
integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
1127211272
dependencies:
1127311273
buffer-from "^1.0.0"
1127411274
source-map "^0.6.0"
1127511275

11276-
source-map-support@^0.5.5, source-map-support@~0.5.12:
11276+
source-map-support@^0.5.5:
1127711277
version "0.5.16"
1127811278
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042"
1127911279
integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==
@@ -11972,9 +11972,9 @@ thunky@^1.0.2:
1197211972
integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
1197311973

1197411974
timers-browserify@^2.0.4:
11975-
version "2.0.11"
11976-
resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f"
11977-
integrity sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==
11975+
version "2.0.12"
11976+
resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee"
11977+
integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==
1197811978
dependencies:
1197911979
setimmediate "^1.0.4"
1198011980

@@ -12579,23 +12579,23 @@ walkdir@^0.4.0:
1257912579
resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39"
1258012580
integrity sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==
1258112581

12582-
watchpack-chokidar2@^2.0.0:
12583-
version "2.0.0"
12584-
resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz#9948a1866cbbd6cb824dea13a7ed691f6c8ddff0"
12585-
integrity sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA==
12582+
watchpack-chokidar2@^2.0.1:
12583+
version "2.0.1"
12584+
resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957"
12585+
integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==
1258612586
dependencies:
1258712587
chokidar "^2.1.8"
1258812588

1258912589
watchpack@^1.7.4:
12590-
version "1.7.4"
12591-
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.4.tgz#6e9da53b3c80bb2d6508188f5b200410866cd30b"
12592-
integrity sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg==
12590+
version "1.7.5"
12591+
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453"
12592+
integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==
1259312593
dependencies:
1259412594
graceful-fs "^4.1.2"
1259512595
neo-async "^2.5.0"
1259612596
optionalDependencies:
1259712597
chokidar "^3.4.1"
12598-
watchpack-chokidar2 "^2.0.0"
12598+
watchpack-chokidar2 "^2.0.1"
1259912599

1260012600
wbuf@^1.1.0, wbuf@^1.7.3:
1260112601
version "1.7.3"

src/analytics/analytics.module.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { NgModule, Optional } from '@angular/core';
2-
import { ScreenTrackingService, UserTrackingService } from './analytics.service';
2+
import { ScreenTrackingService } from './screen-tracking.service';
33
import { AngularFireAnalytics } from './analytics';
4+
import { UserTrackingService } from './user-tracking.service';
45

56
@NgModule({
67
providers: [ AngularFireAnalytics ]

0 commit comments

Comments
 (0)