/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import {combineLatest, Observable, OperatorFunction} from 'rxjs'; 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('INITIAL_VALUE'); declare type INTERIM_VALUES = typeof INITIAL_VALUE | boolean | UrlTree; 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; }); }