From 2338339da770f2033745d7044687eff6cc95ae07 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Tue, 1 Aug 2023 09:29:35 -0400 Subject: [PATCH] fix(nav): improve reliability of swipe back gesture when quickly swiping back --- core/src/components/nav/nav.tsx | 41 +++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/core/src/components/nav/nav.tsx b/core/src/components/nav/nav.tsx index d6290dee719..6cc7a321f3c 100644 --- a/core/src/components/nav/nav.tsx +++ b/core/src/components/nav/nav.tsx @@ -31,7 +31,7 @@ import { VIEW_STATE_ATTACHED, VIEW_STATE_DESTROYED, VIEW_STATE_NEW, convertToVie export class Nav implements NavOutlet { private transInstr: TransitionInstruction[] = []; private sbAni?: Animation; - private animationEnabled = true; + private gestureOrAnimationInProgress = false; private useRouter = false; private isTransitioning = false; private destroyed = false; @@ -869,7 +869,36 @@ export class Nav implements NavOutlet { // or if it is a portal (modal, actionsheet, etc.) const opts = ti.opts!; - const progressCallback = opts.progressAnimation ? (ani: Animation | undefined) => (this.sbAni = ani) : undefined; + const progressCallback = opts.progressAnimation + ? (ani: Animation | undefined) => { + /** + * Because this progress callback is called asynchronously + * it is possible for the gesture to start and end before + * the animation is ever set. In that scenario, we should + * immediately call progressEnd so that the transition promise + * resolves and the gesture does not get locked up. + */ + if (ani !== undefined && !this.gestureOrAnimationInProgress) { + this.gestureOrAnimationInProgress = true; + ani.onFinish( + () => { + this.gestureOrAnimationInProgress = false; + }, + { oneTimeCallback: true } + ); + + /** + * Playing animation to beginning + * with a duration of 0 prevents + * any flickering when the animation + * is later cleaned up. + */ + ani.progressEnd(0, 0, 0); + } else { + this.sbAni = ani; + } + } + : undefined; const mode = getIonMode(this); const enteringEl = enteringView.element!; const leavingEl = leavingView && leavingView.element!; @@ -1008,15 +1037,16 @@ export class Nav implements NavOutlet { private canStart(): boolean { return ( + !this.gestureOrAnimationInProgress && !!this.swipeGesture && !this.isTransitioning && this.transInstr.length === 0 && - this.animationEnabled && this.canGoBackSync() ); } private onStart() { + this.gestureOrAnimationInProgress = true; this.pop({ direction: 'back', progressAnimation: true }); } @@ -1028,10 +1058,9 @@ export class Nav implements NavOutlet { private onEnd(shouldComplete: boolean, stepValue: number, dur: number) { if (this.sbAni) { - this.animationEnabled = false; this.sbAni.onFinish( () => { - this.animationEnabled = true; + this.gestureOrAnimationInProgress = false; }, { oneTimeCallback: true } ); @@ -1055,6 +1084,8 @@ export class Nav implements NavOutlet { } this.sbAni.progressEnd(shouldComplete ? 1 : 0, newStepValue, dur); + } else { + this.gestureOrAnimationInProgress = false; } }