Skip to content

Commit

Permalink
fix(compat): correct injectors to fix issue with compat API on v19 (#…
Browse files Browse the repository at this point in the history
…3595)

* fix: run inject in angular injection context
* fix: pass injector to pendingUntilEvent pipe
* TestBed for database test, inject env for Auth and RC

---------

Co-authored-by: James Daniels <[email protected]>
  • Loading branch information
rosostolato and jamesdaniels authored Jan 9, 2025
1 parent bc926a8 commit e567618
Show file tree
Hide file tree
Showing 12 changed files with 57 additions and 39 deletions.
6 changes: 4 additions & 2 deletions src/compat/auth/auth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isPlatformServer } from '@angular/common';
import { Inject, Injectable, InjectionToken, NgZone, Optional, PLATFORM_ID } from '@angular/core';
import { EnvironmentInjector, Inject, Injectable, InjectionToken, NgZone, Optional, PLATFORM_ID, inject } from '@angular/core';
import { pendingUntilEvent } from '@angular/core/rxjs-interop';
import { ɵAngularFireSchedulers } from '@angular/fire';
import { AppCheckInstances } from '@angular/fire/app-check';
Expand Down Expand Up @@ -55,6 +55,8 @@ export const ɵauthFactory = (
})
export class AngularFireAuth {

private readonly injector = inject(EnvironmentInjector);

/**
* Observable of authentication state; as of Firebase 4.0 this is only triggered via sign-in/out
*/
Expand Down Expand Up @@ -122,7 +124,7 @@ export class AngularFireAuth {

const redirectResult = auth.pipe(
switchMap(auth => auth.getRedirectResult().then(it => it, () => null)),
pendingUntilEvent(),
pendingUntilEvent(this.injector),
shareReplay({ bufferSize: 1, refCount: false }),
);

Expand Down
4 changes: 2 additions & 2 deletions src/compat/database/database.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ describe('AngularFireDatabase', () => {

it('should accept a Firebase App in the constructor', (done) => {
const schedulers = TestBed.runInInjectionContext(() => new ɵAngularFireSchedulers());
const database = new AngularFireDatabase(
const database = TestBed.runInInjectionContext(() => new AngularFireDatabase(
app.options, rando(), undefined, {}, zone, schedulers, undefined, undefined,
undefined, undefined, undefined, undefined, undefined, undefined, undefined,
);
));
expect(database instanceof AngularFireDatabase).toEqual(true);
// try { database.database.app.delete().then(done, done); } catch(e) { done(); }
done();
Expand Down
7 changes: 4 additions & 3 deletions src/compat/database/database.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Inject, Injectable, InjectionToken, NgZone, Optional, PLATFORM_ID, inject } from '@angular/core';
import { EnvironmentInjector, Inject, Injectable, InjectionToken, NgZone, Optional, PLATFORM_ID, inject } from '@angular/core';
import { ɵAngularFireSchedulers } from '@angular/fire';
import { AppCheckInstances } from '@angular/fire/app-check';
import { FIREBASE_APP_NAME, FIREBASE_OPTIONS, ɵcacheInstance, ɵfirebaseAppFactory } from '@angular/fire/compat';
Expand Down Expand Up @@ -31,6 +31,7 @@ export const USE_EMULATOR = new InjectionToken<UseEmulatorArguments>('angularfir
})
export class AngularFireDatabase {
public readonly database: firebase.database.Database;
private readonly injector = inject(EnvironmentInjector);

constructor(
@Inject(FIREBASE_OPTIONS) options: FirebaseOptions,
Expand Down Expand Up @@ -73,12 +74,12 @@ export class AngularFireDatabase {
if (queryFn) {
query = queryFn(ref);
}
return createListReference<T>(query, this);
return createListReference<T>(query, this, this.injector);
}

object<T>(pathOrRef: PathReference): AngularFireObject<T> {
const ref = inject(NgZone).runOutsideAngular(() => getRef(this.database, pathOrRef));
return createObjectReference<T>(ref, this);
return createObjectReference<T>(ref, this, this.injector);
}

createPushId() {
Expand Down
12 changes: 6 additions & 6 deletions src/compat/database/list/create-reference.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NgZone, inject } from '@angular/core';
import { Injector, NgZone, inject } from '@angular/core';
import { pendingUntilEvent } from '@angular/core/rxjs-interop';
import type { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
Expand All @@ -10,7 +10,7 @@ import { createRemoveMethod } from './remove';
import { snapshotChanges } from './snapshot-changes';
import { stateChanges } from './state-changes';

export function createListReference<T= any>(query: DatabaseQuery, afDatabase: AngularFireDatabase): AngularFireList<T> {
export function createListReference<T= any>(query: DatabaseQuery, afDatabase: AngularFireDatabase, injector?: Injector): AngularFireList<T> {
const outsideAngularScheduler = afDatabase.schedulers.outsideAngular;
const refInZone = inject(NgZone).run(() => query.ref);
return {
Expand All @@ -20,13 +20,13 @@ export function createListReference<T= any>(query: DatabaseQuery, afDatabase: An
push: (data: T) => refInZone.push(data),
remove: createRemoveMethod(refInZone),
snapshotChanges(events?: ChildEvent[]) {
return snapshotChanges<T>(query, events, outsideAngularScheduler).pipe(pendingUntilEvent());
return snapshotChanges<T>(query, events, outsideAngularScheduler).pipe(pendingUntilEvent(injector));
},
stateChanges(events?: ChildEvent[]) {
return stateChanges<T>(query, events, outsideAngularScheduler).pipe(pendingUntilEvent());
return stateChanges<T>(query, events, outsideAngularScheduler).pipe(pendingUntilEvent(injector));
},
auditTrail(events?: ChildEvent[]) {
return auditTrail<T>(query, events, outsideAngularScheduler).pipe(pendingUntilEvent());
return auditTrail<T>(query, events, outsideAngularScheduler).pipe(pendingUntilEvent(injector));
},
valueChanges<K extends string>(events?: ChildEvent[], options?: {idField?: K}): Observable<(T & Record<string, string>)[]> {
const snapshotChanges$ = snapshotChanges<T>(query, events, outsideAngularScheduler);
Expand All @@ -43,7 +43,7 @@ export function createListReference<T= any>(query: DatabaseQuery, afDatabase: An
return a.payload.val() as T & Record<string, string>
}
})),
pendingUntilEvent()
pendingUntilEvent(injector)
);
}
};
Expand Down
7 changes: 4 additions & 3 deletions src/compat/database/object/create-reference.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { Injector } from '@angular/core';
import { pendingUntilEvent } from '@angular/core/rxjs-interop';
import { map } from 'rxjs/operators';
import { AngularFireDatabase } from '../database';
import { AngularFireObject, DatabaseQuery } from '../interfaces';
import { createObjectSnapshotChanges } from './snapshot-changes';

export function createObjectReference<T= any>(query: DatabaseQuery, afDatabase: AngularFireDatabase): AngularFireObject<T> {
export function createObjectReference<T= any>(query: DatabaseQuery, afDatabase: AngularFireDatabase, injector?: Injector): AngularFireObject<T> {
return {
query,
snapshotChanges<T>() {
return createObjectSnapshotChanges<T>(query, afDatabase.schedulers.outsideAngular)().pipe(
pendingUntilEvent()
pendingUntilEvent(injector)
);
},
update(data: Partial<T>) { return query.ref.update(data as any) as Promise<void>; },
Expand All @@ -18,7 +19,7 @@ export function createObjectReference<T= any>(query: DatabaseQuery, afDatabase:
valueChanges<T>() {
const snapshotChanges$ = createObjectSnapshotChanges(query, afDatabase.schedulers.outsideAngular)();
return snapshotChanges$.pipe(
pendingUntilEvent(),
pendingUntilEvent(injector),
map(action => action.payload.exists() ? action.payload.val() as T : null)
);
},
Expand Down
13 changes: 8 additions & 5 deletions src/compat/firestore/collection-group/collection-group.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { EnvironmentInjector, inject } from '@angular/core';
import { pendingUntilEvent } from '@angular/core/rxjs-interop';
import firebase from 'firebase/compat/app';
import { Observable, from } from 'rxjs';
Expand Down Expand Up @@ -27,6 +28,8 @@ import { fromCollectionRef } from '../observable/fromRef';
* fakeStock.valueChanges().subscribe(value => console.log(value));
*/
export class AngularFirestoreCollectionGroup<T = DocumentData> {
private readonly injector = inject(EnvironmentInjector);

/**
* The constructor takes in a CollectionGroupQuery to provide wrapper methods
* for data operations and data streaming.
Expand All @@ -43,14 +46,14 @@ export class AngularFirestoreCollectionGroup<T = DocumentData> {
stateChanges(events?: DocumentChangeType[]): Observable<DocumentChangeAction<T>[]> {
if (!events || events.length === 0) {
return docChanges<T>(this.query, this.afs.schedulers.outsideAngular).pipe(
pendingUntilEvent()
pendingUntilEvent(this.injector)
);
}
return docChanges<T>(this.query, this.afs.schedulers.outsideAngular)
.pipe(
map(actions => actions.filter(change => events.indexOf(change.type) > -1)),
filter(changes => changes.length > 0),
pendingUntilEvent()
pendingUntilEvent(this.injector)
);
}

Expand All @@ -70,7 +73,7 @@ export class AngularFirestoreCollectionGroup<T = DocumentData> {
const validatedEvents = validateEventsArray(events);
const scheduledSortedChanges$ = sortedChanges<T>(this.query, validatedEvents, this.afs.schedulers.outsideAngular);
return scheduledSortedChanges$.pipe(
pendingUntilEvent()
pendingUntilEvent(this.injector)
);
}

Expand Down Expand Up @@ -98,7 +101,7 @@ export class AngularFirestoreCollectionGroup<T = DocumentData> {
return a.data();
}
})),
pendingUntilEvent()
pendingUntilEvent(this.injector)
);
}

Expand All @@ -107,7 +110,7 @@ export class AngularFirestoreCollectionGroup<T = DocumentData> {
*/
get(options?: firebase.firestore.GetOptions) {
return from(this.query.get(options)).pipe(
pendingUntilEvent()
pendingUntilEvent(this.injector)
);
}

Expand Down
11 changes: 7 additions & 4 deletions src/compat/firestore/collection/collection.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { EnvironmentInjector, inject } from '@angular/core';
import { pendingUntilEvent } from '@angular/core/rxjs-interop';
import firebase from 'firebase/compat/app';
import { Observable, from } from 'rxjs';
Expand Down Expand Up @@ -41,6 +42,8 @@ export function validateEventsArray(events?: DocumentChangeType[]) {
* fakeStock.valueChanges().subscribe(value => console.log(value));
*/
export class AngularFirestoreCollection<T = DocumentData> {
private readonly injector = inject(EnvironmentInjector);

/**
* The constructor takes in a CollectionReference and Query to provide wrapper methods
* for data operations and data streaming.
Expand Down Expand Up @@ -74,7 +77,7 @@ export class AngularFirestoreCollection<T = DocumentData> {
pairwise(),
filter(([prior, current]: DocumentChangeTuple<T>) => current.length > 0 || !prior),
map(([, current]) => current),
pendingUntilEvent()
pendingUntilEvent(this.injector)
);
}

Expand All @@ -94,7 +97,7 @@ export class AngularFirestoreCollection<T = DocumentData> {
const validatedEvents = validateEventsArray(events);
const scheduledSortedChanges$ = sortedChanges<T>(this.query, validatedEvents, this.afs.schedulers.outsideAngular);
return scheduledSortedChanges$.pipe(
pendingUntilEvent()
pendingUntilEvent(this.injector)
);
}

Expand All @@ -121,7 +124,7 @@ export class AngularFirestoreCollection<T = DocumentData> {
return a.data();
}
})),
pendingUntilEvent()
pendingUntilEvent(this.injector)
);
}

Expand All @@ -130,7 +133,7 @@ export class AngularFirestoreCollection<T = DocumentData> {
*/
get(options?: firebase.firestore.GetOptions) {
return from(this.query.get(options)).pipe(
pendingUntilEvent(),
pendingUntilEvent(this.injector)
);
}

Expand Down
6 changes: 4 additions & 2 deletions src/compat/firestore/document/document.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { EnvironmentInjector, inject } from '@angular/core';
import { pendingUntilEvent } from '@angular/core/rxjs-interop';
import firebase from 'firebase/compat/app';
import { Observable, from } from 'rxjs';
Expand Down Expand Up @@ -30,6 +31,7 @@ import { fromDocRef } from '../observable/fromRef';
* Observable.from(fakeStock).subscribe(value => console.log(value));
*/
export class AngularFirestoreDocument<T = DocumentData> {
private readonly injector = inject(EnvironmentInjector);

/**
* The constructor takes in a DocumentReference to provide wrapper methods
Expand Down Expand Up @@ -74,7 +76,7 @@ export class AngularFirestoreDocument<T = DocumentData> {
snapshotChanges(): Observable<Action<DocumentSnapshot<T>>> {
const scheduledFromDocRef$ = fromDocRef<T>(this.ref, this.afs.schedulers.outsideAngular);
return scheduledFromDocRef$.pipe(
pendingUntilEvent()
pendingUntilEvent(this.injector)
);
}

Expand Down Expand Up @@ -102,7 +104,7 @@ export class AngularFirestoreDocument<T = DocumentData> {
*/
get(options?: firebase.firestore.GetOptions) {
return from(this.ref.get(options)).pipe(
pendingUntilEvent(),
pendingUntilEvent(this.injector)
);
}
}
5 changes: 3 additions & 2 deletions src/compat/firestore/firestore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export function associateQuery<T>(collectionRef: CollectionReference<T>, queryFn
export class AngularFirestore {
public readonly firestore: firebase.firestore.Firestore;
public readonly persistenceEnabled$: Observable<boolean>;
private readonly ngZone = inject(NgZone);

/**
* Each Feature of AngularFire has a FirebaseApp injected. This way we
Expand Down Expand Up @@ -197,7 +198,7 @@ export class AngularFirestore {
collectionRef = pathOrRef;
}
const { ref, query } = associateQuery<T>(collectionRef, queryFn);
const refInZone = inject(NgZone).run(() => ref);
const refInZone = this.ngZone.run(() => ref);
return new AngularFirestoreCollection<T>(refInZone, query, this);
}

Expand Down Expand Up @@ -227,7 +228,7 @@ export class AngularFirestore {
} else {
ref = pathOrRef;
}
const refInZone = inject(NgZone).run(() => ref);
const refInZone = this.ngZone.run(() => ref);
return new AngularFirestoreDocument<T>(refInZone, this);
}

Expand Down
6 changes: 4 additions & 2 deletions src/compat/remote-config/remote-config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Inject, Injectable, InjectionToken, NgZone, Optional, PLATFORM_ID } from '@angular/core';
import { EnvironmentInjector, Inject, Injectable, InjectionToken, NgZone, Optional, PLATFORM_ID, inject } from '@angular/core';
import { pendingUntilEvent } from '@angular/core/rxjs-interop';
import { ɵAngularFireSchedulers } from '@angular/fire';
import { ɵPromiseProxy, ɵapplyMixins, ɵlazySDKProxy } from '@angular/fire/compat';
Expand Down Expand Up @@ -121,6 +121,8 @@ export class AngularFireRemoteConfig {
readonly booleans: Observable<Record<string, boolean | undefined>> & Record<string, Observable<boolean>>;
readonly strings: Observable<Record<string, string | undefined>> & Record<string, Observable<string | undefined>>;

private readonly injector = inject(EnvironmentInjector);

constructor(
@Inject(FIREBASE_OPTIONS) options: FirebaseOptions,
@Optional() @Inject(FIREBASE_APP_NAME) name: string | null | undefined,
Expand Down Expand Up @@ -186,7 +188,7 @@ export class AngularFireRemoteConfig {

this.parameters = concat(default$, existing$, fresh$).pipe(
scanToParametersArray(remoteConfig$),
pendingUntilEvent(),
pendingUntilEvent(this.injector),
shareReplay({ bufferSize: 1, refCount: true })
);

Expand Down
10 changes: 6 additions & 4 deletions src/compat/storage/ref.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Injector } from '@angular/core';
import { pendingUntilEvent } from '@angular/core/rxjs-interop';
import { observeOutsideAngular } from '@angular/fire';
import { Observable, from, of } from 'rxjs';
Expand All @@ -22,21 +23,22 @@ export interface AngularFireStorageReference {
* creates observable methods from promise based methods.
*/
export function createStorageRef(
ref: Reference
ref: Reference,
injector?: Injector
): AngularFireStorageReference {
return {
getDownloadURL: () => of(undefined).pipe(
observeOutsideAngular,
switchMap(() => ref.getDownloadURL()),
pendingUntilEvent()
pendingUntilEvent(injector)
),
getMetadata: () => of(undefined).pipe(
observeOutsideAngular,
switchMap(() => ref.getMetadata()),
pendingUntilEvent()
pendingUntilEvent(injector)
),
delete: () => from(ref.delete()),
child: (path: string) => createStorageRef(ref.child(path)),
child: (path: string) => createStorageRef(ref.child(path), injector),
updateMetadata: (meta: SettableMetadata) => from(ref.updateMetadata(meta)),
put: (data: any, metadata?: UploadMetadata) => {
const task = ref.put(data, metadata);
Expand Down
9 changes: 5 additions & 4 deletions src/compat/storage/storage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Inject, Injectable, InjectionToken, NgZone, Optional, PLATFORM_ID } from '@angular/core';
import { EnvironmentInjector, Inject, Injectable, InjectionToken, NgZone, Optional, PLATFORM_ID, inject } from '@angular/core';
import { ɵAngularFireSchedulers } from '@angular/fire';
import { AppCheckInstances } from '@angular/fire/app-check';
import { FIREBASE_APP_NAME, FIREBASE_OPTIONS, ɵcacheInstance, ɵfirebaseAppFactory } from '@angular/fire/compat';
Expand Down Expand Up @@ -27,6 +27,7 @@ export const USE_EMULATOR = new InjectionToken<UseEmulatorArguments>('angularfir
})
export class AngularFireStorage {
public readonly storage: firebase.storage.Storage;
private readonly injector = inject(EnvironmentInjector);

constructor(
@Inject(FIREBASE_OPTIONS) options: FirebaseOptions,
Expand Down Expand Up @@ -59,16 +60,16 @@ export class AngularFireStorage {
}

ref(path: string) {
return createStorageRef(this.storage.ref(path));
return createStorageRef(this.storage.ref(path), this.injector);
}

refFromURL(path: string) {
return createStorageRef(this.storage.refFromURL(path));
return createStorageRef(this.storage.refFromURL(path), this.injector);
}

upload(path: string, data: any, metadata?: UploadMetadata) {
const storageRef = this.storage.ref(path);
const ref = createStorageRef(storageRef);
const ref = createStorageRef(storageRef, this.injector);
return ref.put(data, metadata);
}

Expand Down

0 comments on commit e567618

Please sign in to comment.