Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fix incorrect nav animations #28297

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
@@ -1,7 +1,7 @@
import { Location } from '@angular/common';
import { ComponentRef, NgZone } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import type { AnimationBuilder, RouterDirection } from '@ionic/core/components';
import type { AnimationBuilder, NavDirection, RouterDirection } from '@ionic/core/components';

import { bindLifecycleEvents } from '../../providers/angular-delegate';
import { NavController } from '../../providers/nav-controller';
Expand Down Expand Up @@ -62,50 +62,72 @@ export class StackController {
}

setActive(enteringView: RouteView): Promise<StackDidChangeEvent> {
const consumeResult = this.navCtrl.consumeTransition();
const { isDirectionBasedOnNavigationIds, ...consumeResult } = this.navCtrl.consumeTransition();
let { direction, animation, animationBuilder } = consumeResult;
const leavingView = this.activeView;
const tabSwitch = isTabSwitch(enteringView, leavingView);
if (tabSwitch) {
direction = 'back';
animation = undefined;
}

const viewsSnapshot = this.views.slice();

let currentNavigation;
const currentNavigation = this.router.getCurrentNavigation();

const router = this.router as any;
/**
* If the navigation action sets `replaceUrl: true` then we need to make sure
* we remove the last item from our views stack
*/
if (currentNavigation?.extras?.replaceUrl && currentNavigation?.trigger !== 'popstate') {
if (this.views.length > 0) {
this.views.splice(-1, 1);
}
}

// Angular >= 7.2.0
if (router.getCurrentNavigation) {
currentNavigation = router.getCurrentNavigation();
// determine direction based on the order of the views in the stack
const leavingView = this.activeView;
const isEnteringViewReused = this.views.includes(enteringView);
const leavingViewIndex = leavingView ? this.views.indexOf(leavingView) : -1;
const enteringViewIndex = isEnteringViewReused ? this.views.indexOf(enteringView) : this.views.length;
const suggestedDirectionBasedOnStackOrder: NavDirection | undefined =
leavingViewIndex === -1 ? undefined : enteringViewIndex < leavingViewIndex ? 'back' : 'forward';

// Angular < 7.2.0
} else if (router.navigations?.value) {
currentNavigation = router.navigations.value;
}
/**
* The user triggered a back navigation on a page that was navigated to with root. In this case, the new page
* becomes the root and the leavingView is removed.
*
* This can happen e.g. when navigating to a page with navigateRoot and then using the browser back button
*/
const isPopStateTransitionFromRootPage =
direction === 'back' && leavingView?.root && currentNavigation?.trigger === 'popstate';

/**
* If the navigation action
* sets `replaceUrl: true`
* then we need to make sure
* we remove the last item
* from our views stack
* whether direction based on stack order takes precedence over direction based on navigation ids
*
* only applied if the user did not explicitly set the direction
* (e.g. via the NavController, routerLink directive etc.)
*/
if (currentNavigation?.extras?.replaceUrl) {
if (this.views.length > 0) {
this.views.splice(-1, 1);
const useDirectionBasedOnStackOrder = isDirectionBasedOnNavigationIds && suggestedDirectionBasedOnStackOrder;

if (isPopStateTransitionFromRootPage) {
direction = 'root';
animation = undefined;

if (leavingViewIndex >= 0) {
this.views.splice(leavingViewIndex, 1);
}
} else if (useDirectionBasedOnStackOrder) {
direction = suggestedDirectionBasedOnStackOrder;
animation = suggestedDirectionBasedOnStackOrder;
}

const tabSwitch = isTabSwitch(enteringView, leavingView);
if (tabSwitch) {
direction = 'back';
animation = undefined;
}

const reused = this.views.includes(enteringView);
const views = this.insertView(enteringView, direction);

// Trigger change detection before transition starts
// This will call ngOnInit() the first time too, just after the view
// was attached to the dom, but BEFORE the transition starts
if (!reused) {
if (!isEnteringViewReused) {
enteringView.ref.changeDetectorRef.detectChanges();
}

Expand Down
Expand Up @@ -14,6 +14,7 @@ export const insertView = (views: RouteView[], view: RouteView, direction: Route

const setRoot = (views: RouteView[], view: RouteView) => {
views = views.filter((v) => v.stackId !== view.stackId);
view.root = true;
views.push(view);
return views;
};
Expand Down Expand Up @@ -110,4 +111,5 @@ export interface RouteView {
savedExtras?: NavigationExtras;
unlistenEvents: () => void;
animationBuilder?: AnimationBuilder;
root?: boolean;
}
8 changes: 6 additions & 2 deletions packages/angular/common/src/providers/nav-controller.ts
Expand Up @@ -37,9 +37,9 @@ export class NavController {
if (router) {
router.events.subscribe((ev) => {
if (ev instanceof NavigationStart) {
// restoredState is set if the browser back/forward button is used
const id = ev.restoredState ? ev.restoredState.navigationId : ev.id;
this.guessDirection = id < this.lastNavId ? 'back' : 'forward';
this.guessAnimation = !ev.restoredState ? this.guessDirection : undefined;
this.guessAnimation = this.guessDirection = id < this.lastNavId ? 'back' : 'forward';
this.lastNavId = this.guessDirection === 'forward' ? ev.id : id;
}
});
Expand Down Expand Up @@ -180,11 +180,14 @@ export class NavController {
direction: RouterDirection;
animation: NavDirection | undefined;
animationBuilder: AnimationBuilder | undefined;
isDirectionBasedOnNavigationIds: boolean;
} {
let direction: RouterDirection = 'root';
let animation: NavDirection | undefined;
const animationBuilder = this.animationBuilder;

const isDirectionBasedOnNavigationIds = this.direction === 'auto';

if (this.direction === 'auto') {
direction = this.guessDirection;
animation = this.guessAnimation;
Expand All @@ -200,6 +203,7 @@ export class NavController {
direction,
animation,
animationBuilder,
isDirectionBasedOnNavigationIds,
};
}

Expand Down