diff --git a/packages/desktop-client/src/components/ManageRulesPage.tsx b/packages/desktop-client/src/components/ManageRulesPage.tsx
index 59e2327e44..615f256e17 100644
--- a/packages/desktop-client/src/components/ManageRulesPage.tsx
+++ b/packages/desktop-client/src/components/ManageRulesPage.tsx
@@ -1,14 +1,19 @@
import React from 'react';
+import { ErrorBoundary } from 'react-error-boundary';
import { useTranslation } from 'react-i18next';
+import { FeatureErrorFallback } from '#components/FeatureErrorFallback';
+
import { ManageRules } from './ManageRules';
import { Page } from './Page';
export function ManageRulesPage() {
const { t } = useTranslation();
return (
-
-
-
+
+
+
+
+
);
}
diff --git a/packages/desktop-client/src/components/accounts/Account.tsx b/packages/desktop-client/src/components/accounts/Account.tsx
index 39615c68d8..cc7255cce2 100644
--- a/packages/desktop-client/src/components/accounts/Account.tsx
+++ b/packages/desktop-client/src/components/accounts/Account.tsx
@@ -1,5 +1,6 @@
import React, { createRef, PureComponent, useEffect, useMemo } from 'react';
import type { ReactElement, RefObject } from 'react';
+import { ErrorBoundary } from 'react-error-boundary';
import { Trans } from 'react-i18next';
import { Navigate, useLocation, useParams } from 'react-router';
@@ -43,6 +44,7 @@ import {
useUpdateAccountMutation,
} from '#accounts';
import { markAccountRead } from '#accounts/accountsSlice';
+import { FeatureErrorFallback } from '#components/FeatureErrorFallback';
import type { SavedFilter } from '#components/filters/SavedFilterMenuButton';
import { TransactionList } from '#components/transactions/TransactionList';
import { validateAccountName } from '#components/util/accountValidation';
@@ -2031,48 +2033,50 @@ export function Account() {
createPayee.mutateAsync({ name });
return (
-
-
-
- setShowBalances(String(showBalances))
- }
- showNetWorthChart={String(showNetWorthChart) === 'true'}
- setShowNetWorthChart={val => setShowNetWorthChart(String(val))}
- showCleared={String(hideCleared) !== 'true'}
- setShowCleared={val => setHideCleared(String(!val))}
- showReconciled={String(hideReconciled) !== 'true'}
- setShowReconciled={val => setHideReconciled(String(!val))}
- showExtraBalances={String(showExtraBalances) === 'true'}
- setShowExtraBalances={extraBalances =>
- setShowExtraBalances(String(extraBalances))
- }
- payees={payees}
- modalShowing={modalShowing}
- accountsSyncing={accountsSyncing}
- filterConditions={filterConditions}
- categoryGroups={categoryGroups}
- accountId={params.id}
- categoryId={location?.state?.categoryId}
- location={location}
- savedFilters={savedFiters}
- onReopenAccount={onReopenAccount}
- onUpdateAccount={onUpdateAccount}
- onUnlinkAccount={onUnlinkAccount}
- onSyncAndDownload={onSyncAndDownload}
- onCreatePayee={onCreatePayee}
- />
-
-
+
+
+
+
+ setShowBalances(String(showBalances))
+ }
+ showNetWorthChart={String(showNetWorthChart) === 'true'}
+ setShowNetWorthChart={val => setShowNetWorthChart(String(val))}
+ showCleared={String(hideCleared) !== 'true'}
+ setShowCleared={val => setHideCleared(String(!val))}
+ showReconciled={String(hideReconciled) !== 'true'}
+ setShowReconciled={val => setHideReconciled(String(!val))}
+ showExtraBalances={String(showExtraBalances) === 'true'}
+ setShowExtraBalances={extraBalances =>
+ setShowExtraBalances(String(extraBalances))
+ }
+ payees={payees}
+ modalShowing={modalShowing}
+ accountsSyncing={accountsSyncing}
+ filterConditions={filterConditions}
+ categoryGroups={categoryGroups}
+ accountId={params.id}
+ categoryId={location?.state?.categoryId}
+ location={location}
+ savedFilters={savedFiters}
+ onReopenAccount={onReopenAccount}
+ onUpdateAccount={onUpdateAccount}
+ onUnlinkAccount={onUnlinkAccount}
+ onSyncAndDownload={onSyncAndDownload}
+ onCreatePayee={onCreatePayee}
+ />
+
+
+
);
}
diff --git a/packages/desktop-client/src/components/budget/DynamicBudgetTable.tsx b/packages/desktop-client/src/components/budget/DynamicBudgetTable.tsx
index 1ac488b49b..88a8debc8f 100644
--- a/packages/desktop-client/src/components/budget/DynamicBudgetTable.tsx
+++ b/packages/desktop-client/src/components/budget/DynamicBudgetTable.tsx
@@ -1,12 +1,14 @@
// @ts-strict-ignore
import React, { useEffect } from 'react';
import type { ComponentProps } from 'react';
+import { ErrorBoundary } from 'react-error-boundary';
import { useHotkeys } from 'react-hotkeys-hook';
import { AutoSizer } from 'react-virtualized-auto-sizer';
import { View } from '@actual-app/components/view';
import * as monthUtils from '@actual-app/core/shared/months';
+import { FeatureErrorFallback } from '#components/FeatureErrorFallback';
import { useGlobalPref } from '#hooks/useGlobalPref';
import { useBudgetMonthCount } from './BudgetMonthCountContext';
@@ -131,20 +133,22 @@ const DynamicBudgetTable = ({
}}
>
-
-
+
+
+
+
);
diff --git a/packages/desktop-client/src/components/schedules/index.tsx b/packages/desktop-client/src/components/schedules/index.tsx
index 587855c997..0b640bd32e 100644
--- a/packages/desktop-client/src/components/schedules/index.tsx
+++ b/packages/desktop-client/src/components/schedules/index.tsx
@@ -1,4 +1,5 @@
import React, { useCallback, useMemo, useState } from 'react';
+import { ErrorBoundary } from 'react-error-boundary';
import { Trans, useTranslation } from 'react-i18next';
import { Button } from '@actual-app/components/button';
@@ -9,6 +10,7 @@ import { q } from '@actual-app/core/shared/query';
import type { ScheduleEntity } from '@actual-app/core/types/models';
import { Search } from '#components/common/Search';
+import { FeatureErrorFallback } from '#components/FeatureErrorFallback';
import { Page } from '#components/Page';
import { useSchedules } from '#hooks/useSchedules';
import { pushModal } from '#modals/modalsSlice';
@@ -85,66 +87,68 @@ export function Schedules() {
} = useSchedules({ query: schedulesQuery });
return (
-
-
-
-
-
-
-
-
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
-
-
-
+
+
);
}
diff --git a/packages/desktop-client/src/components/sidebar/Sidebar.tsx b/packages/desktop-client/src/components/sidebar/Sidebar.tsx
index 61db2565f7..1cd658eac0 100644
--- a/packages/desktop-client/src/components/sidebar/Sidebar.tsx
+++ b/packages/desktop-client/src/components/sidebar/Sidebar.tsx
@@ -1,5 +1,6 @@
import React, { useState } from 'react';
import type { CSSProperties } from 'react';
+import { ErrorBoundary } from 'react-error-boundary';
import { useTranslation } from 'react-i18next';
import { useResponsive } from '@actual-app/components/hooks/useResponsive';
@@ -11,6 +12,7 @@ import * as Platform from '@actual-app/core/shared/platform';
import { css } from '@emotion/css';
import { Resizable } from 're-resizable';
+import { FeatureErrorFallback } from '#components/FeatureErrorFallback';
import { useGlobalPref } from '#hooks/useGlobalPref';
import { useLocalPref } from '#hooks/useLocalPref';
import { useResizeObserver } from '#hooks/useResizeObserver';
@@ -67,69 +69,75 @@ export function Sidebar() {
});
return (
-
-
+
-
- {!sidebar.alwaysFloats && (
-
- )}
-
-
-
+
+ {!sidebar.alwaysFloats && (
+
+ )}
+
-
+
+
-
+
+
+
+
-
-
+
+
);
}
diff --git a/packages/desktop-client/src/components/transactions/TransactionList.tsx b/packages/desktop-client/src/components/transactions/TransactionList.tsx
index b22ef7c93c..09d2cc8167 100644
--- a/packages/desktop-client/src/components/transactions/TransactionList.tsx
+++ b/packages/desktop-client/src/components/transactions/TransactionList.tsx
@@ -2,6 +2,7 @@
// TODO: remove strict
import { useCallback, useLayoutEffect, useRef } from 'react';
import type { RefObject } from 'react';
+import { ErrorBoundary } from 'react-error-boundary';
import { useTranslation } from 'react-i18next';
import { theme } from '@actual-app/components/theme';
@@ -29,6 +30,7 @@ import type {
TransactionFilterEntity,
} from '@actual-app/core/types/models';
+import { FeatureErrorFallback } from '#components/FeatureErrorFallback';
import type { TableHandleRef } from '#components/table';
import { isValidBoundaryDrop } from '#hooks/useDragDrop';
import type { DropPosition } from '#hooks/useDragDrop';
@@ -722,53 +724,55 @@ export function TransactionList({
);
return (
-
+
+
+
);
}
diff --git a/upcoming-release-notes/7497.md b/upcoming-release-notes/7497.md
new file mode 100644
index 0000000000..19f9f5da05
--- /dev/null
+++ b/upcoming-release-notes/7497.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [tempiz]
+---
+
+Add scoped error boundaries to prevent feature-level crashes from taking down the entire app