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(router-outlet): improve reliability of swipe back gesture when quickly swiping back #23527

Merged
merged 7 commits into from
Jul 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 41 additions & 8 deletions core/src/components/router-outlet/route-outlet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class RouterOutlet implements ComponentInterface, NavOutlet {
private waitPromise?: Promise<void>;
private gesture?: Gesture;
private ani?: Animation;
private animationEnabled = true;
private gestureOrAnimationInProgress = false;

@Element() el!: HTMLElement;

Expand Down Expand Up @@ -61,17 +61,22 @@ export class RouterOutlet implements ComponentInterface, NavOutlet {
@Event({ bubbles: false }) ionNavDidChange!: EventEmitter<void>;

async connectedCallback() {
const onStart = () => {
this.gestureOrAnimationInProgress = true;
if (this.swipeHandler) {
this.swipeHandler.onStart();
}
}

this.gesture = (await import('../../utils/gesture/swipe-back')).createSwipeBackGesture(
this.el,
() => !!this.swipeHandler && this.swipeHandler.canStart() && this.animationEnabled,
() => this.swipeHandler && this.swipeHandler.onStart(),
() => !this.gestureOrAnimationInProgress && !!this.swipeHandler && this.swipeHandler.canStart(),
() => onStart(),
step => this.ani && this.ani.progressStep(step),
(shouldComplete, step, dur) => {
if (this.ani) {
this.animationEnabled = false;

this.ani.onFinish(() => {
this.animationEnabled = true;
this.gestureOrAnimationInProgress = false;

if (this.swipeHandler) {
this.swipeHandler.onEnd(shouldComplete);
Expand All @@ -97,7 +102,8 @@ export class RouterOutlet implements ComponentInterface, NavOutlet {
}

this.ani.progressEnd(shouldComplete ? 1 : 0, newStepValue, dur);

} else {
this.gestureOrAnimationInProgress = false;
}
}
);
Expand Down Expand Up @@ -191,7 +197,34 @@ export class RouterOutlet implements ComponentInterface, NavOutlet {
leavingEl,
baseEl: el,
progressCallback: (opts.progressAnimation
? ani => this.ani = ani
? ani => {
/**
* 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;
if (this.swipeHandler) {
this.swipeHandler.onEnd(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.ani = ani;
}
}
: undefined
),
...opts,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ describe('Swipe To Go Back', () => {
cy.ionPageVisible('main');
cy.ionNav('ion-item', 'Details');
cy.ionPageVisible('details');
cy.ionPageHidden('main');
cy.ionSwipeToGoBack(true);
cy.ionPageVisible('main');
});
Expand Down