Skip to content

Commit ffda578

Browse files
authored
chore: cleanup onSnapshot implementations (#148)
1 parent 62d3176 commit ffda578

File tree

4 files changed

+101
-251
lines changed

4 files changed

+101
-251
lines changed

packages/firebase-firestore/common.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ICollectionReference, IDocumentReference, IFieldPath, IFieldValue, IGeoPoint, ITimestamp } from '.';
1+
import { SnapshotListenOptions } from '.';
22

33
export enum GetOptions {
44
Default = 'default',
@@ -11,3 +11,34 @@ export enum DocumentChangeType {
1111
Removed = 'removed',
1212
Modified = 'modified',
1313
}
14+
15+
type Observer<SnapshotType> = { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: SnapshotType) => void }
16+
17+
export type OnSnapshotParameters<SnapshotType> =
18+
| [observer: Observer<SnapshotType>]
19+
| [options: SnapshotListenOptions, observer: Observer<SnapshotType>]
20+
| [onNext: (snapshot: SnapshotType) => void, onError?: (error: Error) => void, onCompletion?: () => void]
21+
| [options: SnapshotListenOptions, onNext: (snapshot: SnapshotType) => void, onError?: (error: Error) => void, onCompletion?: () => void]
22+
23+
export function parseOnSnapshotArgs<SnapshotType>(args: OnSnapshotParameters<SnapshotType>) {
24+
const result: SnapshotListenOptions & Observer<SnapshotType> = {
25+
includeMetadataChanges: false
26+
}
27+
for (let i = 0; i < args.length; ++i) {
28+
if (args[i] instanceof Function) {
29+
// Assign whichever of next, error, complete were passed starting here
30+
const fnArgsMappingOrder = ['next', 'error', 'complete'] as const
31+
args.slice(i).forEach((fn, idx) => result[fnArgsMappingOrder[idx]] = fn)
32+
break
33+
} else if (typeof args[i] === 'object') {
34+
if ('includeMetadataChanges' in args[i]) {
35+
result.includeMetadataChanges = Boolean((<SnapshotListenOptions>args[i]).includeMetadataChanges)
36+
} else { // Must be Observer
37+
Object.assign(result, args[i])
38+
}
39+
} else {
40+
throw new Error(`Invalid argument to onSnapshot at position ${i} of type ${typeof args[i]}: ${args[i]}`)
41+
}
42+
}
43+
return result
44+
}

packages/firebase-firestore/index.android.ts

Lines changed: 29 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { GetOptions, DocumentChangeType } from './common';
1+
import { GetOptions, DocumentChangeType, parseOnSnapshotArgs, OnSnapshotParameters } from './common';
22

33
export { GetOptions, DocumentChangeType };
44

@@ -506,68 +506,23 @@ export class Query<T extends DocumentData = DocumentData> implements IQuery<T> {
506506
return Query.fromNative(this.native.limitToLast(limitToLast));
507507
}
508508

509-
onSnapshot(observer: { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: QuerySnapshot) => void });
510-
onSnapshot(options: SnapshotListenOptions, observer: { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: QuerySnapshot) => void });
511-
onSnapshot(onNext: (snapshot: QuerySnapshot) => void, onError?: (error: Error) => void, onCompletion?: () => void);
512-
onSnapshot(options: SnapshotListenOptions, onNext: (snapshot: QuerySnapshot) => void, onError?: (error: Error) => void, onCompletion?: () => void);
513-
onSnapshot(options: any, onNext?: any, onError?: any, onCompletion?: any): any {
514-
let listener;
515-
let includeMetadataChanges = com.google.firebase.firestore.MetadataChanges.EXCLUDE;
516-
const argsCount = arguments.length;
517-
if (typeof arguments[0] === 'object') {
518-
if (typeof options?.includeMetadataChanges === 'boolean') {
519-
includeMetadataChanges = com.google.firebase.firestore.MetadataChanges.INCLUDE;
520-
}
521-
}
509+
onSnapshot(observer: { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: QuerySnapshot) => void; }): () => void;
510+
onSnapshot(options: SnapshotListenOptions, observer: { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: QuerySnapshot) => void; }): () => void;
511+
onSnapshot(onNext: (snapshot: QuerySnapshot) => void, onError?: (error: Error) => void, onCompletion?: () => void): () => void;
512+
onSnapshot(options: SnapshotListenOptions, onNext: (snapshot: QuerySnapshot) => void, onError?: (error: Error) => void, onCompletion?: () => void): () => void;
513+
onSnapshot(...args: OnSnapshotParameters<QuerySnapshot>): () => void {
514+
const { includeMetadataChanges, ...handlers } = parseOnSnapshotArgs(args);
522515

523-
listener = this.native.addSnapshotListener(
524-
includeMetadataChanges,
516+
const listener = this.native.addSnapshotListener(
517+
includeMetadataChanges
518+
? com.google.firebase.firestore.MetadataChanges.INCLUDE
519+
: com.google.firebase.firestore.MetadataChanges.EXCLUDE,
525520
new com.google.firebase.firestore.EventListener<com.google.firebase.firestore.QuerySnapshot>({
526-
onEvent(ss, error: com.google.firebase.firestore.FirebaseFirestoreException) {
527-
if (argsCount > 1) {
528-
if (typeof options === 'object') {
529-
if (typeof onNext === 'object') {
530-
if (error) {
531-
onNext?.error?.(FirebaseError.fromNative(error));
532-
} else {
533-
onNext?.complete?.();
534-
onNext?.next?.(QuerySnapshot.fromNative(ss));
535-
}
536-
} else {
537-
if (error) {
538-
onError?.(FirebaseError.fromNative(error));
539-
} else {
540-
// onError -> onCompletion
541-
onCompletion?.();
542-
// options -> onNext
543-
onNext?.(QuerySnapshot.fromNative(ss));
544-
}
545-
}
546-
} else {
547-
if (error) {
548-
//onError -> onNext
549-
onNext?.(FirebaseError.fromNative(error));
550-
} else {
551-
// onCompletion ->
552-
onError?.();
553-
// options -> onNext
554-
options?.(QuerySnapshot.fromNative(ss));
555-
}
556-
}
521+
onEvent(querySnapshot, error: com.google.firebase.firestore.FirebaseFirestoreException) {
522+
if (error) {
523+
handlers.error?.(FirebaseError.fromNative(error));
557524
} else {
558-
if (typeof arguments[1] === 'function') {
559-
// onNext -> options
560-
if (!error) {
561-
options?.(QuerySnapshot.fromNative(ss));
562-
}
563-
} else {
564-
if (error) {
565-
options?.error?.(FirebaseError.fromNative(error));
566-
} else {
567-
options?.complete?.();
568-
options?.next?.(QuerySnapshot.fromNative(ss));
569-
}
570-
}
525+
handlers.next?.(QuerySnapshot.fromNative(querySnapshot));
571526
}
572527
},
573528
})
@@ -948,68 +903,23 @@ export class DocumentReference<T extends DocumentData = DocumentData> implements
948903
});
949904
}
950905

951-
onSnapshot(observer: { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: DocumentSnapshot<T>) => void });
952-
onSnapshot(options: SnapshotListenOptions, observer: { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: DocumentSnapshot<T>) => void });
953-
onSnapshot(onNext: (snapshot: DocumentSnapshot<T>) => void, onError?: (error: Error) => void, onCompletion?: () => void);
954-
onSnapshot(options: SnapshotListenOptions, onNext: (snapshot: DocumentSnapshot<T>) => void, onError?: (error: Error) => void, onCompletion?: () => void);
955-
onSnapshot(options: any, onNext?: any, onError?: any, onCompletion?: any) {
956-
let listener;
957-
let includeMetadataChanges = com.google.firebase.firestore.MetadataChanges.EXCLUDE;
958-
const argsCount = arguments.length;
959-
if (typeof arguments[0] === 'object') {
960-
if (typeof options?.includeMetadataChanges === 'boolean') {
961-
includeMetadataChanges = com.google.firebase.firestore.MetadataChanges.INCLUDE;
962-
}
963-
}
906+
onSnapshot(observer: { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: DocumentSnapshot<T>) => void; }): () => void;
907+
onSnapshot(options: SnapshotListenOptions, observer: { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: DocumentSnapshot<T>) => void; }): () => void;
908+
onSnapshot(onNext: (snapshot: DocumentSnapshot<T>) => void, onError?: (error: Error) => void, onCompletion?: () => void): () => void;
909+
onSnapshot(options: SnapshotListenOptions, onNext: (snapshot: DocumentSnapshot<T>) => void, onError?: (error: Error) => void, onCompletion?: () => void): () => void;
910+
onSnapshot(...args: OnSnapshotParameters<DocumentSnapshot<T>>): () => void {
911+
const { includeMetadataChanges, ...handlers } = parseOnSnapshotArgs(args);
964912

965-
listener = this.native.addSnapshotListener(
966-
includeMetadataChanges,
913+
const listener = this.native.addSnapshotListener(
914+
includeMetadataChanges
915+
? com.google.firebase.firestore.MetadataChanges.INCLUDE
916+
: com.google.firebase.firestore.MetadataChanges.EXCLUDE,
967917
new com.google.firebase.firestore.EventListener<com.google.firebase.firestore.DocumentSnapshot>({
968-
onEvent(ss, error: com.google.firebase.firestore.FirebaseFirestoreException) {
969-
if (argsCount > 1) {
970-
if (typeof options === 'object') {
971-
if (typeof onNext === 'object') {
972-
if (error) {
973-
onNext?.error?.(FirebaseError.fromNative(error));
974-
} else {
975-
onNext?.complete?.();
976-
onNext?.next?.(DocumentSnapshot.fromNative(ss));
977-
}
978-
} else {
979-
if (error) {
980-
onError?.(FirebaseError.fromNative(error));
981-
} else {
982-
// onError -> onCompletion
983-
onCompletion?.();
984-
// options -> onNext
985-
onNext?.(DocumentSnapshot.fromNative(ss));
986-
}
987-
}
988-
} else {
989-
if (error) {
990-
//onError -> onNext
991-
onNext?.(FirebaseError.fromNative(error));
992-
} else {
993-
// onCompletion ->
994-
onError?.();
995-
// options -> onNext
996-
options?.(DocumentSnapshot.fromNative(ss));
997-
}
998-
}
918+
onEvent(docSnapshot, error: com.google.firebase.firestore.FirebaseFirestoreException) {
919+
if (error) {
920+
handlers.error?.(FirebaseError.fromNative(error));
999921
} else {
1000-
if (typeof arguments[1] === 'function') {
1001-
// onNext -> options
1002-
if (!error) {
1003-
options?.(DocumentSnapshot.fromNative(ss));
1004-
}
1005-
} else {
1006-
if (error) {
1007-
options?.error?.(FirebaseError.fromNative(error));
1008-
} else {
1009-
options?.complete?.();
1010-
options?.next?.(DocumentSnapshot.fromNative(ss));
1011-
}
1012-
}
922+
handlers.next?.(DocumentSnapshot.fromNative(docSnapshot));
1013923
}
1014924
},
1015925
})

packages/firebase-firestore/index.d.ts

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ export interface IQuery<T extends DocumentData = DocumentData> {
2323

2424
limitToLast(limitToLast: number): IQuery<T>;
2525

26-
onSnapshot(observer: { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: IQuerySnapshot<T>) => void });
26+
/** NOTE: Although an onCompletion callback can be provided, it will never be called because the snapshot stream is never-ending. */
27+
onSnapshot(observer: { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: IQuerySnapshot<T>) => void }): () => void;
2728

2829
onSnapshot(
2930
options: SnapshotListenOptions,
@@ -32,11 +33,11 @@ export interface IQuery<T extends DocumentData = DocumentData> {
3233
error?: (error: Error) => void;
3334
next?: (snapshot: IQuerySnapshot<T>) => void;
3435
}
35-
);
36+
): () => void;
3637

37-
onSnapshot(onNext: (snapshot: IQuerySnapshot<T>) => void, onError?: (error: Error) => void, onCompletion?: () => void);
38+
onSnapshot(onNext: (snapshot: IQuerySnapshot<T>) => void, onError?: (error: Error) => void, onCompletion?: () => void): () => void;
3839

39-
onSnapshot(options: SnapshotListenOptions, onNext: (snapshot: IQuerySnapshot<T>) => void, onError?: (error: Error) => void, onCompletion?: () => void);
40+
onSnapshot(options: SnapshotListenOptions, onNext: (snapshot: IQuerySnapshot<T>) => void, onError?: (error: Error) => void, onCompletion?: () => void): () => void;
4041

4142
orderBy(fieldPath: keyof T | IFieldPath, directionStr?: 'asc' | 'desc'): IQuery<T>;
4243

@@ -106,7 +107,8 @@ export interface IDocumentReference<T extends DocumentData = DocumentData> {
106107

107108
get(options?: GetOptions): Promise<IDocumentSnapshot<T>>;
108109

109-
onSnapshot(observer: { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: IDocumentSnapshot<T>) => void });
110+
/** NOTE: Although an onCompletion callback can be provided, it will never be called because the snapshot stream is never-ending. */
111+
onSnapshot(observer: { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: IDocumentSnapshot<T>) => void }): () => void;
110112

111113
onSnapshot(
112114
options: SnapshotListenOptions,
@@ -115,11 +117,11 @@ export interface IDocumentReference<T extends DocumentData = DocumentData> {
115117
error?: (error: Error) => void;
116118
next?: (snapshot: IDocumentSnapshot<T>) => void;
117119
}
118-
);
120+
): () => void;
119121

120-
onSnapshot(onNext: (snapshot: IDocumentSnapshot<T>) => void, onError?: (error: Error) => void, onCompletion?: () => void);
122+
onSnapshot(onNext: (snapshot: IDocumentSnapshot<T>) => void, onError?: (error: Error) => void, onCompletion?: () => void): () => void;
121123

122-
onSnapshot(options: SnapshotListenOptions, onNext: (snapshot: IDocumentSnapshot<T>) => void, onError?: (error: Error) => void, onCompletion?: () => void);
124+
onSnapshot(options: SnapshotListenOptions, onNext: (snapshot: IDocumentSnapshot<T>) => void, onError?: (error: Error) => void, onCompletion?: () => void): () => void;
123125

124126
set(data: T, options?: SetOptions): Promise<void>;
125127

@@ -307,10 +309,10 @@ export declare class Query<T extends DocumentData = DocumentData> implements IQu
307309

308310
limitToLast(limitToLast: number): Query;
309311

310-
onSnapshot(observer: { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: QuerySnapshot) => void });
311-
onSnapshot(options: SnapshotListenOptions, observer: { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: QuerySnapshot) => void });
312-
onSnapshot(onNext: (snapshot: QuerySnapshot) => void, onError?: (error: Error) => void, onCompletion?: () => void);
313-
onSnapshot(options: SnapshotListenOptions, onNext: (snapshot: QuerySnapshot) => void, onError?: (error: Error) => void, onCompletion?: () => void);
312+
onSnapshot(observer: { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: QuerySnapshot) => void }): () => void;
313+
onSnapshot(options: SnapshotListenOptions, observer: { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: QuerySnapshot) => void }): () => void;
314+
onSnapshot(onNext: (snapshot: QuerySnapshot) => void, onError?: (error: Error) => void, onCompletion?: () => void): () => void;
315+
onSnapshot(options: SnapshotListenOptions, onNext: (snapshot: QuerySnapshot) => void, onError?: (error: Error) => void, onCompletion?: () => void): () => void;
314316

315317
orderBy(fieldPath: keyof DocumentData | FieldPath, directionStr: 'asc' | 'desc' = 'asc'): Query;
316318

@@ -388,10 +390,10 @@ export declare class DocumentReference<T extends DocumentData = DocumentData> im
388390

389391
get(options?: GetOptions): Promise<DocumentSnapshot<T>>;
390392

391-
onSnapshot(observer: { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: DocumentSnapshot<T>) => void });
392-
onSnapshot(options: SnapshotListenOptions, observer: { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: DocumentSnapshot<T>) => void });
393-
onSnapshot(onNext: (snapshot: DocumentSnapshot<T>) => void, onError?: (error: Error) => void, onCompletion?: () => void);
394-
onSnapshot(options: SnapshotListenOptions, onNext: (snapshot: DocumentSnapshot<T>) => void, onError?: (error: Error) => void, onCompletion?: () => void);
393+
onSnapshot(observer: { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: DocumentSnapshot<T>) => void }): () => void;
394+
onSnapshot(options: SnapshotListenOptions, observer: { complete?: () => void; error?: (error: Error) => void; next?: (snapshot: DocumentSnapshot<T>) => void }): () => void;
395+
onSnapshot(onNext: (snapshot: DocumentSnapshot<T>) => void, onError?: (error: Error) => void, onCompletion?: () => void): () => void;
396+
onSnapshot(options: SnapshotListenOptions, onNext: (snapshot: DocumentSnapshot<T>) => void, onError?: (error: Error) => void, onCompletion?: () => void): () => void;
395397

396398
set(data: T, options?: SetOptions): Promise<void>;
397399

0 commit comments

Comments
 (0)