Skip to content

Commit

Permalink
feat(core): Adding global caches to survive/warn on HMR, cleanup, etc. (
Browse files Browse the repository at this point in the history
#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
  • Loading branch information
jamesdaniels committed Nov 17, 2020
1 parent b00e14b commit b666a80
Show file tree
Hide file tree
Showing 25 changed files with 501 additions and 234 deletions.
16 changes: 16 additions & 0 deletions README.md
Expand Up @@ -41,6 +41,10 @@ export class MyApp {

## Compatibility

### Angular and Firebase versions

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.

| Angular | Firebase | AngularFire |
| --------|----------|--------------|
| 11 | 7,8 | @next |
Expand All @@ -53,6 +57,18 @@ export class MyApp {

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

### Polyfills

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:

| API | Environments | Suggested Polyfill | License |
|-----|--------------|--------------------|---------|
| 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 |
| `globalThis` | [most](https://caniuse.com/mdn-javascript_builtins_globalthis) | [`globalThis`](https://github.com/es-shims/globalThis#readme) | MIT |
| `Proxy` | [IE 11<br>Safari &lt; 10](https://caniuse.com/proxy) | [`proxy-polyfill`](https://github.com/GoogleChrome/proxy-polyfill#readme) | Apache 2.0 |
| `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 |
| `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 |

## Resources

[Quickstart](docs/install-and-setup.md) - Get your first application up and running by following our quickstart guide.
Expand Down
2 changes: 1 addition & 1 deletion sample/package.json
Expand Up @@ -4,7 +4,7 @@
"scripts": {
"ng": "ng",
"start": "ng serve",
"started:emulated": "concurrently -n ng,firebase -c red,yellow \"ng serve -c emulated\" \"firebase emulators:start\"",
"start:emulated": "concurrently -n ng,firebase -c red,yellow \"ng serve -c emulated\" \"firebase emulators:start\"",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
Expand Down
10 changes: 7 additions & 3 deletions sample/src/app/app.module.ts
Expand Up @@ -35,12 +35,15 @@ import { HomeComponent } from './home/home.component';
import { AuthComponent } from './auth/auth.component';
import { MessagingComponent } from './messaging/messaging.component';
import { FunctionsComponent } from './functions/functions.component';
import { FirestoreOfflineComponent } from './firestore-offline/firestore-offline.component';
import { FirestoreOfflineModule } from './firestore-offline/firestore-offline.module';

@NgModule({
declarations: [
AppComponent,
StorageComponent,
FirestoreComponent,
FirestoreOfflineComponent,
DatabaseComponent,
RemoteConfigComponent,
HomeComponent,
Expand All @@ -56,21 +59,22 @@ import { FunctionsComponent } from './functions/functions.component';
AngularFireModule.initializeApp(environment.firebase),
AngularFireStorageModule,
AngularFireDatabaseModule,
AngularFirestoreModule.enablePersistence({ synchronizeTabs: true }),
AngularFirestoreModule,
AngularFireAuthModule,
AngularFireAuthGuardModule,
AngularFireRemoteConfigModule,
AngularFireMessagingModule,
AngularFireAnalyticsModule,
AngularFireFunctionsModule,
AngularFirePerformanceModule,
AngularFireAuthGuardModule
AngularFireAuthGuardModule,
FirestoreOfflineModule
],
providers: [
UserTrackingService,
ScreenTrackingService,
PerformanceMonitoringService,
{ provide: ANALYTICS_DEBUG_MODE, useValue: false },
{ provide: ANALYTICS_DEBUG_MODE, useValue: true },
{ provide: COLLECTION_ENABLED, useValue: true },
{ provide: USE_AUTH_EMULATOR, useValue: environment.useEmulators ? ['localhost', 9099] : undefined },
{ provide: USE_DATABASE_EMULATOR, useValue: environment.useEmulators ? ['localhost', 9000] : undefined },
Expand Down
15 changes: 12 additions & 3 deletions sample/src/app/auth/auth.component.ts
Expand Up @@ -2,7 +2,7 @@ import { Component, OnInit, OnDestroy, PLATFORM_ID } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';
import firebase from 'firebase/app';
import { Subscription } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { map } from 'rxjs/operators';
import { trace } from '@angular/fire/performance';
import { Inject } from '@angular/core';
import { isPlatformServer } from '@angular/common';
Expand All @@ -13,7 +13,9 @@ import { isPlatformServer } from '@angular/common';
<p>
Auth!
{{ (auth.user | async)?.uid | json }}
{{ (auth.credential | async)?.operationType | json }}
<button (click)="login()" *ngIf="showLoginButton">Log in with Google</button>
<button (click)="loginAnonymously()" *ngIf="showLoginButton">Log in anonymously</button>
<button (click)="logout()" *ngIf="showLogoutButton">Log out</button>
</p>
`,
Expand Down Expand Up @@ -46,12 +48,19 @@ export class AuthComponent implements OnInit, OnDestroy {
}
}

login() {
this.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider());
async login() {
const user = await this.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider());
// TODO sign into offline app
}

async loginAnonymously() {
const user = await this.auth.signInAnonymously();
// TODO sign into offline app
}

logout() {
this.auth.signOut();
// TODO sign out of offline app
}

}
@@ -0,0 +1,25 @@
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';

import { FirestoreOfflineComponent } from './firestore-offline.component';

describe('FirestoreComponent', () => {
let component: FirestoreOfflineComponent;
let fixture: ComponentFixture<FirestoreOfflineComponent>;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ FirestoreOfflineComponent ]
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(FirestoreOfflineComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
37 changes: 37 additions & 0 deletions sample/src/app/firestore-offline/firestore-offline.component.ts
@@ -0,0 +1,37 @@
import { Component, OnInit } from '@angular/core';
import { AngularFirestore } from '@angular/fire/firestore';
import { Observable } from 'rxjs';
import { startWith, tap } from 'rxjs/operators';
import { makeStateKey, TransferState } from '@angular/platform-browser';
import { trace } from '@angular/fire/performance';
import { AngularFirestoreOffline } from './firestore-offline.module';

@Component({
selector: 'app-firestore-offline',
template: `<p>
Firestore Offline!
{{ testDocValue$ | async | json }}
{{ persistenceEnabled$ | async }}
</p>`,
styles: [``]
})
export class FirestoreOfflineComponent implements OnInit {

public readonly persistenceEnabled$: Observable<boolean>;
public readonly testDocValue$: Observable<any>;

constructor(state: TransferState, firestore: AngularFirestoreOffline) {
const doc = firestore.doc('test/1');
const key = makeStateKey(doc.ref.path);
const existing = state.get(key, undefined);
this.testDocValue$ = firestore.doc('test/1').valueChanges().pipe(
trace('firestore'),
existing ? startWith(existing) : tap(it => state.set(key, it))
);
this.persistenceEnabled$ = firestore.persistenceEnabled$;
}

ngOnInit(): void {
}

}
28 changes: 28 additions & 0 deletions sample/src/app/firestore-offline/firestore-offline.module.ts
@@ -0,0 +1,28 @@
import { Inject, Injectable, InjectionToken, NgModule, NgZone, Optional, PLATFORM_ID } from '@angular/core';
import { FirebaseOptions, FIREBASE_OPTIONS } from '@angular/fire';
import { USE_EMULATOR } from '@angular/fire/firestore';
import { AngularFirestore, SETTINGS, Settings } from '@angular/fire/firestore';
import { USE_EMULATOR as USE_AUTH_EMULATOR } from '@angular/fire/auth';

export const FIRESTORE_OFFLINE = new InjectionToken<AngularFirestore>('my.firestore');

@Injectable()
export class AngularFirestoreOffline extends AngularFirestore {
constructor(
@Inject(FIREBASE_OPTIONS) options: FirebaseOptions,
@Optional() @Inject(SETTINGS) settings: Settings | null,
// tslint:disable-next-line:ban-types
@Inject(PLATFORM_ID) platformId: Object,
zone: NgZone,
@Optional() @Inject(USE_EMULATOR) useEmulator: any,
@Optional() @Inject(USE_AUTH_EMULATOR) useAuthEmulator: any,
) {
super(options, 'offline', true, settings, platformId, zone, { synchronizeTabs: true }, useEmulator, useAuthEmulator);
}
}

@NgModule({
providers: [ AngularFirestoreOffline ]
}) export class FirestoreOfflineModule {

}
1 change: 1 addition & 0 deletions sample/src/app/home/home.component.ts
Expand Up @@ -8,6 +8,7 @@ import { FirebaseApp } from '@angular/fire';
{{ firebaseApp.name }}
<!-- TODO wrap the Zone issue <app-database></app-database> -->
<app-firestore></app-firestore>
<app-firestore-offline></app-firestore-offline>
<app-storage></app-storage>
<app-auth></app-auth>
<app-remote-config></app-remote-config>
Expand Down
46 changes: 23 additions & 23 deletions sample/yarn.lock
Expand Up @@ -2295,9 +2295,9 @@ acorn-walk@^7.1.1:
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==

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

acorn@^7.1.1:
version "7.4.0"
Expand Down Expand Up @@ -2373,7 +2373,7 @@ ajv@6.12.4:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"

ajv@6.12.6, ajv@^6.12.5:
ajv@6.12.6, ajv@^6.10.2, ajv@^6.12.5:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
Expand All @@ -2383,7 +2383,7 @@ ajv@6.12.6, ajv@^6.12.5:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"

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

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

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

browserify-sign@^4.0.0:
Expand Down Expand Up @@ -11265,15 +11265,15 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2:
source-map-url "^0.4.0"
urix "^0.1.0"

source-map-support@0.5.19, source-map-support@^0.5.17, source-map-support@~0.5.19:
source-map-support@0.5.19, source-map-support@^0.5.17, source-map-support@~0.5.12, source-map-support@~0.5.19:
version "0.5.19"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
dependencies:
buffer-from "^1.0.0"
source-map "^0.6.0"

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

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

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

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

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

wbuf@^1.1.0, wbuf@^1.7.3:
version "1.7.3"
Expand Down
3 changes: 2 additions & 1 deletion src/analytics/analytics.module.ts
@@ -1,6 +1,7 @@
import { NgModule, Optional } from '@angular/core';
import { ScreenTrackingService, UserTrackingService } from './analytics.service';
import { ScreenTrackingService } from './screen-tracking.service';
import { AngularFireAnalytics } from './analytics';
import { UserTrackingService } from './user-tracking.service';

@NgModule({
providers: [ AngularFireAnalytics ]
Expand Down

0 comments on commit b666a80

Please sign in to comment.