From a003dd8dc2ad50db20c684d5a2b1e748ad339a42 Mon Sep 17 00:00:00 2001 From: Jessica Janiuk Date: Fri, 8 Jul 2022 22:07:52 +0000 Subject: [PATCH] Revert "refactor(router): Simplify the prioritizeGuardValue logic" (#46759) This reverts commit f32fface4763d789a8b7cc4f4489f98be4e1073a. PR Close #46759 --- .../src/operators/prioritized_guard_value.ts | 63 ++++++++++--------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/packages/router/src/operators/prioritized_guard_value.ts b/packages/router/src/operators/prioritized_guard_value.ts index 62b5ececcd5..2334841c119 100644 --- a/packages/router/src/operators/prioritized_guard_value.ts +++ b/packages/router/src/operators/prioritized_guard_value.ts @@ -7,43 +7,48 @@ */ import {combineLatest, Observable, OperatorFunction} from 'rxjs'; -import {filter, map, startWith, switchMap, take} from 'rxjs/operators'; +import {filter, map, scan, startWith, switchMap, take} from 'rxjs/operators'; import {UrlTree} from '../url_tree'; +import {isUrlTree} from '../utils/type_guards'; -const INITIAL_VALUE = Symbol(); +const INITIAL_VALUE = Symbol('INITIAL_VALUE'); 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( - 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), - ); + .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; }); }