diff --git a/.changeset/few-snails-walk.md b/.changeset/few-snails-walk.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/few-snails-walk.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/firestore/exp/index.node.ts b/packages/firestore/exp/index.node.ts index 5113219901e..1b7d4774d4c 100644 --- a/packages/firestore/exp/index.node.ts +++ b/packages/firestore/exp/index.node.ts @@ -54,6 +54,7 @@ export { getDocFromCache, getDocFromServer, onSnapshot, + onSnapshotsInSync, setDoc, updateDoc, deleteDoc, diff --git a/packages/firestore/exp/src/api/reference.ts b/packages/firestore/exp/src/api/reference.ts index bbc30bd0449..3f1bcdea6b0 100644 --- a/packages/firestore/exp/src/api/reference.ts +++ b/packages/firestore/exp/src/api/reference.ts @@ -29,6 +29,7 @@ import { cast } from '../../../lite/src/api/util'; import { DocumentSnapshot, QuerySnapshot } from './snapshot'; import { addDocSnapshotListener, + addSnapshotsInSyncListener, addQuerySnapshotListener, applyFirestoreDataConverter, getDocsViaSnapshotListener, @@ -431,6 +432,45 @@ export function onSnapshot( }; } +// TODO(firestorexp): Make sure these overloads are tested via the Firestore +// integration tests +export function onSnapshotsInSync( + firestore: firestore.FirebaseFirestore, + observer: { + next?: (value: void) => void; + error?: (error: firestore.FirestoreError) => void; + complete?: () => void; + } +): Unsubscribe; +export function onSnapshotsInSync( + firestore: firestore.FirebaseFirestore, + onSync: () => void +): Unsubscribe; +export function onSnapshotsInSync( + firestore: firestore.FirebaseFirestore, + arg: unknown +): Unsubscribe { + const firestoreImpl = cast(firestore, Firestore); + const observer = isPartialObserver(arg) + ? (arg as PartialObserver) + : { + next: arg as () => void + }; + + const asyncObserver = firestoreImpl + ._getFirestoreClient() + .then(firestoreClient => + addSnapshotsInSyncListener(firestoreClient, observer) + ); + + // TODO(firestorexp): Add test that verifies that we don't raise a snapshot if + // unsubscribe is called before `asyncObserver` resolves. + return () => { + // eslint-disable-next-line @typescript-eslint/no-floating-promises + asyncObserver.then(unsubscribe => unsubscribe()); + }; +} + /** * Converts a ViewSnapshot that contains the single document specified by `ref` * to a DocumentSnapshot. diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index 47cab0ec39e..9625ac3086c 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -462,37 +462,19 @@ export class Firestore implements firestore.FirebaseFirestore, FirebaseService { this.ensureClientConfigured(); if (isPartialObserver(arg)) { - return this.onSnapshotsInSyncInternal(arg as PartialObserver); + return addSnapshotsInSyncListener( + this._firestoreClient!, + arg as PartialObserver + ); } else { validateArgType('Firestore.onSnapshotsInSync', 'function', 1, arg); const observer: PartialObserver = { next: arg as () => void }; - return this.onSnapshotsInSyncInternal(observer); + return addSnapshotsInSyncListener(this._firestoreClient!, observer); } } - private onSnapshotsInSyncInternal( - observer: PartialObserver - ): Unsubscribe { - const errHandler = (err: Error): void => { - throw fail('Uncaught Error in onSnapshotsInSync'); - }; - const asyncObserver = new AsyncObserver({ - next: () => { - if (observer.next) { - observer.next(); - } - }, - error: errHandler - }); - this._firestoreClient!.addSnapshotsInSyncListener(asyncObserver); - return () => { - asyncObserver.mute(); - this._firestoreClient!.removeSnapshotsInSyncListener(asyncObserver); - }; - } - ensureClientConfigured(): FirestoreClient { if (!this._firestoreClient) { // Kick off starting the client but don't actually wait for it. @@ -675,6 +657,29 @@ export class Firestore implements firestore.FirebaseFirestore, FirebaseService { } } +/** Registers the listener for onSnapshotsInSync() */ +export function addSnapshotsInSyncListener( + firestoreClient: FirestoreClient, + observer: PartialObserver +): Unsubscribe { + const errHandler = (err: Error): void => { + throw fail('Uncaught Error in onSnapshotsInSync'); + }; + const asyncObserver = new AsyncObserver({ + next: () => { + if (observer.next) { + observer.next(); + } + }, + error: errHandler + }); + firestoreClient.addSnapshotsInSyncListener(asyncObserver); + return () => { + asyncObserver.mute(); + firestoreClient.removeSnapshotsInSyncListener(asyncObserver); + }; +} + /** * A reference to a transaction. */