diff --git a/packages/router/src/operators/prioritized_guard_value.ts b/packages/router/src/operators/prioritized_guard_value.ts index 2334841c119..62b5ececcd5 100644 --- a/packages/router/src/operators/prioritized_guard_value.ts +++ b/packages/router/src/operators/prioritized_guard_value.ts @@ -7,48 +7,43 @@ */ import {combineLatest, Observable, OperatorFunction} from 'rxjs'; -import {filter, map, scan, startWith, switchMap, take} from 'rxjs/operators'; +import {filter, map, startWith, switchMap, take} from 'rxjs/operators'; import {UrlTree} from '../url_tree'; -import {isUrlTree} from '../utils/type_guards'; -const INITIAL_VALUE = Symbol('INITIAL_VALUE'); +const INITIAL_VALUE = Symbol(); declare type INTERIM_VALUES = typeof INITIAL_VALUE | boolean | UrlTree; +/** + * Takes an array of observables and returns the result of the first to emit something other than + * `true`. + * + * If all Observables emit `true`, then this operator emits true. + * Also note that only the first value of each observable is used. + */ export function prioritizedGuardValue(): OperatorFunction[], boolean|UrlTree> { return switchMap(obs => { return combineLatest(obs.map(o => o.pipe(take(1), startWith(INITIAL_VALUE as INTERIM_VALUES)))) - .pipe( - scan( - (acc: INTERIM_VALUES, list: INTERIM_VALUES[]) => { - let isPending = false; - return list.reduce((innerAcc, val, i: number) => { - if (innerAcc !== INITIAL_VALUE) return innerAcc; - - // Toggle pending flag if any values haven't been set yet - if (val === INITIAL_VALUE) isPending = true; - - // Any other return values are only valid if we haven't yet hit a pending - // call. This guarantees that in the case of a guard at the bottom of the - // tree that returns a redirect, we will wait for the higher priority - // guard at the top to finish before performing the redirect. - if (!isPending) { - // Early return when we hit a `false` value as that should always - // cancel navigation - if (val === false) return val; - - if (i === list.length - 1 || isUrlTree(val)) { - return val; - } - } - - return innerAcc; - }, acc); - }, - INITIAL_VALUE), - filter(item => item !== INITIAL_VALUE), - map(item => isUrlTree(item) ? item : item === true), // - take(1)) as Observable; + .pipe( + map((results: INTERIM_VALUES[]) => { + for (const result of results) { + if (result === true) { + // If result is true, check the next one + continue; + } else if (result === INITIAL_VALUE) { + // If guard has not finished, we need to stop processing. + return INITIAL_VALUE; + } else { + // Result finished and was not true. Return the result. + return result; + } + } + // Everything resolved to true. Return true. + return true; + }), + filter((item): item is boolean|UrlTree => item !== INITIAL_VALUE), + take(1), + ); }); }