Skip to content

Commit 21522ab

Browse files
authored
fix(afs): Allow multiple subscribers by using share, closes #1191 (#1192)
* Allow multiple subscribers by using share * Filter out duplicate inserts * Added additional tests
1 parent ef3a8e3 commit 21522ab

File tree

5 files changed

+94
-6
lines changed

5 files changed

+94
-6
lines changed

src/firestore/collection/changes.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,12 @@ export function combineChanges(current: firebase.firestore.DocumentChange[], cha
5555
*/
5656
export function combineChange(combined: firebase.firestore.DocumentChange[], change: firebase.firestore.DocumentChange): firebase.firestore.DocumentChange[] {
5757
switch(change.type) {
58-
case 'added':
59-
combined.splice(change.newIndex, 0, change);
58+
case 'added':
59+
if (combined[change.newIndex] && combined[change.newIndex].doc.id == change.doc.id) {
60+
// Not sure why the duplicates are getting fired
61+
} else {
62+
combined.splice(change.newIndex, 0, change);
63+
}
6064
break;
6165
case 'modified':
6266
// When an item changes position we first remove it

src/firestore/collection/collection.spec.ts

+81
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,33 @@ describe('AngularFirestoreCollection', () => {
7575

7676
});
7777

78+
it('should handle multiple subscriptions (hot)', async (done: any) => {
79+
const ITEMS = 4;
80+
const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
81+
const changes = stocks.valueChanges();
82+
const sub = changes.subscribe(() => {}).add(
83+
changes.take(1).subscribe(data => {
84+
expect(data.length).toEqual(ITEMS);
85+
sub.unsubscribe();
86+
})
87+
).add(() => {
88+
deleteThemAll(names, ref).then(done).catch(done.fail);
89+
});
90+
});
91+
92+
it('should handle multiple subscriptions (warm)', async (done: any) => {
93+
const ITEMS = 4;
94+
const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
95+
const changes = stocks.valueChanges();
96+
changes.take(1).subscribe(() => {}).add(() => {
97+
const sub = changes.take(1).subscribe(data => {
98+
expect(data.length).toEqual(ITEMS);
99+
}).add(() => {
100+
deleteThemAll(names, ref).then(done).catch(done.fail);
101+
});
102+
});
103+
});
104+
78105
it('should handle dynamic queries that return empty sets', async (done) => {
79106
const ITEMS = 10;
80107
let count = 0;
@@ -129,6 +156,33 @@ describe('AngularFirestoreCollection', () => {
129156
});
130157
});
131158

159+
it('should handle multiple subscriptions (hot)', async (done: any) => {
160+
const ITEMS = 4;
161+
const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
162+
const changes = stocks.snapshotChanges();
163+
const sub = changes.subscribe(() => {}).add(
164+
changes.take(1).subscribe(data => {
165+
expect(data.length).toEqual(ITEMS);
166+
sub.unsubscribe();
167+
})
168+
).add(() => {
169+
deleteThemAll(names, ref).then(done).catch(done.fail);
170+
});
171+
});
172+
173+
it('should handle multiple subscriptions (warm)', async (done: any) => {
174+
const ITEMS = 4;
175+
const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
176+
const changes = stocks.snapshotChanges();
177+
changes.take(1).subscribe(() => {}).add(() => {
178+
const sub = changes.take(1).subscribe(data => {
179+
expect(data.length).toEqual(ITEMS);
180+
}).add(() => {
181+
deleteThemAll(names, ref).then(done).catch(done.fail);
182+
});
183+
});
184+
});
185+
132186
it('should update order on queries', async (done) => {
133187
const ITEMS = 10;
134188
let count = 0;
@@ -279,6 +333,33 @@ describe('AngularFirestoreCollection', () => {
279333
}
280334
});
281335
});
336+
337+
it('should handle multiple subscriptions (hot)', async (done: any) => {
338+
const ITEMS = 4;
339+
const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
340+
const changes = stocks.stateChanges();
341+
const sub = changes.subscribe(() => {}).add(
342+
changes.take(1).subscribe(data => {
343+
expect(data.length).toEqual(ITEMS);
344+
sub.unsubscribe();
345+
})
346+
).add(() => {
347+
deleteThemAll(names, ref).then(done).catch(done.fail);
348+
});
349+
});
350+
351+
it('should handle multiple subscriptions (warm)', async (done: any) => {
352+
const ITEMS = 4;
353+
const { randomCollectionName, ref, stocks, names } = await collectionHarness(afs, ITEMS);
354+
const changes = stocks.stateChanges();
355+
changes.take(1).subscribe(() => {}).add(() => {
356+
const sub = changes.take(1).subscribe(data => {
357+
expect(data.length).toEqual(ITEMS);
358+
}).add(() => {
359+
deleteThemAll(names, ref).then(done).catch(done.fail);
360+
});
361+
});
362+
});
282363

283364
it('should be able to filter stateChanges() types - modified', async (done) => {
284365
const ITEMS = 10;

src/firestore/collection/collection.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export class AngularFirestoreCollection<T> {
8383
}
8484

8585
/**
86-
* Create a stream of synchronized shanges. This method keeps the local array in sorted
86+
* Create a stream of synchronized changes. This method keeps the local array in sorted
8787
* query order.
8888
* @param events
8989
*/
@@ -96,8 +96,8 @@ export class AngularFirestoreCollection<T> {
9696
* Listen to all documents in the collection and its possible query as an Observable.
9797
*/
9898
valueChanges(events?: firebase.firestore.DocumentChangeType[]): Observable<T[]> {
99-
return this.snapshotChanges()
100-
.map(actions => actions.map(a => a.payload.doc.data()) as T[]);
99+
return fromCollectionRef(this.query)
100+
.map(actions => actions.payload.docs.map(a => a.data()) as T[]);
101101
}
102102

103103
/**

src/firestore/observable/fromRef.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import { Subscription } from 'rxjs/Subscription';
55
import { observeOn } from 'rxjs/operator/observeOn';
66
import { ZoneScheduler } from 'angularfire2';
77
import { Action, Reference } from '../interfaces';
8+
89
import 'rxjs/add/operator/map';
10+
import 'rxjs/add/operator/share';
911

1012
function _fromRef<T, R>(ref: Reference<T>): Observable<R> {
1113
const ref$ = new Observable(subscriber => {
@@ -16,7 +18,7 @@ function _fromRef<T, R>(ref: Reference<T>): Observable<R> {
1618
}
1719

1820
export function fromRef<R>(ref: firebase.firestore.DocumentReference | firebase.firestore.Query) {
19-
return _fromRef<typeof ref, R>(ref);
21+
return _fromRef<typeof ref, R>(ref).share();
2022
}
2123

2224
export function fromDocRef(ref: firebase.firestore.DocumentReference): Observable<Action<firebase.firestore.DocumentSnapshot>>{

tools/build.js

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const GLOBALS = {
4444
'rxjs/add/observable/fromPromise': 'Rx.Observable.prototype',
4545
'rxjs/add/operator/delay': 'Rx.Observable',
4646
'rxjs/add/operator/debounce': 'Rx.Observable',
47+
'rxjs/add/operator/share': 'Rx.Observable',
4748
'rxjs/observable/fromEvent': 'Rx.Observable',
4849
'rxjs/observable/from': 'Rx.Observable',
4950
'rxjs/operator': 'Rx.Observable.prototype',

0 commit comments

Comments
 (0)