Skip to content

Commit 03d614a

Browse files
committed
includeMetadataChanges!
1 parent feac635 commit 03d614a

File tree

5 files changed

+50
-49
lines changed

5 files changed

+50
-49
lines changed

src/firestore/collection/changes.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { fromCollectionRef } from '../observable/fromRef';
22
import { Observable } from 'rxjs';
33
import { map, filter, scan } from 'rxjs/operators';
4+
import { firestore } from 'firebase/app';
45

56
import { Query, DocumentChangeType, DocumentChange, DocumentChangeAction, Action } from '../interfaces';
67

@@ -9,22 +10,22 @@ import { Query, DocumentChangeType, DocumentChange, DocumentChangeAction, Action
910
* order of occurence.
1011
* @param query
1112
*/
12-
export function docChanges<T>(query: Query): Observable<DocumentChangeAction<T>[]> {
13-
return fromCollectionRef(query)
13+
export function docChanges<T>(query: Query, options?: firestore.SnapshotListenOptions): Observable<DocumentChangeAction<T>[]> {
14+
return fromCollectionRef(query, options)
1415
.pipe(
1516
map(action =>
16-
action.payload.docChanges()
17+
action.payload.docChanges(options)
1718
.map(change => ({ type: change.type, payload: change } as DocumentChangeAction<T>))));
1819
}
1920

2021
/**
2122
* Return a stream of document changes on a query. These results are in sort order.
2223
* @param query
2324
*/
24-
export function sortedChanges<T>(query: Query, events: DocumentChangeType[]): Observable<DocumentChangeAction<T>[]> {
25-
return fromCollectionRef(query)
25+
export function sortedChanges<T>(query: Query, events: DocumentChangeType[], options?: firestore.SnapshotListenOptions): Observable<DocumentChangeAction<T>[]> {
26+
return fromCollectionRef(query, options)
2627
.pipe(
27-
map(changes => changes.payload.docChanges()),
28+
map(changes => changes.payload.docChanges(options)),
2829
scan((current, changes) => combineChanges(current, changes, events), []),
2930
map(changes => changes.map(c => ({ type: c.type, payload: c } as DocumentChangeAction<T>))));
3031
}

src/firestore/collection/collection.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ describe('AngularFirestoreCollection', () => {
4949
const ITEMS = 4;
5050
const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
5151

52-
const sub = stocks.valueChanges().subscribe(data => {
52+
const sub = stocks.valueChanges({idField: 'id', metadataField: 'meta'}).subscribe(data => {
5353
// unsub immediately as we will be deleting data at the bottom
5454
// and that will trigger another subscribe callback and fail
5555
// the test
@@ -59,6 +59,8 @@ describe('AngularFirestoreCollection', () => {
5959
// if the collection state is altered during a test run
6060
expect(data.length).toEqual(ITEMS);
6161
data.forEach(stock => {
62+
console.log(stock.id);
63+
console.log(stock.meta);
6264
// We used the same piece of data so they should all equal
6365
expect(stock).toEqual(FAKE_STOCK_DATA);
6466
});

src/firestore/collection/collection.ts

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ export function validateEventsArray(events?: DocumentChangeType[]) {
1616
return events;
1717
}
1818

19+
export type ChangeOptions = firestore.SnapshotListenOptions & { events?: DocumentChangeType[] };
20+
21+
// SEMVER @ v6 only allow options
1922
/**
2023
* AngularFirestoreCollection service
2124
*
@@ -61,42 +64,39 @@ export class AngularFirestoreCollection<T=DocumentData> {
6164
* your own data structure.
6265
* @param events
6366
*/
64-
stateChanges(events?: DocumentChangeType[]): Observable<DocumentChangeAction<T>[]> {
65-
if(!events || events.length === 0) {
66-
return this.afs.scheduler.keepUnstableUntilFirst(
67-
this.afs.scheduler.runOutsideAngular(
68-
docChanges<T>(this.query)
69-
)
70-
);
71-
}
72-
return this.afs.scheduler.keepUnstableUntilFirst(
73-
this.afs.scheduler.runOutsideAngular(
74-
docChanges<T>(this.query)
75-
)
67+
stateChanges(eventsOrOptions?: DocumentChangeType[]|ChangeOptions): Observable<DocumentChangeAction<T>[]> {
68+
const events = eventsOrOptions && (Array.isArray(eventsOrOptions) ? eventsOrOptions : eventsOrOptions.events) || [];
69+
const options = eventsOrOptions && !Array.isArray(eventsOrOptions) ? eventsOrOptions : {};
70+
const ret = this.afs.scheduler.keepUnstableUntilFirst(
71+
this.afs.scheduler.runOutsideAngular(
72+
docChanges<T>(this.query, options)
7673
)
77-
.pipe(
78-
map(actions => actions.filter(change => events.indexOf(change.type) > -1)),
79-
filter(changes => changes.length > 0)
80-
);
74+
);
75+
return events.length === 0 ? ret : ret.pipe(
76+
map(actions => actions.filter(change => events.indexOf(change.type) > -1)),
77+
filter(changes => changes.length > 0)
78+
);
8179
}
8280

8381
/**
8482
* Create a stream of changes as they occur it time. This method is similar to stateChanges()
8583
* but it collects each event in an array over time.
8684
* @param events
8785
*/
88-
auditTrail(events?: DocumentChangeType[]): Observable<DocumentChangeAction<T>[]> {
89-
return this.stateChanges(events).pipe(scan((current, action) => [...current, ...action], []));
86+
auditTrail(eventsOrOptions?: DocumentChangeType[]|ChangeOptions): Observable<DocumentChangeAction<T>[]> {
87+
return this.stateChanges(eventsOrOptions).pipe(scan((current, action) => [...current, ...action], []));
9088
}
9189

9290
/**
9391
* Create a stream of synchronized changes. This method keeps the local array in sorted
9492
* query order.
9593
* @param events
9694
*/
97-
snapshotChanges(events?: DocumentChangeType[]): Observable<DocumentChangeAction<T>[]> {
95+
snapshotChanges(eventsOrOptions?: DocumentChangeType[]|ChangeOptions): Observable<DocumentChangeAction<T>[]> {
96+
const events = eventsOrOptions && (Array.isArray(eventsOrOptions) ? eventsOrOptions : eventsOrOptions.events) || [];
97+
const options = eventsOrOptions && !Array.isArray(eventsOrOptions) ? eventsOrOptions : {};
9898
const validatedEvents = validateEventsArray(events);
99-
const sortedChanges$ = sortedChanges<T>(this.query, validatedEvents);
99+
const sortedChanges$ = sortedChanges<T>(this.query, validatedEvents, options);
100100
const scheduledSortedChanges$ = this.afs.scheduler.runOutsideAngular(sortedChanges$);
101101
return this.afs.scheduler.keepUnstableUntilFirst(scheduledSortedChanges$);
102102
}
@@ -110,22 +110,19 @@ export class AngularFirestoreCollection<T=DocumentData> {
110110
*/
111111
valueChanges(): Observable<T[]>
112112
valueChanges({}): Observable<T[]>
113-
valueChanges<K extends string>(options: {idField: K}): Observable<(T & { [T in K]: string })[]>
114-
valueChanges<K extends string>(options: {idField?: K} = {}): Observable<T[]> {
115-
const fromCollectionRef$ = fromCollectionRef<T>(this.query);
113+
valueChanges<K extends string, L extends string>(options: { idField: K, metadataField: L }): Observable<(T & { [T in L]: firestore.SnapshotMetadata } & { [T in K]: string })[]>
114+
valueChanges<L extends string>(options: { metadataField: L }): Observable<(T & { [T in L]: firestore.SnapshotMetadata })[]>
115+
valueChanges<K extends string>(options: { idField: K}): Observable<(T & { [T in K]: string })[]>
116+
valueChanges(options: {idField?: string, metadataField?: string} = {}): Observable<T[]> {
117+
const fromCollectionRef$ = fromCollectionRef<T>(this.query, options.metadataField ? { includeMetadataChanges: true } : {});
116118
const scheduled$ = this.afs.scheduler.runOutsideAngular(fromCollectionRef$);
117119
return this.afs.scheduler.keepUnstableUntilFirst(scheduled$)
118120
.pipe(
119-
map(actions => actions.payload.docs.map(a => {
120-
if (options.idField) {
121-
return {
122-
...a.data() as Object,
123-
...{ [options.idField]: a.id }
124-
} as T & { [T in K]: string };
125-
} else {
126-
return a.data()
127-
}
128-
}))
121+
map(actions => actions.payload.docs.map(a => ({
122+
...(a.data() as Object),
123+
...(options.metadataField ? { [options.metadataField]: a.metadata } : {}),
124+
...(options.idField ? { [options.idField]: a.id } : {})
125+
} as T)))
129126
);
130127
}
131128

src/firestore/interfaces.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export interface Action<T> {
4949
};
5050

5151
export interface Reference<T> {
52-
onSnapshot: (sub: Subscriber<any>) => any;
52+
onSnapshot: (options: firestore.SnapshotListenOptions, sub: Subscriber<any>) => any;
5353
}
5454

5555
// A convience type for making a query.

src/firestore/observable/fromRef.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
import { Observable, Subscriber } from 'rxjs';
22
import { DocumentReference, Query, Action, Reference, DocumentSnapshot, QuerySnapshot } from '../interfaces';
33
import { map, share } from 'rxjs/operators';
4+
import { firestore } from 'firebase';
45

5-
function _fromRef<T, R>(ref: Reference<T>): Observable<R> {
6+
function _fromRef<T, R>(ref: Reference<T>, options?: firestore.SnapshotListenOptions): Observable<R> {
67
return new Observable(subscriber => {
7-
const unsubscribe = ref.onSnapshot(subscriber);
8+
const unsubscribe = ref.onSnapshot(options || {}, subscriber);
89
return { unsubscribe };
910
});
1011
}
1112

12-
export function fromRef<R>(ref: DocumentReference | Query) {
13-
return _fromRef<typeof ref, R>(ref).pipe(share());
13+
export function fromRef<R>(ref: DocumentReference | Query, options?: firestore.SnapshotListenOptions) {
14+
return _fromRef<typeof ref, R>(ref, options).pipe(share());
1415
}
1516

16-
export function fromDocRef<T>(ref: DocumentReference): Observable<Action<DocumentSnapshot<T>>>{
17-
return fromRef<DocumentSnapshot<T>>(ref)
17+
export function fromDocRef<T>(ref: DocumentReference, options?: firestore.SnapshotListenOptions): Observable<Action<DocumentSnapshot<T>>>{
18+
return fromRef<DocumentSnapshot<T>>(ref, options)
1819
.pipe(
1920
map(payload => ({ payload, type: 'value' }))
2021
);
2122
}
2223

23-
export function fromCollectionRef<T>(ref: Query): Observable<Action<QuerySnapshot<T>>> {
24-
return fromRef<QuerySnapshot<T>>(ref).pipe(map(payload => ({ payload, type: 'query' })));
24+
export function fromCollectionRef<T>(ref: Query, options?: firestore.SnapshotListenOptions): Observable<Action<QuerySnapshot<T>>> {
25+
return fromRef<QuerySnapshot<T>>(ref, options).pipe(map(payload => ({ payload, type: 'query' })));
2526
}

0 commit comments

Comments
 (0)