Skip to content

Commit 28aef60

Browse files
committed
fix(afs): mutiple subscribes/unsubscribes could still get confused
1 parent 86e2c24 commit 28aef60

File tree

3 files changed

+47
-18
lines changed

3 files changed

+47
-18
lines changed

src/firestore/collection/changes.ts

+17-9
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { DocumentChangeAction, Action } from '../interfaces';
1616
*/
1717
export function docChanges(query: firebase.firestore.Query): Observable<DocumentChangeAction[]> {
1818
return fromCollectionRef(query)
19+
.filter(action => !!action)
1920
.map(action =>
2021
action.payload.docChanges
2122
.map(change => ({ type: change.type, payload: change })));
@@ -27,7 +28,7 @@ export function docChanges(query: firebase.firestore.Query): Observable<Document
2728
*/
2829
export function sortedChanges(query: firebase.firestore.Query, events: firebase.firestore.DocumentChangeType[]): Observable<DocumentChangeAction[]> {
2930
return fromCollectionRef(query)
30-
.map(changes => changes.payload.docChanges)
31+
.map(changes => changes && changes.payload.docChanges)
3132
.scan((current, changes) => combineChanges(current, changes, events), [])
3233
.map(changes => changes.map(c => ({ type: c.type, payload: c })))
3334
.filter(changes => changes.length > 0)
@@ -41,14 +42,21 @@ export function sortedChanges(query: firebase.firestore.Query, events: firebase.
4142
* @param changes
4243
* @param events
4344
*/
44-
export function combineChanges(current: firebase.firestore.DocumentChange[], changes: firebase.firestore.DocumentChange[], events: firebase.firestore.DocumentChangeType[]) {
45-
changes.forEach(change => {
46-
// skip unwanted change types
47-
if(events.indexOf(change.type) > -1) {
48-
current = combineChange(current, change);
49-
}
50-
});
51-
return current;
45+
export function combineChanges(current: firebase.firestore.DocumentChange[], changes: firebase.firestore.DocumentChange[] | undefined, events: firebase.firestore.DocumentChangeType[]) {
46+
if (changes) {
47+
changes.forEach(change => {
48+
// skip unwanted change types
49+
if(events.indexOf(change.type) > -1) {
50+
current = combineChange(current, change);
51+
}
52+
});
53+
return current;
54+
} else {
55+
// in the case of undefined, empty current
56+
// if you do odd things with the subscribes/unsubscrbes you can mess things
57+
// up and get double or tripled results
58+
return [];
59+
}
5260
}
5361

5462
/**

src/firestore/collection/collection.spec.ts

+27-8
Original file line numberDiff line numberDiff line change
@@ -74,28 +74,45 @@ describe('AngularFirestoreCollection', () => {
7474

7575
});
7676

77-
it('should handle multiple subscriptions', async (done: any) => {
77+
it('should handle multiple subscriptions (hot)', async (done: any) => {
7878
const ITEMS = 4;
7979
const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
8080
const changes = stocks.valueChanges();
8181
const sub = changes.subscribe(() => {}).add(
82-
changes.subscribe(data => {
82+
changes.take(1).subscribe(data => {
8383
expect(data.length).toEqual(ITEMS);
8484
sub.unsubscribe();
8585
})
86-
).add(done);
86+
).add(() => {
87+
deleteThemAll(names, ref).then(done).catch(done.fail);
88+
});
89+
});
90+
91+
it('should handle multiple subscriptions (warm)', async (done: any) => {
92+
const ITEMS = 4;
93+
const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
94+
const changes = stocks.valueChanges();
95+
changes.take(1).subscribe(() => {}).add(() => {
96+
const sub = changes.take(1).subscribe(data => {
97+
expect(data.length).toEqual(ITEMS);
98+
}).add(() => {
99+
deleteThemAll(names, ref).then(done).catch(done.fail);
100+
});
101+
});
87102
});
88103

89-
it('should handle multiple subscriptions + cold observer', async (done: any) => {
104+
it('should handle multiple subscriptions (cold)', async (done: any) => {
90105
const ITEMS = 4;
91106
const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
92107
const changes = stocks.valueChanges();
93-
const sub = changes.take(1).subscribe(() => {
108+
const sub = changes.subscribe(() => {
94109
sub.unsubscribe();
95110
}).add(() => {
96111
changes.take(1).subscribe(data => {
97112
expect(data.length).toEqual(ITEMS);
98-
}).add(done);
113+
}).add(() => {
114+
deleteThemAll(names, ref).then(done).catch(done.fail);
115+
});
99116
});
100117
});
101118

@@ -132,11 +149,13 @@ describe('AngularFirestoreCollection', () => {
132149
const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
133150
const changes = stocks.snapshotChanges();
134151
const sub = changes.subscribe(() => {}).add(
135-
changes.subscribe(data => {
152+
changes.take(1).subscribe(data => {
136153
expect(data.length).toEqual(ITEMS);
137154
sub.unsubscribe();
138155
})
139-
).add(done);
156+
).add(() => {
157+
deleteThemAll(names, ref).then(done).catch(done.fail);
158+
});
140159
});
141160

142161
it('should update order on queries', async (done) => {

src/firestore/observable/fromRef.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'rxjs/add/operator/share';
1111

1212
function _fromRef<T, R>(ref: Reference<T>): Observable<R> {
1313
const ref$ = new Observable(subscriber => {
14+
subscriber.next(undefined); // fire an undefined to let subcribers know this is a new Observable
1415
const unsubscribe = ref.onSnapshot(subscriber);
1516
return { unsubscribe };
1617
});
@@ -23,9 +24,10 @@ export function fromRef<R>(ref: firebase.firestore.DocumentReference | firebase.
2324

2425
export function fromDocRef(ref: firebase.firestore.DocumentReference): Observable<Action<firebase.firestore.DocumentSnapshot>>{
2526
return fromRef<firebase.firestore.DocumentSnapshot>(ref)
27+
.filter(payload => !!payload)
2628
.map(payload => ({ payload, type: 'value' }));
2729
}
2830

2931
export function fromCollectionRef(ref: firebase.firestore.Query): Observable<Action<firebase.firestore.QuerySnapshot>> {
30-
return fromRef<firebase.firestore.QuerySnapshot>(ref).map(payload => ({ payload, type: 'query' }))
32+
return fromRef<firebase.firestore.QuerySnapshot>(ref).map(payload => payload && ({ payload, type: 'query' }))
3133
}

0 commit comments

Comments
 (0)