From 18e3a16299eb79df2edd6a425ee758679a832b70 Mon Sep 17 00:00:00 2001 From: Trevor Farlow <trevdor@users.noreply.github.com> Date: Sat, 20 May 2023 16:01:10 -0600 Subject: [PATCH] Update to React Router v5.1 conventions (#1045) --- .../src/components/FinancesApp.js | 183 ++++++------- .../desktop-client/src/components/Modals.js | 246 +++++++----------- .../desktop-client/src/components/Titlebar.js | 84 +++--- .../src/components/accounts/Account.js | 26 +- .../src/components/accounts/MobileAccount.js | 8 +- .../src/components/manager/ManagementApp.js | 54 ++-- .../src/components/modals/ManageRulesModal.js | 2 +- .../src/components/reports/index.js | 12 +- upcoming-release-notes/1045.md | 6 + 9 files changed, 267 insertions(+), 354 deletions(-) create mode 100644 upcoming-release-notes/1045.md diff --git a/packages/desktop-client/src/components/FinancesApp.js b/packages/desktop-client/src/components/FinancesApp.js index 6ec499df2..74e592dd7 100644 --- a/packages/desktop-client/src/components/FinancesApp.js +++ b/packages/desktop-client/src/components/FinancesApp.js @@ -57,118 +57,82 @@ import PostsOfflineNotification from './schedules/PostsOfflineNotification'; import Settings from './settings'; import Titlebar, { TitlebarProvider } from './Titlebar'; -function PageRoute({ - path, - component: Component, - redirectTo = '/budget', - worksInNarrow = true, -}) { +function NarrowNotSupported({ children, redirectTo = '/budget' }) { const { isNarrowWidth } = useResponsive(); - return worksInNarrow || !isNarrowWidth ? ( - <Route - path={path} - children={props => { - return ( - <View - style={{ - flex: 1, - display: props.match ? 'flex' : 'none', - }} - > - <Component {...props} /> - </View> - ); - }} - /> - ) : ( - <Redirect to={redirectTo} /> - ); -} - -function NonPageRoute({ - redirectTo = '/budget', - worksInNarrow = true, - ...routeProps -}) { - const { isNarrowWidth } = useResponsive(); - - return worksInNarrow || !isNarrowWidth ? ( - <Route {...routeProps} /> - ) : ( - <Redirect to={redirectTo} /> - ); + return isNarrowWidth ? <Redirect to={redirectTo} /> : children; } function Routes({ location }) { const { isNarrowWidth } = useResponsive(); return ( <Switch location={location}> - <Redirect from="/" exact to="/budget" /> - - <PageRoute path="/reports" component={Reports} worksInNarrow={false} /> - - <PageRoute - path="/budget" - component={isNarrowWidth ? MobileBudget : Budget} - /> - - <NonPageRoute - path="/schedules" - exact - component={Schedules} - worksInNarrow={false} - /> - <NonPageRoute - path="/schedule/edit" - exact - component={EditSchedule} - worksInNarrow={false} - /> - <NonPageRoute - path="/schedule/edit/:id" - component={EditSchedule} - worksInNarrow={false} - /> - <NonPageRoute - path="/schedule/link" - component={LinkSchedule} - worksInNarrow={false} - /> - <NonPageRoute - path="/schedule/discover" - component={DiscoverSchedules} - worksInNarrow={false} - /> - <NonPageRoute - path="/schedule/posts-offline-notification" - component={PostsOfflineNotification} - /> - - <NonPageRoute path="/payees" exact component={ManagePayeesPage} /> - <NonPageRoute path="/rules" exact component={ManageRulesPage} /> - <NonPageRoute path="/settings" component={Settings} /> - <NonPageRoute - path="/nordigen/link" - exact - component={NordigenLink} - worksInNarrow={false} - /> - - <NonPageRoute - path="/accounts/:id" - exact - children={props => { + <Route path="/" exact render={() => <Redirect to="/budget" />} /> + + <Route path="/reports"> + <NarrowNotSupported> + <Reports /> + </NarrowNotSupported> + </Route> + + <Route path="/budget"> + {isNarrowWidth ? <MobileBudget /> : <Budget />} + </Route> + + <Route path="/schedules" exact> + <NarrowNotSupported> + <Schedules /> + </NarrowNotSupported> + </Route> + <Route path="/schedule/edit" exact> + <NarrowNotSupported> + <EditSchedule /> + </NarrowNotSupported> + </Route> + <Route path="/schedule/edit/:id"> + <NarrowNotSupported> + <EditSchedule /> + </NarrowNotSupported> + </Route> + <Route path="/schedule/link"> + <NarrowNotSupported> + <LinkSchedule /> + </NarrowNotSupported> + </Route> + <Route path="/schedule/discover"> + <NarrowNotSupported> + <DiscoverSchedules /> + </NarrowNotSupported> + </Route> + <Route path="/schedule/posts-offline-notification"> + <PostsOfflineNotification /> + </Route> + + <Route path="/payees" exact> + <ManagePayeesPage /> + </Route> + <Route path="/rules" exact> + <ManageRulesPage /> + </Route> + <Route path="/settings"> + <Settings /> + </Route> + <Route path="/nordigen/link" exact> + <NarrowNotSupported> + <NordigenLink /> + </NarrowNotSupported> + </Route> + + <Route path="/accounts/:id" exact> + {props => { const AcctCmp = isNarrowWidth ? MobileAccount : Account; return ( props.match && <AcctCmp key={props.match.params.id} {...props} /> ); }} - /> - <NonPageRoute - path="/accounts" - exact - component={isNarrowWidth ? MobileAccounts : Account} - /> + </Route> + <Route path="/accounts" exact> + {isNarrowWidth ? <MobileAccounts /> : <Account />} + </Route> </Switch> ); } @@ -327,13 +291,10 @@ function FinancesApp(props) { <View style={{ flexDirection: 'row', flex: 1 }}> <FloatableSidebar /> - <div + <View style={{ flex: 1, - display: 'flex', - flexDirection: 'column', overflow: 'hidden', - position: 'relative', width: '100%', }} > @@ -362,11 +323,17 @@ function FinancesApp(props) { </div> <Switch> - <Route path="/budget" component={MobileNavTabs} /> - <Route path="/accounts" component={MobileNavTabs} /> - <Route path="/settings" component={MobileNavTabs} /> + <Route path="/budget"> + <MobileNavTabs /> + </Route> + <Route path="/accounts"> + <MobileNavTabs /> + </Route> + <Route path="/settings"> + <MobileNavTabs /> + </Route> </Switch> - </div> + </View> </View> </View> </CompatRouter> diff --git a/packages/desktop-client/src/components/Modals.js b/packages/desktop-client/src/components/Modals.js index 46df9f5b2..c18eeb859 100644 --- a/packages/desktop-client/src/components/Modals.js +++ b/packages/desktop-client/src/components/Modals.js @@ -112,164 +112,110 @@ function Modals({ /> </Route> - <Route - path="/confirm-category-delete" - render={() => { - const { category, group, onDelete } = options; - return ( - <ConfirmCategoryDelete - modalProps={modalProps} - actions={actions} - category={categories.find(c => c.id === category)} - group={categoryGroups.find(g => g.id === group)} - categoryGroups={categoryGroups} - onDelete={onDelete} - /> - ); - }} - /> + <Route path="/confirm-category-delete"> + <ConfirmCategoryDelete + modalProps={modalProps} + actions={actions} + category={categories.find(c => c.id === options.category)} + group={categoryGroups.find(g => g.id === options.group)} + categoryGroups={categoryGroups} + onDelete={options.onDelete} + /> + </Route> - <Route - path="/load-backup" - render={() => ( - <LoadBackup - watchUpdates - budgetId={budgetId} - modalProps={modalProps} - actions={actions} - /> - )} - /> + <Route path="/load-backup"> + <LoadBackup + watchUpdates + budgetId={budgetId} + modalProps={modalProps} + actions={actions} + /> + </Route> - <Route - path="/manage-rules" - render={() => { - return ( - <ManageRulesModal - history={history} - modalProps={modalProps} - payeeId={options.payeeId} - /> - ); - }} - /> + <Route path="/manage-rules"> + <ManageRulesModal + history={history} + modalProps={modalProps} + payeeId={options.payeeId} + /> + </Route> - <Route - path="/edit-rule" - render={() => { - return ( - <EditRule - history={history} - modalProps={modalProps} - defaultRule={options.rule} - onSave={options.onSave} - /> - ); - }} - /> + <Route path="/edit-rule"> + <EditRule + history={history} + modalProps={modalProps} + defaultRule={options.rule} + onSave={options.onSave} + /> + </Route> - <Route - path="/merge-unused-payees" - render={() => { - return ( - <MergeUnusedPayees - history={history} - modalProps={modalProps} - payeeIds={options.payeeIds} - targetPayeeId={options.targetPayeeId} - /> - ); - }} - /> + <Route path="/merge-unused-payees"> + <MergeUnusedPayees + history={history} + modalProps={modalProps} + payeeIds={options.payeeIds} + targetPayeeId={options.targetPayeeId} + /> + </Route> - <Route - path="/plaid-external-msg" - render={() => { - return ( - <PlaidExternalMsg - modalProps={modalProps} - actions={actions} - onMoveExternal={options.onMoveExternal} - onClose={() => { - options.onClose && options.onClose(); - send('poll-web-token-stop'); - }} - onSuccess={options.onSuccess} - /> - ); - }} - /> - <Route - path="/nordigen-init" - render={() => { - return ( - <NordigenInitialise - modalProps={modalProps} - onSuccess={options.onSuccess} - /> - ); - }} - /> - <Route - path="/nordigen-external-msg" - render={() => { - return ( - <NordigenExternalMsg - modalProps={modalProps} - actions={actions} - onMoveExternal={options.onMoveExternal} - onClose={() => { - options.onClose && options.onClose(); - send('nordigen-poll-web-token-stop'); - }} - onSuccess={options.onSuccess} - /> - ); - }} - /> + <Route path="/plaid-external-msg"> + <PlaidExternalMsg + modalProps={modalProps} + actions={actions} + onMoveExternal={options.onMoveExternal} + onClose={() => { + options.onClose && options.onClose(); + send('poll-web-token-stop'); + }} + onSuccess={options.onSuccess} + /> + </Route> + <Route path="/nordigen-init"> + <NordigenInitialise + modalProps={modalProps} + onSuccess={options.onSuccess} + /> + </Route> + <Route path="/nordigen-external-msg"> + <NordigenExternalMsg + modalProps={modalProps} + actions={actions} + onMoveExternal={options.onMoveExternal} + onClose={() => { + options.onClose && options.onClose(); + send('nordigen-poll-web-token-stop'); + }} + onSuccess={options.onSuccess} + /> + </Route> - <Route - path="/create-encryption-key" - render={() => { - return ( - <CreateEncryptionKey - key={name} - modalProps={modalProps} - actions={actions} - options={options} - /> - ); - }} - /> + <Route path="/create-encryption-key"> + <CreateEncryptionKey + key={name} + modalProps={modalProps} + actions={actions} + options={options} + /> + </Route> - <Route - path="/fix-encryption-key" - render={() => { - return ( - <FixEncryptionKey - key={name} - modalProps={modalProps} - actions={actions} - options={options} - /> - ); - }} - /> + <Route path="/fix-encryption-key"> + <FixEncryptionKey + key={name} + modalProps={modalProps} + actions={actions} + options={options} + /> + </Route> - <Route - path="/edit-field" - render={() => { - return ( - <EditField - key={name} - modalProps={modalProps} - actions={actions} - name={options.name} - onSubmit={options.onSubmit} - /> - ); - }} - /> + <Route path="/edit-field"> + <EditField + key={name} + modalProps={modalProps} + actions={actions} + name={options.name} + onSubmit={options.onSubmit} + /> + </Route> <Route path="/budget-summary"> <BudgetSummary diff --git a/packages/desktop-client/src/components/Titlebar.js b/packages/desktop-client/src/components/Titlebar.js index 1b80258ad..407e4c641 100644 --- a/packages/desktop-client/src/components/Titlebar.js +++ b/packages/desktop-client/src/components/Titlebar.js @@ -6,7 +6,7 @@ import React, { useContext, } from 'react'; import { connect } from 'react-redux'; -import { Switch, Route, withRouter } from 'react-router-dom'; +import { Switch, Route, useLocation, useHistory } from 'react-router-dom'; import { css, media } from 'glamor'; @@ -258,7 +258,6 @@ function BudgetTitlebar({ globalPrefs, saveGlobalPrefs, localPrefs }) { } function Titlebar({ - location, globalPrefs, saveGlobalPrefs, localPrefs, @@ -269,6 +268,8 @@ function Titlebar({ style, sync, }) { + let history = useHistory(); + let location = useLocation(); let sidebar = useSidebar(); let { isNarrowWidth } = useResponsive(); const serverURL = useServerURL(); @@ -320,45 +321,30 @@ function Titlebar({ )} <Switch> - <Route - path="/accounts" - exact - children={props => { - let state = props.location.state || {}; - return state.goBack ? ( - <Button onClick={() => props.history.goBack()} bare> - <ArrowLeft - width={10} - height={10} - style={{ marginRight: 5, color: 'currentColor' }} - />{' '} - Back - </Button> - ) : null; - }} - /> + <Route path="/accounts" exact> + {location.state?.goBack ? ( + <Button onClick={() => history.goBack()} bare> + <ArrowLeft + width={10} + height={10} + style={{ marginRight: 5, color: 'currentColor' }} + />{' '} + Back + </Button> + ) : null} + </Route> - <Route - path="/accounts/:id" - exact - children={props => { - return ( - props.match && <AccountSyncCheck id={props.match.params.id} /> - ); - }} - /> + <Route path="/accounts/:id" exact> + <AccountSyncCheck /> + </Route> - <Route - path="/budget" - exact - children={() => ( - <BudgetTitlebar - globalPrefs={globalPrefs} - saveGlobalPrefs={saveGlobalPrefs} - localPrefs={localPrefs} - /> - )} - /> + <Route path="/budget" exact> + <BudgetTitlebar + globalPrefs={globalPrefs} + saveGlobalPrefs={saveGlobalPrefs} + localPrefs={localPrefs} + /> + </Route> </Switch> <View style={{ flex: 1 }} /> <UncategorizedButton /> @@ -374,14 +360,12 @@ function Titlebar({ ); } -export default withRouter( - connect( - state => ({ - globalPrefs: state.prefs.global, - localPrefs: state.prefs.local, - userData: state.user.data, - floatingSidebar: state.prefs.global.floatingSidebar, - }), - actions, - )(Titlebar), -); +export default connect( + state => ({ + globalPrefs: state.prefs.global, + localPrefs: state.prefs.local, + userData: state.user.data, + floatingSidebar: state.prefs.global.floatingSidebar, + }), + actions, +)(Titlebar); diff --git a/packages/desktop-client/src/components/accounts/Account.js b/packages/desktop-client/src/components/accounts/Account.js index 7d7ae712c..921799996 100644 --- a/packages/desktop-client/src/components/accounts/Account.js +++ b/packages/desktop-client/src/components/accounts/Account.js @@ -2001,6 +2001,7 @@ class AccountInternal extends PureComponent { function AccountHack(props) { let { dispatch: splitsExpandedDispatch } = useSplitsExpanded(); + return ( <AccountInternal {...props} @@ -2009,8 +2010,12 @@ function AccountHack(props) { ); } -export default function Account(props) { +export default function Account() { const syncEnabled = useFeatureFlag('syncAccount'); + let params = useParams(); + let location = useLocation(); + let activeLocation = useActiveLocation(); + let state = useSelector(state => ({ newTransactions: state.queries.newTransactions, matchedTransactions: state.queries.matchedTransactions, @@ -2019,16 +2024,11 @@ export default function Account(props) { categoryGroups: state.queries.categories.grouped, dateFormat: state.prefs.local.dateFormat || 'MM/dd/yyyy', hideFraction: state.prefs.local.hideFraction || false, - expandSplits: props.match && state.prefs.local['expand-splits'], - showBalances: - props.match && - state.prefs.local['show-balances-' + props.match.params.id], - showCleared: - props.match && - !state.prefs.local['hide-cleared-' + props.match.params.id], + expandSplits: state.prefs.local['expand-splits'], + showBalances: params.id && state.prefs.local['show-balances-' + params.id], + showCleared: params.id && !state.prefs.local['hide-cleared-' + params.id], showExtraBalances: - props.match && - state.prefs.local['show-extra-balances-' + props.match.params.id], + params.id && state.prefs.local['show-extra-balances-' + params.id], payees: state.queries.payees, modalShowing: state.modals.modalStack.length > 0, accountsSyncing: state.account.accountsSyncing, @@ -2042,10 +2042,6 @@ export default function Account(props) { [dispatch], ); - let params = useParams(); - let location = useLocation(); - let activeLocation = useActiveLocation(); - let transform = useMemo(() => { let filterByAccount = queries.getAccountFilter(params.id, '_account'); let filterByPayee = queries.getAccountFilter( @@ -2085,7 +2081,7 @@ export default function Account(props) { !!(activeLocation.state && activeLocation.state.locationPtr) } accountId={params.id} - location={props.location} + location={location} /> </SplitsExpandedProvider> </SchedulesProvider> diff --git a/packages/desktop-client/src/components/accounts/MobileAccount.js b/packages/desktop-client/src/components/accounts/MobileAccount.js index cefcc54d5..2db1f2ca3 100644 --- a/packages/desktop-client/src/components/accounts/MobileAccount.js +++ b/packages/desktop-client/src/components/accounts/MobileAccount.js @@ -1,5 +1,6 @@ import React, { useEffect, useMemo, useState } from 'react'; import { connect, useDispatch, useSelector } from 'react-redux'; +import { useParams } from 'react-router-dom'; import { useNavigate } from 'react-router-dom-v5-compat'; import debounce from 'debounce'; @@ -88,12 +89,9 @@ function Account(props) { [dispatch], ); - const { id: accountId } = props.match.params; + const { id: accountId } = useParams(); - const makeRootQuery = () => { - const { id } = props.match.params || {}; - return queries.makeTransactionsQuery(id); - }; + const makeRootQuery = () => queries.makeTransactionsQuery(accountId); const updateQuery = query => { if (paged) { diff --git a/packages/desktop-client/src/components/manager/ManagementApp.js b/packages/desktop-client/src/components/manager/ManagementApp.js index d5724e0da..7c90c2514 100644 --- a/packages/desktop-client/src/components/manager/ManagementApp.js +++ b/packages/desktop-client/src/components/manager/ManagementApp.js @@ -151,16 +151,20 @@ function ManagementApp({ {userData && files ? ( <> <Switch> - <Route exact path="/config-server" component={ConfigServer} /> - <Route - exact - path="/change-password" - component={ChangePassword} - /> + <Route exact path="/config-server"> + <ConfigServer /> + </Route> + <Route exact path="/change-password"> + <ChangePassword /> + </Route> {files && files.length > 0 ? ( - <Route exact path="/" component={BudgetList} /> + <Route exact path="/"> + <BudgetList /> + </Route> ) : ( - <Route exact path="/" component={WelcomeScreen} /> + <Route exact path="/"> + <WelcomeScreen /> + </Route> )} {/* Redirect all other pages to this route */} <Route path="/" render={() => <Redirect to="/" />} /> @@ -176,23 +180,27 @@ function ManagementApp({ }} > <Switch> - <Route exact path="/config-server" component={null} /> - <Route - exact - path="/" - render={() => ( - <LoggedInUser style={{ padding: '4px 7px' }} /> - )} - /> + <Route exact path="/config-server" children={null} /> + <Route exact path="/"> + <LoggedInUser style={{ padding: '4px 7px' }} /> + </Route> </Switch> </View> </> ) : ( <Switch> - <Route exact path="/login" component={Login} /> - <Route exact path="/error" component={Error} /> - <Route exact path="/config-server" component={ConfigServer} /> - <Route exact path="/bootstrap" component={Bootstrap} /> + <Route exact path="/login"> + <Login /> + </Route> + <Route exact path="/error"> + <Error /> + </Route> + <Route exact path="/config-server"> + <ConfigServer /> + </Route> + <Route exact path="/bootstrap"> + <Bootstrap /> + </Route> {/* Redirect all other pages to this route */} <Route path="/" render={() => <Redirect to="/bootstrap" />} /> </Switch> @@ -201,8 +209,10 @@ function ManagementApp({ )} <Switch> - <Route exact path="/config-server" component={null} /> - <Route path="/" component={ServerURL} /> + <Route exact path="/config-server" children={null} /> + <Route path="/"> + <ServerURL /> + </Route> </Switch> <Version /> </View> diff --git a/packages/desktop-client/src/components/modals/ManageRulesModal.js b/packages/desktop-client/src/components/modals/ManageRulesModal.js index 759585454..1eb05415a 100644 --- a/packages/desktop-client/src/components/modals/ManageRulesModal.js +++ b/packages/desktop-client/src/components/modals/ManageRulesModal.js @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { useLocation } from 'react-router-dom-v5-compat'; +import { useLocation } from 'react-router-dom'; import { isNonProductionEnvironment } from 'loot-core/src/shared/environment'; diff --git a/packages/desktop-client/src/components/reports/index.js b/packages/desktop-client/src/components/reports/index.js index 08ef98347..75af78499 100644 --- a/packages/desktop-client/src/components/reports/index.js +++ b/packages/desktop-client/src/components/reports/index.js @@ -10,9 +10,15 @@ import Overview from './Overview'; export default function Reports() { return ( <View style={{ flex: 1 }} data-testid="reports-page"> - <Route path="/reports" exact component={Overview} /> - <Route path="/reports/net-worth" exact component={NetWorth} /> - <Route path="/reports/cash-flow" exact component={CashFlow} /> + <Route path="/reports" exact> + <Overview /> + </Route> + <Route path="/reports/net-worth" exact> + <NetWorth /> + </Route> + <Route path="/reports/cash-flow" exact> + <CashFlow /> + </Route> </View> ); } diff --git a/upcoming-release-notes/1045.md b/upcoming-release-notes/1045.md new file mode 100644 index 000000000..f31fafc1b --- /dev/null +++ b/upcoming-release-notes/1045.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [trevdor] +--- + +Update to React Router v5.1 conventions to facilitate the v6 upgrade. -- GitLab