From 49232f1ca08fde7ff4cc62c60b651946174dd665 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Mon, 13 Jun 2022 13:24:24 -0400 Subject: [PATCH 01/24] fix(react): add base swipe to go back --- .../src/ReactRouter/StackManager.tsx | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index a4f3f072e05..5a73c3a193a 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -25,6 +25,7 @@ export class StackManager extends React.PureComponent; ionRouterOutlet?: React.ReactElement; routerOutletElement: HTMLIonRouterOutletElement | undefined; + skipTransition: boolean; stackContextValue: StackContextState = { registerIonPage: this.registerIonPage.bind(this), @@ -39,6 +40,7 @@ export class StackManager extends React.PureComponent { const config = getConfig(); const swipeEnabled = config && config.get('swipeBackEnabled', routerOutlet.mode === 'ios'); - if (swipeEnabled) { - return this.context.canGoBack(); - } else { - return false; - } + if (!swipeEnabled) return false; + + const enteringViewItem = this.context.findViewItemByRouteInfo({ pathname: this.props.routeInfo.pushedByRoute || '' } as any, this.id); + + return !!enteringViewItem; }; - const onStart = () => { - this.context.goBack(); + const onStart = async () => { + const { routeInfo } = this.props; + const enteringViewItem = this.context.findViewItemByRouteInfo({ pathname: routeInfo.pushedByRoute || '' } as any, this.id); + const leavingViewItem = this.context.findViewItemByRouteInfo(routeInfo, this.id); + + if (enteringViewItem && leavingViewItem) { + await this.transitionPage(routeInfo, enteringViewItem, leavingViewItem, 'back', true); + } + + return Promise.resolve(); }; routerOutlet.swipeHandler = { canStart, @@ -207,14 +217,17 @@ export class StackManager extends React.PureComponent Date: Mon, 13 Jun 2022 18:05:44 +0000 Subject: [PATCH 02/24] chore(): base onEnd implementation --- .../react-router/src/ReactRouter/StackManager.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index 5a73c3a193a..6bcd4442e13 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -207,10 +207,20 @@ export class StackManager extends React.PureComponent { + if (shouldContinue) { + this.skipTransition = true; + + this.context.goBack(); + } else { + return false; + } + } + routerOutlet.swipeHandler = { canStart, onStart, - onEnd: (_shouldContinue) => true, + onEnd }; } From 6f9a5b6f9428ab25407bc23a8a7bb7720600fe64 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Thu, 30 Jun 2022 15:39:01 +0000 Subject: [PATCH 03/24] fix(react-router): hide entering page when aborting a swipe --- .../react-router/src/ReactRouter/StackManager.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index 6bcd4442e13..6b311735a5c 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -189,7 +189,7 @@ export class StackManager extends React.PureComponent { const config = getConfig(); const swipeEnabled = config && config.get('swipeBackEnabled', routerOutlet.mode === 'ios'); - if (!swipeEnabled) return false; + if (!swipeEnabled) { return false; } const enteringViewItem = this.context.findViewItemByRouteInfo({ pathname: this.props.routeInfo.pushedByRoute || '' } as any, this.id); @@ -198,6 +198,7 @@ export class StackManager extends React.PureComponent { const { routeInfo } = this.props; + const enteringViewItem = this.context.findViewItemByRouteInfo({ pathname: routeInfo.pushedByRoute || '' } as any, this.id); const leavingViewItem = this.context.findViewItemByRouteInfo(routeInfo, this.id); @@ -213,7 +214,15 @@ export class StackManager extends React.PureComponent Date: Thu, 30 Jun 2022 15:40:25 +0000 Subject: [PATCH 04/24] fix(react-router): do not duplicate transition when completing swipe back --- packages/react-router/src/ReactRouter/StackManager.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index 6b311735a5c..ba4c806f71f 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -240,6 +240,11 @@ export class StackManager extends React.PureComponent Date: Thu, 30 Jun 2022 15:49:50 +0000 Subject: [PATCH 05/24] fix(react-router): do not hide leaving view if using a gesture --- packages/react-router/src/ReactRouter/StackManager.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index ba4c806f71f..bb254b42aeb 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -275,7 +275,7 @@ export class StackManager extends React.PureComponent Date: Thu, 30 Jun 2022 15:59:15 +0000 Subject: [PATCH 06/24] fix(react-router): correctly hide page when aborting swipe --- packages/react-router/src/ReactRouter/StackManager.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index bb254b42aeb..3a064e91747 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -221,8 +221,12 @@ export class StackManager extends React.PureComponent Date: Thu, 30 Jun 2022 18:46:36 +0000 Subject: [PATCH 07/24] fix(react-router): leaving page is now unmounted after gesture --- packages/react-router/src/ReactRouter/StackManager.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index 3a064e91747..ed10886e5db 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -213,6 +213,13 @@ export class StackManager extends React.PureComponent Date: Thu, 30 Jun 2022 19:26:25 +0000 Subject: [PATCH 08/24] chore(): add comments --- .../src/ReactRouter/StackManager.tsx | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index ed10886e5db..31a9400b1ce 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -202,6 +202,11 @@ export class StackManager extends React.PureComponent { if (shouldContinue) { + /** + * This ensures that going back + * does not cause a duplicate + * page transition. + */ this.skipTransition = true; - this.context.goBack(); + /** + * Unmount the leaving view so + * that it is removed from the DOM. + */ const leavingViewItem = this.context.findViewItemByRouteInfo(this.props.routeInfo, this.id); if (leavingViewItem) { leavingViewItem.mount = false; this.forceUpdate(); } - return true; } else { /** * In the event that the swipe @@ -235,8 +247,6 @@ export class StackManager extends React.PureComponent Date: Thu, 30 Jun 2022 19:29:33 +0000 Subject: [PATCH 09/24] chore(): code clean up --- packages/react-router/src/ReactRouter/StackManager.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index 31a9400b1ce..aae862b2367 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -260,8 +260,8 @@ export class StackManager extends React.PureComponent Date: Thu, 30 Jun 2022 20:10:10 +0000 Subject: [PATCH 10/24] test(react): add tests --- .../cypress/integration/swipe-to-go-back.js | 78 ++++++++++++++++++- packages/react-router/test-app/src/App.tsx | 34 ++++---- .../react-router/test-app/src/pages/Main.tsx | 5 +- .../test-app/src/pages/tabs/Tabs.tsx | 2 +- 4 files changed, 99 insertions(+), 20 deletions(-) diff --git a/packages/react-router/test-app/cypress/integration/swipe-to-go-back.js b/packages/react-router/test-app/cypress/integration/swipe-to-go-back.js index 973ec70da4c..45e7b2a90fa 100644 --- a/packages/react-router/test-app/cypress/integration/swipe-to-go-back.js +++ b/packages/react-router/test-app/cypress/integration/swipe-to-go-back.js @@ -5,13 +5,87 @@ describe('Swipe To Go Back', () => { This spec tests that swipe to go back works */ - it('/swipe-to-go-back, ', () => { + it('should swipe and abort', () => { cy.visit(`http://localhost:${port}/swipe-to-go-back`); cy.ionPageVisible('main'); + + cy.ionNav('ion-item', 'Details'); + cy.ionPageVisible('details'); + cy.ionPageHidden('main'); + + cy.ionSwipeToGoBack(false, 'ion-router-outlet#swipe-to-go-back'); + cy.ionPageVisible('details'); + cy.ionPageHidden('main'); + }); + + it('should swipe and go back', () => { + cy.visit(`http://localhost:${port}/swipe-to-go-back`); + cy.ionPageVisible('main'); + cy.ionNav('ion-item', 'Details'); cy.ionPageVisible('details'); cy.ionPageHidden('main'); - cy.ionSwipeToGoBack(true); + + cy.ionSwipeToGoBack(true, 'ion-router-outlet#swipe-to-go-back'); cy.ionPageVisible('main'); }); + + it('should swipe and abort within a tab', () => { + cy.visit(`http://localhost:${port}/tabs/tab1`); + cy.ionPageVisible('tab1'); + + cy.get('#child-one').click(); + cy.ionPageHidden('tab1'); + cy.ionPageVisible('tab1child1'); + + cy.ionSwipeToGoBack(false, 'ion-tabs ion-router-outlet'); + + cy.ionPageHidden('tab1'); + cy.ionPageVisible('tab1child1') + }); + + it('should swipe and go back within a tab', () => { + cy.visit(`http://localhost:${port}/tabs/tab1`); + cy.ionPageVisible('tab1'); + + cy.get('#child-one').click(); + cy.ionPageHidden('tab1'); + cy.ionPageVisible('tab1child1'); + + cy.ionSwipeToGoBack(true, 'ion-tabs ion-router-outlet'); + + cy.ionPageVisible('tab1'); + cy.ionPageDoesNotExist('tab1child1') + }); + + it('should swipe and go back to correct tab after switching tabs', () => { + cy.visit(`http://localhost:${port}`); + cy.ionPageVisible('home'); + + cy.get('#go-to-tabs').click(); + cy.ionPageHidden('home'); + cy.ionPageVisible('tab1'); + cy.ionPageVisible('tabs'); + + cy.get('#child-one').click(); + cy.ionPageHidden('tab1'); + cy.ionPageVisible('tab1child1'); + + cy.get('ion-tab-button#tab-button-tab2').click(); + cy.ionPageVisible('tab2'); + cy.ionPageHidden('tab1child1'); + + cy.get('ion-tab-button#tab-button-tab1').click(); + cy.ionPageVisible('tab1child1'); + cy.ionPageHidden('tab2'); + + cy.ionSwipeToGoBack(true, 'ion-tabs ion-router-outlet'); + + cy.ionPageVisible('tab1'); + cy.ionPageDoesNotExist('tab1child1'); + + cy.ionSwipeToGoBack(true, 'ion-tabs ion-router-outlet'); + cy.ionPageVisible('home'); + cy.ionPageDoesNotExist('tabs'); + }); }); diff --git a/packages/react-router/test-app/src/App.tsx b/packages/react-router/test-app/src/App.tsx index 617bbf09af3..09014c0d445 100644 --- a/packages/react-router/test-app/src/App.tsx +++ b/packages/react-router/test-app/src/App.tsx @@ -1,4 +1,4 @@ -import { IonApp, setupIonicReact } from '@ionic/react'; +import { IonApp, setupIonicReact, IonRouterOutlet } from '@ionic/react'; import React from 'react'; import { Route } from 'react-router-dom'; @@ -43,21 +43,23 @@ const App: React.FC = () => { return ( - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + ); diff --git a/packages/react-router/test-app/src/pages/Main.tsx b/packages/react-router/test-app/src/pages/Main.tsx index 6075da45f51..2f4f99c7d7b 100644 --- a/packages/react-router/test-app/src/pages/Main.tsx +++ b/packages/react-router/test-app/src/pages/Main.tsx @@ -14,7 +14,7 @@ interface MainProps {} const Main: React.FC = () => { return ( - + Main @@ -58,6 +58,9 @@ const Main: React.FC = () => { Refs + + Tabs + diff --git a/packages/react-router/test-app/src/pages/tabs/Tabs.tsx b/packages/react-router/test-app/src/pages/tabs/Tabs.tsx index f6a2fa3ab02..c74aed1ca9f 100644 --- a/packages/react-router/test-app/src/pages/tabs/Tabs.tsx +++ b/packages/react-router/test-app/src/pages/tabs/Tabs.tsx @@ -22,7 +22,7 @@ interface TabsProps {} const Tabs: React.FC = () => { return ( - + From 973f14ddacbba3715ec969e53f92f51f793f218b Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Fri, 1 Jul 2022 13:20:27 +0000 Subject: [PATCH 11/24] fix(react): fix duration animation --- packages/react-router/src/ReactRouter/StackManager.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index aae862b2367..069b543cae2 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -280,6 +280,7 @@ export class StackManager extends React.PureComponent Date: Fri, 1 Jul 2022 23:55:49 +0000 Subject: [PATCH 12/24] fix(react-router): improve reliability of fix --- .../src/ReactRouter/StackManager.tsx | 60 ++++++++----------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index 069b543cae2..a0e0b0dffc0 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -25,7 +25,7 @@ export class StackManager extends React.PureComponent; ionRouterOutlet?: React.ReactElement; routerOutletElement: HTMLIonRouterOutletElement | undefined; - skipTransition: boolean; + prevProps?: StackManagerProps; stackContextValue: StackContextState = { registerIonPage: this.registerIonPage.bind(this), @@ -40,7 +40,7 @@ export class StackManager extends React.PureComponent { const { routeInfo } = this.props; - const enteringViewItem = this.context.findViewItemByRouteInfo({ pathname: routeInfo.pushedByRoute || '' } as any, this.id); + const propsToUse = (this.prevProps && this.prevProps.routeInfo.pathname === routeInfo.pushedByRoute) ? this.prevProps.routeInfo : { pathname: routeInfo.pushedByRoute || '' } as any; + const enteringViewItem = this.context.findViewItemByRouteInfo(propsToUse, this.id); const leavingViewItem = this.context.findViewItemByRouteInfo(routeInfo, this.id); /** @@ -215,23 +232,7 @@ export class StackManager extends React.PureComponent { if (shouldContinue) { - /** - * This ensures that going back - * does not cause a duplicate - * page transition. - */ - this.skipTransition = true; this.context.goBack(); - - /** - * Unmount the leaving view so - * that it is removed from the DOM. - */ - const leavingViewItem = this.context.findViewItemByRouteInfo(this.props.routeInfo, this.id); - if (leavingViewItem) { - leavingViewItem.mount = false; - this.forceUpdate(); - } } else { /** * In the event that the swipe @@ -239,7 +240,9 @@ export class StackManager extends React.PureComponent Date: Sat, 2 Jul 2022 00:12:27 +0000 Subject: [PATCH 13/24] fix(react-router): improve unmounting logic --- packages/react-router/src/ReactRouter/StackManager.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index a0e0b0dffc0..5b3f8cb34ca 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -232,7 +232,15 @@ export class StackManager extends React.PureComponent { if (shouldContinue) { + const { routeInfo } = this.props; + const leavingViewItem = this.context.findViewItemByRouteInfo(routeInfo, this.id); + if (leavingViewItem) { + leavingViewItem.mount = false; + this.forceUpdate(); + } + this.context.goBack(); + } else { /** * In the event that the swipe From 38d7fa59bb81a8d1f58622638bfb652d4465e326 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Sat, 2 Jul 2022 00:40:16 +0000 Subject: [PATCH 14/24] test(react-router): test swiping in tabs mulitple times --- .../cypress/integration/swipe-to-go-back.js | 32 +++++++++++++++++++ .../test-app/src/pages/tabs/Tabs.tsx | 20 ++++++++++++ 2 files changed, 52 insertions(+) diff --git a/packages/react-router/test-app/cypress/integration/swipe-to-go-back.js b/packages/react-router/test-app/cypress/integration/swipe-to-go-back.js index 45e7b2a90fa..e50ae59c4fc 100644 --- a/packages/react-router/test-app/cypress/integration/swipe-to-go-back.js +++ b/packages/react-router/test-app/cypress/integration/swipe-to-go-back.js @@ -88,4 +88,36 @@ describe('Swipe To Go Back', () => { cy.ionPageVisible('home'); cy.ionPageDoesNotExist('tabs'); }); + + it('should be able to swipe back from child tab page after visiting', () => { + cy.visit(`http://localhost:${port}/tabs/tab1`); + cy.ionPageVisible('tab1'); + + cy.get('#child-one').click(); + cy.ionPageHidden('tab1'); + cy.ionPageVisible('tab1child1'); + + cy.get('#child-two').click(); + cy.ionPageHidden('tab1child1'); + cy.ionPageVisible('tab1child2'); + + cy.ionSwipeToGoBack(true, 'ion-tabs ion-router-outlet'); + + cy.ionPageDoesNotExist('tab1child2'); + cy.ionPageVisible('tab1child1'); + + cy.ionSwipeToGoBack(true, 'ion-tabs ion-router-outlet'); + + cy.ionPageDoesNotExist('tab1child1'); + cy.ionPageVisible('tab1'); + + cy.get('#child-one').click(); + cy.ionPageHidden('tab1'); + cy.ionPageVisible('tab1child1'); + + cy.ionSwipeToGoBack(true, 'ion-tabs ion-router-outlet'); + + cy.ionPageDoesNotExist('tab1child1'); + cy.ionPageVisible('tab1'); + }) }); diff --git a/packages/react-router/test-app/src/pages/tabs/Tabs.tsx b/packages/react-router/test-app/src/pages/tabs/Tabs.tsx index c74aed1ca9f..d27907821d9 100644 --- a/packages/react-router/test-app/src/pages/tabs/Tabs.tsx +++ b/packages/react-router/test-app/src/pages/tabs/Tabs.tsx @@ -27,6 +27,7 @@ const Tabs: React.FC = () => { + @@ -71,7 +72,26 @@ const Tab1Child1 = () => { + Tab 1 Child 1 + Go to Tab1Child2 + + + ); +}; +const Tab1Child2 = () => { + return ( + + + + + + + Tab1 + + + + Tab 1 Child 2 ); From 2f349fcd375b77d4482276f732fda57863380975 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Sat, 2 Jul 2022 00:45:07 +0000 Subject: [PATCH 15/24] fix(react-router): avoid flicker when unmounting --- .../src/ReactRouter/StackManager.tsx | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index 5b3f8cb34ca..44a941516f3 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -232,15 +232,27 @@ export class StackManager extends React.PureComponent { if (shouldContinue) { - const { routeInfo } = this.props; + /** + * Note: We must call goBack() + * before unmounting the leaving view + * otherwise users will get a blank + * screen briefly. Since this.props.routeInfo + * will have the latest routeInfo, we need + * to take a copy of the old routeInfo + * so that we can unmount the correct view. + * Otherwise, we will end up unmounting the + * view that just transitioned in. + */ + const routeInfo = { ...this.props.routeInfo }; + + this.context.goBack(); + const leavingViewItem = this.context.findViewItemByRouteInfo(routeInfo, this.id); if (leavingViewItem) { leavingViewItem.mount = false; this.forceUpdate(); } - this.context.goBack(); - } else { /** * In the event that the swipe From c589215100227885bc1f19575f0007d3eec4464e Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Sat, 2 Jul 2022 01:23:25 +0000 Subject: [PATCH 16/24] chore(): bandaid for flaky test --- packages/react-router/test-app/cypress/support/commands.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-router/test-app/cypress/support/commands.js b/packages/react-router/test-app/cypress/support/commands.js index 8b9fc58cba9..fd0168ef734 100644 --- a/packages/react-router/test-app/cypress/support/commands.js +++ b/packages/react-router/test-app/cypress/support/commands.js @@ -102,7 +102,7 @@ Cypress.Commands.add('ionMenuNav', (contains) => { Cypress.Commands.add('ionTabClick', (tabText) => { // TODO: figure out how to get rid of this wait. Switching tabs after a forward nav to a details page needs it - cy.wait(250); + cy.wait(500); cy.contains('ion-tab-button', tabText).click({ force: true }); // cy.get('ion-tab-button.tab-selected').contains(tabText) }); @@ -126,4 +126,4 @@ Cypress.Commands.add('ionMenuClick', () => { Cypress.Commands.add('ionHardwareBackEvent', () => { cy.document().trigger('backbutton'); -}); \ No newline at end of file +}); From 75a6f30cbbdde7d2aba7b5eea6296b5922e5c17e Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Tue, 5 Jul 2022 19:52:56 +0000 Subject: [PATCH 17/24] fix(react-router): avoid flickering with swipe to go back --- .../src/ReactRouter/StackManager.tsx | 42 +++++++------------ 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index 44a941516f3..59d146a5f2a 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -26,6 +26,7 @@ export class StackManager extends React.PureComponent { if (shouldContinue) { - /** - * Note: We must call goBack() - * before unmounting the leaving view - * otherwise users will get a blank - * screen briefly. Since this.props.routeInfo - * will have the latest routeInfo, we need - * to take a copy of the old routeInfo - * so that we can unmount the correct view. - * Otherwise, we will end up unmounting the - * view that just transitioned in. - */ - const routeInfo = { ...this.props.routeInfo }; + this.skipTransition = true; this.context.goBack(); - - const leavingViewItem = this.context.findViewItemByRouteInfo(routeInfo, this.id); - if (leavingViewItem) { - leavingViewItem.mount = false; - this.forceUpdate(); - } - } else { /** * In the event that the swipe @@ -286,6 +263,17 @@ export class StackManager extends React.PureComponent Date: Tue, 5 Jul 2022 21:00:45 +0000 Subject: [PATCH 18/24] fix(react-router): ensure leaving view is unmounted without a flicker --- .../src/ReactRouter/StackManager.tsx | 61 ++++++++++++------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index 59d146a5f2a..71e6abd729f 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -263,15 +263,44 @@ export class StackManager extends React.PureComponent { + const skipTransition = this.skipTransition; + + /** + * If the transition was handled + * via the swipe to go back gesture, + * then we do not want to perform + * another transition. + * + * We skip adding ion-page or ion-page-invisible + * because the entering view already exists in the DOM. + * If we added the classes, there would be a flicker where + * the view would be briefly hidden. + */ + if (skipTransition) { + /** + * We need to reset skipTransition before + * we call routerOutlet.commit otherwise + * the transition triggered by the swipe + * to go back gesture would reset it. In + * that case you would see a duplicate + * transition triggered by handlePageTransition + * in componentDidUpdate. + */ + this.skipTransition = false; + } else { + enteringEl.classList.add('ion-page'); + enteringEl.classList.add('ion-page-invisible'); + } + + await routerOutlet.commit(enteringEl, leavingEl, { + deepWait: true, + duration: skipTransition || directionToUse === undefined ? 0 : undefined, + direction: directionToUse, + showGoBack: !!routeInfo.pushedByRoute, + progressAnimation, + animationBuilder: routeInfo.routeAnimation, + }); } const routerOutlet = this.routerOutletElement!; @@ -310,20 +339,6 @@ export class StackManager extends React.PureComponent Date: Thu, 7 Jul 2022 21:56:10 +0000 Subject: [PATCH 19/24] fix(react-router): do not update view item match in swipe gesture --- .../src/ReactRouter/ReactRouterViewStack.tsx | 4 ++-- packages/react-router/src/ReactRouter/StackManager.tsx | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/react-router/src/ReactRouter/ReactRouterViewStack.tsx b/packages/react-router/src/ReactRouter/ReactRouterViewStack.tsx index b5fb9f843d0..c117e0bd62a 100644 --- a/packages/react-router/src/ReactRouter/ReactRouterViewStack.tsx +++ b/packages/react-router/src/ReactRouter/ReactRouterViewStack.tsx @@ -107,9 +107,9 @@ export class ReactRouterViewStack extends ViewStacks { return children; } - findViewItemByRouteInfo(routeInfo: RouteInfo, outletId?: string) { + findViewItemByRouteInfo(routeInfo: RouteInfo, outletId?: string, updateMatch = true) { const { viewItem, match } = this.findViewItemByPath(routeInfo.pathname, outletId); - if (viewItem && match) { + if (updateMatch && viewItem && match) { viewItem.routeData.match = match; } return viewItem; diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index 71e6abd729f..219978784a3 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -202,17 +202,17 @@ export class StackManager extends React.PureComponent { const { routeInfo } = this.props; const propsToUse = (this.prevProps && this.prevProps.routeInfo.pathname === routeInfo.pushedByRoute) ? this.prevProps.routeInfo : { pathname: routeInfo.pushedByRoute || '' } as any; - const enteringViewItem = this.context.findViewItemByRouteInfo(propsToUse, this.id); - const leavingViewItem = this.context.findViewItemByRouteInfo(routeInfo, this.id); + const enteringViewItem = this.context.findViewItemByRouteInfo(propsToUse, this.id, false); + const leavingViewItem = this.context.findViewItemByRouteInfo(routeInfo, this.id, false); /** * When the gesture starts, kick off @@ -239,7 +239,7 @@ export class StackManager extends React.PureComponent Date: Thu, 7 Jul 2022 22:02:20 +0000 Subject: [PATCH 20/24] chore(): fix types --- .../react-router/src/ReactRouter/ReactRouterViewStack.tsx | 5 +++-- packages/react/src/routing/RouteManagerContext.ts | 2 +- packages/react/src/routing/ViewStacks.ts | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/react-router/src/ReactRouter/ReactRouterViewStack.tsx b/packages/react-router/src/ReactRouter/ReactRouterViewStack.tsx index c117e0bd62a..798cd582281 100644 --- a/packages/react-router/src/ReactRouter/ReactRouterViewStack.tsx +++ b/packages/react-router/src/ReactRouter/ReactRouterViewStack.tsx @@ -107,9 +107,10 @@ export class ReactRouterViewStack extends ViewStacks { return children; } - findViewItemByRouteInfo(routeInfo: RouteInfo, outletId?: string, updateMatch = true) { + findViewItemByRouteInfo(routeInfo: RouteInfo, outletId?: string, updateMatch?: boolean) { const { viewItem, match } = this.findViewItemByPath(routeInfo.pathname, outletId); - if (updateMatch && viewItem && match) { + const shouldUpdateMatch = updateMatch === undefined || updateMatch === true; + if (shouldUpdateMatch && viewItem && match) { viewItem.routeData.match = match; } return viewItem; diff --git a/packages/react/src/routing/RouteManagerContext.ts b/packages/react/src/routing/RouteManagerContext.ts index 044fd7de8b0..a37eb22bb2e 100644 --- a/packages/react/src/routing/RouteManagerContext.ts +++ b/packages/react/src/routing/RouteManagerContext.ts @@ -16,7 +16,7 @@ export interface RouteManagerContextState { ) => ViewItem; findViewItemByPathname(pathname: string, outletId?: string): ViewItem | undefined; findLeavingViewItemByRouteInfo: (routeInfo: RouteInfo, outletId?: string) => ViewItem | undefined; - findViewItemByRouteInfo: (routeInfo: RouteInfo, outletId?: string) => ViewItem | undefined; + findViewItemByRouteInfo: (routeInfo: RouteInfo, outletId?: string, updateMatch?: boolean) => ViewItem | undefined; getChildrenToRender: ( outletId: string, ionRouterOutlet: React.ReactElement, diff --git a/packages/react/src/routing/ViewStacks.ts b/packages/react/src/routing/ViewStacks.ts index 6bcbc207875..59e6afe9612 100644 --- a/packages/react/src/routing/ViewStacks.ts +++ b/packages/react/src/routing/ViewStacks.ts @@ -65,7 +65,7 @@ export abstract class ViewStacks { page?: HTMLElement ): ViewItem; abstract findViewItemByPathname(pathname: string, outletId?: string): ViewItem | undefined; - abstract findViewItemByRouteInfo(routeInfo: RouteInfo, outletId?: string): ViewItem | undefined; + abstract findViewItemByRouteInfo(routeInfo: RouteInfo, outletId?: string, updateMatch?: boolean): ViewItem | undefined; abstract findLeavingViewItemByRouteInfo( routeInfo: RouteInfo, outletId?: string From 4fdde4dc5eb0dae8bafe1f56e7309fcbc35036de Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Thu, 7 Jul 2022 22:14:38 +0000 Subject: [PATCH 21/24] fix(react-router): do not swipe back to instance of same page, do not swipe back to root url --- .../src/ReactRouter/StackManager.tsx | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index 219978784a3..5b83d448b48 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -204,7 +204,26 @@ export class StackManager extends React.PureComponent { From 422955e83f312b1cce16a06e64a0016457fc71a5 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Thu, 7 Jul 2022 22:17:44 +0000 Subject: [PATCH 22/24] test(react-router): add another test --- .../test-app/cypress/integration/swipe-to-go-back.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/react-router/test-app/cypress/integration/swipe-to-go-back.js b/packages/react-router/test-app/cypress/integration/swipe-to-go-back.js index e50ae59c4fc..4bf7f472fa9 100644 --- a/packages/react-router/test-app/cypress/integration/swipe-to-go-back.js +++ b/packages/react-router/test-app/cypress/integration/swipe-to-go-back.js @@ -120,4 +120,12 @@ describe('Swipe To Go Back', () => { cy.ionPageDoesNotExist('tab1child1'); cy.ionPageVisible('tab1'); }) + + it('should not swipe to go back to the same view you are on', () => { + cy.visit(`http://localhost:${port}`); + cy.ionPageVisible('home'); + + cy.ionSwipeToGoBack(false); + cy.ionPageVisible('home'); + }) }); From 9fa6e35d1ccb23b328e5b1f19fdf0070e4d19edd Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Fri, 8 Jul 2022 15:39:33 +0000 Subject: [PATCH 23/24] fix(react-router): do not hide parameterized views --- .../react-router/src/ReactRouter/StackManager.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index 5b83d448b48..01c2b3641c2 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -259,8 +259,21 @@ export class StackManager extends React.PureComponent Date: Fri, 8 Jul 2022 15:51:38 +0000 Subject: [PATCH 24/24] test(react-router): add another test --- .../cypress/integration/swipe-to-go-back.js | 12 ++++++ packages/react-router/test-app/src/App.tsx | 2 + .../react-router/test-app/src/pages/Main.tsx | 3 ++ .../test-app/src/pages/params/Params.tsx | 41 +++++++++++++++++++ 4 files changed, 58 insertions(+) create mode 100644 packages/react-router/test-app/src/pages/params/Params.tsx diff --git a/packages/react-router/test-app/cypress/integration/swipe-to-go-back.js b/packages/react-router/test-app/cypress/integration/swipe-to-go-back.js index 4bf7f472fa9..f5b4ac89254 100644 --- a/packages/react-router/test-app/cypress/integration/swipe-to-go-back.js +++ b/packages/react-router/test-app/cypress/integration/swipe-to-go-back.js @@ -128,4 +128,16 @@ describe('Swipe To Go Back', () => { cy.ionSwipeToGoBack(false); cy.ionPageVisible('home'); }) + + it('should not hide a parameterized page when swiping and aborting', () => { + cy.visit(`http://localhost:${port}/params/0`); + cy.ionPageVisible('params-0'); + + cy.get('#next-page').click(); + cy.ionPageVisible('params-1'); + + cy.ionSwipeToGoBack(false); + + cy.ionPageVisible('params-1'); + }) }); diff --git a/packages/react-router/test-app/src/App.tsx b/packages/react-router/test-app/src/App.tsx index 09014c0d445..3a2314f285f 100644 --- a/packages/react-router/test-app/src/App.tsx +++ b/packages/react-router/test-app/src/App.tsx @@ -36,6 +36,7 @@ import Refs from './pages/refs/Refs'; import DynamicIonpageClassnames from './pages/dynamic-ionpage-classnames/DynamicIonpageClassnames'; import Tabs from './pages/tabs/Tabs'; import TabsSecondary from './pages/tabs/TabsSecondary'; +import Params from './pages/params/Params'; setupIonicReact(); @@ -59,6 +60,7 @@ const App: React.FC = () => { + diff --git a/packages/react-router/test-app/src/pages/Main.tsx b/packages/react-router/test-app/src/pages/Main.tsx index 2f4f99c7d7b..83c882d0eb1 100644 --- a/packages/react-router/test-app/src/pages/Main.tsx +++ b/packages/react-router/test-app/src/pages/Main.tsx @@ -61,6 +61,9 @@ const Main: React.FC = () => { Tabs + + Params + diff --git a/packages/react-router/test-app/src/pages/params/Params.tsx b/packages/react-router/test-app/src/pages/params/Params.tsx new file mode 100644 index 00000000000..18f49d45888 --- /dev/null +++ b/packages/react-router/test-app/src/pages/params/Params.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { + IonButtons, + IonBackButton, + IonButton, + IonContent, + IonHeader, + IonPage, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { RouteComponentProps } from 'react-router'; + +interface PageProps +extends RouteComponentProps<{ + id: string; +}> {} + + +const Page: React.FC = ({ match }) => { + const parseID = parseInt(match.params.id); + return ( + + + + Params { match.params.id } + + + + + + + Go to next param +
+ Page ID: { match.params.id } +
+
+ ); +}; + +export default Page;