From e886f311639628b571ad28a933fb0f774c0d5dec Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez Date: Tue, 17 Dec 2024 14:56:24 -0800 Subject: [PATCH 01/13] Initial upgrade to redux toolkit, more fixes needed e.g. removing non-serializable values from the state --- packages/desktop-client/package.json | 6 +- .../desktop-client/src/auth/AuthProvider.tsx | 4 +- .../src/auth/ProtectedRoute.tsx | 6 +- .../desktop-client/src/components/App.tsx | 9 +- .../src/components/AppBackground.tsx | 4 +- .../src/components/BankSyncStatus.tsx | 8 +- .../src/components/FinancesApp.tsx | 9 +- .../src/components/HelpMenu.tsx | 4 +- .../src/components/LoggedInUser.tsx | 11 +- .../src/components/ManageRules.tsx | 4 +- .../desktop-client/src/components/Modals.tsx | 4 +- .../src/components/Notifications.tsx | 13 +- .../src/components/Titlebar.tsx | 4 +- .../src/components/UpdateNotification.tsx | 11 +- .../src/components/accounts/Account.tsx | 28 ++-- .../components/accounts/AccountSyncCheck.tsx | 7 +- .../autocomplete/PayeeAutocomplete.test.tsx | 2 +- .../autocomplete/PayeeAutocomplete.tsx | 4 +- .../src/components/budget/index.tsx | 4 +- .../src/components/manager/BudgetList.tsx | 10 +- .../src/components/manager/ConfigServer.tsx | 4 +- .../src/components/manager/ManagementApp.tsx | 12 +- .../src/components/manager/WelcomeScreen.tsx | 4 +- .../manager/subscribe/Bootstrap.tsx | 4 +- .../components/manager/subscribe/Login.tsx | 4 +- .../manager/subscribe/OpenIdCallback.ts | 5 +- .../mobile/accounts/AccountTransactions.tsx | 10 +- .../components/mobile/accounts/Accounts.tsx | 2 +- .../components/mobile/budget/BudgetTable.jsx | 6 +- .../mobile/budget/CategoryTransactions.tsx | 4 +- .../src/components/mobile/budget/index.tsx | 4 +- .../mobile/transactions/TransactionEdit.jsx | 10 +- .../mobile/transactions/TransactionList.jsx | 4 +- .../transactions/TransactionListItem.tsx | 6 +- .../src/components/modals/BudgetListModal.tsx | 4 +- .../components/modals/CloseAccountModal.tsx | 2 +- .../src/components/modals/CoverModal.tsx | 4 +- .../components/modals/CreateAccountModal.tsx | 4 +- .../modals/CreateEncryptionKeyModal.tsx | 2 +- .../modals/CreateLocalAccountModal.tsx | 4 +- .../src/components/modals/EditRuleModal.jsx | 2 +- .../modals/EnvelopeBudgetSummaryModal.tsx | 4 +- .../modals/GoCardlessExternalMsgModal.tsx | 4 +- .../ImportTransactionsModal.jsx | 4 +- .../src/components/modals/LoadBackupModal.tsx | 4 +- .../modals/MergeUnusedPayeesModal.tsx | 6 +- .../modals/OutOfSyncMigrationsModal.tsx | 4 +- .../modals/SelectLinkedAccountsModal.jsx | 2 +- .../src/components/modals/TransferModal.tsx | 4 +- .../components/modals/TransferOwnership.tsx | 9 +- .../manager/ConfirmChangeDocumentDir.tsx | 4 +- .../modals/manager/DeleteFileModal.tsx | 4 +- .../modals/manager/DuplicateFileModal.tsx | 4 +- .../modals/manager/FilesSettingsModal.tsx | 6 +- .../modals/manager/ImportActualModal.tsx | 4 +- .../components/modals/manager/ImportModal.tsx | 4 +- .../modals/manager/ImportYNAB4Modal.tsx | 4 +- .../modals/manager/ImportYNAB5Modal.tsx | 4 +- .../payees/ManagePayeesWithData.tsx | 16 +-- .../src/components/reports/Overview.tsx | 4 +- .../components/reports/reports/Calendar.tsx | 4 +- .../components/reports/reports/CashFlow.tsx | 4 +- .../reports/reports/CustomReportListCards.tsx | 4 +- .../components/reports/reports/NetWorth.tsx | 4 +- .../components/reports/reports/Spending.tsx | 4 +- .../components/reports/reports/Summary.tsx | 4 +- .../schedules/PostsOfflineNotification.tsx | 4 +- .../components/schedules/ScheduleDetails.tsx | 4 +- .../src/components/schedules/ScheduleLink.tsx | 4 +- .../src/components/schedules/index.tsx | 4 +- .../src/components/settings/AuthSettings.tsx | 4 +- .../src/components/settings/Encryption.tsx | 2 +- .../src/components/settings/Reset.tsx | 2 +- .../src/components/settings/index.tsx | 2 +- .../src/components/sidebar/Account.tsx | 4 +- .../src/components/sidebar/Accounts.tsx | 13 +- .../src/components/sidebar/BudgetName.tsx | 4 +- .../src/components/sidebar/Sidebar.tsx | 4 +- .../desktop-client/src/components/table.tsx | 4 +- .../SelectedTransactionsButton.tsx | 4 +- .../transactions/TransactionList.jsx | 4 +- .../transactions/TransactionMenu.tsx | 4 +- .../transactions/TransactionsTable.jsx | 6 +- .../transactions/TransactionsTable.test.jsx | 2 +- .../src/components/util/GenericInput.jsx | 4 +- packages/desktop-client/src/global-events.ts | 2 +- packages/desktop-client/src/gocardless.ts | 6 +- .../desktop-client/src/hooks/useAccounts.ts | 12 +- .../desktop-client/src/hooks/useActions.ts | 7 +- .../desktop-client/src/hooks/useCategories.ts | 12 +- .../src/hooks/useFailedAccounts.ts | 10 +- .../desktop-client/src/hooks/useGlobalPref.ts | 10 +- .../src/hooks/useMetadataPref.ts | 10 +- .../desktop-client/src/hooks/useModalState.ts | 10 +- .../desktop-client/src/hooks/usePayees.ts | 20 ++- .../desktop-client/src/hooks/useSelected.tsx | 9 +- .../src/hooks/useSplitsExpanded.tsx | 33 +++-- .../src/hooks/useSyncServerStatus.ts | 7 +- .../desktop-client/src/hooks/useSyncedPref.ts | 8 +- .../src/hooks/useSyncedPrefs.ts | 8 +- .../src/hooks/useTransactionBatchActions.ts | 6 +- packages/desktop-client/src/hooks/useUndo.ts | 2 +- .../src/hooks/useUpdatedAccounts.ts | 6 +- packages/desktop-client/src/index.tsx | 40 +----- packages/desktop-client/src/redux/index.ts | 11 ++ packages/desktop-client/src/redux/mock.tsx | 10 ++ packages/desktop-client/src/setupTests.js | 2 +- packages/loot-core/package.json | 2 +- .../loot-core/src/client/actions/account.ts | 16 +-- packages/loot-core/src/client/actions/app.ts | 20 +-- .../loot-core/src/client/actions/backups.ts | 6 +- .../loot-core/src/client/actions/budgets.ts | 30 ++--- .../loot-core/src/client/actions/prefs.ts | 12 +- .../loot-core/src/client/actions/queries.ts | 38 +++--- packages/loot-core/src/client/actions/sync.ts | 8 +- .../loot-core/src/client/actions/types.d.ts | 6 - packages/loot-core/src/client/actions/user.ts | 8 +- packages/loot-core/src/client/constants.ts | 4 +- .../loot-core/src/client/reducers/account.ts | 10 +- packages/loot-core/src/client/reducers/app.ts | 13 -- .../src/client/state-types/account.d.ts | 2 +- .../loot-core/src/client/state-types/app.d.ts | 20 +-- .../src/client/state-types/index.d.ts | 5 - packages/loot-core/src/client/store/index.ts | 43 +++++++ packages/loot-core/src/client/store/mock.ts | 13 ++ packages/loot-core/src/mocks/redux.tsx | 21 --- .../src/platform/client/undo/index.d.ts | 1 + .../src/platform/client/undo/index.web.ts | 1 + yarn.lock | 120 +++++++++++------- 129 files changed, 535 insertions(+), 556 deletions(-) create mode 100644 packages/desktop-client/src/redux/index.ts create mode 100644 packages/desktop-client/src/redux/mock.tsx delete mode 100644 packages/loot-core/src/client/actions/types.d.ts create mode 100644 packages/loot-core/src/client/store/index.ts create mode 100644 packages/loot-core/src/client/store/mock.ts delete mode 100644 packages/loot-core/src/mocks/redux.tsx diff --git a/packages/desktop-client/package.json b/packages/desktop-client/package.json index 358eee53548..c423ab96160 100644 --- a/packages/desktop-client/package.json +++ b/packages/desktop-client/package.json @@ -10,6 +10,7 @@ "@fontsource/redacted-script": "^5.0.21", "@juggle/resize-observer": "^3.4.0", "@playwright/test": "1.41.1", + "@reduxjs/toolkit": "^2.5.0", "@rollup/plugin-inject": "^5.0.5", "@svgr/cli": "^8.1.0", "@swc/core": "^1.5.3", @@ -24,7 +25,6 @@ "@types/react-dom": "^18.2.1", "@types/react-grid-layout": "^1", "@types/react-modal": "^3.16.0", - "@types/react-redux": "^7.1.25", "@types/uuid": "^9.0.2", "@types/webpack-bundle-analyzer": "^4.6.3", "@use-gesture/react": "^10.3.0", @@ -61,15 +61,13 @@ "react-i18next": "^14.1.2", "react-markdown": "^8.0.7", "react-modal": "3.16.1", - "react-redux": "7.2.9", + "react-redux": "^9.2.0", "react-router-dom": "6.21.3", "react-simple-pull-to-refresh": "^1.3.3", "react-spring": "^9.7.3", "react-stately": "^3.33.0", "react-virtualized-auto-sizer": "^1.0.21", "recharts": "^2.10.4", - "redux": "^4.2.1", - "redux-thunk": "^2.4.2", "remark-gfm": "^3.0.1", "rollup-plugin-visualizer": "^5.12.0", "sass": "^1.70.0", diff --git a/packages/desktop-client/src/auth/AuthProvider.tsx b/packages/desktop-client/src/auth/AuthProvider.tsx index e0d5903783d..f8105064c52 100644 --- a/packages/desktop-client/src/auth/AuthProvider.tsx +++ b/packages/desktop-client/src/auth/AuthProvider.tsx @@ -1,9 +1,9 @@ import React, { createContext, useContext, type ReactNode } from 'react'; -import { useSelector } from 'react-redux'; import { type State } from 'loot-core/client/state-types'; import { useServerURL } from '../components/ServerContext'; +import { useAppSelector } from '../redux'; import { type Permissions } from './types'; @@ -18,7 +18,7 @@ type AuthProviderProps = { }; export const AuthProvider = ({ children }: AuthProviderProps) => { - const userData = useSelector((state: State) => state.user.data); + const userData = useAppSelector((state: State) => state.user.data); const serverUrl = useServerURL(); const hasPermission = (permission?: Permissions) => { diff --git a/packages/desktop-client/src/auth/ProtectedRoute.tsx b/packages/desktop-client/src/auth/ProtectedRoute.tsx index 5dacd055782..8704fadacb3 100644 --- a/packages/desktop-client/src/auth/ProtectedRoute.tsx +++ b/packages/desktop-client/src/auth/ProtectedRoute.tsx @@ -1,10 +1,10 @@ import { useEffect, useState, type ReactElement } from 'react'; -import { useSelector } from 'react-redux'; import { type RemoteFile, type SyncedLocalFile } from 'loot-core/types/file'; import { View } from '../components/common/View'; import { useMetadataPref } from '../hooks/useMetadataPref'; +import { useAppSelector } from '../redux'; import { useAuth } from './AuthProvider'; import { type Permissions } from './types'; @@ -23,13 +23,13 @@ export const ProtectedRoute = ({ const { hasPermission } = useAuth(); const [permissionGranted, setPermissionGranted] = useState(false); const [cloudFileId] = useMetadataPref('cloudFileId'); - const allFiles = useSelector(state => state.budgets.allFiles || []); + const allFiles = useAppSelector(state => state.budgets.allFiles || []); const remoteFiles = allFiles.filter( (f): f is SyncedLocalFile | RemoteFile => f.state === 'remote' || f.state === 'synced' || f.state === 'detached', ); const currentFile = remoteFiles.find(f => f.cloudFileId === cloudFileId); - const userData = useSelector(state => state.user.data); + const userData = useAppSelector(state => state.user.data); useEffect(() => { const hasRequiredPermission = hasPermission(permission); diff --git a/packages/desktop-client/src/components/App.tsx b/packages/desktop-client/src/components/App.tsx index acc0f361c87..e948894ca3b 100644 --- a/packages/desktop-client/src/components/App.tsx +++ b/packages/desktop-client/src/components/App.tsx @@ -9,7 +9,6 @@ import { } from 'react-error-boundary'; import { HotkeysProvider } from 'react-hotkeys-hook'; import { useTranslation } from 'react-i18next'; -import { useDispatch, useSelector } from 'react-redux'; import { BrowserRouter } from 'react-router-dom'; import { @@ -20,7 +19,6 @@ import { sync, } from 'loot-core/client/actions'; import { SpreadsheetProvider } from 'loot-core/client/SpreadsheetProvider'; -import { type State } from 'loot-core/client/state-types'; import * as Platform from 'loot-core/src/client/platform'; import { init as initConnection, @@ -30,6 +28,7 @@ import { import { useActions } from '../hooks/useActions'; import { useMetadataPref } from '../hooks/useMetadataPref'; import { installPolyfills } from '../polyfills'; +import { useAppDispatch, useAppSelector } from '../redux'; import { styles, hasHiddenScrollbars, ThemeStyle, useTheme } from '../style'; import { ExposeNavigate } from '../util/router-tools'; @@ -50,8 +49,8 @@ function AppInner() { const [cloudFileId] = useMetadataPref('cloudFileId'); const { t } = useTranslation(); const { showBoundary: showErrorBoundary } = useErrorBoundary(); - const dispatch = useDispatch(); - const userData = useSelector((state: State) => state.user.data); + const dispatch = useAppDispatch(); + const userData = useAppSelector(state => state.user.data); const { signOut, addNotification } = useActions(); const maybeUpdate = async (cb?: () => T): Promise => { @@ -159,7 +158,7 @@ export function App() { const [hiddenScrollbars, setHiddenScrollbars] = useState( hasHiddenScrollbars(), ); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); useEffect(() => { function checkScrollbars() { diff --git a/packages/desktop-client/src/components/AppBackground.tsx b/packages/desktop-client/src/components/AppBackground.tsx index 9d57bafdde8..e3e5ef3c3b4 100644 --- a/packages/desktop-client/src/components/AppBackground.tsx +++ b/packages/desktop-client/src/components/AppBackground.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import { useSelector } from 'react-redux'; import { useTransition, animated } from 'react-spring'; import { css } from '@emotion/css'; import { AnimatedLoading } from '../icons/AnimatedLoading'; +import { useAppSelector } from '../redux'; import { theme } from '../style'; import { Background } from './Background'; @@ -16,7 +16,7 @@ type AppBackgroundProps = { }; export function AppBackground({ isLoading }: AppBackgroundProps) { - const loadingText = useSelector(state => state.app.loadingText); + const loadingText = useAppSelector(state => state.app.loadingText); const showLoading = isLoading || loadingText !== null; const transitions = useTransition(loadingText, { from: { opacity: 0, transform: 'translateY(-100px)' }, diff --git a/packages/desktop-client/src/components/BankSyncStatus.tsx b/packages/desktop-client/src/components/BankSyncStatus.tsx index 52130e0c198..657b99a03ad 100644 --- a/packages/desktop-client/src/components/BankSyncStatus.tsx +++ b/packages/desktop-client/src/components/BankSyncStatus.tsx @@ -1,10 +1,8 @@ import React from 'react'; import { Trans } from 'react-i18next'; -import { useSelector } from 'react-redux'; import { useTransition, animated } from 'react-spring'; -import { type State } from 'loot-core/src/client/state-types'; - +import { useAppSelector } from '../redux'; import { theme, styles } from '../style'; import { AnimatedRefresh } from './AnimatedRefresh'; @@ -12,8 +10,8 @@ import { Text } from './common/Text'; import { View } from './common/View'; export function BankSyncStatus() { - const accountsSyncing = useSelector( - (state: State) => state.account.accountsSyncing, + const accountsSyncing = useAppSelector( + state => state.account.accountsSyncing, ); const accountsSyncingCount = accountsSyncing.length; const count = accountsSyncingCount; diff --git a/packages/desktop-client/src/components/FinancesApp.tsx b/packages/desktop-client/src/components/FinancesApp.tsx index 8b987636ee3..0edeec8bd2f 100644 --- a/packages/desktop-client/src/components/FinancesApp.tsx +++ b/packages/desktop-client/src/components/FinancesApp.tsx @@ -1,7 +1,6 @@ // @ts-strict-ignore import React, { type ReactElement, useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; -import { useDispatch, useSelector } from 'react-redux'; import { Route, Routes, @@ -11,7 +10,6 @@ import { } from 'react-router-dom'; import { addNotification, sync } from 'loot-core/client/actions'; -import { type State } from 'loot-core/src/client/state-types'; import * as undo from 'loot-core/src/platform/client/undo'; import { ProtectedRoute } from '../auth/ProtectedRoute'; @@ -20,6 +18,7 @@ import { useAccounts } from '../hooks/useAccounts'; import { useLocalPref } from '../hooks/useLocalPref'; import { useMetaThemeColor } from '../hooks/useMetaThemeColor'; import { useNavigate } from '../hooks/useNavigate'; +import { useAppSelector, useAppDispatch } from '../redux'; import { theme } from '../style'; import { getIsOutdated, getLatestVersion } from '../util/versions'; @@ -86,13 +85,11 @@ export function FinancesApp() { const { isNarrowWidth } = useResponsive(); useMetaThemeColor(isNarrowWidth ? theme.mobileViewTheme : null); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const { t } = useTranslation(); const accounts = useAccounts(); - const accountsLoaded = useSelector( - (state: State) => state.queries.accountsLoaded, - ); + const accountsLoaded = useAppSelector(state => state.queries.accountsLoaded); const [lastUsedVersion, setLastUsedVersion] = useLocalPref( 'flags.updateNotificationShownForVersion', diff --git a/packages/desktop-client/src/components/HelpMenu.tsx b/packages/desktop-client/src/components/HelpMenu.tsx index e0e790dc74b..ac33fb9cf93 100644 --- a/packages/desktop-client/src/components/HelpMenu.tsx +++ b/packages/desktop-client/src/components/HelpMenu.tsx @@ -1,7 +1,6 @@ import { forwardRef, useRef } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { useToggle } from 'usehooks-ts'; @@ -11,6 +10,7 @@ import { pushModal } from 'loot-core/client/actions/modals'; import { useFeatureFlag } from '../hooks/useFeatureFlag'; import { SvgHelp } from '../icons/v2/Help'; +import { useAppDispatch } from '../redux'; import { Button } from './common/Button2'; import { Menu } from './common/Menu'; @@ -52,7 +52,7 @@ export const HelpMenu = () => { const [isMenuOpen, toggleMenuOpen, setMenuOpen] = useToggle(); const menuButtonRef = useRef(null); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const page = useLocation().pathname; const handleItemSelect = (item: HelpMenuItem) => { diff --git a/packages/desktop-client/src/components/LoggedInUser.tsx b/packages/desktop-client/src/components/LoggedInUser.tsx index 0c0662d3855..6bc78bcbd22 100644 --- a/packages/desktop-client/src/components/LoggedInUser.tsx +++ b/packages/desktop-client/src/components/LoggedInUser.tsx @@ -1,10 +1,8 @@ import React, { useState, useEffect, useRef, type CSSProperties } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch, useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { closeBudget, getUserData, signOut } from 'loot-core/client/actions'; -import { type State } from 'loot-core/src/client/state-types'; import { type RemoteFile, type SyncedLocalFile } from 'loot-core/types/file'; import { type TransObjectLiteral } from 'loot-core/types/util'; @@ -12,6 +10,7 @@ import { useAuth } from '../auth/AuthProvider'; import { Permissions } from '../auth/types'; import { useMetadataPref } from '../hooks/useMetadataPref'; import { useNavigate } from '../hooks/useNavigate'; +import { useAppSelector, useAppDispatch } from '../redux'; import { theme, styles } from '../style'; import { Button } from './common/Button2'; @@ -34,9 +33,9 @@ export function LoggedInUser({ color, }: LoggedInUserProps) { const { t } = useTranslation(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const navigate = useNavigate(); - const userData = useSelector((state: State) => state.user.data); + const userData = useAppSelector(state => state.user.data); const [loading, setLoading] = useState(true); const [menuOpen, setMenuOpen] = useState(false); const serverUrl = useServerURL(); @@ -46,12 +45,12 @@ export function LoggedInUser({ const location = useLocation(); const { hasPermission } = useAuth(); const multiuserEnabled = useMultiuserEnabled(); - const allFiles = useSelector(state => state.budgets.allFiles || []); + const allFiles = useAppSelector(state => state.budgets.allFiles || []); const remoteFiles = allFiles.filter( f => f.state === 'remote' || f.state === 'synced' || f.state === 'detached', ) as (SyncedLocalFile | RemoteFile)[]; const currentFile = remoteFiles.find(f => f.cloudFileId === cloudFileId); - const hasSyncedPrefs = useSelector((state: State) => state.prefs.synced); + const hasSyncedPrefs = useAppSelector(state => state.prefs.synced); useEffect(() => { async function init() { diff --git a/packages/desktop-client/src/components/ManageRules.tsx b/packages/desktop-client/src/components/ManageRules.tsx index 36643423ba5..d6938e65b6f 100644 --- a/packages/desktop-client/src/components/ManageRules.tsx +++ b/packages/desktop-client/src/components/ManageRules.tsx @@ -8,7 +8,6 @@ import React, { type Dispatch, } from 'react'; import { useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { useSchedules } from 'loot-core/client/data-hooks/schedules'; import { q } from 'loot-core/shared/query'; @@ -25,6 +24,7 @@ import { useAccounts } from '../hooks/useAccounts'; import { useCategories } from '../hooks/useCategories'; import { usePayees } from '../hooks/usePayees'; import { useSelected, SelectedProvider } from '../hooks/useSelected'; +import { useAppDispatch } from '../redux'; import { theme } from '../style'; import { Button } from './common/Button2'; @@ -113,7 +113,7 @@ export function ManageRules({ const [allRules, setAllRules] = useState([]); const [page, setPage] = useState(0); const [filter, setFilter] = useState(''); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const { schedules = [] } = useSchedules({ query: useMemo(() => q('schedules').select('*'), []), diff --git a/packages/desktop-client/src/components/Modals.tsx b/packages/desktop-client/src/components/Modals.tsx index 013d4672fe0..dfad7259fd5 100644 --- a/packages/desktop-client/src/components/Modals.tsx +++ b/packages/desktop-client/src/components/Modals.tsx @@ -1,7 +1,6 @@ // @ts-strict-ignore import React, { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { closeModal } from 'loot-core/client/actions'; @@ -10,6 +9,7 @@ import * as monthUtils from 'loot-core/src/shared/months'; import { useMetadataPref } from '../hooks/useMetadataPref'; import { useModalState } from '../hooks/useModalState'; +import { useAppDispatch } from '../redux'; import { ModalTitle, ModalHeader } from './common/Modal'; import { AccountAutocompleteModal } from './modals/AccountAutocompleteModal'; @@ -78,7 +78,7 @@ import { NamespaceContext } from './spreadsheet/NamespaceContext'; export function Modals() { const location = useLocation(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const { modalStack } = useModalState(); const [budgetId] = useMetadataPref('id'); diff --git a/packages/desktop-client/src/components/Notifications.tsx b/packages/desktop-client/src/components/Notifications.tsx index 59bd7adcb7f..bcfe12cb64e 100644 --- a/packages/desktop-client/src/components/Notifications.tsx +++ b/packages/desktop-client/src/components/Notifications.tsx @@ -7,16 +7,15 @@ import React, { type CSSProperties, } from 'react'; import { useTranslation } from 'react-i18next'; -import { useDispatch, useSelector } from 'react-redux'; import { css } from '@emotion/css'; import { removeNotification } from 'loot-core/client/actions'; -import { type State } from 'loot-core/src/client/state-types'; import type { NotificationWithId } from 'loot-core/src/client/state-types/notifications'; import { AnimatedLoading } from '../icons/AnimatedLoading'; import { SvgDelete } from '../icons/v0'; +import { useAppSelector, useAppDispatch } from '../redux'; import { styles, theme } from '../style'; import { Button, ButtonWithLoading } from './common/Button2'; @@ -263,14 +262,12 @@ function Notification({ } export function Notifications({ style }: { style?: CSSProperties }) { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const { isNarrowWidth } = useResponsive(); - const notifications = useSelector( - (state: State) => state.notifications.notifications, - ); - const notificationInset = useSelector( - (state: State) => state.notifications.inset, + const notifications = useAppSelector( + state => state.notifications.notifications, ); + const notificationInset = useAppSelector(state => state.notifications.inset); return ( state.app.updateInfo); - const showUpdateNotification = useSelector( - (state: State) => state.app.showUpdateNotification, + const updateInfo = useAppSelector(state => state.app.updateInfo); + const showUpdateNotification = useAppSelector( + state => state.app.showUpdateNotification, ); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const onRestart = () => { dispatch(updateApp()); }; diff --git a/packages/desktop-client/src/components/accounts/Account.tsx b/packages/desktop-client/src/components/accounts/Account.tsx index 4be9f28866b..12be08d43d6 100644 --- a/packages/desktop-client/src/components/accounts/Account.tsx +++ b/packages/desktop-client/src/components/accounts/Account.tsx @@ -8,7 +8,6 @@ import React, { useEffect, } from 'react'; import { Trans } from 'react-i18next'; -import { useSelector } from 'react-redux'; import { Navigate, useParams, useLocation } from 'react-router-dom'; import { debounce } from 'debounce'; @@ -29,6 +28,7 @@ import { type PagedQuery, } from 'loot-core/src/client/query-helpers'; import { send, listen } from 'loot-core/src/platform/client/fetch'; +import * as undo from 'loot-core/src/platform/client/undo'; import { currentDay } from 'loot-core/src/shared/months'; import { q, type Query } from 'loot-core/src/shared/query'; import { @@ -68,6 +68,7 @@ import { } from '../../hooks/useSplitsExpanded'; import { useSyncedPref } from '../../hooks/useSyncedPref'; import { useTransactionBatchActions } from '../../hooks/useTransactionBatchActions'; +import { useAppSelector } from '../../redux'; import { styles, theme } from '../../style'; import { Button } from '../common/Button2'; import { Text } from '../common/Text'; @@ -261,8 +262,6 @@ type AccountInternalProps = { showExtraBalances?: boolean; setShowExtraBalances: (newValue: boolean) => void; modalShowing?: boolean; - setLastUndoState: (state: null) => void; - lastUndoState: { current: UndoState | null }; accounts: AccountEntity[]; getPayees: () => Promise; updateAccount: (newAccount: AccountEntity) => void; @@ -412,7 +411,7 @@ class AccountInternal extends PureComponent< } } - this.props.setLastUndoState(null); + undo.setUndoState('current', null); }; const unlistens = [listen('undo-event', onUndo)]; @@ -428,8 +427,9 @@ class AccountInternal extends PureComponent< // If there is a pending undo, apply it immediately (this happens // when an undo changes the location to this page) - if (this.props.lastUndoState && this.props.lastUndoState.current) { - onUndo(this.props.lastUndoState.current); + const lastUndoState = undo.getUndoState('current'); + if (lastUndoState) { + onUndo(lastUndoState as UndoState); } } @@ -1860,8 +1860,10 @@ export function Account() { const location = useLocation(); const { grouped: categoryGroups } = useCategories(); - const newTransactions = useSelector(state => state.queries.newTransactions); - const matchedTransactions = useSelector( + const newTransactions = useAppSelector( + state => state.queries.newTransactions, + ); + const matchedTransactions = useAppSelector( state => state.queries.matchedTransactions, ); const accounts = useAccounts(); @@ -1882,9 +1884,12 @@ export function Account() { const [showExtraBalances, setShowExtraBalances] = useSyncedPref( `show-extra-balances-${params.id || 'all-accounts'}`, ); - const modalShowing = useSelector(state => state.modals.modalStack.length > 0); - const accountsSyncing = useSelector(state => state.account.accountsSyncing); - const lastUndoState = useSelector(state => state.app.lastUndoState); + const modalShowing = useAppSelector( + state => state.modals.modalStack.length > 0, + ); + const accountsSyncing = useAppSelector( + state => state.account.accountsSyncing, + ); const filterConditions = location?.state?.filterConditions || []; const savedFiters = useFilters(); @@ -1923,7 +1928,6 @@ export function Account() { payees={payees} modalShowing={modalShowing} accountsSyncing={accountsSyncing} - lastUndoState={lastUndoState} filterConditions={filterConditions} categoryGroups={categoryGroups} {...actionCreators} diff --git a/packages/desktop-client/src/components/accounts/AccountSyncCheck.tsx b/packages/desktop-client/src/components/accounts/AccountSyncCheck.tsx index 489b6e92e19..0d8093dcff2 100644 --- a/packages/desktop-client/src/components/accounts/AccountSyncCheck.tsx +++ b/packages/desktop-client/src/components/accounts/AccountSyncCheck.tsx @@ -1,6 +1,5 @@ import React, { useCallback, useRef, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch, useSelector } from 'react-redux'; import { useParams } from 'react-router-dom'; import { unlinkAccount } from 'loot-core/client/actions'; @@ -8,7 +7,9 @@ import { type AccountEntity } from 'loot-core/types/models'; import { authorizeBank } from '../../gocardless'; import { useAccounts } from '../../hooks/useAccounts'; +import { useFailedAccounts } from '../../hooks/useFailedAccounts'; import { SvgExclamationOutline } from '../../icons/v1'; +import { useAppDispatch } from '../../redux'; import { theme } from '../../style'; import { Button } from '../common/Button2'; import { Link } from '../common/Link'; @@ -82,8 +83,8 @@ function useErrorMessage() { export function AccountSyncCheck() { const accounts = useAccounts(); - const failedAccounts = useSelector(state => state.account.failedAccounts); - const dispatch = useDispatch(); + const failedAccounts = useFailedAccounts(); + const dispatch = useAppDispatch(); const { id } = useParams(); const [open, setOpen] = useState(false); const triggerRef = useRef(null); diff --git a/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.test.tsx b/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.test.tsx index cd9f20a122c..b5b3aa925e7 100644 --- a/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.test.tsx +++ b/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.test.tsx @@ -3,11 +3,11 @@ import userEvent from '@testing-library/user-event'; import { vi } from 'vitest'; import { generateAccount } from 'loot-core/src/mocks'; -import { TestProvider } from 'loot-core/src/mocks/redux'; import type { AccountEntity, PayeeEntity } from 'loot-core/types/models'; import { AuthProvider } from '../../auth/AuthProvider'; import { useCommonPayees } from '../../hooks/usePayees'; +import { TestProvider } from '../../redux/mock'; import { ResponsiveProvider } from '../responsive/ResponsiveProvider'; import { diff --git a/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx b/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx index b1f02818a4e..c0a6bf87caa 100644 --- a/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx +++ b/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx @@ -12,7 +12,6 @@ import React, { type CSSProperties, } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { css, cx } from '@emotion/css'; @@ -27,6 +26,7 @@ import { import { useAccounts } from '../../hooks/useAccounts'; import { useCommonPayees, usePayees } from '../../hooks/usePayees'; import { SvgAdd, SvgBookmark } from '../../icons/v1'; +import { useAppDispatch } from '../../redux'; import { theme, styles } from '../../style'; import { Button } from '../common/Button'; import { TextOneLine } from '../common/TextOneLine'; @@ -320,7 +320,7 @@ export function PayeeAutocomplete({ return [{ id: 'new', favorite: 0, name: '' }, ...filteredSuggestions]; }, [commonPayees, payees, focusTransferPayees, accounts, hasPayeeInput]); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); async function handleSelect(idOrIds, rawInputValue) { if (!clearOnBlur) { diff --git a/packages/desktop-client/src/components/budget/index.tsx b/packages/desktop-client/src/components/budget/index.tsx index 0012f46f0a7..f57ed655702 100644 --- a/packages/desktop-client/src/components/budget/index.tsx +++ b/packages/desktop-client/src/components/budget/index.tsx @@ -1,7 +1,6 @@ // @ts-strict-ignore import React, { memo, useMemo, useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { addNotification, @@ -26,6 +25,7 @@ import { useGlobalPref } from '../../hooks/useGlobalPref'; import { useLocalPref } from '../../hooks/useLocalPref'; import { useNavigate } from '../../hooks/useNavigate'; import { useSyncedPref } from '../../hooks/useSyncedPref'; +import { useAppDispatch } from '../../redux'; import { styles } from '../../style'; import { View } from '../common/View'; import { NamespaceContext } from '../spreadsheet/NamespaceContext'; @@ -67,7 +67,7 @@ function BudgetInner(props: BudgetInnerProps) { const { t } = useTranslation(); const currentMonth = monthUtils.currentMonth(); const spreadsheet = useSpreadsheet(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const navigate = useNavigate(); const [summaryCollapsed, setSummaryCollapsedPref] = useLocalPref( 'budget.summaryCollapsed', diff --git a/packages/desktop-client/src/components/manager/BudgetList.tsx b/packages/desktop-client/src/components/manager/BudgetList.tsx index 03b8ab1300e..502b6f7f10a 100644 --- a/packages/desktop-client/src/components/manager/BudgetList.tsx +++ b/packages/desktop-client/src/components/manager/BudgetList.tsx @@ -6,7 +6,6 @@ import React, { useCallback, } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch, useSelector } from 'react-redux'; import { closeAndDownloadBudget, @@ -43,6 +42,7 @@ import { SvgUserGroup, } from '../../icons/v1'; import { SvgCloudUnknown, SvgKey, SvgRefreshArrow } from '../../icons/v2'; +import { useAppSelector, useAppDispatch } from '../../redux'; import { styles, theme } from '../../style'; import { tokens } from '../../tokens'; import { Button } from '../common/Button2'; @@ -498,12 +498,12 @@ function BudgetListHeader({ } export function BudgetList({ showHeader = true, quickSwitchMode = false }) { - const dispatch = useDispatch(); - const allFiles = useSelector(state => state.budgets.allFiles || []); + const dispatch = useAppDispatch(); + const allFiles = useAppSelector(state => state.budgets.allFiles || []); const multiuserEnabled = useMultiuserEnabled(); const [id] = useMetadataPref('id'); const [currentUserId, setCurrentUserId] = useState(''); - const userData = useSelector(state => state.user.data); + const userData = useAppSelector(state => state.user.data); const fetchUsers = useCallback(async () => { try { @@ -670,7 +670,7 @@ type UserAccessForFileProps = { }; function UserAccessForFile({ fileId, currentUserId }: UserAccessForFileProps) { - const allFiles = useSelector(state => state.budgets.allFiles || []); + const allFiles = useAppSelector(state => state.budgets.allFiles || []); const remoteFiles = allFiles.filter( f => f.state === 'remote' || f.state === 'synced' || f.state === 'detached', ) as (SyncedLocalFile | RemoteFile)[]; diff --git a/packages/desktop-client/src/components/manager/ConfigServer.tsx b/packages/desktop-client/src/components/manager/ConfigServer.tsx index 09511e249e7..60c9d835aa6 100644 --- a/packages/desktop-client/src/components/manager/ConfigServer.tsx +++ b/packages/desktop-client/src/components/manager/ConfigServer.tsx @@ -1,7 +1,6 @@ // @ts-strict-ignore import React, { useState, useEffect, useCallback } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { createBudget, loggedIn, signOut } from 'loot-core/client/actions'; import { @@ -11,6 +10,7 @@ import { import { useGlobalPref } from '../../hooks/useGlobalPref'; import { useNavigate } from '../../hooks/useNavigate'; +import { useAppDispatch } from '../../redux'; import { theme } from '../../style'; import { Button, ButtonWithLoading } from '../common/Button2'; import { BigInput } from '../common/Input'; @@ -23,7 +23,7 @@ import { Title } from './subscribe/common'; export function ConfigServer() { const { t } = useTranslation(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const navigate = useNavigate(); const [url, setUrl] = useState(''); const currentUrl = useServerURL(); diff --git a/packages/desktop-client/src/components/manager/ManagementApp.tsx b/packages/desktop-client/src/components/manager/ManagementApp.tsx index 05f22b26dd9..1a723eae82a 100644 --- a/packages/desktop-client/src/components/manager/ManagementApp.tsx +++ b/packages/desktop-client/src/components/manager/ManagementApp.tsx @@ -1,5 +1,4 @@ import React, { useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { Navigate, Route, Routes } from 'react-router-dom'; import { loggedIn, setAppState } from 'loot-core/client/actions'; @@ -7,6 +6,7 @@ import { loggedIn, setAppState } from 'loot-core/client/actions'; import { ProtectedRoute } from '../../auth/ProtectedRoute'; import { Permissions } from '../../auth/types'; import { useMetaThemeColor } from '../../hooks/useMetaThemeColor'; +import { useAppSelector, useAppDispatch } from '../../redux'; import { theme } from '../../style'; import { tokens } from '../../tokens'; import { @@ -62,16 +62,16 @@ export function ManagementApp() { isNarrowWidth ? theme.mobileConfigServerViewTheme : undefined, ); - const files = useSelector(state => state.budgets.allFiles); - const isLoading = useSelector(state => state.app.loadingText !== null); - const userData = useSelector(state => state.user.data); + const files = useAppSelector(state => state.budgets.allFiles); + const isLoading = useAppSelector(state => state.app.loadingText !== null); + const userData = useAppSelector(state => state.user.data); const multiuserEnabled = useMultiuserEnabled(); - const managerHasInitialized = useSelector( + const managerHasInitialized = useAppSelector( state => state.app.managerHasInitialized, ); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); // runs on mount only useEffect(() => { diff --git a/packages/desktop-client/src/components/manager/WelcomeScreen.tsx b/packages/desktop-client/src/components/manager/WelcomeScreen.tsx index 9866f16d32a..f2d64d6d845 100644 --- a/packages/desktop-client/src/components/manager/WelcomeScreen.tsx +++ b/packages/desktop-client/src/components/manager/WelcomeScreen.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { createBudget, pushModal } from 'loot-core/client/actions'; +import { useAppDispatch } from '../../redux'; import { styles, theme } from '../../style'; import { Button } from '../common/Button2'; import { Link } from '../common/Link'; @@ -13,7 +13,7 @@ import { View } from '../common/View'; export function WelcomeScreen() { const { t } = useTranslation(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); return ( { const token = new URLSearchParams(window.location.search).get('token'); send('subscribe-set-token', { token: token as string }).then(() => { diff --git a/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx b/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx index 3b6c6780312..8144a6f22c6 100644 --- a/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx +++ b/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx @@ -5,7 +5,6 @@ import React, { useMemo, useState, } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { collapseModals, @@ -38,6 +37,7 @@ import { useAccountPreviewTransactions } from '../../../hooks/useAccountPreviewT import { useDateFormat } from '../../../hooks/useDateFormat'; import { useFailedAccounts } from '../../../hooks/useFailedAccounts'; import { useNavigate } from '../../../hooks/useNavigate'; +import { useAppSelector, useAppDispatch } from '../../../redux'; import { styles, theme } from '../../../style'; import { Button } from '../../common/Button2'; import { Text } from '../../common/Text'; @@ -97,7 +97,9 @@ export function AccountTransactions({ function AccountHeader({ account }: { readonly account: AccountEntity }) { const failedAccounts = useFailedAccounts(); - const syncingAccountIds = useSelector(state => state.account.accountsSyncing); + const syncingAccountIds = useAppSelector( + state => state.account.accountsSyncing, + ); const pending = useMemo( () => syncingAccountIds.includes(account.id), [syncingAccountIds, account.id], @@ -107,7 +109,7 @@ function AccountHeader({ account }: { readonly account: AccountEntity }) { [failedAccounts, account.id], ); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const onSave = useCallback( (account: AccountEntity) => { @@ -251,7 +253,7 @@ function TransactionListWithPreviews({ }); const dateFormat = useDateFormat() || 'MM/dd/yyyy'; - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const navigate = useNavigate(); const onRefresh = useCallback(() => { diff --git a/packages/desktop-client/src/components/mobile/accounts/Accounts.tsx b/packages/desktop-client/src/components/mobile/accounts/Accounts.tsx index 3a2668004cd..16cb2f958dd 100644 --- a/packages/desktop-client/src/components/mobile/accounts/Accounts.tsx +++ b/packages/desktop-client/src/components/mobile/accounts/Accounts.tsx @@ -1,6 +1,5 @@ import React, { type CSSProperties, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { useDispatch, useSelector } from 'react-redux'; import { css } from '@emotion/css'; @@ -13,6 +12,7 @@ import { useFailedAccounts } from '../../../hooks/useFailedAccounts'; import { useNavigate } from '../../../hooks/useNavigate'; import { useSyncedPref } from '../../../hooks/useSyncedPref'; import { SvgAdd, SvgCheveronRight } from '../../../icons/v1'; +import { useDispatch, useSelector } from '../../../redux'; import { theme, styles } from '../../../style'; import { makeAmountFullStyle } from '../../budget/util'; import { Button } from '../../common/Button2'; diff --git a/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx b/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx index c2bf9cd50b2..d2ff3ade29a 100644 --- a/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx +++ b/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx @@ -1,6 +1,5 @@ import React, { memo, useCallback, useRef } from 'react'; import { useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { css } from '@emotion/css'; import { AutoTextSize } from 'auto-text-size'; @@ -31,6 +30,7 @@ import { SvgCheveronRight, } from '../../../icons/v1'; import { SvgViewShow } from '../../../icons/v2'; +import { useAppDispatch } from '../../../redux'; import { theme, styles } from '../../../style'; import { BalanceWithCarryover } from '../../budget/BalanceWithCarryover'; import { makeAmountGrey, makeBalanceAmountStyle } from '../../budget/util'; @@ -230,7 +230,7 @@ function BudgetCell({ }) { const { t } = useTranslation(); const columnWidth = getColumnWidth(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const format = useFormat(); const { showUndoNotification } = useUndo(); const [budgetType = 'rollover'] = useSyncedPref('budgetType'); @@ -408,7 +408,7 @@ const ExpenseCategory = memo(function ExpenseCategory({ const [budgetType = 'rollover'] = useSyncedPref('budgetType'); const modalBudgetType = budgetType === 'rollover' ? 'envelope' : 'tracking'; - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const { showUndoNotification } = useUndo(); const { list: categories } = useCategories(); const categoriesById = groupById(categories); diff --git a/packages/desktop-client/src/components/mobile/budget/CategoryTransactions.tsx b/packages/desktop-client/src/components/mobile/budget/CategoryTransactions.tsx index 4eee5f6e2cb..9626c6e9cfa 100644 --- a/packages/desktop-client/src/components/mobile/budget/CategoryTransactions.tsx +++ b/packages/desktop-client/src/components/mobile/budget/CategoryTransactions.tsx @@ -1,5 +1,4 @@ import React, { useCallback, useEffect, useState } from 'react'; -import { useDispatch } from 'react-redux'; import { getPayees } from 'loot-core/client/actions'; import { @@ -18,6 +17,7 @@ import { import { useDateFormat } from '../../../hooks/useDateFormat'; import { useNavigate } from '../../../hooks/useNavigate'; +import { useAppDispatch } from '../../../redux'; import { TextOneLine } from '../../common/TextOneLine'; import { View } from '../../common/View'; import { MobilePageHeader, Page } from '../../Page'; @@ -34,7 +34,7 @@ export function CategoryTransactions({ category, month, }: CategoryTransactionsProps) { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const navigate = useNavigate(); const baseTransactionsQuery = useCallback( diff --git a/packages/desktop-client/src/components/mobile/budget/index.tsx b/packages/desktop-client/src/components/mobile/budget/index.tsx index 20d233753e9..bcec3e25233 100644 --- a/packages/desktop-client/src/components/mobile/budget/index.tsx +++ b/packages/desktop-client/src/components/mobile/budget/index.tsx @@ -1,6 +1,5 @@ // @ts-strict-ignore import React, { useCallback, useEffect, useState } from 'react'; -import { useDispatch } from 'react-redux'; import { applyBudgetAction, @@ -25,6 +24,7 @@ import { useCategories } from '../../../hooks/useCategories'; import { useLocalPref } from '../../../hooks/useLocalPref'; import { useSyncedPref } from '../../../hooks/useSyncedPref'; import { AnimatedLoading } from '../../../icons/AnimatedLoading'; +import { useAppDispatch } from '../../../redux'; import { theme } from '../../../style'; import { prewarmMonth } from '../../budget/util'; import { View } from '../../common/View'; @@ -55,7 +55,7 @@ export function Budget() { const [_numberFormat] = useSyncedPref('numberFormat'); const numberFormat = _numberFormat || 'comma-dot'; const [hideFraction] = useSyncedPref('hideFraction'); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); useEffect(() => { async function init() { diff --git a/packages/desktop-client/src/components/mobile/transactions/TransactionEdit.jsx b/packages/desktop-client/src/components/mobile/transactions/TransactionEdit.jsx index 3e0abc54898..c4852d5d852 100644 --- a/packages/desktop-client/src/components/mobile/transactions/TransactionEdit.jsx +++ b/packages/desktop-client/src/components/mobile/transactions/TransactionEdit.jsx @@ -8,7 +8,6 @@ import React, { useCallback, } from 'react'; import { useTranslation } from 'react-i18next'; -import { useDispatch, useSelector } from 'react-redux'; import { useLocation, useParams } from 'react-router-dom'; import { @@ -55,6 +54,7 @@ import { import { SvgSplit } from '../../../icons/v0'; import { SvgAdd, SvgPiggyBank, SvgTrash } from '../../../icons/v1'; import { SvgPencilWriteAlternate } from '../../../icons/v2'; +import { useAppSelector, useAppDispatch } from '../../../redux'; import { styles, theme } from '../../../style'; import { Button } from '../../common/Button'; import { Text } from '../../common/Text'; @@ -451,7 +451,7 @@ const TransactionEditInner = memo(function TransactionEditInner({ }) { const { t } = useTranslation(); const navigate = useNavigate(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const transactions = useMemo( () => unserializedTransactions.map(t => serializeTransaction(t, dateFormat)) || @@ -1022,7 +1022,7 @@ function TransactionEditUnconnected({ const { transactionId } = useParams(); const { state: locationState } = useLocation(); const navigate = useNavigate(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [transactions, setTransactions] = useState([]); const [fetchedTransactions, setFetchedTransactions] = useState([]); const isAdding = useRef(false); @@ -1246,7 +1246,9 @@ function TransactionEditUnconnected({ export const TransactionEdit = props => { const { list: categories } = useCategories(); const payees = usePayees(); - const lastTransaction = useSelector(state => state.queries.lastTransaction); + const lastTransaction = useAppSelector( + state => state.queries.lastTransaction, + ); const accounts = useAccounts(); const dateFormat = useDateFormat() || 'MM/dd/yyyy'; diff --git a/packages/desktop-client/src/components/mobile/transactions/TransactionList.jsx b/packages/desktop-client/src/components/mobile/transactions/TransactionList.jsx index f577126fdb8..d3e57533ea3 100644 --- a/packages/desktop-client/src/components/mobile/transactions/TransactionList.jsx +++ b/packages/desktop-client/src/components/mobile/transactions/TransactionList.jsx @@ -7,7 +7,6 @@ import React, { } from 'react'; import { ListBox, Section, Header, Collection } from 'react-aria-components'; import { useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { setNotificationInset } from 'loot-core/client/actions'; import { groupById, integerToCurrency } from 'loot-core/shared/util'; @@ -27,6 +26,7 @@ import { useUndo } from '../../../hooks/useUndo'; import { AnimatedLoading } from '../../../icons/AnimatedLoading'; import { SvgDelete } from '../../../icons/v0'; import { SvgDotsHorizontalTriple } from '../../../icons/v1'; +import { useAppDispatch } from '../../../redux'; import { styles, theme } from '../../../style'; import { Button } from '../../common/Button2'; import { Menu } from '../../common/Menu'; @@ -246,7 +246,7 @@ function SelectedTransactionsFloatingActionBar({ transactions, style }) { const { list: categories } = useCategories(); const categoriesById = useMemo(() => groupById(categories), [categories]); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); useEffect(() => { dispatch(setNotificationInset({ bottom: NOTIFICATION_BOTTOM_INSET })); return () => { diff --git a/packages/desktop-client/src/components/mobile/transactions/TransactionListItem.tsx b/packages/desktop-client/src/components/mobile/transactions/TransactionListItem.tsx index db898ec9ef8..90cc5ad9fb7 100644 --- a/packages/desktop-client/src/components/mobile/transactions/TransactionListItem.tsx +++ b/packages/desktop-client/src/components/mobile/transactions/TransactionListItem.tsx @@ -4,7 +4,6 @@ import React, { } from 'react'; import { mergeProps } from 'react-aria'; import { ListBoxItem } from 'react-aria-components'; -import { useSelector } from 'react-redux'; import { PressResponder, @@ -25,6 +24,7 @@ import { SvgCheckCircle1, SvgLockClosed, } from '../../../icons/v2'; +import { useAppSelector } from '../../../redux'; import { styles, theme } from '../../../style'; import { makeAmountFullStyle } from '../../budget/util'; import { Button } from '../../common/Button2'; @@ -59,7 +59,9 @@ export function TransactionListItem({ const transferAccount = useAccount(payee?.transfer_acct || ''); const isPreview = isPreviewId(transaction?.id || ''); - const newTransactions = useSelector(state => state.queries.newTransactions); + const newTransactions = useAppSelector( + state => state.queries.newTransactions, + ); const { longPressProps } = useLongPress({ accessibilityDescription: 'Long press to select multiple transactions', diff --git a/packages/desktop-client/src/components/modals/BudgetListModal.tsx b/packages/desktop-client/src/components/modals/BudgetListModal.tsx index 10e4e5e5829..2fb8f674ee1 100644 --- a/packages/desktop-client/src/components/modals/BudgetListModal.tsx +++ b/packages/desktop-client/src/components/modals/BudgetListModal.tsx @@ -1,8 +1,8 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; import { useMetadataPref } from '../../hooks/useMetadataPref'; +import { useAppSelector } from '../../redux'; import { Modal, ModalHeader, ModalCloseButton } from '../common/Modal'; import { Text } from '../common/Text'; import { View } from '../common/View'; @@ -11,7 +11,7 @@ import { BudgetList } from '../manager/BudgetList'; export function BudgetListModal() { const { t } = useTranslation(); const [id] = useMetadataPref('id'); - const currentFile = useSelector(state => + const currentFile = useAppSelector(state => state.budgets.allFiles?.find(f => 'id' in f && f.id === id), ); diff --git a/packages/desktop-client/src/components/modals/CloseAccountModal.tsx b/packages/desktop-client/src/components/modals/CloseAccountModal.tsx index 2b476bd37d1..98a606bc0a9 100644 --- a/packages/desktop-client/src/components/modals/CloseAccountModal.tsx +++ b/packages/desktop-client/src/components/modals/CloseAccountModal.tsx @@ -2,7 +2,6 @@ import React, { type FormEvent, useState, type CSSProperties } from 'react'; import { Form } from 'react-aria-components'; import { useTranslation, Trans } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { closeAccount, @@ -15,6 +14,7 @@ import { type TransObjectLiteral } from 'loot-core/types/util'; import { useAccounts } from '../../hooks/useAccounts'; import { useCategories } from '../../hooks/useCategories'; +import { useDispatch } from '../../redux'; import { styles, theme } from '../../style'; import { AccountAutocomplete } from '../autocomplete/AccountAutocomplete'; import { CategoryAutocomplete } from '../autocomplete/CategoryAutocomplete'; diff --git a/packages/desktop-client/src/components/modals/CoverModal.tsx b/packages/desktop-client/src/components/modals/CoverModal.tsx index 30a94eb76f5..ec8aa7cb0d1 100644 --- a/packages/desktop-client/src/components/modals/CoverModal.tsx +++ b/packages/desktop-client/src/components/modals/CoverModal.tsx @@ -1,11 +1,11 @@ import React, { useCallback, useMemo, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { pushModal } from 'loot-core/client/actions'; import { type CategoryEntity } from 'loot-core/src/types/models'; import { useCategories } from '../../hooks/useCategories'; +import { useAppDispatch } from '../../redux'; import { styles } from '../../style'; import { addToBeBudgetedGroup, @@ -49,7 +49,7 @@ export function CoverModal({ }, [categoryId, originalCategoryGroups, showToBeBudgeted]); const [fromCategoryId, setFromCategoryId] = useState(null); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const onCategoryClick = useCallback(() => { dispatch( diff --git a/packages/desktop-client/src/components/modals/CreateAccountModal.tsx b/packages/desktop-client/src/components/modals/CreateAccountModal.tsx index 85d438a65fb..00e7874b30d 100644 --- a/packages/desktop-client/src/components/modals/CreateAccountModal.tsx +++ b/packages/desktop-client/src/components/modals/CreateAccountModal.tsx @@ -1,7 +1,6 @@ import React, { useEffect, useState } from 'react'; import { DialogTrigger } from 'react-aria-components'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { pushModal } from 'loot-core/client/actions'; import { send } from 'loot-core/src/platform/client/fetch'; @@ -13,6 +12,7 @@ import { useGoCardlessStatus } from '../../hooks/useGoCardlessStatus'; import { useSimpleFinStatus } from '../../hooks/useSimpleFinStatus'; import { useSyncServerStatus } from '../../hooks/useSyncServerStatus'; import { SvgDotsHorizontalTriple } from '../../icons/v1'; +import { useAppDispatch } from '../../redux'; import { theme } from '../../style'; import { Warning } from '../alerts'; import { Button, ButtonWithLoading } from '../common/Button2'; @@ -34,7 +34,7 @@ export function CreateAccountModal({ upgradingAccountId }: CreateAccountProps) { const { t } = useTranslation(); const syncServerStatus = useSyncServerStatus(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [isGoCardlessSetupComplete, setIsGoCardlessSetupComplete] = useState< boolean | null >(null); diff --git a/packages/desktop-client/src/components/modals/CreateEncryptionKeyModal.tsx b/packages/desktop-client/src/components/modals/CreateEncryptionKeyModal.tsx index 77ec1b9eb71..1e9c3bbcba2 100644 --- a/packages/desktop-client/src/components/modals/CreateEncryptionKeyModal.tsx +++ b/packages/desktop-client/src/components/modals/CreateEncryptionKeyModal.tsx @@ -2,7 +2,6 @@ import React, { useState } from 'react'; import { Form } from 'react-aria-components'; import { useTranslation, Trans } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { css } from '@emotion/css'; @@ -10,6 +9,7 @@ import { loadAllFiles, loadGlobalPrefs, sync } from 'loot-core/client/actions'; import { send } from 'loot-core/src/platform/client/fetch'; import { getCreateKeyError } from 'loot-core/src/shared/errors'; +import { useDispatch } from '../../redux'; import { styles, theme } from '../../style'; import { ButtonWithLoading } from '../common/Button2'; import { InitialFocus } from '../common/InitialFocus'; diff --git a/packages/desktop-client/src/components/modals/CreateLocalAccountModal.tsx b/packages/desktop-client/src/components/modals/CreateLocalAccountModal.tsx index d2147150b84..273593ba553 100644 --- a/packages/desktop-client/src/components/modals/CreateLocalAccountModal.tsx +++ b/packages/desktop-client/src/components/modals/CreateLocalAccountModal.tsx @@ -2,13 +2,13 @@ import { type FormEvent, useState } from 'react'; import { Form } from 'react-aria-components'; import { useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { closeModal, createAccount } from 'loot-core/client/actions'; import { toRelaxedNumber } from 'loot-core/src/shared/util'; import * as useAccounts from '../../hooks/useAccounts'; import { useNavigate } from '../../hooks/useNavigate'; +import { useAppDispatch } from '../../redux'; import { theme } from '../../style'; import { Button } from '../common/Button2'; import { FormError } from '../common/FormError'; @@ -31,7 +31,7 @@ import { validateAccountName } from '../util/accountValidation'; export function CreateLocalAccountModal() { const { t } = useTranslation(); const navigate = useNavigate(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const accounts = useAccounts.useAccounts(); const [name, setName] = useState(''); const [offbudget, setOffbudget] = useState(false); diff --git a/packages/desktop-client/src/components/modals/EditRuleModal.jsx b/packages/desktop-client/src/components/modals/EditRuleModal.jsx index 78599bb261d..f70aae87093 100644 --- a/packages/desktop-client/src/components/modals/EditRuleModal.jsx +++ b/packages/desktop-client/src/components/modals/EditRuleModal.jsx @@ -1,6 +1,5 @@ import React, { useState, useEffect, useRef, useMemo } from 'react'; import { useTranslation, Trans } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { css } from '@emotion/css'; import { v4 as uuid } from 'uuid'; @@ -37,6 +36,7 @@ import { useFeatureFlag } from '../../hooks/useFeatureFlag'; import { useSelected, SelectedProvider } from '../../hooks/useSelected'; import { SvgDelete, SvgAdd, SvgSubtract } from '../../icons/v0'; import { SvgAlignLeft, SvgCode, SvgInformationOutline } from '../../icons/v1'; +import { useDispatch } from '../../redux'; import { styles, theme } from '../../style'; import { Button } from '../common/Button2'; import { Menu } from '../common/Menu'; diff --git a/packages/desktop-client/src/components/modals/EnvelopeBudgetSummaryModal.tsx b/packages/desktop-client/src/components/modals/EnvelopeBudgetSummaryModal.tsx index 8c9b247da3b..022f96fb01a 100644 --- a/packages/desktop-client/src/components/modals/EnvelopeBudgetSummaryModal.tsx +++ b/packages/desktop-client/src/components/modals/EnvelopeBudgetSummaryModal.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { collapseModals, pushModal } from 'loot-core/client/actions'; import { envelopeBudget } from 'loot-core/client/queries'; @@ -9,6 +8,7 @@ import { format, sheetForMonth, prevMonth } from 'loot-core/src/shared/months'; import { useCategories } from '../../hooks/useCategories'; import { useUndo } from '../../hooks/useUndo'; +import { useAppDispatch } from '../../redux'; import { styles } from '../../style'; import { ToBudgetAmount } from '../budget/envelope/budgetsummary/ToBudgetAmount'; import { TotalsList } from '../budget/envelope/budgetsummary/TotalsList'; @@ -25,7 +25,7 @@ export function EnvelopeBudgetSummaryModal({ month, onBudgetAction, }: EnvelopeBudgetSummaryModalProps) { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const prevMonthName = format(prevMonth(month), 'MMM'); const sheetValue = useEnvelopeSheetValue({ diff --git a/packages/desktop-client/src/components/modals/GoCardlessExternalMsgModal.tsx b/packages/desktop-client/src/components/modals/GoCardlessExternalMsgModal.tsx index 886fff3c88e..c67204861bc 100644 --- a/packages/desktop-client/src/components/modals/GoCardlessExternalMsgModal.tsx +++ b/packages/desktop-client/src/components/modals/GoCardlessExternalMsgModal.tsx @@ -1,7 +1,6 @@ // @ts-strict-ignore import React, { useEffect, useState, useRef } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { pushModal } from 'loot-core/src/client/actions/modals'; import { sendCatch } from 'loot-core/src/platform/client/fetch'; @@ -12,6 +11,7 @@ import { import { useGoCardlessStatus } from '../../hooks/useGoCardlessStatus'; import { AnimatedLoading } from '../../icons/AnimatedLoading'; +import { useAppDispatch } from '../../redux'; import { theme } from '../../style'; import { Error, Warning } from '../alerts'; import { Autocomplete } from '../autocomplete/Autocomplete'; @@ -87,7 +87,7 @@ export function GoCardlessExternalMsgModal({ }: GoCardlessExternalMsgProps) { const { t } = useTranslation(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [waiting, setWaiting] = useState(null); const [success, setSuccess] = useState(false); diff --git a/packages/desktop-client/src/components/modals/ImportTransactionsModal/ImportTransactionsModal.jsx b/packages/desktop-client/src/components/modals/ImportTransactionsModal/ImportTransactionsModal.jsx index 530b2d10324..1eb5b2c0859 100644 --- a/packages/desktop-client/src/components/modals/ImportTransactionsModal/ImportTransactionsModal.jsx +++ b/packages/desktop-client/src/components/modals/ImportTransactionsModal/ImportTransactionsModal.jsx @@ -1,6 +1,5 @@ import React, { useState, useEffect, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import deepEqual from 'deep-equal'; @@ -14,6 +13,7 @@ import { amountToInteger } from 'loot-core/src/shared/util'; import { useDateFormat } from '../../../hooks/useDateFormat'; import { useSyncedPrefs } from '../../../hooks/useSyncedPrefs'; +import { useAppDispatch } from '../../../redux'; import { theme } from '../../../style'; import { Button, ButtonWithLoading } from '../../common/Button2'; import { Input } from '../../common/Input'; @@ -144,7 +144,7 @@ export function ImportTransactionsModal({ options }) { const { t } = useTranslation(); const dateFormat = useDateFormat() || 'MM/dd/yyyy'; const [prefs, savePrefs] = useSyncedPrefs(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [multiplierAmount, setMultiplierAmount] = useState(''); const [loadingState, setLoadingState] = useState('parsing'); diff --git a/packages/desktop-client/src/components/modals/LoadBackupModal.tsx b/packages/desktop-client/src/components/modals/LoadBackupModal.tsx index 0be83b372c1..b156a41a446 100644 --- a/packages/desktop-client/src/components/modals/LoadBackupModal.tsx +++ b/packages/desktop-client/src/components/modals/LoadBackupModal.tsx @@ -1,12 +1,12 @@ import React, { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { loadBackup, makeBackup } from 'loot-core/client/actions'; import { type Backup } from 'loot-core/server/backups'; import { send, listen, unlisten } from 'loot-core/src/platform/client/fetch'; import { useMetadataPref } from '../../hooks/useMetadataPref'; +import { useAppDispatch } from '../../redux'; import { theme } from '../../style'; import { Block } from '../common/Block'; import { Button } from '../common/Button2'; @@ -52,7 +52,7 @@ export function LoadBackupModal({ watchUpdates, backupDisabled, }: LoadBackupModalProps) { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [backups, setBackups] = useState([]); const [prefsBudgetId] = useMetadataPref('id'); const budgetIdToLoad = budgetId ?? prefsBudgetId; diff --git a/packages/desktop-client/src/components/modals/MergeUnusedPayeesModal.tsx b/packages/desktop-client/src/components/modals/MergeUnusedPayeesModal.tsx index acd5ae9b388..cd32d62d711 100644 --- a/packages/desktop-client/src/components/modals/MergeUnusedPayeesModal.tsx +++ b/packages/desktop-client/src/components/modals/MergeUnusedPayeesModal.tsx @@ -1,12 +1,12 @@ import React, { useState, useRef, useEffect, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { useDispatch, useSelector } from 'react-redux'; import { replaceModal } from 'loot-core/src/client/actions/modals'; import { send } from 'loot-core/src/platform/client/fetch'; import { type PayeeEntity } from 'loot-core/types/models'; import { usePayees } from '../../hooks/usePayees'; +import { useAppSelector, useAppDispatch } from '../../redux'; import { theme } from '../../style'; import { Information } from '../alerts'; import { Button } from '../common/Button2'; @@ -28,9 +28,9 @@ export function MergeUnusedPayeesModal({ }: MergeUnusedPayeesModalProps) { const { t } = useTranslation(); const allPayees = usePayees(); - const modalStack = useSelector(state => state.modals.modalStack); + const modalStack = useAppSelector(state => state.modals.modalStack); const isEditingRule = !!modalStack.find(m => m.name === 'edit-rule'); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [shouldCreateRule, setShouldCreateRule] = useState(true); const flashRef = useRef(null); diff --git a/packages/desktop-client/src/components/modals/OutOfSyncMigrationsModal.tsx b/packages/desktop-client/src/components/modals/OutOfSyncMigrationsModal.tsx index 60bd9fc054c..2461da9cc9b 100644 --- a/packages/desktop-client/src/components/modals/OutOfSyncMigrationsModal.tsx +++ b/packages/desktop-client/src/components/modals/OutOfSyncMigrationsModal.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { closeBudget } from 'loot-core/client/actions'; +import { useAppDispatch } from '../../redux'; import { Button } from '../common/Button2'; import { Link } from '../common/Link'; import { Modal, ModalHeader, ModalTitle } from '../common/Modal'; @@ -12,7 +12,7 @@ import { Text } from '../common/Text'; import { View } from '../common/View'; export function OutOfSyncMigrationsModal() { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const { t } = useTranslation(); diff --git a/packages/desktop-client/src/components/modals/SelectLinkedAccountsModal.jsx b/packages/desktop-client/src/components/modals/SelectLinkedAccountsModal.jsx index 98eaf9a8839..d72bd8e8cf0 100644 --- a/packages/desktop-client/src/components/modals/SelectLinkedAccountsModal.jsx +++ b/packages/desktop-client/src/components/modals/SelectLinkedAccountsModal.jsx @@ -1,6 +1,5 @@ import React, { useState } from 'react'; import { useTranslation, Trans } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { closeModal, @@ -10,6 +9,7 @@ import { } from 'loot-core/client/actions'; import { useAccounts } from '../../hooks/useAccounts'; +import { useDispatch } from '../../redux'; import { theme } from '../../style'; import { Autocomplete } from '../autocomplete/Autocomplete'; import { Button } from '../common/Button2'; diff --git a/packages/desktop-client/src/components/modals/TransferModal.tsx b/packages/desktop-client/src/components/modals/TransferModal.tsx index 2abd515a618..a0dff11bfea 100644 --- a/packages/desktop-client/src/components/modals/TransferModal.tsx +++ b/packages/desktop-client/src/components/modals/TransferModal.tsx @@ -1,11 +1,11 @@ import React, { useMemo, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { pushModal } from 'loot-core/client/actions'; import { type CategoryEntity } from 'loot-core/types/models'; import { useCategories } from '../../hooks/useCategories'; +import { useAppDispatch } from '../../redux'; import { styles } from '../../style'; import { addToBeBudgetedGroup, @@ -55,7 +55,7 @@ export function TransferModal({ const [amount, setAmount] = useState(0); const [toCategoryId, setToCategoryId] = useState(null); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const openCategoryModal = () => { dispatch( diff --git a/packages/desktop-client/src/components/modals/TransferOwnership.tsx b/packages/desktop-client/src/components/modals/TransferOwnership.tsx index bb2a198c48c..38ee1c9e768 100644 --- a/packages/desktop-client/src/components/modals/TransferOwnership.tsx +++ b/packages/desktop-client/src/components/modals/TransferOwnership.tsx @@ -1,9 +1,7 @@ import { useEffect, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch, useSelector } from 'react-redux'; import { addNotification, closeAndLoadBudget } from 'loot-core/client/actions'; -import { type State } from 'loot-core/client/state-types'; import { send } from 'loot-core/platform/client/fetch'; import { getUserAccessErrors } from 'loot-core/shared/errors'; import { type Budget } from 'loot-core/types/budget'; @@ -12,6 +10,7 @@ import { type Handlers } from 'loot-core/types/handlers'; import { useActions } from '../../hooks/useActions'; import { useMetadataPref } from '../../hooks/useMetadataPref'; +import { useAppDispatch, useAppSelector } from '../../redux'; import { styles, theme } from '../../style'; import { Button } from '../common/Button2'; import { Modal, ModalCloseButton, ModalHeader } from '../common/Modal'; @@ -30,18 +29,18 @@ export function TransferOwnership({ }: TransferOwnershipProps) { const { t } = useTranslation(); - const userData = useSelector((state: State) => state.user.data); + const userData = useAppSelector(state => state.user.data); const actions = useActions(); const [userId, setUserId] = useState(''); const [error, setError] = useState(null); const [availableUsers, setAvailableUsers] = useState<[string, string][]>([]); const [cloudFileId] = useMetadataPref('cloudFileId'); - const allFiles = useSelector(state => state.budgets.allFiles || []); + const allFiles = useAppSelector(state => state.budgets.allFiles || []); const remoteFiles = allFiles.filter( f => f.state === 'remote' || f.state === 'synced' || f.state === 'detached', ) as (SyncedLocalFile | RemoteFile)[]; const currentFile = remoteFiles.find(f => f.cloudFileId === cloudFileId); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [isTransferring, setIsTransferring] = useState(false); useEffect(() => { diff --git a/packages/desktop-client/src/components/modals/manager/ConfirmChangeDocumentDir.tsx b/packages/desktop-client/src/components/modals/manager/ConfirmChangeDocumentDir.tsx index 23e81a4f23e..ce42775688b 100644 --- a/packages/desktop-client/src/components/modals/manager/ConfirmChangeDocumentDir.tsx +++ b/packages/desktop-client/src/components/modals/manager/ConfirmChangeDocumentDir.tsx @@ -1,10 +1,10 @@ import React, { useCallback, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { addNotification } from 'loot-core/client/actions'; import { useGlobalPref } from '../../../hooks/useGlobalPref'; +import { useAppDispatch } from '../../../redux'; import { theme, styles } from '../../../style'; import { Information } from '../../alerts'; import { Button, ButtonWithLoading } from '../../common/Button2'; @@ -48,7 +48,7 @@ export function ConfirmChangeDocumentDirModal({ const [loading, setLoading] = useState(false); const [moveFiles, setMoveFiles] = useState(false); const { t } = useTranslation(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const restartElectronServer = useCallback(() => { globalThis.window.Actual?.restartElectronServer(); diff --git a/packages/desktop-client/src/components/modals/manager/DeleteFileModal.tsx b/packages/desktop-client/src/components/modals/manager/DeleteFileModal.tsx index 3b93804fb86..3c482d43744 100644 --- a/packages/desktop-client/src/components/modals/manager/DeleteFileModal.tsx +++ b/packages/desktop-client/src/components/modals/manager/DeleteFileModal.tsx @@ -1,10 +1,10 @@ import React, { useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { deleteBudget } from 'loot-core/client/actions'; import { type File } from 'loot-core/src/types/file'; +import { useAppDispatch } from '../../../redux'; import { theme } from '../../../style'; import { ButtonWithLoading } from '../../common/Button2'; import { Modal, ModalCloseButton, ModalHeader } from '../../common/Modal'; @@ -22,7 +22,7 @@ export function DeleteFileModal({ file }: DeleteFileProps) { // user. The current user should be able to delete the local file, // but not the remote one const isCloudFile = 'cloudFileId' in file && file.state !== 'broken'; - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [loadingState, setLoadingState] = useState<'cloud' | 'local' | null>( null, diff --git a/packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx b/packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx index a614b97c6d3..5e0a9ffbf07 100644 --- a/packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx +++ b/packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx @@ -1,6 +1,5 @@ import React, { useEffect, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { addNotification, @@ -10,6 +9,7 @@ import { } from 'loot-core/client/actions'; import { type File } from 'loot-core/src/types/file'; +import { useAppDispatch } from '../../../redux'; import { theme } from '../../../style'; import { Button, ButtonWithLoading } from '../../common/Button2'; import { FormError } from '../../common/FormError'; @@ -49,7 +49,7 @@ export function DuplicateFileModal({ // If the state is "broken" that means it was created by another user. const isCloudFile = 'cloudFileId' in file && file.state !== 'broken'; const isLocalFile = 'id' in file; - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [loadingState, setLoadingState] = useState<'cloud' | 'local' | null>( null, diff --git a/packages/desktop-client/src/components/modals/manager/FilesSettingsModal.tsx b/packages/desktop-client/src/components/modals/manager/FilesSettingsModal.tsx index 55d1e7bee68..5b283335dcc 100644 --- a/packages/desktop-client/src/components/modals/manager/FilesSettingsModal.tsx +++ b/packages/desktop-client/src/components/modals/manager/FilesSettingsModal.tsx @@ -1,11 +1,11 @@ import React, { useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { loadAllFiles, pushModal } from 'loot-core/client/actions'; import { useGlobalPref } from '../../../hooks/useGlobalPref'; import { SvgPencil1 } from '../../../icons/v2'; +import { useAppDispatch } from '../../../redux'; import { theme, styles } from '../../../style'; import { Button } from '../../common/Button2'; import { Modal, ModalCloseButton, ModalHeader } from '../../common/Modal'; @@ -16,7 +16,7 @@ function FileLocationSettings() { const [documentDir, _setDocumentDirPref] = useGlobalPref('documentDir'); const [_documentDirChanged, setDirChanged] = useState(false); const { t } = useTranslation(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); async function onChooseDocumentDir() { const chosenDirectory = await window.Actual?.openFileDialog({ @@ -142,7 +142,7 @@ function SelfSignedCertLocationSettings() { } export function FilesSettingsModal() { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); function closeModal(close: () => void) { dispatch(loadAllFiles()); diff --git a/packages/desktop-client/src/components/modals/manager/ImportActualModal.tsx b/packages/desktop-client/src/components/modals/manager/ImportActualModal.tsx index ac31b5757b2..b086049f819 100644 --- a/packages/desktop-client/src/components/modals/manager/ImportActualModal.tsx +++ b/packages/desktop-client/src/components/modals/manager/ImportActualModal.tsx @@ -1,11 +1,11 @@ // @ts-strict-ignore import React, { useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { importBudget } from 'loot-core/src/client/actions/budgets'; import { useNavigate } from '../../../hooks/useNavigate'; +import { useAppDispatch } from '../../../redux'; import { styles, theme } from '../../../style'; import { Block } from '../../common/Block'; import { ButtonWithLoading } from '../../common/Button2'; @@ -33,7 +33,7 @@ function getErrorMessage(error: string): string { export function ImportActualModal() { const { t } = useTranslation(); const navigate = useNavigate(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [error, setError] = useState(null); const [importing, setImporting] = useState(false); diff --git a/packages/desktop-client/src/components/modals/manager/ImportModal.tsx b/packages/desktop-client/src/components/modals/manager/ImportModal.tsx index 3ced17d7093..62bde0e91a0 100644 --- a/packages/desktop-client/src/components/modals/manager/ImportModal.tsx +++ b/packages/desktop-client/src/components/modals/manager/ImportModal.tsx @@ -1,9 +1,9 @@ import React, { useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { pushModal } from 'loot-core/client/actions'; +import { useAppDispatch } from '../../../redux'; import { styles, theme } from '../../../style'; import { Block } from '../../common/Block'; import { Button } from '../../common/Button2'; @@ -23,7 +23,7 @@ function getErrorMessage(error: 'not-ynab4' | boolean) { export function ImportModal() { const { t } = useTranslation(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [error] = useState(false); function onSelectType(type: 'ynab4' | 'ynab5' | 'actual') { diff --git a/packages/desktop-client/src/components/modals/manager/ImportYNAB4Modal.tsx b/packages/desktop-client/src/components/modals/manager/ImportYNAB4Modal.tsx index 2c0a52b9318..b59ed31c079 100644 --- a/packages/desktop-client/src/components/modals/manager/ImportYNAB4Modal.tsx +++ b/packages/desktop-client/src/components/modals/manager/ImportYNAB4Modal.tsx @@ -1,11 +1,11 @@ // @ts-strict-ignore import React, { useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { importBudget } from 'loot-core/src/client/actions/budgets'; import { useNavigate } from '../../../hooks/useNavigate'; +import { useAppDispatch } from '../../../redux'; import { styles, theme } from '../../../style'; import { Block } from '../../common/Block'; import { ButtonWithLoading } from '../../common/Button2'; @@ -25,7 +25,7 @@ function getErrorMessage(error: string): string { export function ImportYNAB4Modal() { const { t } = useTranslation(); const navigate = useNavigate(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [error, setError] = useState(null); const [importing, setImporting] = useState(false); diff --git a/packages/desktop-client/src/components/modals/manager/ImportYNAB5Modal.tsx b/packages/desktop-client/src/components/modals/manager/ImportYNAB5Modal.tsx index 98ee2f49fac..cbf2e24bc18 100644 --- a/packages/desktop-client/src/components/modals/manager/ImportYNAB5Modal.tsx +++ b/packages/desktop-client/src/components/modals/manager/ImportYNAB5Modal.tsx @@ -1,11 +1,11 @@ // @ts-strict-ignore import React, { useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { importBudget } from 'loot-core/src/client/actions/budgets'; import { useNavigate } from '../../../hooks/useNavigate'; +import { useAppDispatch } from '../../../redux'; import { styles, theme } from '../../../style'; import { Block } from '../../common/Block'; import { ButtonWithLoading } from '../../common/Button2'; @@ -28,7 +28,7 @@ function getErrorMessage(error: string): string { export function ImportYNAB5Modal() { const { t } = useTranslation(); const navigate = useNavigate(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [error, setError] = useState(null); const [importing, setImporting] = useState(false); diff --git a/packages/desktop-client/src/components/payees/ManagePayeesWithData.tsx b/packages/desktop-client/src/components/payees/ManagePayeesWithData.tsx index 064023fbad5..fc075bb7f8d 100644 --- a/packages/desktop-client/src/components/payees/ManagePayeesWithData.tsx +++ b/packages/desktop-client/src/components/payees/ManagePayeesWithData.tsx @@ -1,18 +1,18 @@ import React, { useState, useEffect, useCallback } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { getPayees, initiallyLoadPayees, pushModal, - setLastUndoState, } from 'loot-core/client/actions'; import { type UndoState } from 'loot-core/server/undo'; import { send, listen } from 'loot-core/src/platform/client/fetch'; +import * as undo from 'loot-core/src/platform/client/undo'; import { applyChanges, type Diff } from 'loot-core/src/shared/util'; import { type NewRuleEntity, type PayeeEntity } from 'loot-core/types/models'; import { usePayees } from '../../hooks/usePayees'; +import { useAppDispatch } from '../../redux'; import { ManagePayees } from './ManagePayees'; @@ -24,8 +24,7 @@ export function ManagePayeesWithData({ initialSelectedIds, }: ManagePayeesWithDataProps) { const payees = usePayees(); - const lastUndoState = useSelector(state => state.app.lastUndoState); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [ruleCounts, setRuleCounts] = useState({ value: new Map() }); const [orphans, setOrphans] = useState([]); @@ -80,15 +79,16 @@ export function ManagePayeesWithData({ await refetchRuleCounts(); } - await dispatch(setLastUndoState(null)); + undo.setUndoState('current', null); } - if (lastUndoState.current) { - onUndo(lastUndoState.current); + const lastUndoState = undo.getUndoState('current'); + if (lastUndoState) { + onUndo(lastUndoState as UndoState); } return listen('undo-event', onUndo); - }, [dispatch, lastUndoState, refetchRuleCounts, refetchOrphanedPayees]); + }, [dispatch, refetchRuleCounts, refetchOrphanedPayees]); function onViewRules(id: PayeeEntity['id']) { dispatch(pushModal('manage-rules', { payeeId: id })); diff --git a/packages/desktop-client/src/components/reports/Overview.tsx b/packages/desktop-client/src/components/reports/Overview.tsx index 8178454a9b7..c3f01af06cb 100644 --- a/packages/desktop-client/src/components/reports/Overview.tsx +++ b/packages/desktop-client/src/components/reports/Overview.tsx @@ -2,7 +2,6 @@ import React, { useMemo, useRef, useState } from 'react'; import { Responsive, WidthProvider, type Layout } from 'react-grid-layout'; import { useHotkeys } from 'react-hotkeys-hook'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { @@ -22,6 +21,7 @@ import { import { useAccounts } from '../../hooks/useAccounts'; import { useNavigate } from '../../hooks/useNavigate'; import { useSyncedPref } from '../../hooks/useSyncedPref'; +import { useAppDispatch } from '../../redux'; import { breakpoints } from '../../tokens'; import { Button } from '../common/Button2'; import { Menu } from '../common/Menu'; @@ -51,7 +51,7 @@ function isCustomReportWidget(widget: Widget): widget is CustomReportWidget { export function Overview() { const { t } = useTranslation(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [_firstDayOfWeekIdx] = useSyncedPref('firstDayOfWeekIdx'); const firstDayOfWeekIdx = _firstDayOfWeekIdx || '0'; diff --git a/packages/desktop-client/src/components/reports/reports/Calendar.tsx b/packages/desktop-client/src/components/reports/reports/Calendar.tsx index f064616fbc7..563f15bfcd8 100644 --- a/packages/desktop-client/src/components/reports/reports/Calendar.tsx +++ b/packages/desktop-client/src/components/reports/reports/Calendar.tsx @@ -7,7 +7,6 @@ import React, { useCallback, } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { useParams, useSearchParams } from 'react-router-dom'; import { useSpring, animated, config } from 'react-spring'; @@ -48,6 +47,7 @@ import { SvgCheveronDown, SvgCheveronUp, } from '../../../icons/v1'; +import { useAppDispatch } from '../../../redux'; import { styles, theme } from '../../../style'; import { Button } from '../../common/Button2'; import { View } from '../../common/View'; @@ -278,7 +278,7 @@ function CalendarInner({ widget, parameters }: CalendarInnerProps) { run(); }, []); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const navigate = useNavigate(); const { isNarrowWidth } = useResponsive(); const title = widget?.meta?.name || t('Calendar'); diff --git a/packages/desktop-client/src/components/reports/reports/CashFlow.tsx b/packages/desktop-client/src/components/reports/reports/CashFlow.tsx index e7debf2df02..6af86242864 100644 --- a/packages/desktop-client/src/components/reports/reports/CashFlow.tsx +++ b/packages/desktop-client/src/components/reports/reports/CashFlow.tsx @@ -1,6 +1,5 @@ import React, { useState, useEffect, useMemo } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { useParams } from 'react-router-dom'; import * as d from 'date-fns'; @@ -18,6 +17,7 @@ import { import { useFilters } from '../../../hooks/useFilters'; import { useNavigate } from '../../../hooks/useNavigate'; +import { useAppDispatch } from '../../../redux'; import { theme } from '../../../style'; import { AlignedText } from '../../common/AlignedText'; import { Block } from '../../common/Block'; @@ -63,7 +63,7 @@ type CashFlowInnerProps = { }; function CashFlowInner({ widget }: CashFlowInnerProps) { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const { t } = useTranslation(); const { diff --git a/packages/desktop-client/src/components/reports/reports/CustomReportListCards.tsx b/packages/desktop-client/src/components/reports/reports/CustomReportListCards.tsx index cc0a6e14468..991a60f21ea 100644 --- a/packages/desktop-client/src/components/reports/reports/CustomReportListCards.tsx +++ b/packages/desktop-client/src/components/reports/reports/CustomReportListCards.tsx @@ -1,6 +1,5 @@ import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { send, sendCatch } from 'loot-core/platform/client/fetch/index'; import { addNotification } from 'loot-core/src/client/actions'; @@ -13,6 +12,7 @@ import { useCategories } from '../../../hooks/useCategories'; import { usePayees } from '../../../hooks/usePayees'; import { useSyncedPref } from '../../../hooks/useSyncedPref'; import { SvgExclamationSolid } from '../../../icons/v1'; +import { useAppDispatch } from '../../../redux'; import { styles } from '../../../style/index'; import { theme } from '../../../style/theme'; import { Text } from '../../common/Text'; @@ -64,7 +64,7 @@ function CustomReportListCardsInner({ }: Omit & { report: CustomReportEntity; }) { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [nameMenuOpen, setNameMenuOpen] = useState(false); const [earliestTransaction, setEarliestTransaction] = useState(''); diff --git a/packages/desktop-client/src/components/reports/reports/NetWorth.tsx b/packages/desktop-client/src/components/reports/reports/NetWorth.tsx index ce0344486f9..6c854b7739c 100644 --- a/packages/desktop-client/src/components/reports/reports/NetWorth.tsx +++ b/packages/desktop-client/src/components/reports/reports/NetWorth.tsx @@ -1,6 +1,5 @@ import React, { useState, useEffect, useMemo } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { useParams } from 'react-router-dom'; import * as d from 'date-fns'; @@ -15,6 +14,7 @@ import { type TimeFrame, type NetWorthWidget } from 'loot-core/types/models'; import { useAccounts } from '../../../hooks/useAccounts'; import { useFilters } from '../../../hooks/useFilters'; import { useNavigate } from '../../../hooks/useNavigate'; +import { useAppDispatch } from '../../../redux'; import { theme, styles } from '../../../style'; import { Button } from '../../common/Button2'; import { Paragraph } from '../../common/Paragraph'; @@ -52,7 +52,7 @@ type NetWorthInnerProps = { }; function NetWorthInner({ widget }: NetWorthInnerProps) { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const { t } = useTranslation(); const accounts = useAccounts(); diff --git a/packages/desktop-client/src/components/reports/reports/Spending.tsx b/packages/desktop-client/src/components/reports/reports/Spending.tsx index d7ae60e8da6..0d985d41c3c 100644 --- a/packages/desktop-client/src/components/reports/reports/Spending.tsx +++ b/packages/desktop-client/src/components/reports/reports/Spending.tsx @@ -1,6 +1,5 @@ import React, { useState, useMemo, useEffect } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { useParams } from 'react-router-dom'; import * as d from 'date-fns'; @@ -15,6 +14,7 @@ import { type RuleConditionEntity } from 'loot-core/types/models/rule'; import { useFilters } from '../../../hooks/useFilters'; import { useNavigate } from '../../../hooks/useNavigate'; +import { useAppDispatch } from '../../../redux'; import { theme, styles } from '../../../style'; import { AlignedText } from '../../common/AlignedText'; import { Block } from '../../common/Block'; @@ -60,7 +60,7 @@ type SpendingInternalProps = { }; function SpendingInternal({ widget }: SpendingInternalProps) { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const { t } = useTranslation(); const { diff --git a/packages/desktop-client/src/components/reports/reports/Summary.tsx b/packages/desktop-client/src/components/reports/reports/Summary.tsx index 95be1b5b465..b2cf46a096a 100644 --- a/packages/desktop-client/src/components/reports/reports/Summary.tsx +++ b/packages/desktop-client/src/components/reports/reports/Summary.tsx @@ -1,6 +1,5 @@ import React, { useState, useEffect, useMemo, type CSSProperties } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { useParams } from 'react-router-dom'; import { parseISO } from 'date-fns'; @@ -22,6 +21,7 @@ import { SvgEquals } from '../../../icons/v1'; import { SvgCloseParenthesis } from '../../../icons/v2/CloseParenthesis'; import { SvgOpenParenthesis } from '../../../icons/v2/OpenParenthesis'; import { SvgSum } from '../../../icons/v2/Sum'; +import { useAppDispatch } from '../../../redux'; import { theme } from '../../../style'; import { Button } from '../../common/Button2'; import { Text } from '../../common/Text'; @@ -176,7 +176,7 @@ function SummaryInner({ widget }: SummaryInnerProps) { run(); }, []); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const navigate = useNavigate(); const { isNarrowWidth } = useResponsive(); const title = widget?.meta?.name || t('Summary'); diff --git a/packages/desktop-client/src/components/schedules/PostsOfflineNotification.tsx b/packages/desktop-client/src/components/schedules/PostsOfflineNotification.tsx index 2dd79ea0efb..ca53af4c773 100644 --- a/packages/desktop-client/src/components/schedules/PostsOfflineNotification.tsx +++ b/packages/desktop-client/src/components/schedules/PostsOfflineNotification.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { popModal } from 'loot-core/client/actions'; @@ -8,6 +7,7 @@ import { send } from 'loot-core/src/platform/client/fetch'; import { type PayeeEntity } from 'loot-core/types/models'; import { useFormatList } from '../../hooks/useFormatList'; +import { useAppDispatch } from '../../redux'; import { theme } from '../../style'; import { Button } from '../common/Button2'; import { Modal, ModalCloseButton, ModalHeader } from '../common/Modal'; @@ -20,7 +20,7 @@ export function PostsOfflineNotification() { const { t, i18n } = useTranslation(); const location = useLocation(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const locationState = location.state; const payees = diff --git a/packages/desktop-client/src/components/schedules/ScheduleDetails.tsx b/packages/desktop-client/src/components/schedules/ScheduleDetails.tsx index 6d02f34d1ed..9a776d6c273 100644 --- a/packages/desktop-client/src/components/schedules/ScheduleDetails.tsx +++ b/packages/desktop-client/src/components/schedules/ScheduleDetails.tsx @@ -1,7 +1,6 @@ // @ts-strict-ignore import React, { useEffect, useReducer } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { t } from 'i18next'; @@ -22,6 +21,7 @@ import { import { useDateFormat } from '../../hooks/useDateFormat'; import { usePayees } from '../../hooks/usePayees'; import { useSelected, SelectedProvider } from '../../hooks/useSelected'; +import { useAppDispatch } from '../../redux'; import { theme } from '../../style'; import { AccountAutocomplete } from '../autocomplete/AccountAutocomplete'; import { PayeeAutocomplete } from '../autocomplete/PayeeAutocomplete'; @@ -111,7 +111,7 @@ export function ScheduleDetails({ id, transaction }: ScheduleDetailsProps) { const adding = id == null; const fromTrans = transaction != null; const payees = getPayeesById(usePayees()); - const globalDispatch = useDispatch(); + const globalDispatch = useAppDispatch(); const dateFormat = useDateFormat() || 'MM/dd/yyyy'; const [state, dispatch] = useReducer( diff --git a/packages/desktop-client/src/components/schedules/ScheduleLink.tsx b/packages/desktop-client/src/components/schedules/ScheduleLink.tsx index a787fe9fe6e..6e7e3f12cb4 100644 --- a/packages/desktop-client/src/components/schedules/ScheduleLink.tsx +++ b/packages/desktop-client/src/components/schedules/ScheduleLink.tsx @@ -1,7 +1,6 @@ // @ts-strict-ignore import React, { useMemo, useRef, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { pushModal } from 'loot-core/client/actions'; import { useSchedules } from 'loot-core/src/client/data-hooks/schedules'; @@ -13,6 +12,7 @@ import { } from 'loot-core/src/types/models'; import { SvgAdd } from '../../icons/v0'; +import { useAppDispatch } from '../../redux'; import { Button } from '../common/Button2'; import { InitialFocus } from '../common/InitialFocus'; import { Modal, ModalCloseButton, ModalHeader } from '../common/Modal'; @@ -35,7 +35,7 @@ export function ScheduleLink({ }) { const { t } = useTranslation(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [filter, setFilter] = useState(accountName || ''); const schedulesQuery = useMemo( () => q('schedules').filter({ completed: false }).select('*'), diff --git a/packages/desktop-client/src/components/schedules/index.tsx b/packages/desktop-client/src/components/schedules/index.tsx index 02db32b2a6e..1d783a65b13 100644 --- a/packages/desktop-client/src/components/schedules/index.tsx +++ b/packages/desktop-client/src/components/schedules/index.tsx @@ -1,6 +1,5 @@ import React, { useCallback, useMemo, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { pushModal } from 'loot-core/client/actions'; import { q } from 'loot-core/shared/query'; @@ -8,6 +7,7 @@ import { useSchedules } from 'loot-core/src/client/data-hooks/schedules'; import { send } from 'loot-core/src/platform/client/fetch'; import { type ScheduleEntity } from 'loot-core/src/types/models'; +import { useAppDispatch } from '../../redux'; import { theme } from '../../style'; import { Button } from '../common/Button2'; import { Search } from '../common/Search'; @@ -20,7 +20,7 @@ import { type ScheduleItemAction, SchedulesTable } from './SchedulesTable'; export function Schedules() { const { t } = useTranslation(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [filter, setFilter] = useState(''); const onEdit = useCallback( diff --git a/packages/desktop-client/src/components/settings/AuthSettings.tsx b/packages/desktop-client/src/components/settings/AuthSettings.tsx index 229a5ed92b4..bb228b5e64a 100644 --- a/packages/desktop-client/src/components/settings/AuthSettings.tsx +++ b/packages/desktop-client/src/components/settings/AuthSettings.tsx @@ -1,10 +1,10 @@ import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { pushModal } from 'loot-core/client/actions'; import { useFeatureFlag } from '../../hooks/useFeatureFlag'; +import { useAppDispatch } from '../../redux'; import { theme } from '../../style'; import { Button } from '../common/Button2'; import { Label } from '../common/Label'; @@ -18,7 +18,7 @@ export function AuthSettings() { const multiuserEnabled = useMultiuserEnabled(); const loginMethod = useLoginMethod(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const openidAuthFeatureFlag = useFeatureFlag('openidAuth'); return openidAuthFeatureFlag === true ? ( diff --git a/packages/desktop-client/src/components/settings/Encryption.tsx b/packages/desktop-client/src/components/settings/Encryption.tsx index 8a35bf9f738..fa65538415a 100644 --- a/packages/desktop-client/src/components/settings/Encryption.tsx +++ b/packages/desktop-client/src/components/settings/Encryption.tsx @@ -1,10 +1,10 @@ import React from 'react'; import { Trans } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { pushModal } from 'loot-core/client/actions'; import { useMetadataPref } from '../../hooks/useMetadataPref'; +import { useDispatch } from '../../redux'; import { theme } from '../../style'; import { Button } from '../common/Button2'; import { Link } from '../common/Link'; diff --git a/packages/desktop-client/src/components/settings/Reset.tsx b/packages/desktop-client/src/components/settings/Reset.tsx index f698d99544e..a9ef6e293b9 100644 --- a/packages/desktop-client/src/components/settings/Reset.tsx +++ b/packages/desktop-client/src/components/settings/Reset.tsx @@ -1,11 +1,11 @@ import React, { useState } from 'react'; import { Trans } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { resetSync } from 'loot-core/client/actions'; import { send } from 'loot-core/src/platform/client/fetch'; import { useMetadataPref } from '../../hooks/useMetadataPref'; +import { useDispatch } from '../../redux'; import { ButtonWithLoading } from '../common/Button2'; import { Text } from '../common/Text'; diff --git a/packages/desktop-client/src/components/settings/index.tsx b/packages/desktop-client/src/components/settings/index.tsx index e5ada574711..491ed850e60 100644 --- a/packages/desktop-client/src/components/settings/index.tsx +++ b/packages/desktop-client/src/components/settings/index.tsx @@ -1,6 +1,5 @@ import React, { type ReactNode, useEffect } from 'react'; import { useTranslation, Trans } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { css } from '@emotion/css'; @@ -11,6 +10,7 @@ import { listen } from 'loot-core/src/platform/client/fetch'; import { useGlobalPref } from '../../hooks/useGlobalPref'; import { useIsOutdated, useLatestVersion } from '../../hooks/useLatestVersion'; import { useMetadataPref } from '../../hooks/useMetadataPref'; +import { useDispatch } from '../../redux'; import { theme } from '../../style'; import { tokens } from '../../tokens'; import { Button } from '../common/Button2'; diff --git a/packages/desktop-client/src/components/sidebar/Account.tsx b/packages/desktop-client/src/components/sidebar/Account.tsx index d11549f2a04..986d5793893 100644 --- a/packages/desktop-client/src/components/sidebar/Account.tsx +++ b/packages/desktop-client/src/components/sidebar/Account.tsx @@ -1,6 +1,5 @@ // @ts-strict-ignore import React, { type CSSProperties, useRef, useState } from 'react'; -import { useDispatch } from 'react-redux'; import { css, cx } from '@emotion/css'; @@ -14,6 +13,7 @@ import { type AccountEntity } from 'loot-core/src/types/models'; import { useContextMenu } from '../../hooks/useContextMenu'; import { useNotes } from '../../hooks/useNotes'; +import { useAppDispatch } from '../../redux'; import { styles, theme } from '../../style'; import { AlignedText } from '../common/AlignedText'; import { InitialFocus } from '../common/InitialFocus'; @@ -102,7 +102,7 @@ export function Account>({ onDrop, }); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [isEditing, setIsEditing] = useState(false); diff --git a/packages/desktop-client/src/components/sidebar/Accounts.tsx b/packages/desktop-client/src/components/sidebar/Accounts.tsx index 62dae517333..0089b3be8e2 100644 --- a/packages/desktop-client/src/components/sidebar/Accounts.tsx +++ b/packages/desktop-client/src/components/sidebar/Accounts.tsx @@ -1,10 +1,8 @@ import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useDispatch, useSelector } from 'react-redux'; import { moveAccount } from 'loot-core/src/client/actions'; import * as queries from 'loot-core/src/client/queries'; -import { type State } from 'loot-core/src/client/state-types'; import { type AccountEntity } from 'loot-core/types/models'; import { useAccounts } from '../../hooks/useAccounts'; @@ -14,6 +12,7 @@ import { useLocalPref } from '../../hooks/useLocalPref'; import { useOffBudgetAccounts } from '../../hooks/useOffBudgetAccounts'; import { useOnBudgetAccounts } from '../../hooks/useOnBudgetAccounts'; import { useUpdatedAccounts } from '../../hooks/useUpdatedAccounts'; +import { useAppSelector, useAppDispatch } from '../../redux'; import { theme } from '../../style'; import { View } from '../common/View'; @@ -24,7 +23,7 @@ const fontWeight = 600; export function Accounts() { const { t } = useTranslation(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [isDragging, setIsDragging] = useState(false); const accounts = useAccounts(); const failedAccounts = useFailedAccounts(); @@ -32,8 +31,8 @@ export function Accounts() { const offbudgetAccounts = useOffBudgetAccounts(); const onBudgetAccounts = useOnBudgetAccounts(); const closedAccounts = useClosedAccounts(); - const syncingAccountIds = useSelector( - (state: State) => state.account.accountsSyncing, + const syncingAccountIds = useAppSelector( + state => state.account.accountsSyncing, ); const getAccountPath = (account: AccountEntity) => `/accounts/${account.id}`; @@ -120,7 +119,7 @@ export function Accounts() { account={account} connected={!!account.bank} pending={syncingAccountIds.includes(account.id)} - failed={failedAccounts?.has(account.id)} + failed={!!failedAccounts?.[account.id]} updated={updatedAccounts?.includes(account.id)} to={getAccountPath(account)} query={queries.accountBalance(account)} @@ -150,7 +149,7 @@ export function Accounts() { account={account} connected={!!account.bank} pending={syncingAccountIds.includes(account.id)} - failed={failedAccounts?.has(account.id)} + failed={!!failedAccounts?.[account.id]} updated={updatedAccounts?.includes(account.id)} to={getAccountPath(account)} query={queries.accountBalance(account)} diff --git a/packages/desktop-client/src/components/sidebar/BudgetName.tsx b/packages/desktop-client/src/components/sidebar/BudgetName.tsx index 3201d976769..dddfdb6f1c9 100644 --- a/packages/desktop-client/src/components/sidebar/BudgetName.tsx +++ b/packages/desktop-client/src/components/sidebar/BudgetName.tsx @@ -1,6 +1,5 @@ import React, { type ReactNode, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { closeBudget } from 'loot-core/src/client/actions'; import * as Platform from 'loot-core/src/client/platform'; @@ -9,6 +8,7 @@ import { useContextMenu } from '../../hooks/useContextMenu'; import { useMetadataPref } from '../../hooks/useMetadataPref'; import { useNavigate } from '../../hooks/useNavigate'; import { SvgExpandArrow } from '../../icons/v0'; +import { useAppDispatch } from '../../redux'; import { theme } from '../../style'; import { Button } from '../common/Button2'; import { InitialFocus } from '../common/InitialFocus'; @@ -53,7 +53,7 @@ export function BudgetName({ children }: BudgetNameProps) { function EditableBudgetName() { const { t } = useTranslation(); const [budgetName, setBudgetNamePref] = useMetadataPref('budgetName'); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const navigate = useNavigate(); const triggerRef = useRef(null); const [editing, setEditing] = useState(false); diff --git a/packages/desktop-client/src/components/sidebar/Sidebar.tsx b/packages/desktop-client/src/components/sidebar/Sidebar.tsx index 9f9d4c4953c..4fe53e3096b 100644 --- a/packages/desktop-client/src/components/sidebar/Sidebar.tsx +++ b/packages/desktop-client/src/components/sidebar/Sidebar.tsx @@ -1,6 +1,5 @@ import React, { type CSSProperties, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { css } from '@emotion/css'; import { Resizable } from 're-resizable'; @@ -12,6 +11,7 @@ import { useGlobalPref } from '../../hooks/useGlobalPref'; import { useLocalPref } from '../../hooks/useLocalPref'; import { useResizeObserver } from '../../hooks/useResizeObserver'; import { SvgAdd } from '../../icons/v1'; +import { useAppDispatch } from '../../redux'; import { styles, theme } from '../../style'; import { View } from '../common/View'; import { useResponsive } from '../responsive/ResponsiveProvider'; @@ -27,7 +27,7 @@ export function Sidebar() { const hasWindowButtons = !Platform.isBrowser && Platform.OS === 'mac'; const { t } = useTranslation(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const sidebar = useSidebar(); const { width } = useResponsive(); const [isFloating = false, setFloatingSidebarPref] = diff --git a/packages/desktop-client/src/components/table.tsx b/packages/desktop-client/src/components/table.tsx index a253acd89cf..c26ed527d62 100644 --- a/packages/desktop-client/src/components/table.tsx +++ b/packages/desktop-client/src/components/table.tsx @@ -17,7 +17,6 @@ import React, { type MutableRefObject, type CSSProperties, } from 'react'; -import { useStore } from 'react-redux'; import AutoSizer from 'react-virtualized-auto-sizer'; import { @@ -28,6 +27,7 @@ import { useSelectedItems } from '../hooks/useSelected'; import { AnimatedLoading } from '../icons/AnimatedLoading'; import { SvgDelete, SvgExpandArrow } from '../icons/v0'; import { SvgCheckmark } from '../icons/v1'; +import { useAppStore } from '../redux'; import { styles, theme } from '../style'; import { Button } from './common/Button2'; @@ -1229,7 +1229,7 @@ export function useTableNavigator( const containerRef = useRef(); // See `onBlur` for why we need this - const store = useStore(); + const store = useAppStore(); const modalStackLength = useRef(0); // onEdit is passed to children, so make sure it maintains identity diff --git a/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx b/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx index fa1c85836ee..cc99c21bd7f 100644 --- a/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx +++ b/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx @@ -1,7 +1,6 @@ import { useMemo } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; import { useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { pushModal } from 'loot-core/client/actions'; import { isPreviewId } from 'loot-core/shared/transactions'; @@ -9,6 +8,7 @@ import { validForTransfer } from 'loot-core/src/client/transfer'; import { type TransactionEntity } from 'loot-core/types/models'; import { useSelectedItems } from '../../hooks/useSelected'; +import { useAppDispatch } from '../../redux'; import { Menu } from '../common/Menu'; import { SelectedItemsButton } from '../table'; @@ -57,7 +57,7 @@ export function SelectedTransactionsButton({ onMakeAsNonSplitTransactions, }: SelectedTransactionsButtonProps) { const { t } = useTranslation(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const selectedItems = useSelectedItems(); const selectedIds = useMemo(() => [...selectedItems], [selectedItems]); diff --git a/packages/desktop-client/src/components/transactions/TransactionList.jsx b/packages/desktop-client/src/components/transactions/TransactionList.jsx index 849c6daf497..47ceb0ac9ea 100644 --- a/packages/desktop-client/src/components/transactions/TransactionList.jsx +++ b/packages/desktop-client/src/components/transactions/TransactionList.jsx @@ -1,5 +1,4 @@ import React, { useRef, useCallback, useLayoutEffect } from 'react'; -import { useDispatch } from 'react-redux'; import { pushModal } from 'loot-core/client/actions'; import { send } from 'loot-core/src/platform/client/fetch'; @@ -13,6 +12,7 @@ import { import { getChangedValues, applyChanges } from 'loot-core/src/shared/util'; import { useNavigate } from '../../hooks/useNavigate'; +import { useAppDispatch } from '../../redux'; import { theme } from '../../style'; import { TransactionTable } from './TransactionsTable'; @@ -98,7 +98,7 @@ export function TransactionList({ onScheduleAction, onMakeAsNonSplitTransactions, }) { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const transactionsLatest = useRef(); const navigate = useNavigate(); diff --git a/packages/desktop-client/src/components/transactions/TransactionMenu.tsx b/packages/desktop-client/src/components/transactions/TransactionMenu.tsx index e2723014d8f..3d78a37dc29 100644 --- a/packages/desktop-client/src/components/transactions/TransactionMenu.tsx +++ b/packages/desktop-client/src/components/transactions/TransactionMenu.tsx @@ -1,11 +1,11 @@ import React, { type ComponentPropsWithoutRef } from 'react'; import { useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { pushModal } from 'loot-core/client/actions'; import { isPreviewId } from 'loot-core/shared/transactions'; import { type TransactionEntity } from 'loot-core/types/models'; +import { useAppDispatch } from '../../redux'; import { Menu } from '../common/Menu'; type BalanceMenuProps = Omit< @@ -36,7 +36,7 @@ export function TransactionMenu({ ...props }: BalanceMenuProps) { const { t } = useTranslation(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const isPreview = isPreviewId(transaction.id); const linked = !!transaction.schedule; diff --git a/packages/desktop-client/src/components/transactions/TransactionsTable.jsx b/packages/desktop-client/src/components/transactions/TransactionsTable.jsx index adcdaa70672..86beead8180 100644 --- a/packages/desktop-client/src/components/transactions/TransactionsTable.jsx +++ b/packages/desktop-client/src/components/transactions/TransactionsTable.jsx @@ -12,7 +12,6 @@ import React, { } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; import { useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; import { css } from '@emotion/css'; import { @@ -60,6 +59,7 @@ import { SvgCalendar, SvgHyperlink2, } from '../../icons/v2'; +import { useAppDispatch } from '../../redux'; import { styles, theme } from '../../style'; import { AccountAutocomplete } from '../autocomplete/AccountAutocomplete'; import { CategoryAutocomplete } from '../autocomplete/CategoryAutocomplete'; @@ -561,7 +561,7 @@ function PayeeCell({ }) { const isCreatingPayee = useRef(false); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const parentPayee = useParentPayee( payees, @@ -887,7 +887,7 @@ const Transaction = memo(function Transaction({ showSelection, allowSplitTransaction, }) { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const dispatchSelected = useSelectedDispatch(); const triggerRef = useRef(null); diff --git a/packages/desktop-client/src/components/transactions/TransactionsTable.test.jsx b/packages/desktop-client/src/components/transactions/TransactionsTable.test.jsx index 5f5ed4c4170..3acca829459 100644 --- a/packages/desktop-client/src/components/transactions/TransactionsTable.test.jsx +++ b/packages/desktop-client/src/components/transactions/TransactionsTable.test.jsx @@ -12,7 +12,6 @@ import { generateAccount, generateCategoryGroups, } from 'loot-core/src/mocks'; -import { TestProvider } from 'loot-core/src/mocks/redux'; import { initServer } from 'loot-core/src/platform/client/fetch'; import { addSplitTransaction, @@ -25,6 +24,7 @@ import { integerToCurrency } from 'loot-core/src/shared/util'; import { AuthProvider } from '../../auth/AuthProvider'; import { SelectedProviderWithItems } from '../../hooks/useSelected'; import { SplitsExpandedProvider } from '../../hooks/useSplitsExpanded'; +import { TestProvider } from '../../redux/mock'; import { ResponsiveProvider } from '../responsive/ResponsiveProvider'; import { TransactionTable } from './TransactionsTable'; diff --git a/packages/desktop-client/src/components/util/GenericInput.jsx b/packages/desktop-client/src/components/util/GenericInput.jsx index f718cdcb412..bd20e99db85 100644 --- a/packages/desktop-client/src/components/util/GenericInput.jsx +++ b/packages/desktop-client/src/components/util/GenericInput.jsx @@ -1,5 +1,4 @@ import React from 'react'; -import { useSelector } from 'react-redux'; import { useReports } from 'loot-core/client/data-hooks/reports'; import { getMonthYearFormat } from 'loot-core/src/shared/months'; @@ -7,6 +6,7 @@ import { integerToAmount, amountToInteger } from 'loot-core/src/shared/util'; import { useCategories } from '../../hooks/useCategories'; import { useDateFormat } from '../../hooks/useDateFormat'; +import { useAppSelector } from '../../redux'; import { AccountAutocomplete } from '../autocomplete/AccountAutocomplete'; import { Autocomplete } from '../autocomplete/Autocomplete'; import { CategoryAutocomplete } from '../autocomplete/CategoryAutocomplete'; @@ -36,7 +36,7 @@ export function GenericInput({ }) { const { grouped: categoryGroups } = useCategories(); const { data: savedReports } = useReports(); - const saved = useSelector(state => state.queries.saved); + const saved = useAppSelector(state => state.queries.saved); const dateFormat = useDateFormat() || 'MM/dd/yyyy'; const getNumberInputByFormatType = numberFormatType => { diff --git a/packages/desktop-client/src/global-events.ts b/packages/desktop-client/src/global-events.ts index 619f673b259..970d93bbda9 100644 --- a/packages/desktop-client/src/global-events.ts +++ b/packages/desktop-client/src/global-events.ts @@ -75,7 +75,7 @@ export function handleGlobalEvents(actions: BoundActions, store: Store) { if (tagged) { Promise.all(promises).then(() => { - actions.setLastUndoState(undoState); + undo.setUndoState('current', undoState); // If a modal has been tagged, open it instead of navigating if (tagged.openModal) { diff --git a/packages/desktop-client/src/gocardless.ts b/packages/desktop-client/src/gocardless.ts index b9861626bcd..dc4d48b6332 100644 --- a/packages/desktop-client/src/gocardless.ts +++ b/packages/desktop-client/src/gocardless.ts @@ -1,10 +1,10 @@ -import { type Dispatch } from 'loot-core/client/actions/types'; +import { type AppDispatch } from 'loot-core/client/store'; import { pushModal } from 'loot-core/src/client/actions/modals'; import { send } from 'loot-core/src/platform/client/fetch'; import { type GoCardlessToken } from 'loot-core/src/types/models'; function _authorize( - dispatch: Dispatch, + dispatch: AppDispatch, upgradingAccountId: string | undefined, { onSuccess, @@ -40,7 +40,7 @@ function _authorize( } export async function authorizeBank( - dispatch: Dispatch, + dispatch: AppDispatch, { upgradingAccountId }: { upgradingAccountId?: string } = {}, ) { _authorize(dispatch, upgradingAccountId, { diff --git a/packages/desktop-client/src/hooks/useAccounts.ts b/packages/desktop-client/src/hooks/useAccounts.ts index 4c44e9cec93..7bc9b13117f 100644 --- a/packages/desktop-client/src/hooks/useAccounts.ts +++ b/packages/desktop-client/src/hooks/useAccounts.ts @@ -1,14 +1,12 @@ import { useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { getAccounts } from 'loot-core/src/client/actions'; -import { type State } from 'loot-core/src/client/state-types'; + +import { useAppSelector, useAppDispatch } from '../redux'; export function useAccounts() { - const dispatch = useDispatch(); - const accountsLoaded = useSelector( - (state: State) => state.queries.accountsLoaded, - ); + const dispatch = useAppDispatch(); + const accountsLoaded = useAppSelector(state => state.queries.accountsLoaded); useEffect(() => { if (!accountsLoaded) { @@ -16,5 +14,5 @@ export function useAccounts() { } }, []); - return useSelector(state => state.queries.accounts); + return useAppSelector(state => state.queries.accounts); } diff --git a/packages/desktop-client/src/hooks/useActions.ts b/packages/desktop-client/src/hooks/useActions.ts index 2952eaf8a0d..df25c0aefdf 100644 --- a/packages/desktop-client/src/hooks/useActions.ts +++ b/packages/desktop-client/src/hooks/useActions.ts @@ -1,5 +1,4 @@ import { useMemo } from 'react'; -import { useDispatch } from 'react-redux'; import { bindActionCreators } from 'redux'; import { type ThunkAction } from 'redux-thunk'; @@ -7,6 +6,8 @@ import { type ThunkAction } from 'redux-thunk'; import * as actions from 'loot-core/src/client/actions'; import type { Action, State } from 'loot-core/src/client/state-types'; +import { useAppDispatch } from '../redux'; + type ActionReturnType unknown> = ReturnType extends ThunkAction ? ReturnType @@ -21,11 +22,11 @@ export type BoundActions = { // https://react-redux.js.org/api/hooks#recipe-useactions /** - * @deprecated please use actions directly with `useDispatch` + * @deprecated please use actions directly with `useAppDispatch` * @see https://github.com/reduxjs/react-redux/issues/1252#issuecomment-488160930 **/ export function useActions() { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); return useMemo(() => { return bindActionCreators(actions, dispatch); }, [dispatch]) as unknown as BoundActions; diff --git a/packages/desktop-client/src/hooks/useCategories.ts b/packages/desktop-client/src/hooks/useCategories.ts index 4c85fdfaff2..4af19885a55 100644 --- a/packages/desktop-client/src/hooks/useCategories.ts +++ b/packages/desktop-client/src/hooks/useCategories.ts @@ -1,13 +1,13 @@ import { useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { getCategories } from 'loot-core/src/client/actions'; -import { type State } from 'loot-core/src/client/state-types'; + +import { useAppSelector, useAppDispatch } from '../redux'; export function useCategories() { - const dispatch = useDispatch(); - const categoriesLoaded = useSelector( - (state: State) => state.queries.categoriesLoaded, + const dispatch = useAppDispatch(); + const categoriesLoaded = useAppSelector( + state => state.queries.categoriesLoaded, ); useEffect(() => { @@ -16,5 +16,5 @@ export function useCategories() { } }, []); - return useSelector(state => state.queries.categories); + return useAppSelector(state => state.queries.categories); } diff --git a/packages/desktop-client/src/hooks/useFailedAccounts.ts b/packages/desktop-client/src/hooks/useFailedAccounts.ts index 86aeb89959d..4497c006c51 100644 --- a/packages/desktop-client/src/hooks/useFailedAccounts.ts +++ b/packages/desktop-client/src/hooks/useFailedAccounts.ts @@ -1,7 +1,11 @@ -import { useSelector } from 'react-redux'; +import { useMemo } from 'react'; -import { type State } from 'loot-core/src/client/state-types'; +import { useAppSelector } from '../redux'; export function useFailedAccounts() { - return useSelector((state: State) => state.account.failedAccounts); + const failedAccounts = useAppSelector(state => state.account.failedAccounts); + return useMemo( + () => new Map(Object.entries(failedAccounts)), + [failedAccounts], + ); } diff --git a/packages/desktop-client/src/hooks/useGlobalPref.ts b/packages/desktop-client/src/hooks/useGlobalPref.ts index 4f686a2aeff..a6c6fe2468f 100644 --- a/packages/desktop-client/src/hooks/useGlobalPref.ts +++ b/packages/desktop-client/src/hooks/useGlobalPref.ts @@ -1,10 +1,10 @@ import { useCallback } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { saveGlobalPrefs } from 'loot-core/src/client/actions'; -import { type State } from 'loot-core/src/client/state-types'; import { type GlobalPrefs } from 'loot-core/src/types/prefs'; +import { useAppSelector, useAppDispatch } from '../redux'; + type SetGlobalPrefAction = ( value: GlobalPrefs[K], ) => void; @@ -13,7 +13,7 @@ export function useGlobalPref( prefName: K, onSaveGlobalPrefs?: () => void, ): [GlobalPrefs[K], SetGlobalPrefAction] { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const setGlobalPref = useCallback>( value => { dispatch( @@ -27,8 +27,8 @@ export function useGlobalPref( }, [prefName, dispatch, onSaveGlobalPrefs], ); - const globalPref = useSelector( - (state: State) => state.prefs.global?.[prefName] as GlobalPrefs[K], + const globalPref = useAppSelector( + state => state.prefs.global?.[prefName] as GlobalPrefs[K], ); return [globalPref, setGlobalPref]; diff --git a/packages/desktop-client/src/hooks/useMetadataPref.ts b/packages/desktop-client/src/hooks/useMetadataPref.ts index 57d4b7902b7..f4e42222c99 100644 --- a/packages/desktop-client/src/hooks/useMetadataPref.ts +++ b/packages/desktop-client/src/hooks/useMetadataPref.ts @@ -1,10 +1,10 @@ import { useCallback } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { savePrefs } from 'loot-core/client/actions'; -import { type State } from 'loot-core/client/state-types'; import { type MetadataPrefs } from 'loot-core/types/prefs'; +import { useAppSelector, useAppDispatch } from '../redux'; + type SetMetadataPrefAction = ( value: MetadataPrefs[K], ) => void; @@ -12,16 +12,14 @@ type SetMetadataPrefAction = ( export function useMetadataPref( prefName: K, ): [MetadataPrefs[K], SetMetadataPrefAction] { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const setLocalPref = useCallback>( value => { dispatch(savePrefs({ [prefName]: value })); }, [prefName, dispatch], ); - const localPref = useSelector( - (state: State) => state.prefs.local?.[prefName], - ); + const localPref = useAppSelector(state => state.prefs.local?.[prefName]); return [localPref, setLocalPref]; } diff --git a/packages/desktop-client/src/hooks/useModalState.ts b/packages/desktop-client/src/hooks/useModalState.ts index 1810ba7fa6c..94047d4f366 100644 --- a/packages/desktop-client/src/hooks/useModalState.ts +++ b/packages/desktop-client/src/hooks/useModalState.ts @@ -1,10 +1,10 @@ import { useCallback } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { popModal } from 'loot-core/client/actions'; -import { type State } from 'loot-core/client/state-types'; import { type Modal } from 'loot-core/client/state-types/modals'; +import { useAppSelector, useAppDispatch } from '../redux'; + type ModalState = { onClose: () => void; modalStack: Modal[]; @@ -14,9 +14,9 @@ type ModalState = { }; export function useModalState(): ModalState { - const modalStack = useSelector((state: State) => state.modals.modalStack); - const isHidden = useSelector((state: State) => state.modals.isHidden); - const dispatch = useDispatch(); + const modalStack = useAppSelector(state => state.modals.modalStack); + const isHidden = useAppSelector(state => state.modals.isHidden); + const dispatch = useAppDispatch(); const popModalCallback = useCallback(() => { dispatch(popModal()); diff --git a/packages/desktop-client/src/hooks/usePayees.ts b/packages/desktop-client/src/hooks/usePayees.ts index cffa9df0673..6c2fb62ff6e 100644 --- a/packages/desktop-client/src/hooks/usePayees.ts +++ b/packages/desktop-client/src/hooks/usePayees.ts @@ -1,13 +1,13 @@ import { useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { getCommonPayees, getPayees } from 'loot-core/src/client/actions'; -import { type State } from 'loot-core/src/client/state-types'; + +import { useAppSelector, useAppDispatch } from '../redux'; export function useCommonPayees() { - const dispatch = useDispatch(); - const commonPayeesLoaded = useSelector( - (state: State) => state.queries.commonPayeesLoaded, + const dispatch = useAppDispatch(); + const commonPayeesLoaded = useAppSelector( + state => state.queries.commonPayeesLoaded, ); useEffect(() => { @@ -16,14 +16,12 @@ export function useCommonPayees() { } }, []); - return useSelector(state => state.queries.commonPayees); + return useAppSelector(state => state.queries.commonPayees); } export function usePayees() { - const dispatch = useDispatch(); - const payeesLoaded = useSelector( - (state: State) => state.queries.payeesLoaded, - ); + const dispatch = useAppDispatch(); + const payeesLoaded = useAppSelector(state => state.queries.payeesLoaded); useEffect(() => { if (!payeesLoaded) { @@ -31,5 +29,5 @@ export function usePayees() { } }, []); - return useSelector(state => state.queries.payees); + return useAppSelector(state => state.queries.payees); } diff --git a/packages/desktop-client/src/hooks/useSelected.tsx b/packages/desktop-client/src/hooks/useSelected.tsx index ceab8ce5603..62fa2140505 100644 --- a/packages/desktop-client/src/hooks/useSelected.tsx +++ b/packages/desktop-client/src/hooks/useSelected.tsx @@ -10,9 +10,7 @@ import React, { type ReactElement, type ReactNode, } from 'react'; -import { useSelector } from 'react-redux'; -import { type State } from 'loot-core/src/client/state-types'; import { listen } from 'loot-core/src/platform/client/fetch'; import * as undo from 'loot-core/src/platform/client/undo'; import { type UndoState } from 'loot-core/src/server/undo'; @@ -208,8 +206,6 @@ export function useSelected( return () => undo.setUndoState('selectedItems', prevState); }, [state.selectedItems]); - const lastUndoState = useSelector((state: State) => state.app.lastUndoState); - useEffect(() => { function onUndo({ messages, undoTag }: UndoState) { const tagged = undo.getTaggedState(undoTag); @@ -231,8 +227,9 @@ export function useSelected( } } - if (lastUndoState && lastUndoState.current) { - onUndo(lastUndoState.current); + const lastUndoState = undo.getUndoState('current'); + if (lastUndoState) { + onUndo(lastUndoState as UndoState); } return listen('undo-event', onUndo); diff --git a/packages/desktop-client/src/hooks/useSplitsExpanded.tsx b/packages/desktop-client/src/hooks/useSplitsExpanded.tsx index 25386d2dcec..30b767a1055 100644 --- a/packages/desktop-client/src/hooks/useSplitsExpanded.tsx +++ b/packages/desktop-client/src/hooks/useSplitsExpanded.tsx @@ -6,8 +6,8 @@ import React, { useReducer, type Dispatch, type ReactNode, + useRef, } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; import { type SplitMode, @@ -59,7 +59,7 @@ type SplitsStateContext = { const SplitsExpandedContext = createContext({ state: { mode: 'collapse', - ids: new Set(), + ids: [], transitionId: null, }, dispatch: () => { @@ -73,10 +73,10 @@ export function useSplitsExpanded() { return useMemo( () => ({ ...data, - isExpanded: (id: string) => - data.state.mode === 'collapse' - ? !data.state.ids.has(id) - : data.state.ids.has(id), + isExpanded: (id: string) => { + const idSet = new Set(data.state.ids); + return data.state.mode === 'collapse' ? !idSet.has(id) : idSet.has(id); + }, }), [data], ); @@ -91,8 +91,7 @@ export function SplitsExpandedProvider({ children, initialMode = 'expand', }: SplitsExpandedProviderProps) { - const cachedState = useSelector(state => state.app.lastSplitState); - const reduxDispatch = useDispatch(); + const previousState = useRef(null); const [state, dispatch] = useReducer( (state: SplitState, action: Actions): SplitState => { @@ -105,7 +104,7 @@ export function SplitsExpandedProvider({ } else { ids.add(id); } - return { ...state, ids }; + return { ...state, ids: Array.from(ids) }; } case 'open-split': { const ids = new Set([...state.ids]); @@ -115,7 +114,7 @@ export function SplitsExpandedProvider({ } else { ids.add(id); } - return { ...state, ids }; + return { ...state, ids: Array.from(ids) }; } case 'close-splits': { const ids = new Set([...state.ids]); @@ -126,13 +125,13 @@ export function SplitsExpandedProvider({ ids.delete(id); } }); - return { ...state, ids }; + return { ...state, ids: Array.from(ids) }; } case 'set-mode': { return { ...state, mode: action.mode, - ids: new Set(), + ids: [], transitionId: null, }; } @@ -146,14 +145,14 @@ export function SplitsExpandedProvider({ ...state, mode: state.mode === 'expand' ? 'collapse' : 'expand', transitionId: action.id, - ids: new Set(), + ids: [], }; case 'finish-switch-mode': return { ...state, transitionId: null }; } }, - cachedState.current || { - ids: new Set(), + previousState.current || { + ids: [], mode: initialMode, transitionId: null, }, @@ -171,9 +170,9 @@ export function SplitsExpandedProvider({ useEffect(() => { // In a finished state, cache the state if (state.transitionId == null) { - reduxDispatch({ type: 'SET_LAST_SPLIT_STATE', splitState: state }); + previousState.current = state; } - }, [reduxDispatch, state]); + }, [state]); const value = useMemo(() => ({ state, dispatch }), [state, dispatch]); diff --git a/packages/desktop-client/src/hooks/useSyncServerStatus.ts b/packages/desktop-client/src/hooks/useSyncServerStatus.ts index f9a2351d3fd..a15437c90f8 100644 --- a/packages/desktop-client/src/hooks/useSyncServerStatus.ts +++ b/packages/desktop-client/src/hooks/useSyncServerStatus.ts @@ -1,14 +1,11 @@ -import { useSelector } from 'react-redux'; - -import { type State } from 'loot-core/src/client/state-types'; - import { useServerURL } from '../components/ServerContext'; +import { useAppSelector } from '../redux'; type SyncServerStatus = 'offline' | 'no-server' | 'online'; export function useSyncServerStatus(): SyncServerStatus { const serverUrl = useServerURL(); - const userData = useSelector((state: State) => state.user.data); + const userData = useAppSelector(state => state.user.data); if (!serverUrl) { return 'no-server'; diff --git a/packages/desktop-client/src/hooks/useSyncedPref.ts b/packages/desktop-client/src/hooks/useSyncedPref.ts index 67808eeb97a..c44fca5fa5c 100644 --- a/packages/desktop-client/src/hooks/useSyncedPref.ts +++ b/packages/desktop-client/src/hooks/useSyncedPref.ts @@ -1,10 +1,10 @@ import { useCallback } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { saveSyncedPrefs } from 'loot-core/client/actions'; -import { type State } from 'loot-core/client/state-types'; import { type SyncedPrefs } from 'loot-core/src/types/prefs'; +import { useAppSelector, useAppDispatch } from '../redux'; + type SetSyncedPrefAction = ( value: SyncedPrefs[K], ) => void; @@ -12,14 +12,14 @@ type SetSyncedPrefAction = ( export function useSyncedPref( prefName: K, ): [SyncedPrefs[K], SetSyncedPrefAction] { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const setPref = useCallback>( value => { dispatch(saveSyncedPrefs({ [prefName]: value })); }, [prefName, dispatch], ); - const pref = useSelector((state: State) => state.prefs.synced[prefName]); + const pref = useAppSelector(state => state.prefs.synced[prefName]); return [pref, setPref]; } diff --git a/packages/desktop-client/src/hooks/useSyncedPrefs.ts b/packages/desktop-client/src/hooks/useSyncedPrefs.ts index 57493794892..b52a36e9477 100644 --- a/packages/desktop-client/src/hooks/useSyncedPrefs.ts +++ b/packages/desktop-client/src/hooks/useSyncedPrefs.ts @@ -1,22 +1,22 @@ import { useCallback } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { saveSyncedPrefs } from 'loot-core/client/actions'; -import { type State } from 'loot-core/client/state-types'; import { type SyncedPrefs } from 'loot-core/src/types/prefs'; +import { useAppSelector, useAppDispatch } from '../redux'; + type SetSyncedPrefsAction = (value: Partial) => void; /** @deprecated: please use `useSyncedPref` (singular) */ export function useSyncedPrefs(): [SyncedPrefs, SetSyncedPrefsAction] { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const setPrefs = useCallback( newValue => { dispatch(saveSyncedPrefs(newValue)); }, [dispatch], ); - const prefs = useSelector((state: State) => state.prefs.synced); + const prefs = useAppSelector(state => state.prefs.synced); return [prefs, setPrefs]; } diff --git a/packages/desktop-client/src/hooks/useTransactionBatchActions.ts b/packages/desktop-client/src/hooks/useTransactionBatchActions.ts index a49c1efbf53..1d0ef59b3a6 100644 --- a/packages/desktop-client/src/hooks/useTransactionBatchActions.ts +++ b/packages/desktop-client/src/hooks/useTransactionBatchActions.ts @@ -1,5 +1,3 @@ -import { useDispatch } from 'react-redux'; - import { pushModal } from 'loot-core/client/actions'; import { runQuery } from 'loot-core/client/query-helpers'; import { send } from 'loot-core/platform/client/fetch'; @@ -19,6 +17,8 @@ import { type TransactionEntity, } from 'loot-core/types/models'; +import { useAppDispatch } from '../redux'; + type BatchEditProps = { name: keyof TransactionEntity; ids: Array; @@ -55,7 +55,7 @@ type BatchUnlinkScheduleProps = { }; export function useTransactionBatchActions() { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const onBatchEdit = async ({ name, ids, onSuccess }: BatchEditProps) => { const { data } = await runQuery( diff --git a/packages/desktop-client/src/hooks/useUndo.ts b/packages/desktop-client/src/hooks/useUndo.ts index 0ab580f44f2..2e5f8871892 100644 --- a/packages/desktop-client/src/hooks/useUndo.ts +++ b/packages/desktop-client/src/hooks/useUndo.ts @@ -1,10 +1,10 @@ import { useCallback } from 'react'; -import { useDispatch } from 'react-redux'; import { undo, redo, addNotification } from 'loot-core/client/actions'; import { type Notification } from 'loot-core/client/state-types/notifications'; import { useResponsive } from '../components/responsive/ResponsiveProvider'; +import { useDispatch } from '../redux'; type UndoActions = { undo: () => void; diff --git a/packages/desktop-client/src/hooks/useUpdatedAccounts.ts b/packages/desktop-client/src/hooks/useUpdatedAccounts.ts index 483d22c9b5d..de083ceb64d 100644 --- a/packages/desktop-client/src/hooks/useUpdatedAccounts.ts +++ b/packages/desktop-client/src/hooks/useUpdatedAccounts.ts @@ -1,7 +1,5 @@ -import { useSelector } from 'react-redux'; - -import { type State } from 'loot-core/src/client/state-types'; +import { useAppSelector } from '../redux'; export function useUpdatedAccounts() { - return useSelector((state: State) => state.queries.updatedAccounts); + return useAppSelector(state => state.queries.updatedAccounts); } diff --git a/packages/desktop-client/src/index.tsx b/packages/desktop-client/src/index.tsx index 296e6f07ee1..9612414cc79 100644 --- a/packages/desktop-client/src/index.tsx +++ b/packages/desktop-client/src/index.tsx @@ -10,20 +10,12 @@ import './i18n'; import React from 'react'; import { Provider } from 'react-redux'; +import { bindActionCreators } from '@reduxjs/toolkit'; import { createRoot } from 'react-dom/client'; -import { - createStore, - combineReducers, - applyMiddleware, - bindActionCreators, -} from 'redux'; -import thunk from 'redux-thunk'; import * as actions from 'loot-core/src/client/actions'; -import * as constants from 'loot-core/src/client/constants'; import { runQuery } from 'loot-core/src/client/query-helpers'; -import { reducers } from 'loot-core/src/client/reducers'; -import { initialState as initialAppState } from 'loot-core/src/client/reducers/app'; +import { store } from 'loot-core/src/client/store'; import { send } from 'loot-core/src/platform/client/fetch'; import { q } from 'loot-core/src/shared/query'; @@ -37,34 +29,6 @@ import { type BoundActions } from './hooks/useActions'; // focus outline appear from keyboard events. import 'focus-visible'; -const appReducer = combineReducers(reducers); -function rootReducer(state, action) { - if (action.type === constants.CLOSE_BUDGET) { - // Reset the state and only keep around things intentionally. This - // blows away everything else - state = { - budgets: state.budgets, - user: state.user, - prefs: { local: null, global: state.prefs.global }, - app: { - ...initialAppState, - updateInfo: state.updateInfo, - showUpdateNotification: state.showUpdateNotification, - managerHasInitialized: state.app.managerHasInitialized, - loadingText: state.app.loadingText, - }, - }; - } - - return appReducer(state, action); -} - -const compose = window['__REDUX_DEVTOOLS_EXTENSION_COMPOSE__'] || (f => f); -const store = createStore( - rootReducer, - undefined, - compose(applyMiddleware(thunk)), -); const boundActions = bindActionCreators( actions, store.dispatch, diff --git a/packages/desktop-client/src/redux/index.ts b/packages/desktop-client/src/redux/index.ts new file mode 100644 index 00000000000..e3387095679 --- /dev/null +++ b/packages/desktop-client/src/redux/index.ts @@ -0,0 +1,11 @@ +import { useDispatch, useSelector, useStore } from 'react-redux'; + +import { + type AppStore, + type AppDispatch, + type RootState, +} from 'loot-core/client/store'; + +export const useAppStore = useStore.withTypes(); +export const useAppDispatch = useDispatch.withTypes(); +export const useAppSelector = useSelector.withTypes(); diff --git a/packages/desktop-client/src/redux/mock.tsx b/packages/desktop-client/src/redux/mock.tsx new file mode 100644 index 00000000000..2b5e2cc74f7 --- /dev/null +++ b/packages/desktop-client/src/redux/mock.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { Provider } from 'react-redux'; + +import { store, resetStore } from 'loot-core/client/store/mock'; + +resetStore(); + +export function TestProvider({ children }) { + return {children}; +} diff --git a/packages/desktop-client/src/setupTests.js b/packages/desktop-client/src/setupTests.js index 0975add586c..383bcab1b52 100644 --- a/packages/desktop-client/src/setupTests.js +++ b/packages/desktop-client/src/setupTests.js @@ -1,4 +1,4 @@ -import { resetStore } from 'loot-core/src/mocks/redux'; +import { resetStore } from 'loot-core/src/client/store/mock'; import { installPolyfills } from './polyfills'; diff --git a/packages/loot-core/package.json b/packages/loot-core/package.json index cd8257cba33..a5b50a07bbd 100644 --- a/packages/loot-core/package.json +++ b/packages/loot-core/package.json @@ -18,6 +18,7 @@ "license": "ISC", "dependencies": { "@jlongster/sql.js": "^1.6.7", + "@reduxjs/toolkit": "^2.5.0", "@rschedule/core": "^1.5.0", "@rschedule/json-tools": "^1.5.0", "@rschedule/standard-date-adapter": "^1.5.0", @@ -49,7 +50,6 @@ "@types/jest": "^27.5.2", "@types/jlongster__sql.js": "npm:@types/sql.js@latest", "@types/pegjs": "^0.10.3", - "@types/react-redux": "^7.1.25", "@types/uuid": "^9.0.2", "@types/webpack": "^5.28.5", "@types/webpack-bundle-analyzer": "^4.6.3", diff --git a/packages/loot-core/src/client/actions/account.ts b/packages/loot-core/src/client/actions/account.ts index 579366b38b5..e7dd9b69918 100644 --- a/packages/loot-core/src/client/actions/account.ts +++ b/packages/loot-core/src/client/actions/account.ts @@ -10,10 +10,10 @@ import type { SetLastTransactionAction, UpdateNewTransactionsAction, } from '../state-types/queries'; +import { type AppDispatch, type GetRootState } from '../store'; import { addNotification } from './notifications'; import { getPayees, getAccounts } from './queries'; -import type { Dispatch, GetState } from './types'; export function setAccountsSyncing( ids: SetAccountsSyncingAction['ids'], @@ -48,7 +48,7 @@ export function markAccountSuccess( } export function unlinkAccount(id: string) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { await send('account-unlink', { id }); dispatch(markAccountSuccess(id)); dispatch(getAccounts()); @@ -61,7 +61,7 @@ export function linkAccount( upgradingId?: string, offBudget?: boolean, ) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { await send('gocardless-accounts-link', { requisitionId, account, @@ -78,7 +78,7 @@ export function linkAccountSimpleFin( upgradingId?: string, offBudget?: boolean, ) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { await send('simplefin-accounts-link', { externalAccount, upgradingId, @@ -139,7 +139,7 @@ function handleSyncResponse( } export function syncAccounts(id?: string) { - return async (dispatch: Dispatch, getState: GetState) => { + return async (dispatch: AppDispatch, getState: GetRootState) => { // Disallow two parallel sync operations if (getState().account.accountsSyncing.length > 0) { return false; @@ -257,7 +257,7 @@ export function parseTransactions(filepath, options) { } export function importPreviewTransactions(id: string, transactions) { - return async (dispatch: Dispatch): Promise => { + return async (dispatch: AppDispatch): Promise => { const { errors = [], updatedPreview } = await send('transactions-import', { accountId: id, transactions, @@ -278,7 +278,7 @@ export function importPreviewTransactions(id: string, transactions) { } export function importTransactions(id: string, transactions, reconcile = true) { - return async (dispatch: Dispatch): Promise => { + return async (dispatch: AppDispatch): Promise => { if (!reconcile) { await send('api/transactions-add', { accountId: id, @@ -333,7 +333,7 @@ export function markAccountRead(accountId): MarkAccountReadAction { } export function moveAccount(id, targetId) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { await send('account-move', { id, targetId }); dispatch(getAccounts()); dispatch(getPayees()); diff --git a/packages/loot-core/src/client/actions/app.ts b/packages/loot-core/src/client/actions/app.ts index b2a707ffb26..bc199564039 100644 --- a/packages/loot-core/src/client/actions/app.ts +++ b/packages/loot-core/src/client/actions/app.ts @@ -1,13 +1,8 @@ // @ts-strict-ignore import { send } from '../../platform/client/fetch'; import * as constants from '../constants'; -import type { - AppState, - SetAppStateAction, - SetLastUndoStateAction, -} from '../state-types/app'; - -import type { Dispatch } from './types'; +import type { AppState, SetAppStateAction } from '../state-types/app'; +import { type AppDispatch } from '../store'; export function setAppState(state: Partial): SetAppStateAction { return { @@ -17,21 +12,12 @@ export function setAppState(state: Partial): SetAppStateAction { } export function updateApp() { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { await global.Actual.applyAppUpdate(); dispatch(setAppState({ updateInfo: null })); }; } -export function setLastUndoState( - undoState: SetLastUndoStateAction['undoState'], -): SetLastUndoStateAction { - return { - type: constants.SET_LAST_UNDO_STATE, - undoState, - }; -} - // This is only used in the fake web version where everything runs in // the browser. It's a way to send a file to the backend to be // imported into the virtual filesystem. diff --git a/packages/loot-core/src/client/actions/backups.ts b/packages/loot-core/src/client/actions/backups.ts index 92218a8f434..51e5100a7b5 100644 --- a/packages/loot-core/src/client/actions/backups.ts +++ b/packages/loot-core/src/client/actions/backups.ts @@ -1,13 +1,13 @@ // @ts-strict-ignore import { send } from '../../platform/client/fetch'; +import { type AppDispatch, type GetRootState } from '../store'; import { closeBudget, loadBudget } from './budgets'; -import type { Dispatch, GetState } from './types'; // Take in the budget id so that backups can be loaded when a budget // isn't opened export function loadBackup(budgetId, backupId) { - return async (dispatch: Dispatch, getState: GetState) => { + return async (dispatch: AppDispatch, getState: GetRootState) => { const prefs = getState().prefs.local; if (prefs && prefs.id) { await dispatch(closeBudget()); @@ -19,7 +19,7 @@ export function loadBackup(budgetId, backupId) { } export function makeBackup() { - return async (dispatch: Dispatch, getState: GetState) => { + return async (dispatch: AppDispatch, getState: GetRootState) => { const prefs = getState().prefs.local; if (prefs && prefs.id) { await send('backup-make', { id: prefs.id }); diff --git a/packages/loot-core/src/client/actions/budgets.ts b/packages/loot-core/src/client/actions/budgets.ts index 8e0a5bfebd1..4a36f1f49f4 100644 --- a/packages/loot-core/src/client/actions/budgets.ts +++ b/packages/loot-core/src/client/actions/budgets.ts @@ -5,14 +5,14 @@ import { send } from '../../platform/client/fetch'; import { getDownloadError, getSyncError } from '../../shared/errors'; import type { Handlers } from '../../types/handlers'; import * as constants from '../constants'; +import { type AppDispatch, type GetRootState } from '../store'; import { setAppState } from './app'; import { closeModal, pushModal } from './modals'; import { loadPrefs, loadGlobalPrefs } from './prefs'; -import type { Dispatch, GetState } from './types'; export function loadBudgets() { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { const budgets = await send('get-budgets'); dispatch({ @@ -23,7 +23,7 @@ export function loadBudgets() { } export function loadRemoteFiles() { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { const files = await send('get-remote-files'); dispatch({ @@ -34,7 +34,7 @@ export function loadRemoteFiles() { } export function loadAllFiles() { - return async (dispatch: Dispatch, getState: GetState) => { + return async (dispatch: AppDispatch, getState: GetRootState) => { const budgets = await send('get-budgets'); const files = await send('get-remote-files'); @@ -49,7 +49,7 @@ export function loadAllFiles() { } export function loadBudget(id: string, options = {}) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { dispatch(setAppState({ loadingText: t('Loading...') })); // Loading a budget may fail @@ -90,7 +90,7 @@ export function loadBudget(id: string, options = {}) { } export function closeBudget() { - return async (dispatch: Dispatch, getState: GetState) => { + return async (dispatch: AppDispatch, getState: GetRootState) => { const prefs = getState().prefs.local; if (prefs && prefs.id) { // This clears out all the app state so the user starts fresh @@ -107,7 +107,7 @@ export function closeBudget() { } export function closeBudgetUI() { - return async (dispatch: Dispatch, getState: GetState) => { + return async (dispatch: AppDispatch, getState: GetRootState) => { const prefs = getState().prefs.local; if (prefs && prefs.id) { dispatch({ type: constants.CLOSE_BUDGET }); @@ -116,14 +116,14 @@ export function closeBudgetUI() { } export function deleteBudget(id?: string, cloudFileId?: string) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { await send('delete-budget', { id, cloudFileId }); await dispatch(loadAllFiles()); }; } export function createBudget({ testMode = false, demoMode = false } = {}) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { dispatch( setAppState({ loadingText: @@ -180,7 +180,7 @@ export function duplicateBudget({ */ cloudSync?: boolean; }) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { try { dispatch( setAppState({ @@ -219,7 +219,7 @@ export function importBudget( filepath: string, type: Parameters[0]['type'], ) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { const { error } = await send('import-budget', { filepath, type }); if (error) { throw new Error(error); @@ -232,7 +232,7 @@ export function importBudget( } export function uploadBudget(id: string) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { const { error } = await send('upload-budget', { id }); if (error) { return { error }; @@ -244,21 +244,21 @@ export function uploadBudget(id: string) { } export function closeAndLoadBudget(fileId: string) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { await dispatch(closeBudget()); await dispatch(loadBudget(fileId)); }; } export function closeAndDownloadBudget(cloudFileId: string) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { await dispatch(closeBudget()); dispatch(downloadBudget(cloudFileId, { replace: true })); }; } export function downloadBudget(cloudFileId: string, { replace = false } = {}) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { dispatch( setAppState({ loadingText: t('Downloading...'), diff --git a/packages/loot-core/src/client/actions/prefs.ts b/packages/loot-core/src/client/actions/prefs.ts index 21cd7a5d556..a777f390466 100644 --- a/packages/loot-core/src/client/actions/prefs.ts +++ b/packages/loot-core/src/client/actions/prefs.ts @@ -6,12 +6,12 @@ import { type SyncedPrefs, } from '../../types/prefs'; import * as constants from '../constants'; +import { type AppDispatch, type GetRootState } from '../store'; import { closeModal } from './modals'; -import type { Dispatch, GetState } from './types'; export function loadPrefs() { - return async (dispatch: Dispatch, getState: GetState) => { + return async (dispatch: AppDispatch, getState: GetRootState) => { const prefs = await send('load-prefs'); // Remove any modal state if switching between budgets @@ -45,7 +45,7 @@ export function loadPrefs() { } export function savePrefs(prefs: MetadataPrefs) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { await send('save-prefs', prefs); dispatch({ type: constants.MERGE_LOCAL_PREFS, @@ -55,7 +55,7 @@ export function savePrefs(prefs: MetadataPrefs) { } export function loadGlobalPrefs() { - return async (dispatch: Dispatch, getState: GetState) => { + return async (dispatch: AppDispatch, getState: GetRootState) => { const globalPrefs = await send('load-global-prefs'); dispatch({ type: constants.SET_PREFS, @@ -71,7 +71,7 @@ export function saveGlobalPrefs( prefs: GlobalPrefs, onSaveGlobalPrefs?: () => void, ) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { await send('save-global-prefs', prefs); dispatch({ type: constants.MERGE_GLOBAL_PREFS, @@ -82,7 +82,7 @@ export function saveGlobalPrefs( } export function saveSyncedPrefs(prefs: SyncedPrefs) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { await Promise.all( Object.entries(prefs).map(([prefName, value]) => send('preferences/save', { diff --git a/packages/loot-core/src/client/actions/queries.ts b/packages/loot-core/src/client/actions/queries.ts index 25401608a52..fa36f4efcf2 100644 --- a/packages/loot-core/src/client/actions/queries.ts +++ b/packages/loot-core/src/client/actions/queries.ts @@ -5,13 +5,13 @@ import throttle from 'throttleit'; import { send } from '../../platform/client/fetch'; import { type AccountEntity } from '../../types/models'; import * as constants from '../constants'; +import { type AppDispatch, type GetRootState } from '../store'; import { pushModal } from './modals'; import { addNotification, addGenericErrorNotification } from './notifications'; -import type { Dispatch, GetState } from './types'; export function applyBudgetAction(month, type, args) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { switch (type) { case 'budget-amount': await send('budget/budget-amount', { @@ -145,7 +145,7 @@ export function applyBudgetAction(month, type, args) { } export function getCategories() { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { const categories = await send('get-categories'); dispatch({ type: constants.LOAD_CATEGORIES, @@ -161,7 +161,7 @@ export function createCategory( isIncome: boolean, hidden: boolean, ) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { const id = await send('category-create', { name, groupId, @@ -174,7 +174,7 @@ export function createCategory( } export function deleteCategory(id: string, transferId?: string) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { const { error } = await send('category-delete', { id, transferId }); if (error) { @@ -204,28 +204,28 @@ export function deleteCategory(id: string, transferId?: string) { } export function updateCategory(category) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { await send('category-update', category); dispatch(getCategories()); }; } export function moveCategory(id, groupId, targetId) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { await send('category-move', { id, groupId, targetId }); await dispatch(getCategories()); }; } export function moveCategoryGroup(id, targetId) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { await send('category-group-move', { id, targetId }); await dispatch(getCategories()); }; } export function createGroup(name) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { const id = await send('category-group-create', { name }); dispatch(getCategories()); return id; @@ -254,7 +254,7 @@ export function deleteGroup(id, transferId?) { } export function getPayees() { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { const payees = await send('payees-get'); dispatch({ type: constants.LOAD_PAYEES, @@ -265,7 +265,7 @@ export function getPayees() { } export function getCommonPayees() { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { const payees = await send('common-payees-get'); dispatch({ type: constants.LOAD_COMMON_PAYEES, @@ -276,7 +276,7 @@ export function getCommonPayees() { } export function initiallyLoadPayees() { - return async (dispatch: Dispatch, getState: GetState) => { + return async (dispatch: AppDispatch, getState: GetRootState) => { if (getState().queries.payees.length === 0) { return dispatch(getPayees()); } @@ -284,7 +284,7 @@ export function initiallyLoadPayees() { } export function createPayee(name: string) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { const id = await send('payee-create', { name: name.trim() }); dispatch(getPayees()); return id; @@ -292,7 +292,7 @@ export function createPayee(name: string) { } export function getAccounts() { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { const accounts = await send('accounts-get'); dispatch({ type: constants.LOAD_ACCOUNTS, accounts }); return accounts; @@ -300,14 +300,14 @@ export function getAccounts() { } export function updateAccount(account: AccountEntity) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { dispatch({ type: constants.UPDATE_ACCOUNT, account }); await send('account-update', account); }; } export function createAccount(name, balance, offBudget) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { const id = await send('account-create', { name, balance, offBudget }); await dispatch(getAccounts()); await dispatch(getPayees()); @@ -316,7 +316,7 @@ export function createAccount(name, balance, offBudget) { } export function openAccountCloseModal(accountId) { - return async (dispatch: Dispatch, getState: GetState) => { + return async (dispatch: AppDispatch, getState: GetRootState) => { const { balance, numTransactions } = await send('account-properties', { id: accountId, }); @@ -340,7 +340,7 @@ export function closeAccount( categoryId: string, forced?: boolean, ) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { await send('account-close', { id: accountId, transferAccountId, @@ -352,7 +352,7 @@ export function closeAccount( } export function reopenAccount(accountId) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { await send('account-reopen', { id: accountId }); dispatch(getAccounts()); }; diff --git a/packages/loot-core/src/client/actions/sync.ts b/packages/loot-core/src/client/actions/sync.ts index 0d55026f9eb..ca7c341aeb7 100644 --- a/packages/loot-core/src/client/actions/sync.ts +++ b/packages/loot-core/src/client/actions/sync.ts @@ -1,13 +1,13 @@ import { send } from '../../platform/client/fetch'; import { getUploadError } from '../../shared/errors'; +import { type AppDispatch, type GetRootState } from '../store'; import { syncAccounts } from './account'; import { pushModal } from './modals'; import { loadPrefs } from './prefs'; -import type { Dispatch, GetState } from './types'; export function resetSync() { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { const { error } = await send('sync-reset'); if (error) { @@ -36,7 +36,7 @@ export function resetSync() { } export function sync() { - return async (dispatch: Dispatch, getState: GetState) => { + return async (dispatch: AppDispatch, getState: GetRootState) => { const prefs = getState().prefs.local; if (prefs && prefs.id) { const result = await send('sync'); @@ -53,7 +53,7 @@ export function sync() { } export function syncAndDownload(accountId?: string) { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { // It is *critical* that we sync first because of transaction // reconciliation. We want to get all transactions that other // clients have already made, so that imported transactions can be diff --git a/packages/loot-core/src/client/actions/types.d.ts b/packages/loot-core/src/client/actions/types.d.ts deleted file mode 100644 index ad9e5ee358a..00000000000 --- a/packages/loot-core/src/client/actions/types.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { ThunkDispatch } from 'redux-thunk'; - -import type { State, Action } from '../state-types'; - -export type Dispatch = ThunkDispatch; -export type GetState = () => State; diff --git a/packages/loot-core/src/client/actions/user.ts b/packages/loot-core/src/client/actions/user.ts index 6084b4e5487..5136f6cf6a1 100644 --- a/packages/loot-core/src/client/actions/user.ts +++ b/packages/loot-core/src/client/actions/user.ts @@ -1,12 +1,12 @@ import { send } from '../../platform/client/fetch'; import * as constants from '../constants'; +import { type AppDispatch } from '../store'; import { loadAllFiles, closeBudget } from './budgets'; import { loadGlobalPrefs } from './prefs'; -import type { Dispatch } from './types'; export function getUserData() { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { const data = await send('subscribe-get-user'); dispatch({ @@ -18,7 +18,7 @@ export function getUserData() { } export function loggedIn() { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { await dispatch(getUserData()); // We want to be careful about how we call loadAllFiles. It will @@ -40,7 +40,7 @@ export function loggedIn() { } export function signOut() { - return async (dispatch: Dispatch) => { + return async (dispatch: AppDispatch) => { await send('subscribe-sign-out'); dispatch(getUserData()); diff --git a/packages/loot-core/src/client/constants.ts b/packages/loot-core/src/client/constants.ts index e4f968a22e5..6bd4e2cd459 100644 --- a/packages/loot-core/src/client/constants.ts +++ b/packages/loot-core/src/client/constants.ts @@ -25,8 +25,8 @@ export const ADD_NOTIFICATION = 'ADD_NOTIFICATION'; export const REMOVE_NOTIFICATION = 'REMOVE_NOTIFICATION'; export const SET_NOTIFICATION_INSET = 'SET_NOTIFICATION_INSET'; export const GET_USER_DATA = 'GET_USER_DATA'; -export const SET_LAST_UNDO_STATE = 'SET_LAST_UNDO_STATE'; -export const SET_LAST_SPLIT_STATE = 'SET_LAST_SPLIT_STATE'; +// export const SET_LAST_UNDO_STATE = 'SET_LAST_UNDO_STATE'; +// export const SET_LAST_SPLIT_STATE = 'SET_LAST_SPLIT_STATE'; export const SET_ACCOUNTS_SYNCING = 'SET_ACCOUNTS_SYNCING'; export const ACCOUNT_SYNC_STATUS = 'ACCOUNT_SYNC_STATUS'; export const SIGN_OUT = 'SIGN_OUT'; diff --git a/packages/loot-core/src/client/reducers/account.ts b/packages/loot-core/src/client/reducers/account.ts index 37387c5e52b..b2787a4edd7 100644 --- a/packages/loot-core/src/client/reducers/account.ts +++ b/packages/loot-core/src/client/reducers/account.ts @@ -3,7 +3,7 @@ import type { Action } from '../state-types'; import type { AccountState } from '../state-types/account'; const initialState: AccountState = { - failedAccounts: new Map(), + failedAccounts: {}, accountsSyncing: [], }; @@ -15,14 +15,14 @@ export function update(state = initialState, action: Action): AccountState { accountsSyncing: action.ids, }; case constants.ACCOUNT_SYNC_STATUS: { - const failedAccounts = new Map(state.failedAccounts); + const failedAccounts = { ...state.failedAccounts }; if (action.failed) { - failedAccounts.set(action.id, { + failedAccounts[action.id] = { type: action.errorType, code: action.errorCode, - }); + }; } else { - failedAccounts.delete(action.id); + failedAccounts[action.id] = undefined; } return { ...state, failedAccounts }; diff --git a/packages/loot-core/src/client/reducers/app.ts b/packages/loot-core/src/client/reducers/app.ts index 19d4f73d6bd..db1161f12c1 100644 --- a/packages/loot-core/src/client/reducers/app.ts +++ b/packages/loot-core/src/client/reducers/app.ts @@ -7,8 +7,6 @@ export const initialState: AppState = { updateInfo: null, showUpdateNotification: true, managerHasInitialized: false, - lastUndoState: { current: null }, - lastSplitState: { current: null }, }; export function update(state = initialState, action: Action): AppState { @@ -18,17 +16,6 @@ export function update(state = initialState, action: Action): AppState { ...state, ...action.state, }; - case constants.SET_LAST_UNDO_STATE: - // Intentionally mutate it. Components should never rerender - // looking at this, so we put it in a "box" like a ref. They - // only ever need to look at this on mount. - state.lastUndoState.current = action.undoState; - return state; - - case constants.SET_LAST_SPLIT_STATE: - state.lastSplitState.current = action.splitState; - return state; - default: } return state; diff --git a/packages/loot-core/src/client/state-types/account.d.ts b/packages/loot-core/src/client/state-types/account.d.ts index 5abad2a6df2..87b16a8698e 100644 --- a/packages/loot-core/src/client/state-types/account.d.ts +++ b/packages/loot-core/src/client/state-types/account.d.ts @@ -1,7 +1,7 @@ import type * as constants from '../constants'; export type AccountState = { - failedAccounts: Map; + failedAccounts: { [key: string]: { type: string; code: string } }; accountsSyncing: string[]; }; diff --git a/packages/loot-core/src/client/state-types/app.d.ts b/packages/loot-core/src/client/state-types/app.d.ts index fd4e4f34414..14f2b088ec4 100644 --- a/packages/loot-core/src/client/state-types/app.d.ts +++ b/packages/loot-core/src/client/state-types/app.d.ts @@ -1,9 +1,8 @@ -import type { UndoState } from '../../server/undo'; import type * as constants from '../constants'; export type SplitMode = 'collapse' | 'expand'; export type SplitState = { - ids: Set; + ids: string[]; mode: SplitMode; transitionId: string | null; }; @@ -17,8 +16,6 @@ export type AppState = { } | null; showUpdateNotification: boolean; managerHasInitialized: boolean; - lastUndoState: { current: UndoState | null }; - lastSplitState: { current: SplitState | null }; }; export type SetAppStateAction = { @@ -26,17 +23,4 @@ export type SetAppStateAction = { state: Partial; }; -export type SetLastUndoStateAction = { - type: typeof constants.SET_LAST_UNDO_STATE; - undoState: UndoState | null; -}; - -export type SetLastSplitStateAction = { - type: typeof constants.SET_LAST_SPLIT_STATE; - splitState: SplitState | null; -}; - -export type AppActions = - | SetAppStateAction - | SetLastUndoStateAction - | SetLastSplitStateAction; +export type AppActions = SetAppStateAction; diff --git a/packages/loot-core/src/client/state-types/index.d.ts b/packages/loot-core/src/client/state-types/index.d.ts index 382bbcf83fe..ffc435f33ab 100644 --- a/packages/loot-core/src/client/state-types/index.d.ts +++ b/packages/loot-core/src/client/state-types/index.d.ts @@ -34,8 +34,3 @@ export type State = { queries: QueriesState; user: UserState; }; - -declare module 'react-redux' { - // eslint-disable-next-line @typescript-eslint/no-empty-object-type, @typescript-eslint/consistent-type-definitions - export interface DefaultRootState extends State {} -} diff --git a/packages/loot-core/src/client/store/index.ts b/packages/loot-core/src/client/store/index.ts new file mode 100644 index 00000000000..18a2e2ec8a9 --- /dev/null +++ b/packages/loot-core/src/client/store/index.ts @@ -0,0 +1,43 @@ +import { combineReducers, configureStore } from '@reduxjs/toolkit'; + +import * as constants from '../constants'; +import { reducers } from '../reducers'; +import { initialState as initialAppState } from '../reducers/app'; + +const appReducer = combineReducers(reducers); +const rootReducer: typeof appReducer = (state, action) => { + if (action.type === constants.CLOSE_BUDGET) { + // Reset the state and only keep around things intentionally. This + // blows away everything else + state = { + account: null, + modals: null, + notifications: null, + queries: null, + budgets: state.budgets, + user: state.user, + prefs: { local: null, global: state.prefs.global, synced: null }, + app: { + ...initialAppState, + managerHasInitialized: state.app.managerHasInitialized, + loadingText: state.app.loadingText, + }, + }; + } + + return appReducer(state, action); +}; + +export const store = configureStore({ + reducer: rootReducer, + middleware: getDefaultMiddleware => + getDefaultMiddleware({ + // TODO: Fix this in a separate PR. Remove non-serializable states in the store. + serializableCheck: false, + }), +}); + +export type AppStore = typeof store; +export type RootState = ReturnType; +export type AppDispatch = typeof store.dispatch; +export type GetRootState = typeof store.getState; diff --git a/packages/loot-core/src/client/store/mock.ts b/packages/loot-core/src/client/store/mock.ts new file mode 100644 index 00000000000..67d7dc4259b --- /dev/null +++ b/packages/loot-core/src/client/store/mock.ts @@ -0,0 +1,13 @@ +import { configureStore, combineReducers } from '@reduxjs/toolkit'; + +import { reducers } from '../reducers'; + +const appReducer = combineReducers(reducers); + +export let store = null; + +export function resetStore() { + store = configureStore({ + reducer: appReducer, + }); +} diff --git a/packages/loot-core/src/mocks/redux.tsx b/packages/loot-core/src/mocks/redux.tsx deleted file mode 100644 index 0722bbff90b..00000000000 --- a/packages/loot-core/src/mocks/redux.tsx +++ /dev/null @@ -1,21 +0,0 @@ -// @ts-strict-ignore -import React from 'react'; -import { Provider } from 'react-redux'; - -import { createStore, combineReducers, applyMiddleware } from 'redux'; -import thunk from 'redux-thunk'; - -import { reducers } from '../client/reducers'; - -const appReducer = combineReducers(reducers); -let store = null; - -export function resetStore() { - store = createStore(appReducer, undefined, applyMiddleware(thunk /* log */)); -} - -resetStore(); - -export function TestProvider({ children }) { - return {children}; -} diff --git a/packages/loot-core/src/platform/client/undo/index.d.ts b/packages/loot-core/src/platform/client/undo/index.d.ts index 18090c47d93..184f0930d34 100644 --- a/packages/loot-core/src/platform/client/undo/index.d.ts +++ b/packages/loot-core/src/platform/client/undo/index.d.ts @@ -9,6 +9,7 @@ export type UndoState = { name: string; items: Set; } | null; + current: unknown; }; export function setUndoState>( diff --git a/packages/loot-core/src/platform/client/undo/index.web.ts b/packages/loot-core/src/platform/client/undo/index.web.ts index 15c3300b076..28b4573e7fb 100644 --- a/packages/loot-core/src/platform/client/undo/index.web.ts +++ b/packages/loot-core/src/platform/client/undo/index.web.ts @@ -11,6 +11,7 @@ const currentUndoState: T.UndoState = { url: null, openModal: null, selectedItems: null, + current: null, }; export const setUndoState: T.SetUndoState = function (name, value) { diff --git a/yarn.lock b/yarn.lock index 5f72026d305..601b2d85263 100644 --- a/yarn.lock +++ b/yarn.lock @@ -63,6 +63,7 @@ __metadata: "@fontsource/redacted-script": "npm:^5.0.21" "@juggle/resize-observer": "npm:^3.4.0" "@playwright/test": "npm:1.41.1" + "@reduxjs/toolkit": "npm:^2.5.0" "@rollup/plugin-inject": "npm:^5.0.5" "@svgr/cli": "npm:^8.1.0" "@swc/core": "npm:^1.5.3" @@ -77,7 +78,6 @@ __metadata: "@types/react-dom": "npm:^18.2.1" "@types/react-grid-layout": "npm:^1" "@types/react-modal": "npm:^3.16.0" - "@types/react-redux": "npm:^7.1.25" "@types/uuid": "npm:^9.0.2" "@types/webpack-bundle-analyzer": "npm:^4.6.3" "@use-gesture/react": "npm:^10.3.0" @@ -114,15 +114,13 @@ __metadata: react-i18next: "npm:^14.1.2" react-markdown: "npm:^8.0.7" react-modal: "npm:3.16.1" - react-redux: "npm:7.2.9" + react-redux: "npm:^9.2.0" react-router-dom: "npm:6.21.3" react-simple-pull-to-refresh: "npm:^1.3.3" react-spring: "npm:^9.7.3" react-stately: "npm:^3.33.0" react-virtualized-auto-sizer: "npm:^1.0.21" recharts: "npm:^2.10.4" - redux: "npm:^4.2.1" - redux-thunk: "npm:^2.4.2" remark-gfm: "npm:^3.0.1" rollup-plugin-visualizer: "npm:^5.12.0" sass: "npm:^1.70.0" @@ -1461,7 +1459,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.8, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": +"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": version: 7.25.6 resolution: "@babel/runtime@npm:7.25.6" dependencies: @@ -4590,6 +4588,26 @@ __metadata: languageName: node linkType: hard +"@reduxjs/toolkit@npm:^2.5.0": + version: 2.5.0 + resolution: "@reduxjs/toolkit@npm:2.5.0" + dependencies: + immer: "npm:^10.0.3" + redux: "npm:^5.0.1" + redux-thunk: "npm:^3.1.0" + reselect: "npm:^5.1.0" + peerDependencies: + react: ^16.9.0 || ^17.0.0 || ^18 || ^19 + react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 + peerDependenciesMeta: + react: + optional: true + react-redux: + optional: true + checksum: 10/b24ee7e89bd00c5f3ee5df12cbc1b6395784ff215b9bf75d2c1b211d8494af220057ced666f49c6e815b723252bc3eb704f7be4a3c261ab06ee7b776f4565d4b + languageName: node + linkType: hard + "@remix-run/router@npm:1.14.2": version: 1.14.2 resolution: "@remix-run/router@npm:1.14.2" @@ -5571,16 +5589,6 @@ __metadata: languageName: node linkType: hard -"@types/hoist-non-react-statics@npm:^3.3.0": - version: 3.3.1 - resolution: "@types/hoist-non-react-statics@npm:3.3.1" - dependencies: - "@types/react": "npm:*" - hoist-non-react-statics: "npm:^3.3.0" - checksum: 10/071e6d75a0ed9aa0e9ca2cc529a8c15bf7ac3e4a37aac279772ea6036fd0bf969b67fb627b65cfce65adeab31fec1e9e95b4dcdefeab075b580c0c7174206f63 - languageName: node - linkType: hard - "@types/http-cache-semantics@npm:*": version: 4.0.1 resolution: "@types/http-cache-semantics@npm:4.0.1" @@ -5794,18 +5802,6 @@ __metadata: languageName: node linkType: hard -"@types/react-redux@npm:^7.1.20, @types/react-redux@npm:^7.1.25": - version: 7.1.33 - resolution: "@types/react-redux@npm:7.1.33" - dependencies: - "@types/hoist-non-react-statics": "npm:^3.3.0" - "@types/react": "npm:*" - hoist-non-react-statics: "npm:^3.3.0" - redux: "npm:^4.0.0" - checksum: 10/65f4e0a3f0e532bbbe44ae6522d1fce91bfcb3bacc90904c35d3f819e77932cc489b6945988acb4a2320f6e78c57dd1c149556aa241a68efc51de15a2cd73fc0 - languageName: node - linkType: hard - "@types/react@npm:*, @types/react@npm:^18.2.0": version: 18.2.0 resolution: "@types/react@npm:18.2.0" @@ -5882,6 +5878,13 @@ __metadata: languageName: node linkType: hard +"@types/use-sync-external-store@npm:^0.0.6": + version: 0.0.6 + resolution: "@types/use-sync-external-store@npm:0.0.6" + checksum: 10/a95ce330668501ad9b1c5b7f2b14872ad201e552a0e567787b8f1588b22c7040c7c3d80f142cbb9f92d13c4ea41c46af57a20f2af4edf27f224d352abcfe4049 + languageName: node + linkType: hard + "@types/uuid@npm:^9.0.2": version: 9.0.2 resolution: "@types/uuid@npm:9.0.2" @@ -11501,7 +11504,7 @@ __metadata: languageName: node linkType: hard -"hoist-non-react-statics@npm:^3.3.0, hoist-non-react-statics@npm:^3.3.2": +"hoist-non-react-statics@npm:^3.3.2": version: 3.3.2 resolution: "hoist-non-react-statics@npm:3.3.2" dependencies: @@ -11745,6 +11748,13 @@ __metadata: languageName: node linkType: hard +"immer@npm:^10.0.3": + version: 10.1.1 + resolution: "immer@npm:10.1.1" + checksum: 10/9dacf1e8c201d69191ccd88dc5d733bafe166cd45a5a360c5d7c88f1de0dff974a94114d72b35f3106adfe587fcfb131c545856184a2247d89d735ad25589863 + languageName: node + linkType: hard + "immutable@npm:^4.0.0": version: 4.3.0 resolution: "immutable@npm:4.3.0" @@ -13806,6 +13816,7 @@ __metadata: "@actual-app/api": "npm:*" "@actual-app/crdt": "npm:*" "@jlongster/sql.js": "npm:^1.6.7" + "@reduxjs/toolkit": "npm:^2.5.0" "@rschedule/core": "npm:^1.5.0" "@rschedule/json-tools": "npm:^1.5.0" "@rschedule/standard-date-adapter": "npm:^1.5.0" @@ -13817,7 +13828,6 @@ __metadata: "@types/jest": "npm:^27.5.2" "@types/jlongster__sql.js": "npm:@types/sql.js@latest" "@types/pegjs": "npm:^0.10.3" - "@types/react-redux": "npm:^7.1.25" "@types/uuid": "npm:^9.0.2" "@types/webpack": "npm:^5.28.5" "@types/webpack-bundle-analyzer": "npm:^4.6.3" @@ -16513,24 +16523,22 @@ __metadata: languageName: node linkType: hard -"react-redux@npm:7.2.9": - version: 7.2.9 - resolution: "react-redux@npm:7.2.9" +"react-redux@npm:^9.2.0": + version: 9.2.0 + resolution: "react-redux@npm:9.2.0" dependencies: - "@babel/runtime": "npm:^7.15.4" - "@types/react-redux": "npm:^7.1.20" - hoist-non-react-statics: "npm:^3.3.2" - loose-envify: "npm:^1.4.0" - prop-types: "npm:^15.7.2" - react-is: "npm:^17.0.2" + "@types/use-sync-external-store": "npm:^0.0.6" + use-sync-external-store: "npm:^1.4.0" peerDependencies: - react: ^16.8.3 || ^17 || ^18 + "@types/react": ^18.2.25 || ^19 + react: ^18.0 || ^19 + redux: ^5.0.0 peerDependenciesMeta: - react-dom: + "@types/react": optional: true - react-native: + redux: optional: true - checksum: 10/1c3018bd2601e6d18339281867910b583dcbb3d8856403086e08c00abf0dfe467a94c0d1356bafa8cdf107bf1e2c9899a28486e4778e85c8bc4dfed2076b116f + checksum: 10/b3d2f89f469169475ab0a9f8914d54a336ac9bc6a31af6e8dcfe9901e6fe2cfd8c1a3f6ce7a2f7f3e0928a93fbab833b668804155715598b7f2ad89927d3ff50 languageName: node linkType: hard @@ -16812,16 +16820,16 @@ __metadata: languageName: node linkType: hard -"redux-thunk@npm:^2.4.2": - version: 2.4.2 - resolution: "redux-thunk@npm:2.4.2" +"redux-thunk@npm:^3.1.0": + version: 3.1.0 + resolution: "redux-thunk@npm:3.1.0" peerDependencies: - redux: ^4 - checksum: 10/9bcb1193835128ecebf1e1a1b1a37bc15e8dfbdf6b6ee1b5566dd4c8e4ca05a81175f0c6dda34ab47f87053cd13b74d9f881d59446691d7b192831852b5d7a72 + redux: ^5.0.0 + checksum: 10/38c563db5f0bbec90d2e65cc27f3c870c1b6102e0c071258734fac41cb0e51d31d894125815c2f4133b20aff231f51f028ad99bccc05a7e3249f1a5d5a959ed3 languageName: node linkType: hard -"redux@npm:^4.0.0, redux@npm:^4.2.0, redux@npm:^4.2.1": +"redux@npm:^4.2.0": version: 4.2.1 resolution: "redux@npm:4.2.1" dependencies: @@ -17018,6 +17026,13 @@ __metadata: languageName: node linkType: hard +"reselect@npm:^5.1.0": + version: 5.1.1 + resolution: "reselect@npm:5.1.1" + checksum: 10/1fdae11a39ed9c8d85a24df19517c8372ee24fefea9cce3fae9eaad8e9cefbba5a3d4940c6fe31296b6addf76e035588c55798f7e6e147e1b7c0855f119e7fa5 + languageName: node + linkType: hard + "resize-observer-polyfill@npm:^1.5.1": version: 1.5.1 resolution: "resize-observer-polyfill@npm:1.5.1" @@ -19450,6 +19465,15 @@ __metadata: languageName: node linkType: hard +"use-sync-external-store@npm:^1.4.0": + version: 1.4.0 + resolution: "use-sync-external-store@npm:1.4.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: 10/08bf581a8a2effaefc355e9d18ed025d436230f4cc973db2f593166df357cf63e47b9097b6e5089b594758bde322e1737754ad64905e030d70f8ff7ee671fd01 + languageName: node + linkType: hard + "usehooks-ts@npm:^3.0.1": version: 3.0.1 resolution: "usehooks-ts@npm:3.0.1" From ddb9e3d5fafb65d2fdaeae277ec24ed8676d79e8 Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez Date: Tue, 17 Dec 2024 15:43:23 -0800 Subject: [PATCH 02/13] Fix typecheck and lint --- packages/desktop-client/src/components/table.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/desktop-client/src/components/table.tsx b/packages/desktop-client/src/components/table.tsx index c26ed527d62..499f7a486f8 100644 --- a/packages/desktop-client/src/components/table.tsx +++ b/packages/desktop-client/src/components/table.tsx @@ -19,6 +19,7 @@ import React, { } from 'react'; import AutoSizer from 'react-virtualized-auto-sizer'; +import { useModalState } from '../hooks/useModalState'; import { AvoidRefocusScrollProvider, useProperFocus, @@ -27,7 +28,6 @@ import { useSelectedItems } from '../hooks/useSelected'; import { AnimatedLoading } from '../icons/AnimatedLoading'; import { SvgDelete, SvgExpandArrow } from '../icons/v0'; import { SvgCheckmark } from '../icons/v1'; -import { useAppStore } from '../redux'; import { styles, theme } from '../style'; import { Button } from './common/Button2'; @@ -1229,7 +1229,7 @@ export function useTableNavigator( const containerRef = useRef(); // See `onBlur` for why we need this - const store = useAppStore(); + const modalState = useModalState(); const modalStackLength = useRef(0); // onEdit is passed to children, so make sure it maintains identity @@ -1239,8 +1239,8 @@ export function useTableNavigator( }, []); useEffect(() => { - modalStackLength.current = store.getState().modals.modalStack.length; - }, []); + modalStackLength.current = modalState.modalStack.length; + }, [modalState.modalStack]); function flashInput() { // Force the container to be focused which suppresses the "space @@ -1396,7 +1396,7 @@ export function useTableNavigator( // modal just opened. This way the field still shows an // input, and it will be refocused when the modal closes. const prevNumModals = modalStackLength.current; - const numModals = store.getState().modals.modalStack.length; + const numModals = modalState.modalStack.length; if ( document.hasFocus() && From cd61a9fcef808fab358a4c5b83dff9bf5261121a Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez Date: Tue, 17 Dec 2024 15:50:47 -0800 Subject: [PATCH 03/13] Fix lint and typecheck errors --- packages/desktop-client/src/redux/mock.tsx | 4 ++-- packages/loot-core/src/client/reducers/account.ts | 2 +- packages/loot-core/src/client/reducers/modals.ts | 2 +- .../loot-core/src/client/reducers/notifications.ts | 2 +- packages/loot-core/src/client/reducers/queries.ts | 2 +- packages/loot-core/src/client/store/index.ts | 12 ++++++++---- 6 files changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/desktop-client/src/redux/mock.tsx b/packages/desktop-client/src/redux/mock.tsx index 2b5e2cc74f7..73456103798 100644 --- a/packages/desktop-client/src/redux/mock.tsx +++ b/packages/desktop-client/src/redux/mock.tsx @@ -1,10 +1,10 @@ -import React from 'react'; +import React, { ReactNode } from 'react'; import { Provider } from 'react-redux'; import { store, resetStore } from 'loot-core/client/store/mock'; resetStore(); -export function TestProvider({ children }) { +export function TestProvider({ children }: { children: ReactNode }) { return {children}; } diff --git a/packages/loot-core/src/client/reducers/account.ts b/packages/loot-core/src/client/reducers/account.ts index b2787a4edd7..1afb98b04fa 100644 --- a/packages/loot-core/src/client/reducers/account.ts +++ b/packages/loot-core/src/client/reducers/account.ts @@ -2,7 +2,7 @@ import * as constants from '../constants'; import type { Action } from '../state-types'; import type { AccountState } from '../state-types/account'; -const initialState: AccountState = { +export const initialState: AccountState = { failedAccounts: {}, accountsSyncing: [], }; diff --git a/packages/loot-core/src/client/reducers/modals.ts b/packages/loot-core/src/client/reducers/modals.ts index d0e6840fb08..89f882f49c2 100644 --- a/packages/loot-core/src/client/reducers/modals.ts +++ b/packages/loot-core/src/client/reducers/modals.ts @@ -2,7 +2,7 @@ import * as constants from '../constants'; import type { Action } from '../state-types'; import type { ModalsState } from '../state-types/modals'; -const initialState: ModalsState = { +export const initialState: ModalsState = { modalStack: [], isHidden: false, }; diff --git a/packages/loot-core/src/client/reducers/notifications.ts b/packages/loot-core/src/client/reducers/notifications.ts index b1dd868d9b7..a42efea911a 100644 --- a/packages/loot-core/src/client/reducers/notifications.ts +++ b/packages/loot-core/src/client/reducers/notifications.ts @@ -3,7 +3,7 @@ import * as constants from '../constants'; import type { Action } from '../state-types'; import type { NotificationsState } from '../state-types/notifications'; -const initialState = { +export const initialState = { notifications: [], inset: {}, }; diff --git a/packages/loot-core/src/client/reducers/queries.ts b/packages/loot-core/src/client/reducers/queries.ts index b95214d0b02..0ef1d87107c 100644 --- a/packages/loot-core/src/client/reducers/queries.ts +++ b/packages/loot-core/src/client/reducers/queries.ts @@ -7,7 +7,7 @@ import * as constants from '../constants'; import type { Action } from '../state-types'; import type { QueriesState } from '../state-types/queries'; -const initialState: QueriesState = { +export const initialState: QueriesState = { newTransactions: [], matchedTransactions: [], lastTransaction: null, diff --git a/packages/loot-core/src/client/store/index.ts b/packages/loot-core/src/client/store/index.ts index 18a2e2ec8a9..7eceda63490 100644 --- a/packages/loot-core/src/client/store/index.ts +++ b/packages/loot-core/src/client/store/index.ts @@ -3,6 +3,10 @@ import { combineReducers, configureStore } from '@reduxjs/toolkit'; import * as constants from '../constants'; import { reducers } from '../reducers'; import { initialState as initialAppState } from '../reducers/app'; +import { initialState as initialAccountState } from '../reducers/account'; +import { initialState as initialModalsState } from '../reducers/modals'; +import { initialState as initialNotificationsState } from '../reducers/notifications'; +import { initialState as initialQueriesState } from '../reducers/queries'; const appReducer = combineReducers(reducers); const rootReducer: typeof appReducer = (state, action) => { @@ -10,10 +14,10 @@ const rootReducer: typeof appReducer = (state, action) => { // Reset the state and only keep around things intentionally. This // blows away everything else state = { - account: null, - modals: null, - notifications: null, - queries: null, + account: initialAccountState, + modals: initialModalsState, + notifications: initialNotificationsState, + queries: initialQueriesState, budgets: state.budgets, user: state.user, prefs: { local: null, global: state.prefs.global, synced: null }, From 5927d077092702d9858e8b00940d54f76bc74b54 Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez Date: Wed, 18 Dec 2024 08:51:43 -0800 Subject: [PATCH 04/13] Fix lint and typecheck errors --- .../src/components/sidebar/Accounts.tsx | 8 ++++---- .../src/hooks/useSplitsExpanded.tsx | 2 +- packages/desktop-client/src/redux/mock.tsx | 8 +++----- packages/desktop-client/src/setupTests.js | 4 ++-- .../loot-core/src/client/reducers/account.ts | 2 +- .../loot-core/src/client/reducers/budgets.ts | 2 +- .../loot-core/src/client/reducers/prefs.ts | 2 +- .../loot-core/src/client/reducers/user.ts | 2 +- packages/loot-core/src/client/store/index.ts | 19 +++++++++++++------ packages/loot-core/src/client/store/mock.ts | 10 +++++++--- upcoming-release-notes/4000.md | 6 ++++++ 11 files changed, 40 insertions(+), 25 deletions(-) create mode 100644 upcoming-release-notes/4000.md diff --git a/packages/desktop-client/src/components/sidebar/Accounts.tsx b/packages/desktop-client/src/components/sidebar/Accounts.tsx index 0089b3be8e2..9ba1e929f10 100644 --- a/packages/desktop-client/src/components/sidebar/Accounts.tsx +++ b/packages/desktop-client/src/components/sidebar/Accounts.tsx @@ -119,8 +119,8 @@ export function Accounts() { account={account} connected={!!account.bank} pending={syncingAccountIds.includes(account.id)} - failed={!!failedAccounts?.[account.id]} - updated={updatedAccounts?.includes(account.id)} + failed={!!failedAccounts.get(account.id)} + updated={updatedAccounts.includes(account.id)} to={getAccountPath(account)} query={queries.accountBalance(account)} onDragChange={onDragChange} @@ -149,8 +149,8 @@ export function Accounts() { account={account} connected={!!account.bank} pending={syncingAccountIds.includes(account.id)} - failed={!!failedAccounts?.[account.id]} - updated={updatedAccounts?.includes(account.id)} + failed={!!failedAccounts.get(account.id)} + updated={updatedAccounts.includes(account.id)} to={getAccountPath(account)} query={queries.accountBalance(account)} onDragChange={onDragChange} diff --git a/packages/desktop-client/src/hooks/useSplitsExpanded.tsx b/packages/desktop-client/src/hooks/useSplitsExpanded.tsx index 30b767a1055..3253e0a36e8 100644 --- a/packages/desktop-client/src/hooks/useSplitsExpanded.tsx +++ b/packages/desktop-client/src/hooks/useSplitsExpanded.tsx @@ -91,7 +91,7 @@ export function SplitsExpandedProvider({ children, initialMode = 'expand', }: SplitsExpandedProviderProps) { - const previousState = useRef(null); + const previousState = useRef(null); const [state, dispatch] = useReducer( (state: SplitState, action: Actions): SplitState => { diff --git a/packages/desktop-client/src/redux/mock.tsx b/packages/desktop-client/src/redux/mock.tsx index 73456103798..138259bdc2a 100644 --- a/packages/desktop-client/src/redux/mock.tsx +++ b/packages/desktop-client/src/redux/mock.tsx @@ -1,10 +1,8 @@ -import React, { ReactNode } from 'react'; +import React, { type ReactNode } from 'react'; import { Provider } from 'react-redux'; -import { store, resetStore } from 'loot-core/client/store/mock'; - -resetStore(); +import { mockStore } from 'loot-core/client/store/mock'; export function TestProvider({ children }: { children: ReactNode }) { - return {children}; + return {children}; } diff --git a/packages/desktop-client/src/setupTests.js b/packages/desktop-client/src/setupTests.js index 383bcab1b52..a645eae5bf2 100644 --- a/packages/desktop-client/src/setupTests.js +++ b/packages/desktop-client/src/setupTests.js @@ -1,4 +1,4 @@ -import { resetStore } from 'loot-core/src/client/store/mock'; +import { resetMockStore } from 'loot-core/src/client/store/mock'; import { installPolyfills } from './polyfills'; @@ -16,7 +16,7 @@ vi.mock('react-virtualized-auto-sizer', () => ({ global.Date.now = () => 123456789; global.__resetWorld = () => { - resetStore(); + resetMockStore(); }; process.on('unhandledRejection', reason => { diff --git a/packages/loot-core/src/client/reducers/account.ts b/packages/loot-core/src/client/reducers/account.ts index 1afb98b04fa..ef9e74121cb 100644 --- a/packages/loot-core/src/client/reducers/account.ts +++ b/packages/loot-core/src/client/reducers/account.ts @@ -22,7 +22,7 @@ export function update(state = initialState, action: Action): AccountState { code: action.errorCode, }; } else { - failedAccounts[action.id] = undefined; + delete failedAccounts[action.id]; } return { ...state, failedAccounts }; diff --git a/packages/loot-core/src/client/reducers/budgets.ts b/packages/loot-core/src/client/reducers/budgets.ts index 70d9f535f11..fad8eb9e190 100644 --- a/packages/loot-core/src/client/reducers/budgets.ts +++ b/packages/loot-core/src/client/reducers/budgets.ts @@ -129,7 +129,7 @@ function reconcileFiles( .concat(sorted.filter(f => f.state === 'broken')); } -const initialState: BudgetsState = { +export const initialState: BudgetsState = { budgets: [], remoteFiles: null, allFiles: null, diff --git a/packages/loot-core/src/client/reducers/prefs.ts b/packages/loot-core/src/client/reducers/prefs.ts index 108c9eb551d..dd2c8659930 100644 --- a/packages/loot-core/src/client/reducers/prefs.ts +++ b/packages/loot-core/src/client/reducers/prefs.ts @@ -2,7 +2,7 @@ import * as constants from '../constants'; import type { Action } from '../state-types'; import type { PrefsState } from '../state-types/prefs'; -const initialState: PrefsState = { +export const initialState: PrefsState = { local: {}, global: {}, synced: {}, diff --git a/packages/loot-core/src/client/reducers/user.ts b/packages/loot-core/src/client/reducers/user.ts index fe715884bec..03c17460343 100644 --- a/packages/loot-core/src/client/reducers/user.ts +++ b/packages/loot-core/src/client/reducers/user.ts @@ -1,7 +1,7 @@ import * as constants from '../constants'; import type { UserActions, UserState } from '../state-types/user'; -const initialState: UserState = { +export const initialState: UserState = { data: null, }; diff --git a/packages/loot-core/src/client/store/index.ts b/packages/loot-core/src/client/store/index.ts index 7eceda63490..8c77e00a468 100644 --- a/packages/loot-core/src/client/store/index.ts +++ b/packages/loot-core/src/client/store/index.ts @@ -2,11 +2,14 @@ import { combineReducers, configureStore } from '@reduxjs/toolkit'; import * as constants from '../constants'; import { reducers } from '../reducers'; -import { initialState as initialAppState } from '../reducers/app'; import { initialState as initialAccountState } from '../reducers/account'; +import { initialState as initialAppState } from '../reducers/app'; +import { initialState as initialBudgetsState } from '../reducers/budgets'; import { initialState as initialModalsState } from '../reducers/modals'; import { initialState as initialNotificationsState } from '../reducers/notifications'; +import { initialState as initialPrefsState } from '../reducers/prefs'; import { initialState as initialQueriesState } from '../reducers/queries'; +import { initialState as initialUserState } from '../reducers/user'; const appReducer = combineReducers(reducers); const rootReducer: typeof appReducer = (state, action) => { @@ -18,13 +21,17 @@ const rootReducer: typeof appReducer = (state, action) => { modals: initialModalsState, notifications: initialNotificationsState, queries: initialQueriesState, - budgets: state.budgets, - user: state.user, - prefs: { local: null, global: state.prefs.global, synced: null }, + budgets: state?.budgets || initialBudgetsState, + user: state?.user || initialUserState, + prefs: { + local: initialPrefsState.local, + global: state?.prefs?.global || initialPrefsState.global, + synced: initialPrefsState.synced, + }, app: { ...initialAppState, - managerHasInitialized: state.app.managerHasInitialized, - loadingText: state.app.loadingText, + managerHasInitialized: state?.app?.managerHasInitialized || false, + loadingText: state?.app?.loadingText || null, }, }; } diff --git a/packages/loot-core/src/client/store/mock.ts b/packages/loot-core/src/client/store/mock.ts index 67d7dc4259b..c9a6c46e681 100644 --- a/packages/loot-core/src/client/store/mock.ts +++ b/packages/loot-core/src/client/store/mock.ts @@ -2,12 +2,16 @@ import { configureStore, combineReducers } from '@reduxjs/toolkit'; import { reducers } from '../reducers'; +import { type store as realStore } from './index'; + const appReducer = combineReducers(reducers); -export let store = null; +export let mockStore: typeof realStore | null = null; -export function resetStore() { - store = configureStore({ +export function resetMockStore() { + mockStore = configureStore({ reducer: appReducer, }); } + +resetMockStore(); diff --git a/upcoming-release-notes/4000.md b/upcoming-release-notes/4000.md new file mode 100644 index 00000000000..97d68b9b6b6 --- /dev/null +++ b/upcoming-release-notes/4000.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [joel-jeremy] +--- + +Phase 1 - Migrate to modern redux toolkit APIs From 4d680c196de693821a9e425d72399d63f2539b68 Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez Date: Wed, 18 Dec 2024 11:42:41 -0800 Subject: [PATCH 05/13] Fix typecheck error --- packages/desktop-client/package.json | 1 - packages/loot-core/src/client/store/mock.ts | 6 +++--- yarn.lock | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/desktop-client/package.json b/packages/desktop-client/package.json index c423ab96160..19b8078aceb 100644 --- a/packages/desktop-client/package.json +++ b/packages/desktop-client/package.json @@ -10,7 +10,6 @@ "@fontsource/redacted-script": "^5.0.21", "@juggle/resize-observer": "^3.4.0", "@playwright/test": "1.41.1", - "@reduxjs/toolkit": "^2.5.0", "@rollup/plugin-inject": "^5.0.5", "@svgr/cli": "^8.1.0", "@swc/core": "^1.5.3", diff --git a/packages/loot-core/src/client/store/mock.ts b/packages/loot-core/src/client/store/mock.ts index c9a6c46e681..a232a9c5662 100644 --- a/packages/loot-core/src/client/store/mock.ts +++ b/packages/loot-core/src/client/store/mock.ts @@ -6,12 +6,12 @@ import { type store as realStore } from './index'; const appReducer = combineReducers(reducers); -export let mockStore: typeof realStore | null = null; +export let mockStore: typeof realStore = configureStore({ + reducer: appReducer, +}); export function resetMockStore() { mockStore = configureStore({ reducer: appReducer, }); } - -resetMockStore(); diff --git a/yarn.lock b/yarn.lock index 601b2d85263..0d1859792fa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -63,7 +63,6 @@ __metadata: "@fontsource/redacted-script": "npm:^5.0.21" "@juggle/resize-observer": "npm:^3.4.0" "@playwright/test": "npm:1.41.1" - "@reduxjs/toolkit": "npm:^2.5.0" "@rollup/plugin-inject": "npm:^5.0.5" "@svgr/cli": "npm:^8.1.0" "@swc/core": "npm:^1.5.3" From 8518d446728eef9e805f3019185fd6d34e665b37 Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez Date: Wed, 18 Dec 2024 12:00:03 -0800 Subject: [PATCH 06/13] Cleanup --- packages/desktop-client/src/components/sidebar/Accounts.tsx | 4 ++-- packages/loot-core/src/client/constants.ts | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/desktop-client/src/components/sidebar/Accounts.tsx b/packages/desktop-client/src/components/sidebar/Accounts.tsx index 9ba1e929f10..d05e90b74dc 100644 --- a/packages/desktop-client/src/components/sidebar/Accounts.tsx +++ b/packages/desktop-client/src/components/sidebar/Accounts.tsx @@ -119,7 +119,7 @@ export function Accounts() { account={account} connected={!!account.bank} pending={syncingAccountIds.includes(account.id)} - failed={!!failedAccounts.get(account.id)} + failed={failedAccounts.has(account.id)} updated={updatedAccounts.includes(account.id)} to={getAccountPath(account)} query={queries.accountBalance(account)} @@ -149,7 +149,7 @@ export function Accounts() { account={account} connected={!!account.bank} pending={syncingAccountIds.includes(account.id)} - failed={!!failedAccounts.get(account.id)} + failed={failedAccounts.has(account.id)} updated={updatedAccounts.includes(account.id)} to={getAccountPath(account)} query={queries.accountBalance(account)} diff --git a/packages/loot-core/src/client/constants.ts b/packages/loot-core/src/client/constants.ts index 6bd4e2cd459..40406878536 100644 --- a/packages/loot-core/src/client/constants.ts +++ b/packages/loot-core/src/client/constants.ts @@ -25,8 +25,6 @@ export const ADD_NOTIFICATION = 'ADD_NOTIFICATION'; export const REMOVE_NOTIFICATION = 'REMOVE_NOTIFICATION'; export const SET_NOTIFICATION_INSET = 'SET_NOTIFICATION_INSET'; export const GET_USER_DATA = 'GET_USER_DATA'; -// export const SET_LAST_UNDO_STATE = 'SET_LAST_UNDO_STATE'; -// export const SET_LAST_SPLIT_STATE = 'SET_LAST_SPLIT_STATE'; export const SET_ACCOUNTS_SYNCING = 'SET_ACCOUNTS_SYNCING'; export const ACCOUNT_SYNC_STATUS = 'ACCOUNT_SYNC_STATUS'; export const SIGN_OUT = 'SIGN_OUT'; From b2f97aba1e1d426f2f131086efd41871b46f0cc9 Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez Date: Wed, 1 Jan 2025 12:49:12 -0800 Subject: [PATCH 07/13] Remove useAppStore --- packages/desktop-client/src/redux/index.ts | 9 ++------- packages/loot-core/src/client/store/index.ts | 1 - 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/desktop-client/src/redux/index.ts b/packages/desktop-client/src/redux/index.ts index e3387095679..383f3369974 100644 --- a/packages/desktop-client/src/redux/index.ts +++ b/packages/desktop-client/src/redux/index.ts @@ -1,11 +1,6 @@ -import { useDispatch, useSelector, useStore } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; -import { - type AppStore, - type AppDispatch, - type RootState, -} from 'loot-core/client/store'; +import { type AppDispatch, type RootState } from 'loot-core/client/store'; -export const useAppStore = useStore.withTypes(); export const useAppDispatch = useDispatch.withTypes(); export const useAppSelector = useSelector.withTypes(); diff --git a/packages/loot-core/src/client/store/index.ts b/packages/loot-core/src/client/store/index.ts index 8c77e00a468..12f1f2e4d3b 100644 --- a/packages/loot-core/src/client/store/index.ts +++ b/packages/loot-core/src/client/store/index.ts @@ -48,7 +48,6 @@ export const store = configureStore({ }), }); -export type AppStore = typeof store; export type RootState = ReturnType; export type AppDispatch = typeof store.dispatch; export type GetRootState = typeof store.getState; From 076dcee8829b36bc0f390703b869fabc39f88f97 Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez Date: Wed, 1 Jan 2025 12:54:19 -0800 Subject: [PATCH 08/13] Cleanup --- packages/desktop-client/src/auth/AuthProvider.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/desktop-client/src/auth/AuthProvider.tsx b/packages/desktop-client/src/auth/AuthProvider.tsx index f8105064c52..95ddcccbedf 100644 --- a/packages/desktop-client/src/auth/AuthProvider.tsx +++ b/packages/desktop-client/src/auth/AuthProvider.tsx @@ -1,7 +1,5 @@ import React, { createContext, useContext, type ReactNode } from 'react'; -import { type State } from 'loot-core/client/state-types'; - import { useServerURL } from '../components/ServerContext'; import { useAppSelector } from '../redux'; @@ -18,7 +16,7 @@ type AuthProviderProps = { }; export const AuthProvider = ({ children }: AuthProviderProps) => { - const userData = useAppSelector((state: State) => state.user.data); + const userData = useAppSelector(state => state.user.data); const serverUrl = useServerURL(); const hasPermission = (permission?: Permissions) => { From 27f8702b0de64bfe19e8eb8937754b71e2fbed06 Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez Date: Wed, 1 Jan 2025 12:59:22 -0800 Subject: [PATCH 09/13] Undo renames --- .../desktop-client/src/auth/AuthProvider.tsx | 4 ++-- .../desktop-client/src/auth/ProtectedRoute.tsx | 6 +++--- packages/desktop-client/src/components/App.tsx | 8 ++++---- .../src/components/AppBackground.tsx | 4 ++-- .../src/components/BankSyncStatus.tsx | 6 ++---- .../src/components/FinancesApp.tsx | 6 +++--- .../desktop-client/src/components/HelpMenu.tsx | 4 ++-- .../src/components/LoggedInUser.tsx | 10 +++++----- .../src/components/ManageRules.tsx | 4 ++-- .../desktop-client/src/components/Modals.tsx | 4 ++-- .../src/components/Notifications.tsx | 10 ++++------ .../desktop-client/src/components/Titlebar.tsx | 4 ++-- .../src/components/UpdateNotification.tsx | 8 ++++---- .../src/components/accounts/Account.tsx | 16 +++++----------- .../src/components/accounts/AccountSyncCheck.tsx | 4 ++-- .../autocomplete/PayeeAutocomplete.tsx | 4 ++-- .../src/components/budget/index.tsx | 4 ++-- .../src/components/manager/BudgetList.tsx | 10 +++++----- .../src/components/manager/ConfigServer.tsx | 4 ++-- .../src/components/manager/ManagementApp.tsx | 12 ++++++------ .../src/components/manager/WelcomeScreen.tsx | 4 ++-- .../components/manager/subscribe/Bootstrap.tsx | 4 ++-- .../src/components/manager/subscribe/Login.tsx | 4 ++-- .../manager/subscribe/OpenIdCallback.ts | 4 ++-- .../mobile/accounts/AccountTransactions.tsx | 10 ++++------ .../src/components/mobile/budget/BudgetTable.jsx | 6 +++--- .../mobile/budget/CategoryTransactions.tsx | 4 ++-- .../src/components/mobile/budget/index.tsx | 4 ++-- .../mobile/transactions/TransactionEdit.jsx | 10 ++++------ .../mobile/transactions/TransactionList.jsx | 4 ++-- .../mobile/transactions/TransactionListItem.tsx | 6 ++---- .../src/components/modals/BudgetListModal.tsx | 4 ++-- .../src/components/modals/CoverModal.tsx | 4 ++-- .../src/components/modals/CreateAccountModal.tsx | 4 ++-- .../modals/CreateLocalAccountModal.tsx | 4 ++-- .../modals/EnvelopeBudgetSummaryModal.tsx | 4 ++-- .../modals/GoCardlessExternalMsgModal.tsx | 4 ++-- .../ImportTransactionsModal.jsx | 4 ++-- .../src/components/modals/LoadBackupModal.tsx | 4 ++-- .../components/modals/MergeUnusedPayeesModal.tsx | 6 +++--- .../modals/OutOfSyncMigrationsModal.tsx | 4 ++-- .../src/components/modals/TransferModal.tsx | 4 ++-- .../src/components/modals/TransferOwnership.tsx | 8 ++++---- .../modals/manager/ConfirmChangeDocumentDir.tsx | 4 ++-- .../modals/manager/DeleteFileModal.tsx | 4 ++-- .../modals/manager/DuplicateFileModal.tsx | 4 ++-- .../modals/manager/FilesSettingsModal.tsx | 6 +++--- .../modals/manager/ImportActualModal.tsx | 4 ++-- .../components/modals/manager/ImportModal.tsx | 4 ++-- .../modals/manager/ImportYNAB4Modal.tsx | 4 ++-- .../modals/manager/ImportYNAB5Modal.tsx | 4 ++-- .../components/payees/ManagePayeesWithData.tsx | 4 ++-- .../src/components/reports/Overview.tsx | 4 ++-- .../src/components/reports/reports/Calendar.tsx | 4 ++-- .../src/components/reports/reports/CashFlow.tsx | 4 ++-- .../reports/reports/CustomReportListCards.tsx | 4 ++-- .../src/components/reports/reports/NetWorth.tsx | 4 ++-- .../src/components/reports/reports/Spending.tsx | 4 ++-- .../src/components/reports/reports/Summary.tsx | 4 ++-- .../schedules/PostsOfflineNotification.tsx | 4 ++-- .../src/components/schedules/ScheduleDetails.tsx | 4 ++-- .../src/components/schedules/ScheduleLink.tsx | 4 ++-- .../src/components/schedules/index.tsx | 4 ++-- .../src/components/settings/AuthSettings.tsx | 4 ++-- .../src/components/sidebar/Account.tsx | 4 ++-- .../src/components/sidebar/Accounts.tsx | 8 +++----- .../src/components/sidebar/BudgetName.tsx | 4 ++-- .../src/components/sidebar/Sidebar.tsx | 4 ++-- .../transactions/SelectedTransactionsButton.tsx | 4 ++-- .../components/transactions/TransactionList.jsx | 4 ++-- .../components/transactions/TransactionMenu.tsx | 4 ++-- .../transactions/TransactionsTable.jsx | 6 +++--- .../src/components/util/GenericInput.jsx | 4 ++-- packages/desktop-client/src/hooks/useAccounts.ts | 8 ++++---- packages/desktop-client/src/hooks/useActions.ts | 4 ++-- .../desktop-client/src/hooks/useCategories.ts | 10 ++++------ .../src/hooks/useFailedAccounts.ts | 4 ++-- .../desktop-client/src/hooks/useGlobalPref.ts | 6 +++--- .../desktop-client/src/hooks/useMetadataPref.ts | 6 +++--- .../desktop-client/src/hooks/useModalState.ts | 8 ++++---- packages/desktop-client/src/hooks/usePayees.ts | 14 +++++++------- .../src/hooks/useSyncServerStatus.ts | 4 ++-- .../desktop-client/src/hooks/useSyncedPref.ts | 6 +++--- .../desktop-client/src/hooks/useSyncedPrefs.ts | 6 +++--- .../src/hooks/useTransactionBatchActions.ts | 4 ++-- .../src/hooks/useUpdatedAccounts.ts | 4 ++-- packages/desktop-client/src/redux/index.ts | 9 ++++++--- 87 files changed, 225 insertions(+), 242 deletions(-) diff --git a/packages/desktop-client/src/auth/AuthProvider.tsx b/packages/desktop-client/src/auth/AuthProvider.tsx index 95ddcccbedf..987184f6b20 100644 --- a/packages/desktop-client/src/auth/AuthProvider.tsx +++ b/packages/desktop-client/src/auth/AuthProvider.tsx @@ -1,7 +1,7 @@ import React, { createContext, useContext, type ReactNode } from 'react'; import { useServerURL } from '../components/ServerContext'; -import { useAppSelector } from '../redux'; +import { useSelector } from '../redux'; import { type Permissions } from './types'; @@ -16,7 +16,7 @@ type AuthProviderProps = { }; export const AuthProvider = ({ children }: AuthProviderProps) => { - const userData = useAppSelector(state => state.user.data); + const userData = useSelector(state => state.user.data); const serverUrl = useServerURL(); const hasPermission = (permission?: Permissions) => { diff --git a/packages/desktop-client/src/auth/ProtectedRoute.tsx b/packages/desktop-client/src/auth/ProtectedRoute.tsx index 8704fadacb3..0dad71a1c38 100644 --- a/packages/desktop-client/src/auth/ProtectedRoute.tsx +++ b/packages/desktop-client/src/auth/ProtectedRoute.tsx @@ -4,7 +4,7 @@ import { type RemoteFile, type SyncedLocalFile } from 'loot-core/types/file'; import { View } from '../components/common/View'; import { useMetadataPref } from '../hooks/useMetadataPref'; -import { useAppSelector } from '../redux'; +import { useSelector } from '../redux'; import { useAuth } from './AuthProvider'; import { type Permissions } from './types'; @@ -23,13 +23,13 @@ export const ProtectedRoute = ({ const { hasPermission } = useAuth(); const [permissionGranted, setPermissionGranted] = useState(false); const [cloudFileId] = useMetadataPref('cloudFileId'); - const allFiles = useAppSelector(state => state.budgets.allFiles || []); + const allFiles = useSelector(state => state.budgets.allFiles || []); const remoteFiles = allFiles.filter( (f): f is SyncedLocalFile | RemoteFile => f.state === 'remote' || f.state === 'synced' || f.state === 'detached', ); const currentFile = remoteFiles.find(f => f.cloudFileId === cloudFileId); - const userData = useAppSelector(state => state.user.data); + const userData = useSelector(state => state.user.data); useEffect(() => { const hasRequiredPermission = hasPermission(permission); diff --git a/packages/desktop-client/src/components/App.tsx b/packages/desktop-client/src/components/App.tsx index e948894ca3b..f914d04f7c1 100644 --- a/packages/desktop-client/src/components/App.tsx +++ b/packages/desktop-client/src/components/App.tsx @@ -28,7 +28,7 @@ import { import { useActions } from '../hooks/useActions'; import { useMetadataPref } from '../hooks/useMetadataPref'; import { installPolyfills } from '../polyfills'; -import { useAppDispatch, useAppSelector } from '../redux'; +import { useDispatch, useSelector } from '../redux'; import { styles, hasHiddenScrollbars, ThemeStyle, useTheme } from '../style'; import { ExposeNavigate } from '../util/router-tools'; @@ -49,8 +49,8 @@ function AppInner() { const [cloudFileId] = useMetadataPref('cloudFileId'); const { t } = useTranslation(); const { showBoundary: showErrorBoundary } = useErrorBoundary(); - const dispatch = useAppDispatch(); - const userData = useAppSelector(state => state.user.data); + const dispatch = useDispatch(); + const userData = useSelector(state => state.user.data); const { signOut, addNotification } = useActions(); const maybeUpdate = async (cb?: () => T): Promise => { @@ -158,7 +158,7 @@ export function App() { const [hiddenScrollbars, setHiddenScrollbars] = useState( hasHiddenScrollbars(), ); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); useEffect(() => { function checkScrollbars() { diff --git a/packages/desktop-client/src/components/AppBackground.tsx b/packages/desktop-client/src/components/AppBackground.tsx index e3e5ef3c3b4..423412e31e4 100644 --- a/packages/desktop-client/src/components/AppBackground.tsx +++ b/packages/desktop-client/src/components/AppBackground.tsx @@ -4,7 +4,7 @@ import { useTransition, animated } from 'react-spring'; import { css } from '@emotion/css'; import { AnimatedLoading } from '../icons/AnimatedLoading'; -import { useAppSelector } from '../redux'; +import { useSelector } from '../redux'; import { theme } from '../style'; import { Background } from './Background'; @@ -16,7 +16,7 @@ type AppBackgroundProps = { }; export function AppBackground({ isLoading }: AppBackgroundProps) { - const loadingText = useAppSelector(state => state.app.loadingText); + const loadingText = useSelector(state => state.app.loadingText); const showLoading = isLoading || loadingText !== null; const transitions = useTransition(loadingText, { from: { opacity: 0, transform: 'translateY(-100px)' }, diff --git a/packages/desktop-client/src/components/BankSyncStatus.tsx b/packages/desktop-client/src/components/BankSyncStatus.tsx index 657b99a03ad..843b62d3e3f 100644 --- a/packages/desktop-client/src/components/BankSyncStatus.tsx +++ b/packages/desktop-client/src/components/BankSyncStatus.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { Trans } from 'react-i18next'; import { useTransition, animated } from 'react-spring'; -import { useAppSelector } from '../redux'; +import { useSelector } from '../redux'; import { theme, styles } from '../style'; import { AnimatedRefresh } from './AnimatedRefresh'; @@ -10,9 +10,7 @@ import { Text } from './common/Text'; import { View } from './common/View'; export function BankSyncStatus() { - const accountsSyncing = useAppSelector( - state => state.account.accountsSyncing, - ); + const accountsSyncing = useSelector(state => state.account.accountsSyncing); const accountsSyncingCount = accountsSyncing.length; const count = accountsSyncingCount; diff --git a/packages/desktop-client/src/components/FinancesApp.tsx b/packages/desktop-client/src/components/FinancesApp.tsx index 0edeec8bd2f..a1074fbf3e2 100644 --- a/packages/desktop-client/src/components/FinancesApp.tsx +++ b/packages/desktop-client/src/components/FinancesApp.tsx @@ -18,7 +18,7 @@ import { useAccounts } from '../hooks/useAccounts'; import { useLocalPref } from '../hooks/useLocalPref'; import { useMetaThemeColor } from '../hooks/useMetaThemeColor'; import { useNavigate } from '../hooks/useNavigate'; -import { useAppSelector, useAppDispatch } from '../redux'; +import { useSelector, useDispatch } from '../redux'; import { theme } from '../style'; import { getIsOutdated, getLatestVersion } from '../util/versions'; @@ -85,11 +85,11 @@ export function FinancesApp() { const { isNarrowWidth } = useResponsive(); useMetaThemeColor(isNarrowWidth ? theme.mobileViewTheme : null); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const { t } = useTranslation(); const accounts = useAccounts(); - const accountsLoaded = useAppSelector(state => state.queries.accountsLoaded); + const accountsLoaded = useSelector(state => state.queries.accountsLoaded); const [lastUsedVersion, setLastUsedVersion] = useLocalPref( 'flags.updateNotificationShownForVersion', diff --git a/packages/desktop-client/src/components/HelpMenu.tsx b/packages/desktop-client/src/components/HelpMenu.tsx index ac33fb9cf93..11c292add30 100644 --- a/packages/desktop-client/src/components/HelpMenu.tsx +++ b/packages/desktop-client/src/components/HelpMenu.tsx @@ -10,7 +10,7 @@ import { pushModal } from 'loot-core/client/actions/modals'; import { useFeatureFlag } from '../hooks/useFeatureFlag'; import { SvgHelp } from '../icons/v2/Help'; -import { useAppDispatch } from '../redux'; +import { useDispatch } from '../redux'; import { Button } from './common/Button2'; import { Menu } from './common/Menu'; @@ -52,7 +52,7 @@ export const HelpMenu = () => { const [isMenuOpen, toggleMenuOpen, setMenuOpen] = useToggle(); const menuButtonRef = useRef(null); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const page = useLocation().pathname; const handleItemSelect = (item: HelpMenuItem) => { diff --git a/packages/desktop-client/src/components/LoggedInUser.tsx b/packages/desktop-client/src/components/LoggedInUser.tsx index 6bc78bcbd22..0e715c5a107 100644 --- a/packages/desktop-client/src/components/LoggedInUser.tsx +++ b/packages/desktop-client/src/components/LoggedInUser.tsx @@ -10,7 +10,7 @@ import { useAuth } from '../auth/AuthProvider'; import { Permissions } from '../auth/types'; import { useMetadataPref } from '../hooks/useMetadataPref'; import { useNavigate } from '../hooks/useNavigate'; -import { useAppSelector, useAppDispatch } from '../redux'; +import { useSelector, useDispatch } from '../redux'; import { theme, styles } from '../style'; import { Button } from './common/Button2'; @@ -33,9 +33,9 @@ export function LoggedInUser({ color, }: LoggedInUserProps) { const { t } = useTranslation(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const navigate = useNavigate(); - const userData = useAppSelector(state => state.user.data); + const userData = useSelector(state => state.user.data); const [loading, setLoading] = useState(true); const [menuOpen, setMenuOpen] = useState(false); const serverUrl = useServerURL(); @@ -45,12 +45,12 @@ export function LoggedInUser({ const location = useLocation(); const { hasPermission } = useAuth(); const multiuserEnabled = useMultiuserEnabled(); - const allFiles = useAppSelector(state => state.budgets.allFiles || []); + const allFiles = useSelector(state => state.budgets.allFiles || []); const remoteFiles = allFiles.filter( f => f.state === 'remote' || f.state === 'synced' || f.state === 'detached', ) as (SyncedLocalFile | RemoteFile)[]; const currentFile = remoteFiles.find(f => f.cloudFileId === cloudFileId); - const hasSyncedPrefs = useAppSelector(state => state.prefs.synced); + const hasSyncedPrefs = useSelector(state => state.prefs.synced); useEffect(() => { async function init() { diff --git a/packages/desktop-client/src/components/ManageRules.tsx b/packages/desktop-client/src/components/ManageRules.tsx index d6938e65b6f..7f3b27e9b84 100644 --- a/packages/desktop-client/src/components/ManageRules.tsx +++ b/packages/desktop-client/src/components/ManageRules.tsx @@ -24,7 +24,7 @@ import { useAccounts } from '../hooks/useAccounts'; import { useCategories } from '../hooks/useCategories'; import { usePayees } from '../hooks/usePayees'; import { useSelected, SelectedProvider } from '../hooks/useSelected'; -import { useAppDispatch } from '../redux'; +import { useDispatch } from '../redux'; import { theme } from '../style'; import { Button } from './common/Button2'; @@ -113,7 +113,7 @@ export function ManageRules({ const [allRules, setAllRules] = useState([]); const [page, setPage] = useState(0); const [filter, setFilter] = useState(''); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const { schedules = [] } = useSchedules({ query: useMemo(() => q('schedules').select('*'), []), diff --git a/packages/desktop-client/src/components/Modals.tsx b/packages/desktop-client/src/components/Modals.tsx index dfad7259fd5..4b4919cfbf5 100644 --- a/packages/desktop-client/src/components/Modals.tsx +++ b/packages/desktop-client/src/components/Modals.tsx @@ -9,7 +9,7 @@ import * as monthUtils from 'loot-core/src/shared/months'; import { useMetadataPref } from '../hooks/useMetadataPref'; import { useModalState } from '../hooks/useModalState'; -import { useAppDispatch } from '../redux'; +import { useDispatch } from '../redux'; import { ModalTitle, ModalHeader } from './common/Modal'; import { AccountAutocompleteModal } from './modals/AccountAutocompleteModal'; @@ -78,7 +78,7 @@ import { NamespaceContext } from './spreadsheet/NamespaceContext'; export function Modals() { const location = useLocation(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const { modalStack } = useModalState(); const [budgetId] = useMetadataPref('id'); diff --git a/packages/desktop-client/src/components/Notifications.tsx b/packages/desktop-client/src/components/Notifications.tsx index bcfe12cb64e..dde728d7d9d 100644 --- a/packages/desktop-client/src/components/Notifications.tsx +++ b/packages/desktop-client/src/components/Notifications.tsx @@ -15,7 +15,7 @@ import type { NotificationWithId } from 'loot-core/src/client/state-types/notifi import { AnimatedLoading } from '../icons/AnimatedLoading'; import { SvgDelete } from '../icons/v0'; -import { useAppSelector, useAppDispatch } from '../redux'; +import { useSelector, useDispatch } from '../redux'; import { styles, theme } from '../style'; import { Button, ButtonWithLoading } from './common/Button2'; @@ -262,12 +262,10 @@ function Notification({ } export function Notifications({ style }: { style?: CSSProperties }) { - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const { isNarrowWidth } = useResponsive(); - const notifications = useAppSelector( - state => state.notifications.notifications, - ); - const notificationInset = useAppSelector(state => state.notifications.inset); + const notifications = useSelector(state => state.notifications.notifications); + const notificationInset = useSelector(state => state.notifications.inset); return ( state.app.updateInfo); - const showUpdateNotification = useAppSelector( + const updateInfo = useSelector(state => state.app.updateInfo); + const showUpdateNotification = useSelector( state => state.app.showUpdateNotification, ); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const onRestart = () => { dispatch(updateApp()); }; diff --git a/packages/desktop-client/src/components/accounts/Account.tsx b/packages/desktop-client/src/components/accounts/Account.tsx index 12be08d43d6..75c2fe2819c 100644 --- a/packages/desktop-client/src/components/accounts/Account.tsx +++ b/packages/desktop-client/src/components/accounts/Account.tsx @@ -68,7 +68,7 @@ import { } from '../../hooks/useSplitsExpanded'; import { useSyncedPref } from '../../hooks/useSyncedPref'; import { useTransactionBatchActions } from '../../hooks/useTransactionBatchActions'; -import { useAppSelector } from '../../redux'; +import { useSelector } from '../../redux'; import { styles, theme } from '../../style'; import { Button } from '../common/Button2'; import { Text } from '../common/Text'; @@ -1860,10 +1860,8 @@ export function Account() { const location = useLocation(); const { grouped: categoryGroups } = useCategories(); - const newTransactions = useAppSelector( - state => state.queries.newTransactions, - ); - const matchedTransactions = useAppSelector( + const newTransactions = useSelector(state => state.queries.newTransactions); + const matchedTransactions = useSelector( state => state.queries.matchedTransactions, ); const accounts = useAccounts(); @@ -1884,12 +1882,8 @@ export function Account() { const [showExtraBalances, setShowExtraBalances] = useSyncedPref( `show-extra-balances-${params.id || 'all-accounts'}`, ); - const modalShowing = useAppSelector( - state => state.modals.modalStack.length > 0, - ); - const accountsSyncing = useAppSelector( - state => state.account.accountsSyncing, - ); + const modalShowing = useSelector(state => state.modals.modalStack.length > 0); + const accountsSyncing = useSelector(state => state.account.accountsSyncing); const filterConditions = location?.state?.filterConditions || []; const savedFiters = useFilters(); diff --git a/packages/desktop-client/src/components/accounts/AccountSyncCheck.tsx b/packages/desktop-client/src/components/accounts/AccountSyncCheck.tsx index 0d8093dcff2..f7ec29db71c 100644 --- a/packages/desktop-client/src/components/accounts/AccountSyncCheck.tsx +++ b/packages/desktop-client/src/components/accounts/AccountSyncCheck.tsx @@ -9,7 +9,7 @@ import { authorizeBank } from '../../gocardless'; import { useAccounts } from '../../hooks/useAccounts'; import { useFailedAccounts } from '../../hooks/useFailedAccounts'; import { SvgExclamationOutline } from '../../icons/v1'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { theme } from '../../style'; import { Button } from '../common/Button2'; import { Link } from '../common/Link'; @@ -84,7 +84,7 @@ function useErrorMessage() { export function AccountSyncCheck() { const accounts = useAccounts(); const failedAccounts = useFailedAccounts(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const { id } = useParams(); const [open, setOpen] = useState(false); const triggerRef = useRef(null); diff --git a/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx b/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx index c0a6bf87caa..997219bb474 100644 --- a/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx +++ b/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx @@ -26,7 +26,7 @@ import { import { useAccounts } from '../../hooks/useAccounts'; import { useCommonPayees, usePayees } from '../../hooks/usePayees'; import { SvgAdd, SvgBookmark } from '../../icons/v1'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { theme, styles } from '../../style'; import { Button } from '../common/Button'; import { TextOneLine } from '../common/TextOneLine'; @@ -320,7 +320,7 @@ export function PayeeAutocomplete({ return [{ id: 'new', favorite: 0, name: '' }, ...filteredSuggestions]; }, [commonPayees, payees, focusTransferPayees, accounts, hasPayeeInput]); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); async function handleSelect(idOrIds, rawInputValue) { if (!clearOnBlur) { diff --git a/packages/desktop-client/src/components/budget/index.tsx b/packages/desktop-client/src/components/budget/index.tsx index f57ed655702..f7b61070445 100644 --- a/packages/desktop-client/src/components/budget/index.tsx +++ b/packages/desktop-client/src/components/budget/index.tsx @@ -25,7 +25,7 @@ import { useGlobalPref } from '../../hooks/useGlobalPref'; import { useLocalPref } from '../../hooks/useLocalPref'; import { useNavigate } from '../../hooks/useNavigate'; import { useSyncedPref } from '../../hooks/useSyncedPref'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { styles } from '../../style'; import { View } from '../common/View'; import { NamespaceContext } from '../spreadsheet/NamespaceContext'; @@ -67,7 +67,7 @@ function BudgetInner(props: BudgetInnerProps) { const { t } = useTranslation(); const currentMonth = monthUtils.currentMonth(); const spreadsheet = useSpreadsheet(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const navigate = useNavigate(); const [summaryCollapsed, setSummaryCollapsedPref] = useLocalPref( 'budget.summaryCollapsed', diff --git a/packages/desktop-client/src/components/manager/BudgetList.tsx b/packages/desktop-client/src/components/manager/BudgetList.tsx index 502b6f7f10a..860a4db0c7b 100644 --- a/packages/desktop-client/src/components/manager/BudgetList.tsx +++ b/packages/desktop-client/src/components/manager/BudgetList.tsx @@ -42,7 +42,7 @@ import { SvgUserGroup, } from '../../icons/v1'; import { SvgCloudUnknown, SvgKey, SvgRefreshArrow } from '../../icons/v2'; -import { useAppSelector, useAppDispatch } from '../../redux'; +import { useSelector, useDispatch } from '../../redux'; import { styles, theme } from '../../style'; import { tokens } from '../../tokens'; import { Button } from '../common/Button2'; @@ -498,12 +498,12 @@ function BudgetListHeader({ } export function BudgetList({ showHeader = true, quickSwitchMode = false }) { - const dispatch = useAppDispatch(); - const allFiles = useAppSelector(state => state.budgets.allFiles || []); + const dispatch = useDispatch(); + const allFiles = useSelector(state => state.budgets.allFiles || []); const multiuserEnabled = useMultiuserEnabled(); const [id] = useMetadataPref('id'); const [currentUserId, setCurrentUserId] = useState(''); - const userData = useAppSelector(state => state.user.data); + const userData = useSelector(state => state.user.data); const fetchUsers = useCallback(async () => { try { @@ -670,7 +670,7 @@ type UserAccessForFileProps = { }; function UserAccessForFile({ fileId, currentUserId }: UserAccessForFileProps) { - const allFiles = useAppSelector(state => state.budgets.allFiles || []); + const allFiles = useSelector(state => state.budgets.allFiles || []); const remoteFiles = allFiles.filter( f => f.state === 'remote' || f.state === 'synced' || f.state === 'detached', ) as (SyncedLocalFile | RemoteFile)[]; diff --git a/packages/desktop-client/src/components/manager/ConfigServer.tsx b/packages/desktop-client/src/components/manager/ConfigServer.tsx index 60c9d835aa6..a8020f083dc 100644 --- a/packages/desktop-client/src/components/manager/ConfigServer.tsx +++ b/packages/desktop-client/src/components/manager/ConfigServer.tsx @@ -10,7 +10,7 @@ import { import { useGlobalPref } from '../../hooks/useGlobalPref'; import { useNavigate } from '../../hooks/useNavigate'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { theme } from '../../style'; import { Button, ButtonWithLoading } from '../common/Button2'; import { BigInput } from '../common/Input'; @@ -23,7 +23,7 @@ import { Title } from './subscribe/common'; export function ConfigServer() { const { t } = useTranslation(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const navigate = useNavigate(); const [url, setUrl] = useState(''); const currentUrl = useServerURL(); diff --git a/packages/desktop-client/src/components/manager/ManagementApp.tsx b/packages/desktop-client/src/components/manager/ManagementApp.tsx index 1a723eae82a..872bf98363a 100644 --- a/packages/desktop-client/src/components/manager/ManagementApp.tsx +++ b/packages/desktop-client/src/components/manager/ManagementApp.tsx @@ -6,7 +6,7 @@ import { loggedIn, setAppState } from 'loot-core/client/actions'; import { ProtectedRoute } from '../../auth/ProtectedRoute'; import { Permissions } from '../../auth/types'; import { useMetaThemeColor } from '../../hooks/useMetaThemeColor'; -import { useAppSelector, useAppDispatch } from '../../redux'; +import { useSelector, useDispatch } from '../../redux'; import { theme } from '../../style'; import { tokens } from '../../tokens'; import { @@ -62,16 +62,16 @@ export function ManagementApp() { isNarrowWidth ? theme.mobileConfigServerViewTheme : undefined, ); - const files = useAppSelector(state => state.budgets.allFiles); - const isLoading = useAppSelector(state => state.app.loadingText !== null); - const userData = useAppSelector(state => state.user.data); + const files = useSelector(state => state.budgets.allFiles); + const isLoading = useSelector(state => state.app.loadingText !== null); + const userData = useSelector(state => state.user.data); const multiuserEnabled = useMultiuserEnabled(); - const managerHasInitialized = useAppSelector( + const managerHasInitialized = useSelector( state => state.app.managerHasInitialized, ); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); // runs on mount only useEffect(() => { diff --git a/packages/desktop-client/src/components/manager/WelcomeScreen.tsx b/packages/desktop-client/src/components/manager/WelcomeScreen.tsx index f2d64d6d845..7586402a78b 100644 --- a/packages/desktop-client/src/components/manager/WelcomeScreen.tsx +++ b/packages/desktop-client/src/components/manager/WelcomeScreen.tsx @@ -3,7 +3,7 @@ import { Trans, useTranslation } from 'react-i18next'; import { createBudget, pushModal } from 'loot-core/client/actions'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { styles, theme } from '../../style'; import { Button } from '../common/Button2'; import { Link } from '../common/Link'; @@ -13,7 +13,7 @@ import { View } from '../common/View'; export function WelcomeScreen() { const { t } = useTranslation(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); return ( { const token = new URLSearchParams(window.location.search).get('token'); send('subscribe-set-token', { token: token as string }).then(() => { diff --git a/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx b/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx index 8144a6f22c6..50d2456a467 100644 --- a/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx +++ b/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx @@ -37,7 +37,7 @@ import { useAccountPreviewTransactions } from '../../../hooks/useAccountPreviewT import { useDateFormat } from '../../../hooks/useDateFormat'; import { useFailedAccounts } from '../../../hooks/useFailedAccounts'; import { useNavigate } from '../../../hooks/useNavigate'; -import { useAppSelector, useAppDispatch } from '../../../redux'; +import { useSelector, useDispatch } from '../../../redux'; import { styles, theme } from '../../../style'; import { Button } from '../../common/Button2'; import { Text } from '../../common/Text'; @@ -97,9 +97,7 @@ export function AccountTransactions({ function AccountHeader({ account }: { readonly account: AccountEntity }) { const failedAccounts = useFailedAccounts(); - const syncingAccountIds = useAppSelector( - state => state.account.accountsSyncing, - ); + const syncingAccountIds = useSelector(state => state.account.accountsSyncing); const pending = useMemo( () => syncingAccountIds.includes(account.id), [syncingAccountIds, account.id], @@ -109,7 +107,7 @@ function AccountHeader({ account }: { readonly account: AccountEntity }) { [failedAccounts, account.id], ); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const onSave = useCallback( (account: AccountEntity) => { @@ -253,7 +251,7 @@ function TransactionListWithPreviews({ }); const dateFormat = useDateFormat() || 'MM/dd/yyyy'; - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const navigate = useNavigate(); const onRefresh = useCallback(() => { diff --git a/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx b/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx index d2ff3ade29a..c3976b39103 100644 --- a/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx +++ b/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx @@ -30,7 +30,7 @@ import { SvgCheveronRight, } from '../../../icons/v1'; import { SvgViewShow } from '../../../icons/v2'; -import { useAppDispatch } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { theme, styles } from '../../../style'; import { BalanceWithCarryover } from '../../budget/BalanceWithCarryover'; import { makeAmountGrey, makeBalanceAmountStyle } from '../../budget/util'; @@ -230,7 +230,7 @@ function BudgetCell({ }) { const { t } = useTranslation(); const columnWidth = getColumnWidth(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const format = useFormat(); const { showUndoNotification } = useUndo(); const [budgetType = 'rollover'] = useSyncedPref('budgetType'); @@ -408,7 +408,7 @@ const ExpenseCategory = memo(function ExpenseCategory({ const [budgetType = 'rollover'] = useSyncedPref('budgetType'); const modalBudgetType = budgetType === 'rollover' ? 'envelope' : 'tracking'; - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const { showUndoNotification } = useUndo(); const { list: categories } = useCategories(); const categoriesById = groupById(categories); diff --git a/packages/desktop-client/src/components/mobile/budget/CategoryTransactions.tsx b/packages/desktop-client/src/components/mobile/budget/CategoryTransactions.tsx index 9626c6e9cfa..290f8c25fac 100644 --- a/packages/desktop-client/src/components/mobile/budget/CategoryTransactions.tsx +++ b/packages/desktop-client/src/components/mobile/budget/CategoryTransactions.tsx @@ -17,7 +17,7 @@ import { import { useDateFormat } from '../../../hooks/useDateFormat'; import { useNavigate } from '../../../hooks/useNavigate'; -import { useAppDispatch } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { TextOneLine } from '../../common/TextOneLine'; import { View } from '../../common/View'; import { MobilePageHeader, Page } from '../../Page'; @@ -34,7 +34,7 @@ export function CategoryTransactions({ category, month, }: CategoryTransactionsProps) { - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const navigate = useNavigate(); const baseTransactionsQuery = useCallback( diff --git a/packages/desktop-client/src/components/mobile/budget/index.tsx b/packages/desktop-client/src/components/mobile/budget/index.tsx index bcec3e25233..9ff327e8853 100644 --- a/packages/desktop-client/src/components/mobile/budget/index.tsx +++ b/packages/desktop-client/src/components/mobile/budget/index.tsx @@ -24,7 +24,7 @@ import { useCategories } from '../../../hooks/useCategories'; import { useLocalPref } from '../../../hooks/useLocalPref'; import { useSyncedPref } from '../../../hooks/useSyncedPref'; import { AnimatedLoading } from '../../../icons/AnimatedLoading'; -import { useAppDispatch } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { theme } from '../../../style'; import { prewarmMonth } from '../../budget/util'; import { View } from '../../common/View'; @@ -55,7 +55,7 @@ export function Budget() { const [_numberFormat] = useSyncedPref('numberFormat'); const numberFormat = _numberFormat || 'comma-dot'; const [hideFraction] = useSyncedPref('hideFraction'); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); useEffect(() => { async function init() { diff --git a/packages/desktop-client/src/components/mobile/transactions/TransactionEdit.jsx b/packages/desktop-client/src/components/mobile/transactions/TransactionEdit.jsx index c4852d5d852..ef3066469eb 100644 --- a/packages/desktop-client/src/components/mobile/transactions/TransactionEdit.jsx +++ b/packages/desktop-client/src/components/mobile/transactions/TransactionEdit.jsx @@ -54,7 +54,7 @@ import { import { SvgSplit } from '../../../icons/v0'; import { SvgAdd, SvgPiggyBank, SvgTrash } from '../../../icons/v1'; import { SvgPencilWriteAlternate } from '../../../icons/v2'; -import { useAppSelector, useAppDispatch } from '../../../redux'; +import { useSelector, useDispatch } from '../../../redux'; import { styles, theme } from '../../../style'; import { Button } from '../../common/Button'; import { Text } from '../../common/Text'; @@ -451,7 +451,7 @@ const TransactionEditInner = memo(function TransactionEditInner({ }) { const { t } = useTranslation(); const navigate = useNavigate(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const transactions = useMemo( () => unserializedTransactions.map(t => serializeTransaction(t, dateFormat)) || @@ -1022,7 +1022,7 @@ function TransactionEditUnconnected({ const { transactionId } = useParams(); const { state: locationState } = useLocation(); const navigate = useNavigate(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const [transactions, setTransactions] = useState([]); const [fetchedTransactions, setFetchedTransactions] = useState([]); const isAdding = useRef(false); @@ -1246,9 +1246,7 @@ function TransactionEditUnconnected({ export const TransactionEdit = props => { const { list: categories } = useCategories(); const payees = usePayees(); - const lastTransaction = useAppSelector( - state => state.queries.lastTransaction, - ); + const lastTransaction = useSelector(state => state.queries.lastTransaction); const accounts = useAccounts(); const dateFormat = useDateFormat() || 'MM/dd/yyyy'; diff --git a/packages/desktop-client/src/components/mobile/transactions/TransactionList.jsx b/packages/desktop-client/src/components/mobile/transactions/TransactionList.jsx index d3e57533ea3..cf555dba3bb 100644 --- a/packages/desktop-client/src/components/mobile/transactions/TransactionList.jsx +++ b/packages/desktop-client/src/components/mobile/transactions/TransactionList.jsx @@ -26,7 +26,7 @@ import { useUndo } from '../../../hooks/useUndo'; import { AnimatedLoading } from '../../../icons/AnimatedLoading'; import { SvgDelete } from '../../../icons/v0'; import { SvgDotsHorizontalTriple } from '../../../icons/v1'; -import { useAppDispatch } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { styles, theme } from '../../../style'; import { Button } from '../../common/Button2'; import { Menu } from '../../common/Menu'; @@ -246,7 +246,7 @@ function SelectedTransactionsFloatingActionBar({ transactions, style }) { const { list: categories } = useCategories(); const categoriesById = useMemo(() => groupById(categories), [categories]); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); useEffect(() => { dispatch(setNotificationInset({ bottom: NOTIFICATION_BOTTOM_INSET })); return () => { diff --git a/packages/desktop-client/src/components/mobile/transactions/TransactionListItem.tsx b/packages/desktop-client/src/components/mobile/transactions/TransactionListItem.tsx index 90cc5ad9fb7..dee30eddf0d 100644 --- a/packages/desktop-client/src/components/mobile/transactions/TransactionListItem.tsx +++ b/packages/desktop-client/src/components/mobile/transactions/TransactionListItem.tsx @@ -24,7 +24,7 @@ import { SvgCheckCircle1, SvgLockClosed, } from '../../../icons/v2'; -import { useAppSelector } from '../../../redux'; +import { useSelector } from '../../../redux'; import { styles, theme } from '../../../style'; import { makeAmountFullStyle } from '../../budget/util'; import { Button } from '../../common/Button2'; @@ -59,9 +59,7 @@ export function TransactionListItem({ const transferAccount = useAccount(payee?.transfer_acct || ''); const isPreview = isPreviewId(transaction?.id || ''); - const newTransactions = useAppSelector( - state => state.queries.newTransactions, - ); + const newTransactions = useSelector(state => state.queries.newTransactions); const { longPressProps } = useLongPress({ accessibilityDescription: 'Long press to select multiple transactions', diff --git a/packages/desktop-client/src/components/modals/BudgetListModal.tsx b/packages/desktop-client/src/components/modals/BudgetListModal.tsx index 2fb8f674ee1..1d1d5fc77f5 100644 --- a/packages/desktop-client/src/components/modals/BudgetListModal.tsx +++ b/packages/desktop-client/src/components/modals/BudgetListModal.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { useMetadataPref } from '../../hooks/useMetadataPref'; -import { useAppSelector } from '../../redux'; +import { useSelector } from '../../redux'; import { Modal, ModalHeader, ModalCloseButton } from '../common/Modal'; import { Text } from '../common/Text'; import { View } from '../common/View'; @@ -11,7 +11,7 @@ import { BudgetList } from '../manager/BudgetList'; export function BudgetListModal() { const { t } = useTranslation(); const [id] = useMetadataPref('id'); - const currentFile = useAppSelector(state => + const currentFile = useSelector(state => state.budgets.allFiles?.find(f => 'id' in f && f.id === id), ); diff --git a/packages/desktop-client/src/components/modals/CoverModal.tsx b/packages/desktop-client/src/components/modals/CoverModal.tsx index ec8aa7cb0d1..8123d80f8f4 100644 --- a/packages/desktop-client/src/components/modals/CoverModal.tsx +++ b/packages/desktop-client/src/components/modals/CoverModal.tsx @@ -5,7 +5,7 @@ import { pushModal } from 'loot-core/client/actions'; import { type CategoryEntity } from 'loot-core/src/types/models'; import { useCategories } from '../../hooks/useCategories'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { styles } from '../../style'; import { addToBeBudgetedGroup, @@ -49,7 +49,7 @@ export function CoverModal({ }, [categoryId, originalCategoryGroups, showToBeBudgeted]); const [fromCategoryId, setFromCategoryId] = useState(null); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const onCategoryClick = useCallback(() => { dispatch( diff --git a/packages/desktop-client/src/components/modals/CreateAccountModal.tsx b/packages/desktop-client/src/components/modals/CreateAccountModal.tsx index 00e7874b30d..64597e2a009 100644 --- a/packages/desktop-client/src/components/modals/CreateAccountModal.tsx +++ b/packages/desktop-client/src/components/modals/CreateAccountModal.tsx @@ -12,7 +12,7 @@ import { useGoCardlessStatus } from '../../hooks/useGoCardlessStatus'; import { useSimpleFinStatus } from '../../hooks/useSimpleFinStatus'; import { useSyncServerStatus } from '../../hooks/useSyncServerStatus'; import { SvgDotsHorizontalTriple } from '../../icons/v1'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { theme } from '../../style'; import { Warning } from '../alerts'; import { Button, ButtonWithLoading } from '../common/Button2'; @@ -34,7 +34,7 @@ export function CreateAccountModal({ upgradingAccountId }: CreateAccountProps) { const { t } = useTranslation(); const syncServerStatus = useSyncServerStatus(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const [isGoCardlessSetupComplete, setIsGoCardlessSetupComplete] = useState< boolean | null >(null); diff --git a/packages/desktop-client/src/components/modals/CreateLocalAccountModal.tsx b/packages/desktop-client/src/components/modals/CreateLocalAccountModal.tsx index 273593ba553..72e2e386192 100644 --- a/packages/desktop-client/src/components/modals/CreateLocalAccountModal.tsx +++ b/packages/desktop-client/src/components/modals/CreateLocalAccountModal.tsx @@ -8,7 +8,7 @@ import { toRelaxedNumber } from 'loot-core/src/shared/util'; import * as useAccounts from '../../hooks/useAccounts'; import { useNavigate } from '../../hooks/useNavigate'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { theme } from '../../style'; import { Button } from '../common/Button2'; import { FormError } from '../common/FormError'; @@ -31,7 +31,7 @@ import { validateAccountName } from '../util/accountValidation'; export function CreateLocalAccountModal() { const { t } = useTranslation(); const navigate = useNavigate(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const accounts = useAccounts.useAccounts(); const [name, setName] = useState(''); const [offbudget, setOffbudget] = useState(false); diff --git a/packages/desktop-client/src/components/modals/EnvelopeBudgetSummaryModal.tsx b/packages/desktop-client/src/components/modals/EnvelopeBudgetSummaryModal.tsx index 022f96fb01a..b22ed2a4573 100644 --- a/packages/desktop-client/src/components/modals/EnvelopeBudgetSummaryModal.tsx +++ b/packages/desktop-client/src/components/modals/EnvelopeBudgetSummaryModal.tsx @@ -8,7 +8,7 @@ import { format, sheetForMonth, prevMonth } from 'loot-core/src/shared/months'; import { useCategories } from '../../hooks/useCategories'; import { useUndo } from '../../hooks/useUndo'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { styles } from '../../style'; import { ToBudgetAmount } from '../budget/envelope/budgetsummary/ToBudgetAmount'; import { TotalsList } from '../budget/envelope/budgetsummary/TotalsList'; @@ -25,7 +25,7 @@ export function EnvelopeBudgetSummaryModal({ month, onBudgetAction, }: EnvelopeBudgetSummaryModalProps) { - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const prevMonthName = format(prevMonth(month), 'MMM'); const sheetValue = useEnvelopeSheetValue({ diff --git a/packages/desktop-client/src/components/modals/GoCardlessExternalMsgModal.tsx b/packages/desktop-client/src/components/modals/GoCardlessExternalMsgModal.tsx index c67204861bc..7d119703815 100644 --- a/packages/desktop-client/src/components/modals/GoCardlessExternalMsgModal.tsx +++ b/packages/desktop-client/src/components/modals/GoCardlessExternalMsgModal.tsx @@ -11,7 +11,7 @@ import { import { useGoCardlessStatus } from '../../hooks/useGoCardlessStatus'; import { AnimatedLoading } from '../../icons/AnimatedLoading'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { theme } from '../../style'; import { Error, Warning } from '../alerts'; import { Autocomplete } from '../autocomplete/Autocomplete'; @@ -87,7 +87,7 @@ export function GoCardlessExternalMsgModal({ }: GoCardlessExternalMsgProps) { const { t } = useTranslation(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const [waiting, setWaiting] = useState(null); const [success, setSuccess] = useState(false); diff --git a/packages/desktop-client/src/components/modals/ImportTransactionsModal/ImportTransactionsModal.jsx b/packages/desktop-client/src/components/modals/ImportTransactionsModal/ImportTransactionsModal.jsx index 1eb5b2c0859..9c1092ef96a 100644 --- a/packages/desktop-client/src/components/modals/ImportTransactionsModal/ImportTransactionsModal.jsx +++ b/packages/desktop-client/src/components/modals/ImportTransactionsModal/ImportTransactionsModal.jsx @@ -13,7 +13,7 @@ import { amountToInteger } from 'loot-core/src/shared/util'; import { useDateFormat } from '../../../hooks/useDateFormat'; import { useSyncedPrefs } from '../../../hooks/useSyncedPrefs'; -import { useAppDispatch } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { theme } from '../../../style'; import { Button, ButtonWithLoading } from '../../common/Button2'; import { Input } from '../../common/Input'; @@ -144,7 +144,7 @@ export function ImportTransactionsModal({ options }) { const { t } = useTranslation(); const dateFormat = useDateFormat() || 'MM/dd/yyyy'; const [prefs, savePrefs] = useSyncedPrefs(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const [multiplierAmount, setMultiplierAmount] = useState(''); const [loadingState, setLoadingState] = useState('parsing'); diff --git a/packages/desktop-client/src/components/modals/LoadBackupModal.tsx b/packages/desktop-client/src/components/modals/LoadBackupModal.tsx index b156a41a446..19bbb62fac2 100644 --- a/packages/desktop-client/src/components/modals/LoadBackupModal.tsx +++ b/packages/desktop-client/src/components/modals/LoadBackupModal.tsx @@ -6,7 +6,7 @@ import { type Backup } from 'loot-core/server/backups'; import { send, listen, unlisten } from 'loot-core/src/platform/client/fetch'; import { useMetadataPref } from '../../hooks/useMetadataPref'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { theme } from '../../style'; import { Block } from '../common/Block'; import { Button } from '../common/Button2'; @@ -52,7 +52,7 @@ export function LoadBackupModal({ watchUpdates, backupDisabled, }: LoadBackupModalProps) { - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const [backups, setBackups] = useState([]); const [prefsBudgetId] = useMetadataPref('id'); const budgetIdToLoad = budgetId ?? prefsBudgetId; diff --git a/packages/desktop-client/src/components/modals/MergeUnusedPayeesModal.tsx b/packages/desktop-client/src/components/modals/MergeUnusedPayeesModal.tsx index cd32d62d711..4a22fbfefdd 100644 --- a/packages/desktop-client/src/components/modals/MergeUnusedPayeesModal.tsx +++ b/packages/desktop-client/src/components/modals/MergeUnusedPayeesModal.tsx @@ -6,7 +6,7 @@ import { send } from 'loot-core/src/platform/client/fetch'; import { type PayeeEntity } from 'loot-core/types/models'; import { usePayees } from '../../hooks/usePayees'; -import { useAppSelector, useAppDispatch } from '../../redux'; +import { useSelector, useDispatch } from '../../redux'; import { theme } from '../../style'; import { Information } from '../alerts'; import { Button } from '../common/Button2'; @@ -28,9 +28,9 @@ export function MergeUnusedPayeesModal({ }: MergeUnusedPayeesModalProps) { const { t } = useTranslation(); const allPayees = usePayees(); - const modalStack = useAppSelector(state => state.modals.modalStack); + const modalStack = useSelector(state => state.modals.modalStack); const isEditingRule = !!modalStack.find(m => m.name === 'edit-rule'); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const [shouldCreateRule, setShouldCreateRule] = useState(true); const flashRef = useRef(null); diff --git a/packages/desktop-client/src/components/modals/OutOfSyncMigrationsModal.tsx b/packages/desktop-client/src/components/modals/OutOfSyncMigrationsModal.tsx index 2461da9cc9b..2a904f26345 100644 --- a/packages/desktop-client/src/components/modals/OutOfSyncMigrationsModal.tsx +++ b/packages/desktop-client/src/components/modals/OutOfSyncMigrationsModal.tsx @@ -3,7 +3,7 @@ import { Trans, useTranslation } from 'react-i18next'; import { closeBudget } from 'loot-core/client/actions'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { Button } from '../common/Button2'; import { Link } from '../common/Link'; import { Modal, ModalHeader, ModalTitle } from '../common/Modal'; @@ -12,7 +12,7 @@ import { Text } from '../common/Text'; import { View } from '../common/View'; export function OutOfSyncMigrationsModal() { - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const { t } = useTranslation(); diff --git a/packages/desktop-client/src/components/modals/TransferModal.tsx b/packages/desktop-client/src/components/modals/TransferModal.tsx index a0dff11bfea..9cf1a7fa64b 100644 --- a/packages/desktop-client/src/components/modals/TransferModal.tsx +++ b/packages/desktop-client/src/components/modals/TransferModal.tsx @@ -5,7 +5,7 @@ import { pushModal } from 'loot-core/client/actions'; import { type CategoryEntity } from 'loot-core/types/models'; import { useCategories } from '../../hooks/useCategories'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { styles } from '../../style'; import { addToBeBudgetedGroup, @@ -55,7 +55,7 @@ export function TransferModal({ const [amount, setAmount] = useState(0); const [toCategoryId, setToCategoryId] = useState(null); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const openCategoryModal = () => { dispatch( diff --git a/packages/desktop-client/src/components/modals/TransferOwnership.tsx b/packages/desktop-client/src/components/modals/TransferOwnership.tsx index 38ee1c9e768..d1619964ffd 100644 --- a/packages/desktop-client/src/components/modals/TransferOwnership.tsx +++ b/packages/desktop-client/src/components/modals/TransferOwnership.tsx @@ -10,7 +10,7 @@ import { type Handlers } from 'loot-core/types/handlers'; import { useActions } from '../../hooks/useActions'; import { useMetadataPref } from '../../hooks/useMetadataPref'; -import { useAppDispatch, useAppSelector } from '../../redux'; +import { useDispatch, useSelector } from '../../redux'; import { styles, theme } from '../../style'; import { Button } from '../common/Button2'; import { Modal, ModalCloseButton, ModalHeader } from '../common/Modal'; @@ -29,18 +29,18 @@ export function TransferOwnership({ }: TransferOwnershipProps) { const { t } = useTranslation(); - const userData = useAppSelector(state => state.user.data); + const userData = useSelector(state => state.user.data); const actions = useActions(); const [userId, setUserId] = useState(''); const [error, setError] = useState(null); const [availableUsers, setAvailableUsers] = useState<[string, string][]>([]); const [cloudFileId] = useMetadataPref('cloudFileId'); - const allFiles = useAppSelector(state => state.budgets.allFiles || []); + const allFiles = useSelector(state => state.budgets.allFiles || []); const remoteFiles = allFiles.filter( f => f.state === 'remote' || f.state === 'synced' || f.state === 'detached', ) as (SyncedLocalFile | RemoteFile)[]; const currentFile = remoteFiles.find(f => f.cloudFileId === cloudFileId); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const [isTransferring, setIsTransferring] = useState(false); useEffect(() => { diff --git a/packages/desktop-client/src/components/modals/manager/ConfirmChangeDocumentDir.tsx b/packages/desktop-client/src/components/modals/manager/ConfirmChangeDocumentDir.tsx index ce42775688b..adf3e5dfe35 100644 --- a/packages/desktop-client/src/components/modals/manager/ConfirmChangeDocumentDir.tsx +++ b/packages/desktop-client/src/components/modals/manager/ConfirmChangeDocumentDir.tsx @@ -4,7 +4,7 @@ import { Trans, useTranslation } from 'react-i18next'; import { addNotification } from 'loot-core/client/actions'; import { useGlobalPref } from '../../../hooks/useGlobalPref'; -import { useAppDispatch } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { theme, styles } from '../../../style'; import { Information } from '../../alerts'; import { Button, ButtonWithLoading } from '../../common/Button2'; @@ -48,7 +48,7 @@ export function ConfirmChangeDocumentDirModal({ const [loading, setLoading] = useState(false); const [moveFiles, setMoveFiles] = useState(false); const { t } = useTranslation(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const restartElectronServer = useCallback(() => { globalThis.window.Actual?.restartElectronServer(); diff --git a/packages/desktop-client/src/components/modals/manager/DeleteFileModal.tsx b/packages/desktop-client/src/components/modals/manager/DeleteFileModal.tsx index 3c482d43744..2524c0e4d1d 100644 --- a/packages/desktop-client/src/components/modals/manager/DeleteFileModal.tsx +++ b/packages/desktop-client/src/components/modals/manager/DeleteFileModal.tsx @@ -4,7 +4,7 @@ import { Trans, useTranslation } from 'react-i18next'; import { deleteBudget } from 'loot-core/client/actions'; import { type File } from 'loot-core/src/types/file'; -import { useAppDispatch } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { theme } from '../../../style'; import { ButtonWithLoading } from '../../common/Button2'; import { Modal, ModalCloseButton, ModalHeader } from '../../common/Modal'; @@ -22,7 +22,7 @@ export function DeleteFileModal({ file }: DeleteFileProps) { // user. The current user should be able to delete the local file, // but not the remote one const isCloudFile = 'cloudFileId' in file && file.state !== 'broken'; - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const [loadingState, setLoadingState] = useState<'cloud' | 'local' | null>( null, diff --git a/packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx b/packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx index 5e0a9ffbf07..224aaa0f08f 100644 --- a/packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx +++ b/packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx @@ -9,7 +9,7 @@ import { } from 'loot-core/client/actions'; import { type File } from 'loot-core/src/types/file'; -import { useAppDispatch } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { theme } from '../../../style'; import { Button, ButtonWithLoading } from '../../common/Button2'; import { FormError } from '../../common/FormError'; @@ -49,7 +49,7 @@ export function DuplicateFileModal({ // If the state is "broken" that means it was created by another user. const isCloudFile = 'cloudFileId' in file && file.state !== 'broken'; const isLocalFile = 'id' in file; - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const [loadingState, setLoadingState] = useState<'cloud' | 'local' | null>( null, diff --git a/packages/desktop-client/src/components/modals/manager/FilesSettingsModal.tsx b/packages/desktop-client/src/components/modals/manager/FilesSettingsModal.tsx index 5b283335dcc..ee3c1107def 100644 --- a/packages/desktop-client/src/components/modals/manager/FilesSettingsModal.tsx +++ b/packages/desktop-client/src/components/modals/manager/FilesSettingsModal.tsx @@ -5,7 +5,7 @@ import { loadAllFiles, pushModal } from 'loot-core/client/actions'; import { useGlobalPref } from '../../../hooks/useGlobalPref'; import { SvgPencil1 } from '../../../icons/v2'; -import { useAppDispatch } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { theme, styles } from '../../../style'; import { Button } from '../../common/Button2'; import { Modal, ModalCloseButton, ModalHeader } from '../../common/Modal'; @@ -16,7 +16,7 @@ function FileLocationSettings() { const [documentDir, _setDocumentDirPref] = useGlobalPref('documentDir'); const [_documentDirChanged, setDirChanged] = useState(false); const { t } = useTranslation(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); async function onChooseDocumentDir() { const chosenDirectory = await window.Actual?.openFileDialog({ @@ -142,7 +142,7 @@ function SelfSignedCertLocationSettings() { } export function FilesSettingsModal() { - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); function closeModal(close: () => void) { dispatch(loadAllFiles()); diff --git a/packages/desktop-client/src/components/modals/manager/ImportActualModal.tsx b/packages/desktop-client/src/components/modals/manager/ImportActualModal.tsx index b086049f819..995154b13e7 100644 --- a/packages/desktop-client/src/components/modals/manager/ImportActualModal.tsx +++ b/packages/desktop-client/src/components/modals/manager/ImportActualModal.tsx @@ -5,7 +5,7 @@ import { Trans, useTranslation } from 'react-i18next'; import { importBudget } from 'loot-core/src/client/actions/budgets'; import { useNavigate } from '../../../hooks/useNavigate'; -import { useAppDispatch } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { styles, theme } from '../../../style'; import { Block } from '../../common/Block'; import { ButtonWithLoading } from '../../common/Button2'; @@ -33,7 +33,7 @@ function getErrorMessage(error: string): string { export function ImportActualModal() { const { t } = useTranslation(); const navigate = useNavigate(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const [error, setError] = useState(null); const [importing, setImporting] = useState(false); diff --git a/packages/desktop-client/src/components/modals/manager/ImportModal.tsx b/packages/desktop-client/src/components/modals/manager/ImportModal.tsx index 62bde0e91a0..ef4d1824f05 100644 --- a/packages/desktop-client/src/components/modals/manager/ImportModal.tsx +++ b/packages/desktop-client/src/components/modals/manager/ImportModal.tsx @@ -3,7 +3,7 @@ import { Trans, useTranslation } from 'react-i18next'; import { pushModal } from 'loot-core/client/actions'; -import { useAppDispatch } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { styles, theme } from '../../../style'; import { Block } from '../../common/Block'; import { Button } from '../../common/Button2'; @@ -23,7 +23,7 @@ function getErrorMessage(error: 'not-ynab4' | boolean) { export function ImportModal() { const { t } = useTranslation(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const [error] = useState(false); function onSelectType(type: 'ynab4' | 'ynab5' | 'actual') { diff --git a/packages/desktop-client/src/components/modals/manager/ImportYNAB4Modal.tsx b/packages/desktop-client/src/components/modals/manager/ImportYNAB4Modal.tsx index b59ed31c079..49a80721bf9 100644 --- a/packages/desktop-client/src/components/modals/manager/ImportYNAB4Modal.tsx +++ b/packages/desktop-client/src/components/modals/manager/ImportYNAB4Modal.tsx @@ -5,7 +5,7 @@ import { Trans, useTranslation } from 'react-i18next'; import { importBudget } from 'loot-core/src/client/actions/budgets'; import { useNavigate } from '../../../hooks/useNavigate'; -import { useAppDispatch } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { styles, theme } from '../../../style'; import { Block } from '../../common/Block'; import { ButtonWithLoading } from '../../common/Button2'; @@ -25,7 +25,7 @@ function getErrorMessage(error: string): string { export function ImportYNAB4Modal() { const { t } = useTranslation(); const navigate = useNavigate(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const [error, setError] = useState(null); const [importing, setImporting] = useState(false); diff --git a/packages/desktop-client/src/components/modals/manager/ImportYNAB5Modal.tsx b/packages/desktop-client/src/components/modals/manager/ImportYNAB5Modal.tsx index cbf2e24bc18..8945278e80a 100644 --- a/packages/desktop-client/src/components/modals/manager/ImportYNAB5Modal.tsx +++ b/packages/desktop-client/src/components/modals/manager/ImportYNAB5Modal.tsx @@ -5,7 +5,7 @@ import { Trans, useTranslation } from 'react-i18next'; import { importBudget } from 'loot-core/src/client/actions/budgets'; import { useNavigate } from '../../../hooks/useNavigate'; -import { useAppDispatch } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { styles, theme } from '../../../style'; import { Block } from '../../common/Block'; import { ButtonWithLoading } from '../../common/Button2'; @@ -28,7 +28,7 @@ function getErrorMessage(error: string): string { export function ImportYNAB5Modal() { const { t } = useTranslation(); const navigate = useNavigate(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const [error, setError] = useState(null); const [importing, setImporting] = useState(false); diff --git a/packages/desktop-client/src/components/payees/ManagePayeesWithData.tsx b/packages/desktop-client/src/components/payees/ManagePayeesWithData.tsx index fc075bb7f8d..7ccf5b12c6b 100644 --- a/packages/desktop-client/src/components/payees/ManagePayeesWithData.tsx +++ b/packages/desktop-client/src/components/payees/ManagePayeesWithData.tsx @@ -12,7 +12,7 @@ import { applyChanges, type Diff } from 'loot-core/src/shared/util'; import { type NewRuleEntity, type PayeeEntity } from 'loot-core/types/models'; import { usePayees } from '../../hooks/usePayees'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { ManagePayees } from './ManagePayees'; @@ -24,7 +24,7 @@ export function ManagePayeesWithData({ initialSelectedIds, }: ManagePayeesWithDataProps) { const payees = usePayees(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const [ruleCounts, setRuleCounts] = useState({ value: new Map() }); const [orphans, setOrphans] = useState([]); diff --git a/packages/desktop-client/src/components/reports/Overview.tsx b/packages/desktop-client/src/components/reports/Overview.tsx index c3f01af06cb..5c13de11b9a 100644 --- a/packages/desktop-client/src/components/reports/Overview.tsx +++ b/packages/desktop-client/src/components/reports/Overview.tsx @@ -21,7 +21,7 @@ import { import { useAccounts } from '../../hooks/useAccounts'; import { useNavigate } from '../../hooks/useNavigate'; import { useSyncedPref } from '../../hooks/useSyncedPref'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { breakpoints } from '../../tokens'; import { Button } from '../common/Button2'; import { Menu } from '../common/Menu'; @@ -51,7 +51,7 @@ function isCustomReportWidget(widget: Widget): widget is CustomReportWidget { export function Overview() { const { t } = useTranslation(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const [_firstDayOfWeekIdx] = useSyncedPref('firstDayOfWeekIdx'); const firstDayOfWeekIdx = _firstDayOfWeekIdx || '0'; diff --git a/packages/desktop-client/src/components/reports/reports/Calendar.tsx b/packages/desktop-client/src/components/reports/reports/Calendar.tsx index 563f15bfcd8..d4a723f6716 100644 --- a/packages/desktop-client/src/components/reports/reports/Calendar.tsx +++ b/packages/desktop-client/src/components/reports/reports/Calendar.tsx @@ -47,7 +47,7 @@ import { SvgCheveronDown, SvgCheveronUp, } from '../../../icons/v1'; -import { useAppDispatch } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { styles, theme } from '../../../style'; import { Button } from '../../common/Button2'; import { View } from '../../common/View'; @@ -278,7 +278,7 @@ function CalendarInner({ widget, parameters }: CalendarInnerProps) { run(); }, []); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const navigate = useNavigate(); const { isNarrowWidth } = useResponsive(); const title = widget?.meta?.name || t('Calendar'); diff --git a/packages/desktop-client/src/components/reports/reports/CashFlow.tsx b/packages/desktop-client/src/components/reports/reports/CashFlow.tsx index 6af86242864..9ec6af671fc 100644 --- a/packages/desktop-client/src/components/reports/reports/CashFlow.tsx +++ b/packages/desktop-client/src/components/reports/reports/CashFlow.tsx @@ -17,7 +17,7 @@ import { import { useFilters } from '../../../hooks/useFilters'; import { useNavigate } from '../../../hooks/useNavigate'; -import { useAppDispatch } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { theme } from '../../../style'; import { AlignedText } from '../../common/AlignedText'; import { Block } from '../../common/Block'; @@ -63,7 +63,7 @@ type CashFlowInnerProps = { }; function CashFlowInner({ widget }: CashFlowInnerProps) { - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const { t } = useTranslation(); const { diff --git a/packages/desktop-client/src/components/reports/reports/CustomReportListCards.tsx b/packages/desktop-client/src/components/reports/reports/CustomReportListCards.tsx index 991a60f21ea..b30a4dd1515 100644 --- a/packages/desktop-client/src/components/reports/reports/CustomReportListCards.tsx +++ b/packages/desktop-client/src/components/reports/reports/CustomReportListCards.tsx @@ -12,7 +12,7 @@ import { useCategories } from '../../../hooks/useCategories'; import { usePayees } from '../../../hooks/usePayees'; import { useSyncedPref } from '../../../hooks/useSyncedPref'; import { SvgExclamationSolid } from '../../../icons/v1'; -import { useAppDispatch } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { styles } from '../../../style/index'; import { theme } from '../../../style/theme'; import { Text } from '../../common/Text'; @@ -64,7 +64,7 @@ function CustomReportListCardsInner({ }: Omit & { report: CustomReportEntity; }) { - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const [nameMenuOpen, setNameMenuOpen] = useState(false); const [earliestTransaction, setEarliestTransaction] = useState(''); diff --git a/packages/desktop-client/src/components/reports/reports/NetWorth.tsx b/packages/desktop-client/src/components/reports/reports/NetWorth.tsx index 6c854b7739c..150436fe1f1 100644 --- a/packages/desktop-client/src/components/reports/reports/NetWorth.tsx +++ b/packages/desktop-client/src/components/reports/reports/NetWorth.tsx @@ -14,7 +14,7 @@ import { type TimeFrame, type NetWorthWidget } from 'loot-core/types/models'; import { useAccounts } from '../../../hooks/useAccounts'; import { useFilters } from '../../../hooks/useFilters'; import { useNavigate } from '../../../hooks/useNavigate'; -import { useAppDispatch } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { theme, styles } from '../../../style'; import { Button } from '../../common/Button2'; import { Paragraph } from '../../common/Paragraph'; @@ -52,7 +52,7 @@ type NetWorthInnerProps = { }; function NetWorthInner({ widget }: NetWorthInnerProps) { - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const { t } = useTranslation(); const accounts = useAccounts(); diff --git a/packages/desktop-client/src/components/reports/reports/Spending.tsx b/packages/desktop-client/src/components/reports/reports/Spending.tsx index 0d985d41c3c..a97fc15a2e5 100644 --- a/packages/desktop-client/src/components/reports/reports/Spending.tsx +++ b/packages/desktop-client/src/components/reports/reports/Spending.tsx @@ -14,7 +14,7 @@ import { type RuleConditionEntity } from 'loot-core/types/models/rule'; import { useFilters } from '../../../hooks/useFilters'; import { useNavigate } from '../../../hooks/useNavigate'; -import { useAppDispatch } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { theme, styles } from '../../../style'; import { AlignedText } from '../../common/AlignedText'; import { Block } from '../../common/Block'; @@ -60,7 +60,7 @@ type SpendingInternalProps = { }; function SpendingInternal({ widget }: SpendingInternalProps) { - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const { t } = useTranslation(); const { diff --git a/packages/desktop-client/src/components/reports/reports/Summary.tsx b/packages/desktop-client/src/components/reports/reports/Summary.tsx index b2cf46a096a..1d7e5200edf 100644 --- a/packages/desktop-client/src/components/reports/reports/Summary.tsx +++ b/packages/desktop-client/src/components/reports/reports/Summary.tsx @@ -21,7 +21,7 @@ import { SvgEquals } from '../../../icons/v1'; import { SvgCloseParenthesis } from '../../../icons/v2/CloseParenthesis'; import { SvgOpenParenthesis } from '../../../icons/v2/OpenParenthesis'; import { SvgSum } from '../../../icons/v2/Sum'; -import { useAppDispatch } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { theme } from '../../../style'; import { Button } from '../../common/Button2'; import { Text } from '../../common/Text'; @@ -176,7 +176,7 @@ function SummaryInner({ widget }: SummaryInnerProps) { run(); }, []); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const navigate = useNavigate(); const { isNarrowWidth } = useResponsive(); const title = widget?.meta?.name || t('Summary'); diff --git a/packages/desktop-client/src/components/schedules/PostsOfflineNotification.tsx b/packages/desktop-client/src/components/schedules/PostsOfflineNotification.tsx index ca53af4c773..47e9e70c4f3 100644 --- a/packages/desktop-client/src/components/schedules/PostsOfflineNotification.tsx +++ b/packages/desktop-client/src/components/schedules/PostsOfflineNotification.tsx @@ -7,7 +7,7 @@ import { send } from 'loot-core/src/platform/client/fetch'; import { type PayeeEntity } from 'loot-core/types/models'; import { useFormatList } from '../../hooks/useFormatList'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { theme } from '../../style'; import { Button } from '../common/Button2'; import { Modal, ModalCloseButton, ModalHeader } from '../common/Modal'; @@ -20,7 +20,7 @@ export function PostsOfflineNotification() { const { t, i18n } = useTranslation(); const location = useLocation(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const locationState = location.state; const payees = diff --git a/packages/desktop-client/src/components/schedules/ScheduleDetails.tsx b/packages/desktop-client/src/components/schedules/ScheduleDetails.tsx index 9a776d6c273..bb917358457 100644 --- a/packages/desktop-client/src/components/schedules/ScheduleDetails.tsx +++ b/packages/desktop-client/src/components/schedules/ScheduleDetails.tsx @@ -21,7 +21,7 @@ import { import { useDateFormat } from '../../hooks/useDateFormat'; import { usePayees } from '../../hooks/usePayees'; import { useSelected, SelectedProvider } from '../../hooks/useSelected'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { theme } from '../../style'; import { AccountAutocomplete } from '../autocomplete/AccountAutocomplete'; import { PayeeAutocomplete } from '../autocomplete/PayeeAutocomplete'; @@ -111,7 +111,7 @@ export function ScheduleDetails({ id, transaction }: ScheduleDetailsProps) { const adding = id == null; const fromTrans = transaction != null; const payees = getPayeesById(usePayees()); - const globalDispatch = useAppDispatch(); + const globalDispatch = useDispatch(); const dateFormat = useDateFormat() || 'MM/dd/yyyy'; const [state, dispatch] = useReducer( diff --git a/packages/desktop-client/src/components/schedules/ScheduleLink.tsx b/packages/desktop-client/src/components/schedules/ScheduleLink.tsx index 6e7e3f12cb4..773d460c05d 100644 --- a/packages/desktop-client/src/components/schedules/ScheduleLink.tsx +++ b/packages/desktop-client/src/components/schedules/ScheduleLink.tsx @@ -12,7 +12,7 @@ import { } from 'loot-core/src/types/models'; import { SvgAdd } from '../../icons/v0'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { Button } from '../common/Button2'; import { InitialFocus } from '../common/InitialFocus'; import { Modal, ModalCloseButton, ModalHeader } from '../common/Modal'; @@ -35,7 +35,7 @@ export function ScheduleLink({ }) { const { t } = useTranslation(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const [filter, setFilter] = useState(accountName || ''); const schedulesQuery = useMemo( () => q('schedules').filter({ completed: false }).select('*'), diff --git a/packages/desktop-client/src/components/schedules/index.tsx b/packages/desktop-client/src/components/schedules/index.tsx index 1d783a65b13..e3f39cd1e92 100644 --- a/packages/desktop-client/src/components/schedules/index.tsx +++ b/packages/desktop-client/src/components/schedules/index.tsx @@ -7,7 +7,7 @@ import { useSchedules } from 'loot-core/src/client/data-hooks/schedules'; import { send } from 'loot-core/src/platform/client/fetch'; import { type ScheduleEntity } from 'loot-core/src/types/models'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { theme } from '../../style'; import { Button } from '../common/Button2'; import { Search } from '../common/Search'; @@ -20,7 +20,7 @@ import { type ScheduleItemAction, SchedulesTable } from './SchedulesTable'; export function Schedules() { const { t } = useTranslation(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const [filter, setFilter] = useState(''); const onEdit = useCallback( diff --git a/packages/desktop-client/src/components/settings/AuthSettings.tsx b/packages/desktop-client/src/components/settings/AuthSettings.tsx index bb228b5e64a..83af5bffdce 100644 --- a/packages/desktop-client/src/components/settings/AuthSettings.tsx +++ b/packages/desktop-client/src/components/settings/AuthSettings.tsx @@ -4,7 +4,7 @@ import { Trans, useTranslation } from 'react-i18next'; import { pushModal } from 'loot-core/client/actions'; import { useFeatureFlag } from '../../hooks/useFeatureFlag'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { theme } from '../../style'; import { Button } from '../common/Button2'; import { Label } from '../common/Label'; @@ -18,7 +18,7 @@ export function AuthSettings() { const multiuserEnabled = useMultiuserEnabled(); const loginMethod = useLoginMethod(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const openidAuthFeatureFlag = useFeatureFlag('openidAuth'); return openidAuthFeatureFlag === true ? ( diff --git a/packages/desktop-client/src/components/sidebar/Account.tsx b/packages/desktop-client/src/components/sidebar/Account.tsx index 986d5793893..bc72d78c2f3 100644 --- a/packages/desktop-client/src/components/sidebar/Account.tsx +++ b/packages/desktop-client/src/components/sidebar/Account.tsx @@ -13,7 +13,7 @@ import { type AccountEntity } from 'loot-core/src/types/models'; import { useContextMenu } from '../../hooks/useContextMenu'; import { useNotes } from '../../hooks/useNotes'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { styles, theme } from '../../style'; import { AlignedText } from '../common/AlignedText'; import { InitialFocus } from '../common/InitialFocus'; @@ -102,7 +102,7 @@ export function Account>({ onDrop, }); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const [isEditing, setIsEditing] = useState(false); diff --git a/packages/desktop-client/src/components/sidebar/Accounts.tsx b/packages/desktop-client/src/components/sidebar/Accounts.tsx index d05e90b74dc..b260c155553 100644 --- a/packages/desktop-client/src/components/sidebar/Accounts.tsx +++ b/packages/desktop-client/src/components/sidebar/Accounts.tsx @@ -12,7 +12,7 @@ import { useLocalPref } from '../../hooks/useLocalPref'; import { useOffBudgetAccounts } from '../../hooks/useOffBudgetAccounts'; import { useOnBudgetAccounts } from '../../hooks/useOnBudgetAccounts'; import { useUpdatedAccounts } from '../../hooks/useUpdatedAccounts'; -import { useAppSelector, useAppDispatch } from '../../redux'; +import { useSelector, useDispatch } from '../../redux'; import { theme } from '../../style'; import { View } from '../common/View'; @@ -23,7 +23,7 @@ const fontWeight = 600; export function Accounts() { const { t } = useTranslation(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const [isDragging, setIsDragging] = useState(false); const accounts = useAccounts(); const failedAccounts = useFailedAccounts(); @@ -31,9 +31,7 @@ export function Accounts() { const offbudgetAccounts = useOffBudgetAccounts(); const onBudgetAccounts = useOnBudgetAccounts(); const closedAccounts = useClosedAccounts(); - const syncingAccountIds = useAppSelector( - state => state.account.accountsSyncing, - ); + const syncingAccountIds = useSelector(state => state.account.accountsSyncing); const getAccountPath = (account: AccountEntity) => `/accounts/${account.id}`; diff --git a/packages/desktop-client/src/components/sidebar/BudgetName.tsx b/packages/desktop-client/src/components/sidebar/BudgetName.tsx index dddfdb6f1c9..ca7cb9bad9a 100644 --- a/packages/desktop-client/src/components/sidebar/BudgetName.tsx +++ b/packages/desktop-client/src/components/sidebar/BudgetName.tsx @@ -8,7 +8,7 @@ import { useContextMenu } from '../../hooks/useContextMenu'; import { useMetadataPref } from '../../hooks/useMetadataPref'; import { useNavigate } from '../../hooks/useNavigate'; import { SvgExpandArrow } from '../../icons/v0'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { theme } from '../../style'; import { Button } from '../common/Button2'; import { InitialFocus } from '../common/InitialFocus'; @@ -53,7 +53,7 @@ export function BudgetName({ children }: BudgetNameProps) { function EditableBudgetName() { const { t } = useTranslation(); const [budgetName, setBudgetNamePref] = useMetadataPref('budgetName'); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const navigate = useNavigate(); const triggerRef = useRef(null); const [editing, setEditing] = useState(false); diff --git a/packages/desktop-client/src/components/sidebar/Sidebar.tsx b/packages/desktop-client/src/components/sidebar/Sidebar.tsx index 4fe53e3096b..d7d5394424b 100644 --- a/packages/desktop-client/src/components/sidebar/Sidebar.tsx +++ b/packages/desktop-client/src/components/sidebar/Sidebar.tsx @@ -11,7 +11,7 @@ import { useGlobalPref } from '../../hooks/useGlobalPref'; import { useLocalPref } from '../../hooks/useLocalPref'; import { useResizeObserver } from '../../hooks/useResizeObserver'; import { SvgAdd } from '../../icons/v1'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { styles, theme } from '../../style'; import { View } from '../common/View'; import { useResponsive } from '../responsive/ResponsiveProvider'; @@ -27,7 +27,7 @@ export function Sidebar() { const hasWindowButtons = !Platform.isBrowser && Platform.OS === 'mac'; const { t } = useTranslation(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const sidebar = useSidebar(); const { width } = useResponsive(); const [isFloating = false, setFloatingSidebarPref] = diff --git a/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx b/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx index cc99c21bd7f..f11325572a8 100644 --- a/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx +++ b/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx @@ -8,7 +8,7 @@ import { validForTransfer } from 'loot-core/src/client/transfer'; import { type TransactionEntity } from 'loot-core/types/models'; import { useSelectedItems } from '../../hooks/useSelected'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { Menu } from '../common/Menu'; import { SelectedItemsButton } from '../table'; @@ -57,7 +57,7 @@ export function SelectedTransactionsButton({ onMakeAsNonSplitTransactions, }: SelectedTransactionsButtonProps) { const { t } = useTranslation(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const selectedItems = useSelectedItems(); const selectedIds = useMemo(() => [...selectedItems], [selectedItems]); diff --git a/packages/desktop-client/src/components/transactions/TransactionList.jsx b/packages/desktop-client/src/components/transactions/TransactionList.jsx index 47ceb0ac9ea..89b67068750 100644 --- a/packages/desktop-client/src/components/transactions/TransactionList.jsx +++ b/packages/desktop-client/src/components/transactions/TransactionList.jsx @@ -12,7 +12,7 @@ import { import { getChangedValues, applyChanges } from 'loot-core/src/shared/util'; import { useNavigate } from '../../hooks/useNavigate'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { theme } from '../../style'; import { TransactionTable } from './TransactionsTable'; @@ -98,7 +98,7 @@ export function TransactionList({ onScheduleAction, onMakeAsNonSplitTransactions, }) { - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const transactionsLatest = useRef(); const navigate = useNavigate(); diff --git a/packages/desktop-client/src/components/transactions/TransactionMenu.tsx b/packages/desktop-client/src/components/transactions/TransactionMenu.tsx index 3d78a37dc29..1d87d30a4df 100644 --- a/packages/desktop-client/src/components/transactions/TransactionMenu.tsx +++ b/packages/desktop-client/src/components/transactions/TransactionMenu.tsx @@ -5,7 +5,7 @@ import { pushModal } from 'loot-core/client/actions'; import { isPreviewId } from 'loot-core/shared/transactions'; import { type TransactionEntity } from 'loot-core/types/models'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { Menu } from '../common/Menu'; type BalanceMenuProps = Omit< @@ -36,7 +36,7 @@ export function TransactionMenu({ ...props }: BalanceMenuProps) { const { t } = useTranslation(); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const isPreview = isPreviewId(transaction.id); const linked = !!transaction.schedule; diff --git a/packages/desktop-client/src/components/transactions/TransactionsTable.jsx b/packages/desktop-client/src/components/transactions/TransactionsTable.jsx index 86beead8180..2a4949a8b10 100644 --- a/packages/desktop-client/src/components/transactions/TransactionsTable.jsx +++ b/packages/desktop-client/src/components/transactions/TransactionsTable.jsx @@ -59,7 +59,7 @@ import { SvgCalendar, SvgHyperlink2, } from '../../icons/v2'; -import { useAppDispatch } from '../../redux'; +import { useDispatch } from '../../redux'; import { styles, theme } from '../../style'; import { AccountAutocomplete } from '../autocomplete/AccountAutocomplete'; import { CategoryAutocomplete } from '../autocomplete/CategoryAutocomplete'; @@ -561,7 +561,7 @@ function PayeeCell({ }) { const isCreatingPayee = useRef(false); - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const parentPayee = useParentPayee( payees, @@ -887,7 +887,7 @@ const Transaction = memo(function Transaction({ showSelection, allowSplitTransaction, }) { - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const dispatchSelected = useSelectedDispatch(); const triggerRef = useRef(null); diff --git a/packages/desktop-client/src/components/util/GenericInput.jsx b/packages/desktop-client/src/components/util/GenericInput.jsx index bd20e99db85..3ed5d8ca7d9 100644 --- a/packages/desktop-client/src/components/util/GenericInput.jsx +++ b/packages/desktop-client/src/components/util/GenericInput.jsx @@ -6,7 +6,7 @@ import { integerToAmount, amountToInteger } from 'loot-core/src/shared/util'; import { useCategories } from '../../hooks/useCategories'; import { useDateFormat } from '../../hooks/useDateFormat'; -import { useAppSelector } from '../../redux'; +import { useSelector } from '../../redux'; import { AccountAutocomplete } from '../autocomplete/AccountAutocomplete'; import { Autocomplete } from '../autocomplete/Autocomplete'; import { CategoryAutocomplete } from '../autocomplete/CategoryAutocomplete'; @@ -36,7 +36,7 @@ export function GenericInput({ }) { const { grouped: categoryGroups } = useCategories(); const { data: savedReports } = useReports(); - const saved = useAppSelector(state => state.queries.saved); + const saved = useSelector(state => state.queries.saved); const dateFormat = useDateFormat() || 'MM/dd/yyyy'; const getNumberInputByFormatType = numberFormatType => { diff --git a/packages/desktop-client/src/hooks/useAccounts.ts b/packages/desktop-client/src/hooks/useAccounts.ts index 7bc9b13117f..06b279a0b10 100644 --- a/packages/desktop-client/src/hooks/useAccounts.ts +++ b/packages/desktop-client/src/hooks/useAccounts.ts @@ -2,11 +2,11 @@ import { useEffect } from 'react'; import { getAccounts } from 'loot-core/src/client/actions'; -import { useAppSelector, useAppDispatch } from '../redux'; +import { useSelector, useDispatch } from '../redux'; export function useAccounts() { - const dispatch = useAppDispatch(); - const accountsLoaded = useAppSelector(state => state.queries.accountsLoaded); + const dispatch = useDispatch(); + const accountsLoaded = useSelector(state => state.queries.accountsLoaded); useEffect(() => { if (!accountsLoaded) { @@ -14,5 +14,5 @@ export function useAccounts() { } }, []); - return useAppSelector(state => state.queries.accounts); + return useSelector(state => state.queries.accounts); } diff --git a/packages/desktop-client/src/hooks/useActions.ts b/packages/desktop-client/src/hooks/useActions.ts index df25c0aefdf..f1ed715c7ee 100644 --- a/packages/desktop-client/src/hooks/useActions.ts +++ b/packages/desktop-client/src/hooks/useActions.ts @@ -6,7 +6,7 @@ import { type ThunkAction } from 'redux-thunk'; import * as actions from 'loot-core/src/client/actions'; import type { Action, State } from 'loot-core/src/client/state-types'; -import { useAppDispatch } from '../redux'; +import { useDispatch } from '../redux'; type ActionReturnType unknown> = ReturnType extends ThunkAction @@ -26,7 +26,7 @@ export type BoundActions = { * @see https://github.com/reduxjs/react-redux/issues/1252#issuecomment-488160930 **/ export function useActions() { - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); return useMemo(() => { return bindActionCreators(actions, dispatch); }, [dispatch]) as unknown as BoundActions; diff --git a/packages/desktop-client/src/hooks/useCategories.ts b/packages/desktop-client/src/hooks/useCategories.ts index 4af19885a55..6963c06f725 100644 --- a/packages/desktop-client/src/hooks/useCategories.ts +++ b/packages/desktop-client/src/hooks/useCategories.ts @@ -2,13 +2,11 @@ import { useEffect } from 'react'; import { getCategories } from 'loot-core/src/client/actions'; -import { useAppSelector, useAppDispatch } from '../redux'; +import { useSelector, useDispatch } from '../redux'; export function useCategories() { - const dispatch = useAppDispatch(); - const categoriesLoaded = useAppSelector( - state => state.queries.categoriesLoaded, - ); + const dispatch = useDispatch(); + const categoriesLoaded = useSelector(state => state.queries.categoriesLoaded); useEffect(() => { if (!categoriesLoaded) { @@ -16,5 +14,5 @@ export function useCategories() { } }, []); - return useAppSelector(state => state.queries.categories); + return useSelector(state => state.queries.categories); } diff --git a/packages/desktop-client/src/hooks/useFailedAccounts.ts b/packages/desktop-client/src/hooks/useFailedAccounts.ts index 4497c006c51..86b20b948fc 100644 --- a/packages/desktop-client/src/hooks/useFailedAccounts.ts +++ b/packages/desktop-client/src/hooks/useFailedAccounts.ts @@ -1,9 +1,9 @@ import { useMemo } from 'react'; -import { useAppSelector } from '../redux'; +import { useSelector } from '../redux'; export function useFailedAccounts() { - const failedAccounts = useAppSelector(state => state.account.failedAccounts); + const failedAccounts = useSelector(state => state.account.failedAccounts); return useMemo( () => new Map(Object.entries(failedAccounts)), [failedAccounts], diff --git a/packages/desktop-client/src/hooks/useGlobalPref.ts b/packages/desktop-client/src/hooks/useGlobalPref.ts index a6c6fe2468f..22e2c1ef806 100644 --- a/packages/desktop-client/src/hooks/useGlobalPref.ts +++ b/packages/desktop-client/src/hooks/useGlobalPref.ts @@ -3,7 +3,7 @@ import { useCallback } from 'react'; import { saveGlobalPrefs } from 'loot-core/src/client/actions'; import { type GlobalPrefs } from 'loot-core/src/types/prefs'; -import { useAppSelector, useAppDispatch } from '../redux'; +import { useSelector, useDispatch } from '../redux'; type SetGlobalPrefAction = ( value: GlobalPrefs[K], @@ -13,7 +13,7 @@ export function useGlobalPref( prefName: K, onSaveGlobalPrefs?: () => void, ): [GlobalPrefs[K], SetGlobalPrefAction] { - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const setGlobalPref = useCallback>( value => { dispatch( @@ -27,7 +27,7 @@ export function useGlobalPref( }, [prefName, dispatch, onSaveGlobalPrefs], ); - const globalPref = useAppSelector( + const globalPref = useSelector( state => state.prefs.global?.[prefName] as GlobalPrefs[K], ); diff --git a/packages/desktop-client/src/hooks/useMetadataPref.ts b/packages/desktop-client/src/hooks/useMetadataPref.ts index f4e42222c99..7a16605996e 100644 --- a/packages/desktop-client/src/hooks/useMetadataPref.ts +++ b/packages/desktop-client/src/hooks/useMetadataPref.ts @@ -3,7 +3,7 @@ import { useCallback } from 'react'; import { savePrefs } from 'loot-core/client/actions'; import { type MetadataPrefs } from 'loot-core/types/prefs'; -import { useAppSelector, useAppDispatch } from '../redux'; +import { useSelector, useDispatch } from '../redux'; type SetMetadataPrefAction = ( value: MetadataPrefs[K], @@ -12,14 +12,14 @@ type SetMetadataPrefAction = ( export function useMetadataPref( prefName: K, ): [MetadataPrefs[K], SetMetadataPrefAction] { - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const setLocalPref = useCallback>( value => { dispatch(savePrefs({ [prefName]: value })); }, [prefName, dispatch], ); - const localPref = useAppSelector(state => state.prefs.local?.[prefName]); + const localPref = useSelector(state => state.prefs.local?.[prefName]); return [localPref, setLocalPref]; } diff --git a/packages/desktop-client/src/hooks/useModalState.ts b/packages/desktop-client/src/hooks/useModalState.ts index 94047d4f366..9b01288eea2 100644 --- a/packages/desktop-client/src/hooks/useModalState.ts +++ b/packages/desktop-client/src/hooks/useModalState.ts @@ -3,7 +3,7 @@ import { useCallback } from 'react'; import { popModal } from 'loot-core/client/actions'; import { type Modal } from 'loot-core/client/state-types/modals'; -import { useAppSelector, useAppDispatch } from '../redux'; +import { useSelector, useDispatch } from '../redux'; type ModalState = { onClose: () => void; @@ -14,9 +14,9 @@ type ModalState = { }; export function useModalState(): ModalState { - const modalStack = useAppSelector(state => state.modals.modalStack); - const isHidden = useAppSelector(state => state.modals.isHidden); - const dispatch = useAppDispatch(); + const modalStack = useSelector(state => state.modals.modalStack); + const isHidden = useSelector(state => state.modals.isHidden); + const dispatch = useDispatch(); const popModalCallback = useCallback(() => { dispatch(popModal()); diff --git a/packages/desktop-client/src/hooks/usePayees.ts b/packages/desktop-client/src/hooks/usePayees.ts index 6c2fb62ff6e..91732a6d406 100644 --- a/packages/desktop-client/src/hooks/usePayees.ts +++ b/packages/desktop-client/src/hooks/usePayees.ts @@ -2,11 +2,11 @@ import { useEffect } from 'react'; import { getCommonPayees, getPayees } from 'loot-core/src/client/actions'; -import { useAppSelector, useAppDispatch } from '../redux'; +import { useSelector, useDispatch } from '../redux'; export function useCommonPayees() { - const dispatch = useAppDispatch(); - const commonPayeesLoaded = useAppSelector( + const dispatch = useDispatch(); + const commonPayeesLoaded = useSelector( state => state.queries.commonPayeesLoaded, ); @@ -16,12 +16,12 @@ export function useCommonPayees() { } }, []); - return useAppSelector(state => state.queries.commonPayees); + return useSelector(state => state.queries.commonPayees); } export function usePayees() { - const dispatch = useAppDispatch(); - const payeesLoaded = useAppSelector(state => state.queries.payeesLoaded); + const dispatch = useDispatch(); + const payeesLoaded = useSelector(state => state.queries.payeesLoaded); useEffect(() => { if (!payeesLoaded) { @@ -29,5 +29,5 @@ export function usePayees() { } }, []); - return useAppSelector(state => state.queries.payees); + return useSelector(state => state.queries.payees); } diff --git a/packages/desktop-client/src/hooks/useSyncServerStatus.ts b/packages/desktop-client/src/hooks/useSyncServerStatus.ts index a15437c90f8..d3c1c733b16 100644 --- a/packages/desktop-client/src/hooks/useSyncServerStatus.ts +++ b/packages/desktop-client/src/hooks/useSyncServerStatus.ts @@ -1,11 +1,11 @@ import { useServerURL } from '../components/ServerContext'; -import { useAppSelector } from '../redux'; +import { useSelector } from '../redux'; type SyncServerStatus = 'offline' | 'no-server' | 'online'; export function useSyncServerStatus(): SyncServerStatus { const serverUrl = useServerURL(); - const userData = useAppSelector(state => state.user.data); + const userData = useSelector(state => state.user.data); if (!serverUrl) { return 'no-server'; diff --git a/packages/desktop-client/src/hooks/useSyncedPref.ts b/packages/desktop-client/src/hooks/useSyncedPref.ts index c44fca5fa5c..310328a836a 100644 --- a/packages/desktop-client/src/hooks/useSyncedPref.ts +++ b/packages/desktop-client/src/hooks/useSyncedPref.ts @@ -3,7 +3,7 @@ import { useCallback } from 'react'; import { saveSyncedPrefs } from 'loot-core/client/actions'; import { type SyncedPrefs } from 'loot-core/src/types/prefs'; -import { useAppSelector, useAppDispatch } from '../redux'; +import { useSelector, useDispatch } from '../redux'; type SetSyncedPrefAction = ( value: SyncedPrefs[K], @@ -12,14 +12,14 @@ type SetSyncedPrefAction = ( export function useSyncedPref( prefName: K, ): [SyncedPrefs[K], SetSyncedPrefAction] { - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const setPref = useCallback>( value => { dispatch(saveSyncedPrefs({ [prefName]: value })); }, [prefName, dispatch], ); - const pref = useAppSelector(state => state.prefs.synced[prefName]); + const pref = useSelector(state => state.prefs.synced[prefName]); return [pref, setPref]; } diff --git a/packages/desktop-client/src/hooks/useSyncedPrefs.ts b/packages/desktop-client/src/hooks/useSyncedPrefs.ts index b52a36e9477..cdef01a4e9d 100644 --- a/packages/desktop-client/src/hooks/useSyncedPrefs.ts +++ b/packages/desktop-client/src/hooks/useSyncedPrefs.ts @@ -3,20 +3,20 @@ import { useCallback } from 'react'; import { saveSyncedPrefs } from 'loot-core/client/actions'; import { type SyncedPrefs } from 'loot-core/src/types/prefs'; -import { useAppSelector, useAppDispatch } from '../redux'; +import { useSelector, useDispatch } from '../redux'; type SetSyncedPrefsAction = (value: Partial) => void; /** @deprecated: please use `useSyncedPref` (singular) */ export function useSyncedPrefs(): [SyncedPrefs, SetSyncedPrefsAction] { - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const setPrefs = useCallback( newValue => { dispatch(saveSyncedPrefs(newValue)); }, [dispatch], ); - const prefs = useAppSelector(state => state.prefs.synced); + const prefs = useSelector(state => state.prefs.synced); return [prefs, setPrefs]; } diff --git a/packages/desktop-client/src/hooks/useTransactionBatchActions.ts b/packages/desktop-client/src/hooks/useTransactionBatchActions.ts index 1d0ef59b3a6..ff612ac4c83 100644 --- a/packages/desktop-client/src/hooks/useTransactionBatchActions.ts +++ b/packages/desktop-client/src/hooks/useTransactionBatchActions.ts @@ -17,7 +17,7 @@ import { type TransactionEntity, } from 'loot-core/types/models'; -import { useAppDispatch } from '../redux'; +import { useDispatch } from '../redux'; type BatchEditProps = { name: keyof TransactionEntity; @@ -55,7 +55,7 @@ type BatchUnlinkScheduleProps = { }; export function useTransactionBatchActions() { - const dispatch = useAppDispatch(); + const dispatch = useDispatch(); const onBatchEdit = async ({ name, ids, onSuccess }: BatchEditProps) => { const { data } = await runQuery( diff --git a/packages/desktop-client/src/hooks/useUpdatedAccounts.ts b/packages/desktop-client/src/hooks/useUpdatedAccounts.ts index de083ceb64d..25a7ec729d1 100644 --- a/packages/desktop-client/src/hooks/useUpdatedAccounts.ts +++ b/packages/desktop-client/src/hooks/useUpdatedAccounts.ts @@ -1,5 +1,5 @@ -import { useAppSelector } from '../redux'; +import { useSelector } from '../redux'; export function useUpdatedAccounts() { - return useAppSelector(state => state.queries.updatedAccounts); + return useSelector(state => state.queries.updatedAccounts); } diff --git a/packages/desktop-client/src/redux/index.ts b/packages/desktop-client/src/redux/index.ts index 383f3369974..5cb89bbd39b 100644 --- a/packages/desktop-client/src/redux/index.ts +++ b/packages/desktop-client/src/redux/index.ts @@ -1,6 +1,9 @@ -import { useDispatch, useSelector } from 'react-redux'; +import { + useDispatch as useReduxDispatch, + useSelector as useReduxSelector, +} from 'react-redux'; import { type AppDispatch, type RootState } from 'loot-core/client/store'; -export const useAppDispatch = useDispatch.withTypes(); -export const useAppSelector = useSelector.withTypes(); +export const useDispatch = useReduxDispatch.withTypes(); +export const useSelector = useReduxSelector.withTypes(); From 24433141535965f6af4f6ff6921302d470621e47 Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez Date: Mon, 6 Jan 2025 08:56:19 -0800 Subject: [PATCH 10/13] Code review feedback --- .../desktop-client/src/components/table.tsx | 7 +---- .../src/hooks/useSplitsExpanded.tsx | 29 ++++++++++--------- .../loot-core/src/client/state-types/app.d.ts | 7 ----- 3 files changed, 17 insertions(+), 26 deletions(-) diff --git a/packages/desktop-client/src/components/table.tsx b/packages/desktop-client/src/components/table.tsx index 499f7a486f8..d62de7ed99f 100644 --- a/packages/desktop-client/src/components/table.tsx +++ b/packages/desktop-client/src/components/table.tsx @@ -4,7 +4,6 @@ import React, { useState, useCallback, useRef, - useEffect, useLayoutEffect, useImperativeHandle, useMemo, @@ -1230,7 +1229,7 @@ export function useTableNavigator( // See `onBlur` for why we need this const modalState = useModalState(); - const modalStackLength = useRef(0); + const modalStackLength = useRef(modalState.modalStack.length); // onEdit is passed to children, so make sure it maintains identity const onEdit = useCallback((id: T['id'], field?: string) => { @@ -1238,10 +1237,6 @@ export function useTableNavigator( setFocusedField(id ? field : null); }, []); - useEffect(() => { - modalStackLength.current = modalState.modalStack.length; - }, [modalState.modalStack]); - function flashInput() { // Force the container to be focused which suppresses the "space // pages down" behavior. If we don't do this and the user presses diff --git a/packages/desktop-client/src/hooks/useSplitsExpanded.tsx b/packages/desktop-client/src/hooks/useSplitsExpanded.tsx index 3253e0a36e8..2d178e152d1 100644 --- a/packages/desktop-client/src/hooks/useSplitsExpanded.tsx +++ b/packages/desktop-client/src/hooks/useSplitsExpanded.tsx @@ -9,10 +9,12 @@ import React, { useRef, } from 'react'; -import { - type SplitMode, - type SplitState, -} from 'loot-core/client/state-types/app'; +type SplitMode = 'collapse' | 'expand'; +type SplitState = { + ids: Set; + mode: SplitMode; + transitionId: string | null; +}; type ToggleSplitAction = { type: 'toggle-split'; @@ -59,7 +61,7 @@ type SplitsStateContext = { const SplitsExpandedContext = createContext({ state: { mode: 'collapse', - ids: [], + ids: new Set(), transitionId: null, }, dispatch: () => { @@ -74,8 +76,9 @@ export function useSplitsExpanded() { () => ({ ...data, isExpanded: (id: string) => { - const idSet = new Set(data.state.ids); - return data.state.mode === 'collapse' ? !idSet.has(id) : idSet.has(id); + return data.state.mode === 'collapse' + ? !data.state.ids.has(id) + : data.state.ids.has(id); }, }), [data], @@ -104,7 +107,7 @@ export function SplitsExpandedProvider({ } else { ids.add(id); } - return { ...state, ids: Array.from(ids) }; + return { ...state, ids }; } case 'open-split': { const ids = new Set([...state.ids]); @@ -114,7 +117,7 @@ export function SplitsExpandedProvider({ } else { ids.add(id); } - return { ...state, ids: Array.from(ids) }; + return { ...state, ids }; } case 'close-splits': { const ids = new Set([...state.ids]); @@ -125,13 +128,13 @@ export function SplitsExpandedProvider({ ids.delete(id); } }); - return { ...state, ids: Array.from(ids) }; + return { ...state, ids }; } case 'set-mode': { return { ...state, mode: action.mode, - ids: [], + ids: new Set(), transitionId: null, }; } @@ -145,14 +148,14 @@ export function SplitsExpandedProvider({ ...state, mode: state.mode === 'expand' ? 'collapse' : 'expand', transitionId: action.id, - ids: [], + ids: new Set(), }; case 'finish-switch-mode': return { ...state, transitionId: null }; } }, previousState.current || { - ids: [], + ids: new Set(), mode: initialMode, transitionId: null, }, diff --git a/packages/loot-core/src/client/state-types/app.d.ts b/packages/loot-core/src/client/state-types/app.d.ts index 14f2b088ec4..c0923f568cb 100644 --- a/packages/loot-core/src/client/state-types/app.d.ts +++ b/packages/loot-core/src/client/state-types/app.d.ts @@ -1,12 +1,5 @@ import type * as constants from '../constants'; -export type SplitMode = 'collapse' | 'expand'; -export type SplitState = { - ids: string[]; - mode: SplitMode; - transitionId: string | null; -}; - export type AppState = { loadingText: string | null; updateInfo: { From e171040a733fcbd67836e0a2ea00d1f829c7e958 Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez Date: Mon, 6 Jan 2025 09:19:08 -0800 Subject: [PATCH 11/13] UndoState type --- .../desktop-client/src/components/accounts/Account.tsx | 8 ++++---- .../src/components/payees/ManagePayeesWithData.tsx | 8 ++++---- packages/desktop-client/src/global-events.ts | 2 +- packages/desktop-client/src/hooks/useSelected.tsx | 6 +++--- packages/loot-core/src/platform/client/undo/index.d.ts | 6 +++--- packages/loot-core/src/platform/client/undo/index.web.ts | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/desktop-client/src/components/accounts/Account.tsx b/packages/desktop-client/src/components/accounts/Account.tsx index 75c2fe2819c..4ef75e82894 100644 --- a/packages/desktop-client/src/components/accounts/Account.tsx +++ b/packages/desktop-client/src/components/accounts/Account.tsx @@ -411,7 +411,7 @@ class AccountInternal extends PureComponent< } } - undo.setUndoState('current', null); + undo.setUndoState('undoEvent', null); }; const unlistens = [listen('undo-event', onUndo)]; @@ -427,9 +427,9 @@ class AccountInternal extends PureComponent< // If there is a pending undo, apply it immediately (this happens // when an undo changes the location to this page) - const lastUndoState = undo.getUndoState('current'); - if (lastUndoState) { - onUndo(lastUndoState as UndoState); + const lastUndoEvent = undo.getUndoState('undoEvent'); + if (lastUndoEvent) { + onUndo(lastUndoEvent); } } diff --git a/packages/desktop-client/src/components/payees/ManagePayeesWithData.tsx b/packages/desktop-client/src/components/payees/ManagePayeesWithData.tsx index 7ccf5b12c6b..f7940295f5e 100644 --- a/packages/desktop-client/src/components/payees/ManagePayeesWithData.tsx +++ b/packages/desktop-client/src/components/payees/ManagePayeesWithData.tsx @@ -79,12 +79,12 @@ export function ManagePayeesWithData({ await refetchRuleCounts(); } - undo.setUndoState('current', null); + undo.setUndoState('undoEvent', null); } - const lastUndoState = undo.getUndoState('current'); - if (lastUndoState) { - onUndo(lastUndoState as UndoState); + const lastUndoEvent = undo.getUndoState('undoEvent'); + if (lastUndoEvent) { + onUndo(lastUndoEvent); } return listen('undo-event', onUndo); diff --git a/packages/desktop-client/src/global-events.ts b/packages/desktop-client/src/global-events.ts index 970d93bbda9..8d18609d4b1 100644 --- a/packages/desktop-client/src/global-events.ts +++ b/packages/desktop-client/src/global-events.ts @@ -75,7 +75,7 @@ export function handleGlobalEvents(actions: BoundActions, store: Store) { if (tagged) { Promise.all(promises).then(() => { - undo.setUndoState('current', undoState); + undo.setUndoState('undoEvent', undoState); // If a modal has been tagged, open it instead of navigating if (tagged.openModal) { diff --git a/packages/desktop-client/src/hooks/useSelected.tsx b/packages/desktop-client/src/hooks/useSelected.tsx index 62fa2140505..d26e8c96b70 100644 --- a/packages/desktop-client/src/hooks/useSelected.tsx +++ b/packages/desktop-client/src/hooks/useSelected.tsx @@ -227,9 +227,9 @@ export function useSelected( } } - const lastUndoState = undo.getUndoState('current'); - if (lastUndoState) { - onUndo(lastUndoState as UndoState); + const lastUndoEvent = undo.getUndoState('undoEvent'); + if (lastUndoEvent) { + onUndo(lastUndoEvent); } return listen('undo-event', onUndo); diff --git a/packages/loot-core/src/platform/client/undo/index.d.ts b/packages/loot-core/src/platform/client/undo/index.d.ts index 184f0930d34..2d45779b37c 100644 --- a/packages/loot-core/src/platform/client/undo/index.d.ts +++ b/packages/loot-core/src/platform/client/undo/index.d.ts @@ -1,15 +1,15 @@ -// eslint-disable-next-line import/no-unresolved +import { UndoState as ServerUndoState } from '../../../server/undo'; import { type ModalType } from '../../client/state-types/modals'; export type UndoState = { id?: string; - url: unknown; + url: string; openModal: ModalType; selectedItems: { name: string; items: Set; } | null; - current: unknown; + undoEvent: ServerUndoState | null; }; export function setUndoState>( diff --git a/packages/loot-core/src/platform/client/undo/index.web.ts b/packages/loot-core/src/platform/client/undo/index.web.ts index 28b4573e7fb..d0de3f64678 100644 --- a/packages/loot-core/src/platform/client/undo/index.web.ts +++ b/packages/loot-core/src/platform/client/undo/index.web.ts @@ -11,7 +11,7 @@ const currentUndoState: T.UndoState = { url: null, openModal: null, selectedItems: null, - current: null, + undoEvent: null, }; export const setUndoState: T.SetUndoState = function (name, value) { From 8420ae4771eda77c11e84b4ef68af92d57d6616a Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez Date: Mon, 6 Jan 2025 09:58:46 -0800 Subject: [PATCH 12/13] UndoState type --- packages/loot-core/src/platform/client/undo/index.d.ts | 10 ++++++---- .../loot-core/src/platform/client/undo/index.web.ts | 8 ++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/loot-core/src/platform/client/undo/index.d.ts b/packages/loot-core/src/platform/client/undo/index.d.ts index 2d45779b37c..759474e48bc 100644 --- a/packages/loot-core/src/platform/client/undo/index.d.ts +++ b/packages/loot-core/src/platform/client/undo/index.d.ts @@ -1,10 +1,12 @@ +import { OptionlessModal } from '../../../client/state-types/modals'; import { UndoState as ServerUndoState } from '../../../server/undo'; -import { type ModalType } from '../../client/state-types/modals'; export type UndoState = { - id?: string; - url: string; - openModal: ModalType; + url: string | null; + // Right now, only the payees page uses this. It's only being set to + // `manage-rules` which is an optionless modal. Do we want to also + // support modals with options? + openModal: OptionlessModal | null; selectedItems: { name: string; items: Set; diff --git a/packages/loot-core/src/platform/client/undo/index.web.ts b/packages/loot-core/src/platform/client/undo/index.web.ts index d0de3f64678..39dde9ac0c0 100644 --- a/packages/loot-core/src/platform/client/undo/index.web.ts +++ b/packages/loot-core/src/platform/client/undo/index.web.ts @@ -2,12 +2,16 @@ import { v4 as uuidv4 } from 'uuid'; import type * as T from '.'; +type UndoStateWithId = T.UndoState & { + id?: ReturnType; +}; + // List of recently used states. We don't use a true MRU structure // because our needs are simple and we also do some custom reordering. const HISTORY_SIZE = 40; -let UNDO_STATE_MRU: T.UndoState[] = []; +let UNDO_STATE_MRU: UndoStateWithId[] = []; -const currentUndoState: T.UndoState = { +const currentUndoState: UndoStateWithId = { url: null, openModal: null, selectedItems: null, From 5788ade9c82a4fb72eb438234f78dce73d9ca1c1 Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez Date: Tue, 7 Jan 2025 14:20:16 -0800 Subject: [PATCH 13/13] yarn install --- yarn.lock | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/yarn.lock b/yarn.lock index 0d1859792fa..5f60a120657 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16837,6 +16837,13 @@ __metadata: languageName: node linkType: hard +"redux@npm:^5.0.1": + version: 5.0.1 + resolution: "redux@npm:5.0.1" + checksum: 10/a373f9ed65693ead58bea5ef61c1d6bef39da9f2706db3be6f84815f3a1283230ecd1184efb1b3daa7f807d8211b0181564ca8f336fc6ee0b1e2fa0ba06737c2 + languageName: node + linkType: hard + "reflect.getprototypeof@npm:^1.0.8": version: 1.0.8 resolution: "reflect.getprototypeof@npm:1.0.8"