import { combineLatest, Observable, of, OperatorFunction } from "rxjs"; import { distinctUntilChanged, filter, map, switchMap, take, takeWhile, tap } from "rxjs/operators"; import { notNull, notNullish, notUndefined } from "../model"; export function isNotNullish() { return (source$: Observable) => source$.pipe( filter(notNullish) ); } export function isNotNull() { return (source$: Observable) => source$.pipe( filter(notNull) ); } export function isNotUndefined() { return (source$: Observable) => source$.pipe( filter(notUndefined) ); } export function watchList(select: (item: T) => Observable): OperatorFunction { return (source: Observable): Observable => { return source.pipe( switchMap(items => (!items || items.length === 0) ? of([]) : combineLatest(items.map(item => select(item).pipe(map(() => item)))) ) ) } } export function mergeWatchList(predicate: (item: T) => Observable): OperatorFunction { return (source: Observable): Observable => source.pipe( switchMap(list => (!list || list.length === 0) ? of([]) : combineLatest( list.map(item => predicate(item)) ).pipe( map(listOfLists => listOfLists.flat()), ) ) ) } export function mergeWatchIndex(predicate: (item: T) => Observable<{ [pointer: string]: S }>): OperatorFunction { return (source: Observable): Observable<{ [pointer: string]: S }> => source.pipe( switchMap(list => (!list || list.length === 0) ? of<{ [pointer: string]: S }>({}) : combineLatest( list.map(item => predicate(item)) ).pipe( map(indexList => mergeIndex(indexList)) ) ) ) } export function toIndex(getIndex: (item: T) => string): OperatorFunction { return (source: Observable): Observable<{ [pointer: string]: T }> => source.pipe( map(items => makeIndex(items, getIndex)) ) } export function makeIndex(itemList: T[], getIndex: (item: T) => string): { [pointer: string]: T } { const propsIndexed: { [pointer: string]: T } = {}; for (let index = 0; index < itemList.length; index++) { const item = itemList[index]; propsIndexed[getIndex(item)] = item; } return propsIndexed; } export function mergeIndex(indexes: { [pointer: string]: T }[]): { [pointer: string]: T } { var newIndex: { [pointer: string]: T } = {}; for (let index = 0; index < indexes.length; index++) { const nodeIndex = indexes[index]; newIndex = { ...newIndex, ...nodeIndex }; } return newIndex } export function mandatoryPluckSwitchMap(attr: K, project: (v: T[K]) => Observable | null | undefined, distinct = true): OperatorFunction { return (source: Observable): Observable => { // pluck from object, only emit if changed and switch subscription to projected observable // logic is, that if our mandatory pluck emits a value we found our target and stop listening to the source observable const resObs = source.pipe(mandatoryPluck(attr), distinctUntilChanged(), mandatorySwitchMap(project)) return distinct ? resObs.pipe(distinctUntilChanged()) : resObs; } } export function optionalPluckSwitchMap(attr: K, project: (v: T[K] | null | undefined) => Observable | null | undefined, distinct = true): OperatorFunction { return (source: Observable): Observable => { // pluck from source object emissions and only emit if changed // when the result is here switch subscription to projected observable const resObs = source.pipe(optionalPluck(attr), distinctUntilChanged(), optionalSwitchMap(project)) return distinct ? resObs.pipe(distinctUntilChanged()) : resObs; } } export function mandatorySwitchMap(project: (v: T) => Observable | null | undefined): OperatorFunction { return (source: Observable): Observable => { return source.pipe( switchMap(v => project(v) ?? of(undefined)), mandatory() ); } } export function optionalSwitchMap(project: (v: T | null | undefined) => Observable | null | undefined): OperatorFunction { return (source: Observable): Observable => { return source.pipe( switchMap(v => { const projRes = project(v); return (projRes !== undefined && projRes !== null) ? projRes : of(undefined) }) ); } } export function mandatoryPluck(attr: K): OperatorFunction { return (source: Observable): Observable => { return source.pipe( map(attrs => attrs[attr]), mandatory() ); } } export function optionalPluck(attr: K): OperatorFunction { return (source: Observable): Observable => { return source.pipe( map(v => v ? v[attr] : undefined) ); } } export function mandatory(): OperatorFunction { return (source: Observable): Observable => { return source.pipe( filter(v => v !== null && v !== undefined), ) as Observable; } } export function flattenAndRemoveNullish>(): OperatorFunction { return (source: Observable): Observable => { return source.pipe( map(objs => { const filtered = []; for (let index = 0; index < objs.length; index++) { const dataSet = objs[index]; for (let indexSet = 0; indexSet < dataSet.length; indexSet++) { const obj = dataSet[indexSet]; if (obj !== null && obj !== undefined) { filtered.push(obj); } } } return filtered; }) ) as Observable; } } export function removeNullish>(): OperatorFunction { return (source: Observable): Observable => { return source.pipe( map(objs => { const filtered = []; for (let index = 0; index < objs.length; index++) { const node = objs[index]; if (node) { filtered.push(node); } } return filtered; }) ) as Observable; } }