diff --git a/src/firestore/collection/changes.ts b/src/firestore/collection/changes.ts index c7135b66f..321bdf9e7 100644 --- a/src/firestore/collection/changes.ts +++ b/src/firestore/collection/changes.ts @@ -10,24 +10,24 @@ import { DocumentChangeAction, Action } from '../interfaces'; * order of occurence. * @param query */ -export function docChanges(query: Query): Observable { +export function docChanges(query: Query): Observable[]> { return fromCollectionRef(query) .pipe( map(action => action.payload.docChanges() - .map(change => ({ type: change.type, payload: change })))); + .map(change => ({ type: change.type, payload: change } as DocumentChangeAction)))); } /** * Return a stream of document changes on a query. These results are in sort order. * @param query */ -export function sortedChanges(query: Query, events: DocumentChangeType[]): Observable { +export function sortedChanges(query: Query, events: DocumentChangeType[]): Observable[]> { return fromCollectionRef(query) .pipe( map(changes => changes.payload.docChanges()), scan((current, changes) => combineChanges(current, changes, events), []), - map(changes => changes.map(c => ({ type: c.type, payload: c })))); + map(changes => changes.map(c => ({ type: c.type, payload: c } as DocumentChangeAction)))); } /** diff --git a/src/firestore/collection/collection.ts b/src/firestore/collection/collection.ts index cee8bdba0..621475fff 100644 --- a/src/firestore/collection/collection.ts +++ b/src/firestore/collection/collection.ts @@ -1,11 +1,11 @@ -import { DocumentChangeType, CollectionReference, Query, DocumentReference } from '@firebase/firestore-types'; +import { DocumentChangeType, CollectionReference, Query, DocumentReference, DocumentData } from '@firebase/firestore-types'; import { Observable, Subscriber } from 'rxjs'; import { fromCollectionRef } from '../observable/fromRef'; import { map, filter, scan } from 'rxjs/operators'; import { Injectable } from '@angular/core'; -import { QueryFn, AssociatedReference, DocumentChangeAction } from '../interfaces'; +import { QueryFn, AssociatedReference, DocumentChangeAction, DocumentChange } from '../interfaces'; import { docChanges, sortedChanges } from './changes'; import { AngularFirestoreDocument } from '../document/document'; import { AngularFirestore } from '../firestore'; @@ -40,7 +40,7 @@ export function validateEventsArray(events?: DocumentChangeType[]) { * // Subscribe to changes as snapshots. This provides you data updates as well as delta updates. * fakeStock.valueChanges().subscribe(value => console.log(value)); */ -export class AngularFirestoreCollection { +export class AngularFirestoreCollection { /** * The constructor takes in a CollectionReference and Query to provide wrapper methods * for data operations and data streaming. @@ -62,17 +62,17 @@ export class AngularFirestoreCollection { * your own data structure. * @param events */ - stateChanges(events?: DocumentChangeType[]): Observable { + stateChanges(events?: DocumentChangeType[]): Observable[]> { if(!events || events.length === 0) { return this.afs.scheduler.keepUnstableUntilFirst( this.afs.scheduler.runOutsideAngular( - docChanges(this.query) + docChanges(this.query) ) ); } return this.afs.scheduler.keepUnstableUntilFirst( this.afs.scheduler.runOutsideAngular( - docChanges(this.query) + docChanges(this.query) ) ) .pipe( @@ -86,7 +86,7 @@ export class AngularFirestoreCollection { * but it collects each event in an array over time. * @param events */ - auditTrail(events?: DocumentChangeType[]): Observable { + auditTrail(events?: DocumentChangeType[]): Observable[]> { return this.stateChanges(events).pipe(scan((current, action) => [...current, ...action], [])); } @@ -95,9 +95,9 @@ export class AngularFirestoreCollection { * query order. * @param events */ - snapshotChanges(events?: DocumentChangeType[]): Observable { + snapshotChanges(events?: DocumentChangeType[]): Observable[]> { const validatedEvents = validateEventsArray(events); - const sortedChanges$ = sortedChanges(this.query, validatedEvents); + const sortedChanges$ = sortedChanges(this.query, validatedEvents); const scheduledSortedChanges$ = this.afs.scheduler.runOutsideAngular(sortedChanges$); return this.afs.scheduler.keepUnstableUntilFirst(scheduledSortedChanges$); } @@ -106,11 +106,11 @@ export class AngularFirestoreCollection { * Listen to all documents in the collection and its possible query as an Observable. */ valueChanges(): Observable { - const fromCollectionRef$ = fromCollectionRef(this.query); + const fromCollectionRef$ = fromCollectionRef(this.query); const scheduled$ = this.afs.scheduler.runOutsideAngular(fromCollectionRef$); return this.afs.scheduler.keepUnstableUntilFirst(scheduled$) .pipe( - map(actions => actions.payload.docs.map(a => a.data()) as T[]) + map(actions => actions.payload.docs.map(a => a.data())) ); } diff --git a/src/firestore/document/document.spec.ts b/src/firestore/document/document.spec.ts index 06421e3f1..6be193e13 100644 --- a/src/firestore/document/document.spec.ts +++ b/src/firestore/document/document.spec.ts @@ -57,7 +57,7 @@ describe('AngularFirestoreDocument', () => { const stock = new AngularFirestoreDocument(ref, afs); await stock.set(FAKE_STOCK_DATA); const obs$ = stock.valueChanges(); - obs$.pipe(take(1)).subscribe(async (data: Stock) => { + obs$.pipe(take(1)).subscribe(async data => { expect(JSON.stringify(data)).toBe(JSON.stringify(FAKE_STOCK_DATA)); stock.delete().then(done).catch(done.fail); }); diff --git a/src/firestore/document/document.ts b/src/firestore/document/document.ts index 1f9bb151c..9b1a64b21 100644 --- a/src/firestore/document/document.ts +++ b/src/firestore/document/document.ts @@ -1,6 +1,6 @@ -import { DocumentReference, SetOptions, DocumentSnapshot } from '@firebase/firestore-types'; +import { DocumentReference, SetOptions, DocumentData } from '@firebase/firestore-types'; import { Observable, Subscriber } from 'rxjs'; -import { QueryFn, AssociatedReference, Action } from '../interfaces'; +import { QueryFn, AssociatedReference, Action, DocumentSnapshot } from '../interfaces'; import { fromDocRef } from '../observable/fromRef'; import { map } from 'rxjs/operators'; @@ -31,7 +31,7 @@ import { AngularFirestoreCollection } from '../collection/collection'; * // OR! Transform using Observable.from() and the data is unwrapped for you * Observable.from(fakeStock).subscribe(value => console.log(value)); */ -export class AngularFirestoreDocument { +export class AngularFirestoreDocument { /** * The contstuctor takes in a DocumentReference to provide wrapper methods @@ -70,17 +70,17 @@ export class AngularFirestoreDocument { * @param path * @param queryFn */ - collection(path: string, queryFn?: QueryFn): AngularFirestoreCollection { + collection(path: string, queryFn?: QueryFn): AngularFirestoreCollection { const collectionRef = this.ref.collection(path); const { ref, query } = associateQuery(collectionRef, queryFn); - return new AngularFirestoreCollection(ref, query, this.afs); + return new AngularFirestoreCollection(ref, query, this.afs); } /** * Listen to snapshot updates from the document. */ - snapshotChanges(): Observable> { - const fromDocRef$ = fromDocRef(this.ref); + snapshotChanges(): Observable>> { + const fromDocRef$ = fromDocRef(this.ref); const scheduledFromDocRef$ = this.afs.scheduler.runOutsideAngular(fromDocRef$); return this.afs.scheduler.keepUnstableUntilFirst(scheduledFromDocRef$); } @@ -88,10 +88,10 @@ export class AngularFirestoreDocument { /** * Listen to unwrapped snapshot updates from the document. */ - valueChanges(): Observable { + valueChanges(): Observable { return this.snapshotChanges().pipe( map(action => { - return action.payload.exists ? action.payload.data() as T : null; + return action.payload.data(); }) ); } diff --git a/src/firestore/interfaces.ts b/src/firestore/interfaces.ts index 8b6753c26..4506aede8 100644 --- a/src/firestore/interfaces.ts +++ b/src/firestore/interfaces.ts @@ -1,9 +1,34 @@ import { Subscriber } from 'rxjs'; -import { DocumentChangeType, DocumentChange, CollectionReference, Query } from '@firebase/firestore-types'; +import { DocumentChangeType, QuerySnapshot as _QuerySnapshot, FieldPath, DocumentSnapshot as _DocumentSnapshot, SnapshotOptions, QueryDocumentSnapshot as _QueryDocumentSnapshot, DocumentChange as _DocumentChange, CollectionReference, Query } from '@firebase/firestore-types'; -export interface DocumentChangeAction { +export interface DocumentSnapshotExists extends _DocumentSnapshot { + readonly exists: true; + data(options?: SnapshotOptions): T; +} + +export interface DocumentSnapshotDoesNotExist extends _DocumentSnapshot { + readonly exists: false; + data(options?: SnapshotOptions): undefined; + get(fieldPath: string | FieldPath, options?: SnapshotOptions): undefined; +} + +export type DocumentSnapshot = DocumentSnapshotExists | DocumentSnapshotDoesNotExist; + +export interface QueryDocumentSnapshot extends _QueryDocumentSnapshot { + data(options?: SnapshotOptions): T; +} + +export interface QuerySnapshot extends _QuerySnapshot { + readonly docs: QueryDocumentSnapshot[]; +} + +export interface DocumentChange extends _DocumentChange { + readonly doc: QueryDocumentSnapshot; +} + +export interface DocumentChangeAction { type: DocumentChangeType; - payload: DocumentChange; + payload: DocumentChange; } export interface Action { diff --git a/src/firestore/observable/fromRef.ts b/src/firestore/observable/fromRef.ts index 48ed9bc27..5f3e0d8f9 100644 --- a/src/firestore/observable/fromRef.ts +++ b/src/firestore/observable/fromRef.ts @@ -1,6 +1,6 @@ -import { DocumentReference, Query, QuerySnapshot, DocumentSnapshot } from '@firebase/firestore-types'; +import { DocumentReference, Query } from '@firebase/firestore-types'; import { Observable, Subscriber } from 'rxjs'; -import { Action, Reference } from '../interfaces'; +import { Action, Reference, DocumentSnapshot, QuerySnapshot } from '../interfaces'; import { map, share } from 'rxjs/operators'; function _fromRef(ref: Reference): Observable { @@ -14,13 +14,13 @@ export function fromRef(ref: DocumentReference | Query) { return _fromRef(ref).pipe(share()); } -export function fromDocRef(ref: DocumentReference): Observable>{ - return fromRef(ref) +export function fromDocRef(ref: DocumentReference): Observable>>{ + return fromRef>(ref) .pipe( map(payload => ({ payload, type: 'value' })) ); } -export function fromCollectionRef(ref: Query): Observable> { - return fromRef(ref).pipe(map(payload => ({ payload, type: 'query' }))); +export function fromCollectionRef(ref: Query): Observable>> { + return fromRef>(ref).pipe(map(payload => ({ payload, type: 'query' }))); } diff --git a/yarn.lock b/yarn.lock index 98792c2a2..4f74641a0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -73,7 +73,7 @@ dependencies: "@firebase/auth-types" "0.3.2" -"@firebase/database-types@0.3.1", "@firebase/database-types@^0.3.1": +"@firebase/database-types@^0.3.1": version "0.3.1" resolved "https://registry.yarnpkg.com/@firebase/database-types/-/database-types-0.3.1.tgz#4a15423f3b2cb3bed111f5a353c5c1bb2e2787ba" @@ -1825,12 +1825,6 @@ fast-levenshtein@~2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" -faye-websocket@0.11.1: - version "0.11.1" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.1.tgz#f0efe18c4f56e4f40afc7e06c719fd5ee6188f38" - dependencies: - websocket-driver ">=0.5.1" - file-uri-to-path@1: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -2594,10 +2588,6 @@ http-errors@~1.6.2: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" -http-parser-js@>=0.4.0: - version "0.4.12" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.12.tgz#b9cfbf4a2cf26f0fc34b10ca1489a27771e3474f" - http-proxy-agent@1: version "1.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz#cc1ce38e453bf984a0f7702d2dd59c73d081284a" @@ -5780,17 +5770,6 @@ void-elements@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" -websocket-driver@>=0.5.1: - version "0.7.0" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.0.tgz#0caf9d2d755d93aee049d4bdd0d3fe2cca2a24eb" - dependencies: - http-parser-js ">=0.4.0" - websocket-extensions ">=0.1.1" - -websocket-extensions@>=0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" - whatwg-fetch@2.0.4, whatwg-fetch@>=0.10.0: version "2.0.4" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f"