From 199a67fdf30c37059b324e1b8de0b8c510772e8f Mon Sep 17 00:00:00 2001 From: jeffvli Date: Sun, 16 Nov 2025 21:53:43 -0800 Subject: [PATCH] add error boundary to the app root --- .../error-boundary/root-error-boundary.tsx | 89 +++++++++++++++++++ src/renderer/main.tsx | 53 +++++------ 2 files changed, 117 insertions(+), 25 deletions(-) create mode 100644 src/renderer/components/error-boundary/root-error-boundary.tsx diff --git a/src/renderer/components/error-boundary/root-error-boundary.tsx b/src/renderer/components/error-boundary/root-error-boundary.tsx new file mode 100644 index 000000000..ae00ae310 --- /dev/null +++ b/src/renderer/components/error-boundary/root-error-boundary.tsx @@ -0,0 +1,89 @@ +import { ErrorBoundary } from 'react-error-boundary'; +import { useTranslation } from 'react-i18next'; + +import { Box } from '/@/shared/components/box/box'; +import { Button } from '/@/shared/components/button/button'; +import { Center } from '/@/shared/components/center/center'; +import { Group } from '/@/shared/components/group/group'; +import { Icon } from '/@/shared/components/icon/icon'; +import { Stack } from '/@/shared/components/stack/stack'; +import { Text } from '/@/shared/components/text/text'; + +interface RootErrorFallbackProps { + error: Error; + resetErrorBoundary: () => void; +} + +const RootErrorFallback = ({ error, resetErrorBoundary }: RootErrorFallbackProps) => { + const { t } = useTranslation(); + + const handleReload = () => { + window.location.reload(); + }; + + return ( + +
+ + + + {t('error.genericError')} + + + {error?.message || t('error.genericError')} + + {process.env.NODE_ENV === 'development' && error?.stack && ( + + {error.stack} + + )} + + + + + +
+
+ ); +}; + +interface RootErrorBoundaryProps { + children: React.ReactNode; +} + +export const RootErrorBoundary = ({ children }: RootErrorBoundaryProps) => { + return ( + { + if (process.env.NODE_ENV === 'development') { + console.error('Root error boundary caught an error:', error, errorInfo); + } + }} + onReset={() => {}} + > + {children} + + ); +}; diff --git a/src/renderer/main.tsx b/src/renderer/main.tsx index 13e71113e..0731cf340 100644 --- a/src/renderer/main.tsx +++ b/src/renderer/main.tsx @@ -7,6 +7,7 @@ import { del, get, set } from 'idb-keyval'; import { createRoot } from 'react-dom/client'; import { App } from '/@/renderer/app'; +import { RootErrorBoundary } from '/@/renderer/components/error-boundary/root-error-boundary'; import { queryClient } from '/@/renderer/lib/react-query'; function createIDBPersister(idbValidKey: IDBValidKey = 'reactQuery') { @@ -26,32 +27,34 @@ function createIDBPersister(idbValidKey: IDBValidKey = 'reactQuery') { const indexedDbPersister = createIDBPersister('feishin'); createRoot(document.getElementById('root')!).render( - { - const isSuccess = query.state.status === 'success'; - const isLyricsQueryKey = - query.queryKey.includes('song') && - query.queryKey.includes('lyrics') && - query.queryKey.includes('select'); + + { + const isSuccess = query.state.status === 'success'; + const isLyricsQueryKey = + query.queryKey.includes('song') && + query.queryKey.includes('lyrics') && + query.queryKey.includes('select'); - return isSuccess && isLyricsQueryKey; - }, - }, - hydrateOptions: { - defaultOptions: { - queries: { - gcTime: Infinity, + return isSuccess && isLyricsQueryKey; }, }, - }, - maxAge: Infinity, - persister: indexedDbPersister, - }} - > - - , + hydrateOptions: { + defaultOptions: { + queries: { + gcTime: Infinity, + }, + }, + }, + maxAge: Infinity, + persister: indexedDbPersister, + }} + > + + + , );