-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: added IterationQueryChangeWatcher
- added IterationQueryChangeWatcher to track changes parallel to a FirestoreItemPageIterationInstance
- Loading branch information
Showing
13 changed files
with
241 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 66 additions & 0 deletions
66
packages/dbx-firebase/src/lib/model/store/store.collection.change.directive.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { Directive, forwardRef, Input, Provider, Type } from '@angular/core'; | ||
import { FirestoreDocument, FirestoreQueryConstraint } from "@dereekb/firebase"; | ||
import { Maybe, ArrayOrValue } from '@dereekb/util'; | ||
import { DbxFirebaseCollectionStore } from "./store.collection"; | ||
|
||
/** | ||
* Abstract directive that contains a DbxFirebaseCollectionStore and provides an interface for communicating with other directives. | ||
*/ | ||
@Directive() | ||
export abstract class DbxFirebaseCollectionStoreDirective<T, D extends FirestoreDocument<T> = FirestoreDocument<T>, S extends DbxFirebaseCollectionStore<T, D> = DbxFirebaseCollectionStore<T, D>> { | ||
|
||
constructor(readonly store: S) { } | ||
|
||
readonly pageLoadingState$ = this.store.pageLoadingState$; | ||
|
||
// MARK: Inputs | ||
@Input() | ||
set maxPages(maxPages: Maybe<number>) { | ||
this.store.setMaxPages(maxPages); | ||
} | ||
|
||
@Input() | ||
set itemsPerPage(itemsPerPage: Maybe<number>) { | ||
this.store.setItemsPerPage(itemsPerPage); | ||
} | ||
|
||
@Input() | ||
set constraints(constraints: Maybe<ArrayOrValue<FirestoreQueryConstraint>>) { | ||
this.store.setConstraints(constraints); | ||
} | ||
|
||
next() { | ||
this.store.next(); | ||
} | ||
|
||
restart() { | ||
this.store.restart(); | ||
} | ||
|
||
setConstraints(constraints: Maybe<ArrayOrValue<FirestoreQueryConstraint>>) { | ||
this.store.setConstraints(constraints); | ||
} | ||
|
||
} | ||
|
||
/** | ||
* Configures providers for a DbxFirebaseCollectionStoreDirective. | ||
* | ||
* Can optionally also provide the actual store type to include in the providers array so it is instantiated by Angular. | ||
* | ||
* @param sourceType | ||
*/ | ||
export function provideDbxFirebaseCollectionStoreDirective<S extends DbxFirebaseCollectionStoreDirective<any, any, any>>(sourceType: Type<S>): Provider[]; | ||
export function provideDbxFirebaseCollectionStoreDirective<S extends DbxFirebaseCollectionStore<any, any>, C extends DbxFirebaseCollectionStoreDirective<any, any, S> = DbxFirebaseCollectionStoreDirective<any, any, S>>(sourceType: Type<C>, storeType: Type<S>): Provider[]; | ||
export function provideDbxFirebaseCollectionStoreDirective<S extends DbxFirebaseCollectionStore<any, any>, C extends DbxFirebaseCollectionStoreDirective<any, any, S> = DbxFirebaseCollectionStoreDirective<any, any, S>>(sourceType: Type<C>, storeType?: Type<S>): Provider[] { | ||
const providers: Provider[] = [{ | ||
provide: DbxFirebaseCollectionStoreDirective, | ||
useExisting: forwardRef(() => sourceType) | ||
}]; | ||
|
||
if (storeType) { | ||
providers.push(storeType); | ||
} | ||
|
||
return providers; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
99 changes: 99 additions & 0 deletions
99
packages/firebase/src/lib/common/firestore/query/watcher.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import { groupValues } from '@dereekb/util'; | ||
import { timeHasExpired } from '@dereekb/date'; | ||
import { filter, map, Observable, skip, switchMap } from 'rxjs'; | ||
import { DocumentChange, QuerySnapshot } from '../types'; | ||
import { FirestoreItemPageIterationInstance } from "./iterator"; | ||
|
||
export const DEFAULT_QUERY_CHANGE_WATCHER_DELAY = 1000 * 8; | ||
|
||
export interface IterationQueryChangeWatcherConfig<T> { | ||
readonly instance: FirestoreItemPageIterationInstance<T>; | ||
readonly delay?: number; | ||
} | ||
|
||
export interface IterationQueryChangeWatcher<T> { | ||
|
||
/** | ||
* Streams all subsequent query changes. | ||
*/ | ||
readonly stream$: Observable<QuerySnapshot<T>>; | ||
|
||
/** | ||
* Event stream | ||
*/ | ||
readonly event$: Observable<IterationQueryChangeWatcherEvent<T>>; | ||
|
||
/** | ||
* Change | ||
*/ | ||
readonly change$: Observable<IterationQueryChangeWatcherChangeType>; | ||
|
||
} | ||
|
||
export interface IterationQueryChangeWatcherEvent<T> extends IterationQueryChangeWatcherChangeGroup<T> { | ||
readonly changes: DocumentChange<T>[]; | ||
readonly type: IterationQueryChangeWatcherChangeType; | ||
} | ||
|
||
export interface IterationQueryChangeWatcherChangeGroup<T> { | ||
readonly added: DocumentChange<T>[]; | ||
readonly removed: DocumentChange<T>[]; | ||
readonly modified: DocumentChange<T>[]; | ||
} | ||
|
||
export type IterationQueryChangeWatcherChangeType = 'addedAndRemoved' | 'added' | 'removed' | 'modified' | 'none'; | ||
|
||
export function iterationQueryChangeWatcher<T>(config: IterationQueryChangeWatcherConfig<T>): IterationQueryChangeWatcher<T> { | ||
const { instance, delay: timeUntilActive = DEFAULT_QUERY_CHANGE_WATCHER_DELAY } = config; | ||
const stream$ = instance.snapshotIteration.firstSuccessfulPageResults$.pipe(switchMap((first) => { | ||
const { time, stream } = first.value!.value!; | ||
|
||
// todo: capture the change type. | ||
|
||
return stream().pipe( | ||
skip(1), // skip the first value. | ||
filter(() => timeHasExpired(time, timeUntilActive)) | ||
); | ||
})); | ||
|
||
const event$ = stream$.pipe(map(event => { | ||
const changes = event.docChanges(); | ||
|
||
const results: IterationQueryChangeWatcherChangeGroup<T> = groupValues(changes, (x) => x.type); | ||
(results as any).changes = changes; | ||
(results as any).added = results.added ?? []; | ||
(results as any).removed = results.removed ?? []; | ||
(results as any).modified = results.modified ?? []; | ||
(results as any).type = iterationQueryChangeWatcherChangeTypeForGroup(results); | ||
|
||
return results as IterationQueryChangeWatcherEvent<T>; | ||
})); | ||
|
||
const change$ = event$.pipe(map(x => x.type)); | ||
|
||
return { | ||
stream$, | ||
change$, | ||
event$ | ||
}; | ||
} | ||
|
||
export function iterationQueryChangeWatcherChangeTypeForGroup<T>(group: IterationQueryChangeWatcherChangeGroup<T>): IterationQueryChangeWatcherChangeType { | ||
const hasAdded = group.added.length > 0; | ||
const hasRemoved = group.removed.length > 0; | ||
let type: IterationQueryChangeWatcherChangeType; | ||
|
||
if (hasAdded && hasRemoved) { | ||
type = 'addedAndRemoved'; | ||
} else if (hasAdded) { | ||
type = 'added'; | ||
} else if (hasRemoved) { | ||
type = 'removed'; | ||
} else if (group.modified.length > 0) { | ||
type = 'modified'; | ||
} else { | ||
type = 'none'; | ||
} | ||
|
||
return type; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.