diff --git a/package.json b/package.json
index e699ed6c5..9f150e561 100644
--- a/package.json
+++ b/package.json
@@ -120,7 +120,7 @@
"react-image": "^4.1.0",
"react-loading-skeleton": "^3.5.0",
"react-player": "^2.11.0",
- "react-router": "^7.9.4",
+ "react-router": "^7.9.6",
"react-virtualized-auto-sizer": "^1.0.26",
"react-window": "1.8.11",
"react-window-v2": "npm:react-window@^2.2.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index af70ea5d0..5097c6b26 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -46,7 +46,7 @@ importers:
version: 8.3.8(@mantine/core@8.3.8(@mantine/hooks@8.3.8(react@19.1.0))(@types/react@19.2.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mantine/hooks@8.3.8(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@offlegacy/nuqs-hash-router':
specifier: ^0.1.1
- version: 0.1.1(nuqs@2.7.1(react-router-dom@7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-router@7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0))(react@19.1.0)
+ version: 0.1.1(nuqs@2.7.1(react-router-dom@7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-router@7.9.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0))(react@19.1.0)
'@radix-ui/react-context-menu':
specifier: ^2.2.16
version: 2.2.16(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -151,7 +151,7 @@ importers:
version: https://codeload.github.com/jeffvli/Node-MPV/tar.gz/32b4d64395289ad710c41d481d2707a7acfc228f
nuqs:
specifier: ^2.7.1
- version: 2.7.1(react-router-dom@7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-router@7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)
+ version: 2.7.1(react-router-dom@7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-router@7.9.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)
overlayscrollbars:
specifier: ^2.11.1
version: 2.11.3
@@ -189,8 +189,8 @@ importers:
specifier: ^2.11.0
version: 2.16.0(react@19.1.0)
react-router:
- specifier: ^7.9.4
- version: 7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ specifier: ^7.9.6
+ version: 7.9.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react-virtualized-auto-sizer:
specifier: ^1.0.26
version: 1.0.26(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -4610,6 +4610,16 @@ packages:
react-dom:
optional: true
+ react-router@7.9.6:
+ resolution: {integrity: sha512-Y1tUp8clYRXpfPITyuifmSoE2vncSME18uVLgaqyxh9H35JWpIfzHo+9y3Fzh5odk/jxPW29IgLgzcdwxGqyNA==}
+ engines: {node: '>=20.0.0'}
+ peerDependencies:
+ react: '>=18'
+ react-dom: '>=18'
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+
react-style-singleton@2.2.3:
resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
engines: {node: '>=10'}
@@ -7101,9 +7111,9 @@ snapshots:
mkdirp: 1.0.4
rimraf: 3.0.2
- '@offlegacy/nuqs-hash-router@0.1.1(nuqs@2.7.1(react-router-dom@7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-router@7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0))(react@19.1.0)':
+ '@offlegacy/nuqs-hash-router@0.1.1(nuqs@2.7.1(react-router-dom@7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-router@7.9.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0))(react@19.1.0)':
dependencies:
- nuqs: 2.7.1(react-router-dom@7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-router@7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)
+ nuqs: 2.7.1(react-router-dom@7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-router@7.9.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)
react: 19.1.0
'@pkgjs/parseargs@0.11.0':
@@ -10148,12 +10158,12 @@ snapshots:
dependencies:
boolbase: 1.0.0
- nuqs@2.7.1(react-router-dom@7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-router@7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0):
+ nuqs@2.7.1(react-router-dom@7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-router@7.9.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0):
dependencies:
'@standard-schema/spec': 1.0.0
react: 19.1.0
optionalDependencies:
- react-router: 7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ react-router: 7.9.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react-router-dom: 7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
object-assign@4.1.1: {}
@@ -10571,6 +10581,15 @@ snapshots:
set-cookie-parser: 2.7.1
optionalDependencies:
react-dom: 19.1.0(react@19.1.0)
+ optional: true
+
+ react-router@7.9.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+ dependencies:
+ cookie: 1.0.2
+ react: 19.1.0
+ set-cookie-parser: 2.7.1
+ optionalDependencies:
+ react-dom: 19.1.0(react@19.1.0)
react-style-singleton@2.2.3(@types/react@19.2.5)(react@19.1.0):
dependencies:
diff --git a/src/renderer/features/action-required/components/route-error-boundary.tsx b/src/renderer/features/action-required/components/route-error-boundary.tsx
deleted file mode 100644
index 5e610be1f..000000000
--- a/src/renderer/features/action-required/components/route-error-boundary.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-import { useTranslation } from 'react-i18next';
-import { useNavigate, useRouteError } from 'react-router';
-
-import { AppMenu } from '/@/renderer/features/titlebar/components/app-menu';
-import { AppRoute } from '/@/renderer/router/routes';
-import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
-import { Button } from '/@/shared/components/button/button';
-import { Center } from '/@/shared/components/center/center';
-import { Divider } from '/@/shared/components/divider/divider';
-import { DropdownMenu } from '/@/shared/components/dropdown-menu/dropdown-menu';
-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';
-
-const RouteErrorBoundary = () => {
- const { t } = useTranslation();
- const navigate = useNavigate();
- const error = useRouteError() as any;
- console.error('error', error);
-
- const handleReload = () => {
- navigate(0);
- };
-
- const handleReturn = () => {
- navigate(-1);
- };
-
- const handleHome = () => {
- navigate(AppRoute.HOME);
- };
-
- return (
-
-
-
-
-
-
- {t('error.genericError')}
-
-
- {error?.message}
-
- }
- onClick={handleHome}
- size="md"
- style={{ flex: 0.5 }}
- variant="default"
- >
- {t('page.home.title')}
-
-
-
- }
- size="md"
- style={{ flex: 0.5 }}
- variant="default"
- >
- {t('common.menu')}
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-export default RouteErrorBoundary;
diff --git a/src/renderer/features/action-required/routes/action-required-route.tsx b/src/renderer/features/action-required/routes/action-required-route.tsx
index 087f986a5..0e511d7fa 100644
--- a/src/renderer/features/action-required/routes/action-required-route.tsx
+++ b/src/renderer/features/action-required/routes/action-required-route.tsx
@@ -9,6 +9,7 @@ import { ServerCredentialRequired } from '/@/renderer/features/action-required/c
import { ServerRequired } from '/@/renderer/features/action-required/components/server-required';
import { ServerList } from '/@/renderer/features/servers/components/server-list';
import { AnimatedPage } from '/@/renderer/features/shared/components/animated-page';
+import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary';
import { AppRoute } from '/@/renderer/router/routes';
import { useCurrentServerWithCredential } from '/@/renderer/store';
import { Button } from '/@/shared/components/button/button';
@@ -84,4 +85,12 @@ const ActionRequiredRoute = () => {
);
};
-export default ActionRequiredRoute;
+const ActionRequiredRouteWithBoundary = () => {
+ return (
+
+
+
+ );
+};
+
+export default ActionRequiredRouteWithBoundary;
diff --git a/src/renderer/features/action-required/routes/invalid-route.tsx b/src/renderer/features/action-required/routes/invalid-route.tsx
index c986dbae6..1b6e09d1f 100644
--- a/src/renderer/features/action-required/routes/invalid-route.tsx
+++ b/src/renderer/features/action-required/routes/invalid-route.tsx
@@ -2,6 +2,7 @@ import { useTranslation } from 'react-i18next';
import { useLocation, useNavigate } from 'react-router';
import { AnimatedPage } from '/@/renderer/features/shared/components/animated-page';
+import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { Center } from '/@/shared/components/center/center';
import { Group } from '/@/shared/components/group/group';
@@ -32,4 +33,12 @@ const InvalidRoute = () => {
);
};
-export default InvalidRoute;
+const InvalidRouteWithBoundary = () => {
+ return (
+
+
+
+ );
+};
+
+export default InvalidRouteWithBoundary;
diff --git a/src/renderer/features/albums/routes/album-detail-route.tsx b/src/renderer/features/albums/routes/album-detail-route.tsx
index e04adfbce..0776620f7 100644
--- a/src/renderer/features/albums/routes/album-detail-route.tsx
+++ b/src/renderer/features/albums/routes/album-detail-route.tsx
@@ -13,6 +13,7 @@ import {
} from '/@/renderer/features/shared/components/library-background-overlay';
import { LibraryContainer } from '/@/renderer/features/shared/components/library-container';
import { LibraryHeaderBar } from '/@/renderer/features/shared/components/library-header-bar';
+import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary';
import { useFastAverageColor } from '/@/renderer/hooks';
import { useCurrentServer, useGeneralSettings } from '/@/renderer/store';
import { LibraryItem } from '/@/shared/types/domain-types';
@@ -83,4 +84,12 @@ const AlbumDetailRoute = () => {
);
};
-export default AlbumDetailRoute;
+const AlbumDetailRouteWithBoundary = () => {
+ return (
+
+
+
+ );
+};
+
+export default AlbumDetailRouteWithBoundary;
diff --git a/src/renderer/features/albums/routes/album-list-route.tsx b/src/renderer/features/albums/routes/album-list-route.tsx
index f3d2dce72..15ea87f02 100644
--- a/src/renderer/features/albums/routes/album-list-route.tsx
+++ b/src/renderer/features/albums/routes/album-list-route.tsx
@@ -6,6 +6,7 @@ import { AlbumListContent } from '/@/renderer/features/albums/components/album-l
import { AlbumListHeader } from '/@/renderer/features/albums/components/album-list-header';
import { AnimatedPage } from '/@/renderer/features/shared/components/animated-page';
import { LibraryContainer } from '/@/renderer/features/shared/components/library-container';
+import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary';
const AlbumListRoute = () => {
const { albumArtistId, genreId } = useParams();
@@ -34,4 +35,12 @@ const AlbumListRoute = () => {
);
};
-export default AlbumListRoute;
+const AlbumListRouteWithBoundary = () => {
+ return (
+
+
+
+ );
+};
+
+export default AlbumListRouteWithBoundary;
diff --git a/src/renderer/features/albums/routes/dummy-album-detail-route.tsx b/src/renderer/features/albums/routes/dummy-album-detail-route.tsx
index 844a86530..61bac9c59 100644
--- a/src/renderer/features/albums/routes/dummy-album-detail-route.tsx
+++ b/src/renderer/features/albums/routes/dummy-album-detail-route.tsx
@@ -11,6 +11,7 @@ import { usePlayer } from '/@/renderer/features/player/context/player-context';
import { AnimatedPage } from '/@/renderer/features/shared/components/animated-page';
import { LibraryContainer } from '/@/renderer/features/shared/components/library-container';
import { LibraryHeader } from '/@/renderer/features/shared/components/library-header';
+import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary';
import { PlayButton } from '/@/renderer/features/shared/components/play-button';
import { useCreateFavorite } from '/@/renderer/features/shared/mutations/create-favorite-mutation';
import { useDeleteFavorite } from '/@/renderer/features/shared/mutations/delete-favorite-mutation';
@@ -228,4 +229,12 @@ const DummyAlbumDetailRoute = () => {
);
};
-export default DummyAlbumDetailRoute;
+const DummyAlbumDetailRouteWithBoundary = () => {
+ return (
+
+
+
+ );
+};
+
+export default DummyAlbumDetailRouteWithBoundary;
diff --git a/src/renderer/features/artists/routes/album-artist-detail-route.tsx b/src/renderer/features/artists/routes/album-artist-detail-route.tsx
index bee8a4525..38676ee19 100644
--- a/src/renderer/features/artists/routes/album-artist-detail-route.tsx
+++ b/src/renderer/features/artists/routes/album-artist-detail-route.tsx
@@ -13,6 +13,7 @@ import {
} from '/@/renderer/features/shared/components/library-background-overlay';
import { LibraryContainer } from '/@/renderer/features/shared/components/library-container';
import { LibraryHeaderBar } from '/@/renderer/features/shared/components/library-header-bar';
+import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary';
import { useFastAverageColor } from '/@/renderer/hooks';
import { useCurrentServer, useGeneralSettings } from '/@/renderer/store';
import { LibraryItem } from '/@/shared/types/domain-types';
@@ -89,4 +90,12 @@ const AlbumArtistDetailRoute = () => {
);
};
-export default AlbumArtistDetailRoute;
+const AlbumArtistDetailRouteWithBoundary = () => {
+ return (
+
+
+
+ );
+};
+
+export default AlbumArtistDetailRouteWithBoundary;
diff --git a/src/renderer/features/artists/routes/album-artist-detail-top-songs-list-route.tsx b/src/renderer/features/artists/routes/album-artist-detail-top-songs-list-route.tsx
index 47e649135..1219ccf68 100644
--- a/src/renderer/features/artists/routes/album-artist-detail-top-songs-list-route.tsx
+++ b/src/renderer/features/artists/routes/album-artist-detail-top-songs-list-route.tsx
@@ -8,6 +8,7 @@ import { artistsQueries } from '/@/renderer/features/artists/api/artists-api';
import { AlbumArtistDetailTopSongsListHeader } from '/@/renderer/features/artists/components/album-artist-detail-top-songs-list-header';
import { AnimatedPage } from '/@/renderer/features/shared/components/animated-page';
import { LibraryContainer } from '/@/renderer/features/shared/components/library-container';
+import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary';
import { useCurrentServer } from '/@/renderer/store/auth.store';
import { LibraryItem } from '/@/shared/types/domain-types';
@@ -64,4 +65,12 @@ const AlbumArtistDetailTopSongsListRoute = () => {
);
};
-export default AlbumArtistDetailTopSongsListRoute;
+const AlbumArtistDetailTopSongsListRouteWithBoundary = () => {
+ return (
+
+
+
+ );
+};
+
+export default AlbumArtistDetailTopSongsListRouteWithBoundary;
diff --git a/src/renderer/features/artists/routes/album-artist-list-route.tsx b/src/renderer/features/artists/routes/album-artist-list-route.tsx
index 0a3f5bba2..ffad2da0b 100644
--- a/src/renderer/features/artists/routes/album-artist-list-route.tsx
+++ b/src/renderer/features/artists/routes/album-artist-list-route.tsx
@@ -5,6 +5,7 @@ import { AlbumArtistListContent } from '/@/renderer/features/artists/components/
import { AlbumArtistListHeader } from '/@/renderer/features/artists/components/album-artist-list-header';
import { AnimatedPage } from '/@/renderer/features/shared/components/animated-page';
import { LibraryContainer } from '/@/renderer/features/shared/components/library-container';
+import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary';
import { ItemListKey } from '/@/shared/types/types';
const AlbumArtistListRoute = () => {
@@ -33,4 +34,12 @@ const AlbumArtistListRoute = () => {
);
};
-export default AlbumArtistListRoute;
+const AlbumArtistListRouteWithBoundary = () => {
+ return (
+
+
+
+ );
+};
+
+export default AlbumArtistListRouteWithBoundary;
diff --git a/src/renderer/features/artists/routes/artist-list-route.tsx b/src/renderer/features/artists/routes/artist-list-route.tsx
index dbace88ab..23f1b41bd 100644
--- a/src/renderer/features/artists/routes/artist-list-route.tsx
+++ b/src/renderer/features/artists/routes/artist-list-route.tsx
@@ -5,6 +5,7 @@ import { ArtistListContent } from '/@/renderer/features/artists/components/artis
import { ArtistListHeader } from '/@/renderer/features/artists/components/artist-list-header';
import { AnimatedPage } from '/@/renderer/features/shared/components/animated-page';
import { LibraryContainer } from '/@/renderer/features/shared/components/library-container';
+import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary';
import { ItemListKey } from '/@/shared/types/types';
const ArtistListRoute = () => {
@@ -33,4 +34,12 @@ const ArtistListRoute = () => {
);
};
-export default ArtistListRoute;
+const ArtistListRouteWithBoundary = () => {
+ return (
+
+
+
+ );
+};
+
+export default ArtistListRouteWithBoundary;
diff --git a/src/renderer/features/genres/routes/genre-list-route.tsx b/src/renderer/features/genres/routes/genre-list-route.tsx
index f918bc890..439bd5e49 100644
--- a/src/renderer/features/genres/routes/genre-list-route.tsx
+++ b/src/renderer/features/genres/routes/genre-list-route.tsx
@@ -5,6 +5,7 @@ import { GenreListContent } from '/@/renderer/features/genres/components/genre-l
import { GenreListHeader } from '/@/renderer/features/genres/components/genre-list-header';
import { AnimatedPage } from '/@/renderer/features/shared/components/animated-page';
import { LibraryContainer } from '/@/renderer/features/shared/components/library-container';
+import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary';
import { ItemListKey } from '/@/shared/types/types';
const GenreListRoute = () => {
@@ -33,4 +34,12 @@ const GenreListRoute = () => {
);
};
-export default GenreListRoute;
+const GenreListRouteWithBoundary = () => {
+ return (
+
+
+
+ );
+};
+
+export default GenreListRouteWithBoundary;
diff --git a/src/renderer/features/home/routes/home-route.tsx b/src/renderer/features/home/routes/home-route.tsx
index 0540c1342..596622f45 100644
--- a/src/renderer/features/home/routes/home-route.tsx
+++ b/src/renderer/features/home/routes/home-route.tsx
@@ -10,6 +10,7 @@ import { FeaturedGenres } from '/@/renderer/features/home/components/featured-ge
import { AnimatedPage } from '/@/renderer/features/shared/components/animated-page';
import { LibraryContainer } from '/@/renderer/features/shared/components/library-container';
import { LibraryHeaderBar } from '/@/renderer/features/shared/components/library-header-bar';
+import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary';
import {
HomeItem,
useCurrentServer,
@@ -154,12 +155,14 @@ const HomeRoute = () => {
);
};
-const SuspensedHomeRoute = () => {
+const HomeRouteWithBoundary = () => {
return (
- }>
-
-
+
+ }>
+
+
+
);
};
-export default SuspensedHomeRoute;
+export default HomeRouteWithBoundary;
diff --git a/src/renderer/features/login/routes/login-route.tsx b/src/renderer/features/login/routes/login-route.tsx
index 622dd369e..7a5b33500 100644
--- a/src/renderer/features/login/routes/login-route.tsx
+++ b/src/renderer/features/login/routes/login-route.tsx
@@ -11,6 +11,7 @@ import JellyfinIcon from '/@/renderer/features/servers/assets/jellyfin.png';
import NavidromeIcon from '/@/renderer/features/servers/assets/navidrome.png';
import SubsonicIcon from '/@/renderer/features/servers/assets/opensubsonic.png';
import { AnimatedPage } from '/@/renderer/features/shared/components/animated-page';
+import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary';
import { AppRoute } from '/@/renderer/router/routes';
import { useAuthStoreActions, useCurrentServer } from '/@/renderer/store';
import { Button } from '/@/shared/components/button/button';
@@ -200,4 +201,12 @@ const LoginRoute = () => {
);
};
-export default LoginRoute;
+const LoginRouteWithBoundary = () => {
+ return (
+
+
+
+ );
+};
+
+export default LoginRouteWithBoundary;
diff --git a/src/renderer/features/now-playing/routes/now-playing-route.tsx b/src/renderer/features/now-playing/routes/now-playing-route.tsx
index 74489bdd8..d0c2c318d 100644
--- a/src/renderer/features/now-playing/routes/now-playing-route.tsx
+++ b/src/renderer/features/now-playing/routes/now-playing-route.tsx
@@ -6,6 +6,7 @@ import { PlayQueue } from '/@/renderer/features/now-playing/components/play-queu
import { PlayQueueListControls } from '/@/renderer/features/now-playing/components/play-queue-list-controls';
import { AnimatedPage } from '/@/renderer/features/shared/components/animated-page';
import { LibraryContainer } from '/@/renderer/features/shared/components/library-container';
+import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary';
import { ItemListKey } from '/@/shared/types/types';
const NowPlayingRoute = () => {
@@ -28,4 +29,12 @@ const NowPlayingRoute = () => {
);
};
-export default NowPlayingRoute;
+const NowPlayingRouteWithBoundary = () => {
+ return (
+
+
+
+ );
+};
+
+export default NowPlayingRouteWithBoundary;
diff --git a/src/renderer/features/playlists/routes/playlist-detail-song-list-route.tsx b/src/renderer/features/playlists/routes/playlist-detail-song-list-route.tsx
index 59a71a873..b6b017fb1 100644
--- a/src/renderer/features/playlists/routes/playlist-detail-song-list-route.tsx
+++ b/src/renderer/features/playlists/routes/playlist-detail-song-list-route.tsx
@@ -14,6 +14,7 @@ import { useCreatePlaylist } from '/@/renderer/features/playlists/mutations/crea
import { useDeletePlaylist } from '/@/renderer/features/playlists/mutations/delete-playlist-mutation';
import { AnimatedPage } from '/@/renderer/features/shared/components/animated-page';
import { LibraryContainer } from '/@/renderer/features/shared/components/library-container';
+import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary';
import { AppRoute } from '/@/renderer/router/routes';
import { useCurrentServer, usePlaylistDetailStore } from '/@/renderer/store';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
@@ -233,4 +234,12 @@ const PlaylistDetailSongListRoute = () => {
);
};
-export default PlaylistDetailSongListRoute;
+const PlaylistDetailSongListRouteWithBoundary = () => {
+ return (
+
+
+
+ );
+};
+
+export default PlaylistDetailSongListRouteWithBoundary;
diff --git a/src/renderer/features/playlists/routes/playlist-list-route.tsx b/src/renderer/features/playlists/routes/playlist-list-route.tsx
index 4b8692b71..043bc6f6e 100644
--- a/src/renderer/features/playlists/routes/playlist-list-route.tsx
+++ b/src/renderer/features/playlists/routes/playlist-list-route.tsx
@@ -6,6 +6,7 @@ import { PlaylistListContent } from '/@/renderer/features/playlists/components/p
import { PlaylistListHeader } from '/@/renderer/features/playlists/components/playlist-list-header';
import { AnimatedPage } from '/@/renderer/features/shared/components/animated-page';
import { LibraryContainer } from '/@/renderer/features/shared/components/library-container';
+import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary';
import { ItemListKey } from '/@/shared/types/types';
const PlaylistListRoute = () => {
@@ -35,4 +36,12 @@ const PlaylistListRoute = () => {
);
};
-export default PlaylistListRoute;
+const PlaylistListRouteWithBoundary = () => {
+ return (
+
+
+
+ );
+};
+
+export default PlaylistListRouteWithBoundary;
diff --git a/src/renderer/features/search/routes/search-route.tsx b/src/renderer/features/search/routes/search-route.tsx
index 882425943..6b19ee3a0 100644
--- a/src/renderer/features/search/routes/search-route.tsx
+++ b/src/renderer/features/search/routes/search-route.tsx
@@ -5,6 +5,7 @@ import { SearchContent } from '/@/renderer/features/search/components/search-con
import { SearchHeader } from '/@/renderer/features/search/components/search-header';
import { AnimatedPage } from '/@/renderer/features/shared/components/animated-page';
import { LibraryContainer } from '/@/renderer/features/shared/components/library-container';
+import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary';
const SearchRoute = () => {
const { state: locationState } = useLocation();
@@ -22,4 +23,12 @@ const SearchRoute = () => {
);
};
-export default SearchRoute;
+const SearchRouteWithBoundary = () => {
+ return (
+
+
+
+ );
+};
+
+export default SearchRouteWithBoundary;
diff --git a/src/renderer/features/shared/components/page-error-boundary.tsx b/src/renderer/features/shared/components/page-error-boundary.tsx
new file mode 100644
index 000000000..3385c985d
--- /dev/null
+++ b/src/renderer/features/shared/components/page-error-boundary.tsx
@@ -0,0 +1,97 @@
+import { ErrorBoundary } from 'react-error-boundary';
+import { useTranslation } from 'react-i18next';
+
+import { ServerSelector } from '/@/renderer/features/sidebar/components/server-selector';
+import { Box } from '/@/shared/components/box/box';
+import { Button } from '/@/shared/components/button/button';
+import { Center } from '/@/shared/components/center/center';
+import { Code } from '/@/shared/components/code/code';
+import { Group } from '/@/shared/components/group/group';
+import { Icon } from '/@/shared/components/icon/icon';
+import { Stack } from '/@/shared/components/stack/stack';
+import { TextTitle } from '/@/shared/components/text-title/text-title';
+import { Text } from '/@/shared/components/text/text';
+
+interface PageErrorFallbackProps {
+ error: Error;
+ resetErrorBoundary: () => void;
+}
+
+const PageErrorFallback = ({ error, resetErrorBoundary }: PageErrorFallbackProps) => {
+ const { t } = useTranslation();
+
+ const handleRefresh = () => {
+ window.location.reload();
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+ {t('error.genericError', { postProcess: 'sentenceCase' })}
+
+
+
+ {error?.message || t('error.genericError', { postProcess: 'sentenceCase' })}
+
+ {process.env.NODE_ENV === 'development' && error?.stack && (
+
+ {error.stack}
+
+ )}
+
+
+
+
+
+
+
+ );
+};
+
+interface PageErrorBoundaryProps {
+ children: React.ReactNode;
+}
+
+export const PageErrorBoundary = ({ children }: PageErrorBoundaryProps) => {
+ return (
+ {
+ if (process.env.NODE_ENV === 'development') {
+ console.error('Page error boundary caught an error:', error, errorInfo);
+ }
+ }}
+ onReset={() => {}}
+ >
+ {children}
+
+ );
+};
diff --git a/src/renderer/components/error-boundary/router-error-boundary.tsx b/src/renderer/features/shared/components/router-error-boundary.tsx
similarity index 87%
rename from src/renderer/components/error-boundary/router-error-boundary.tsx
rename to src/renderer/features/shared/components/router-error-boundary.tsx
index 52dea1674..770b8259a 100644
--- a/src/renderer/components/error-boundary/router-error-boundary.tsx
+++ b/src/renderer/features/shared/components/router-error-boundary.tsx
@@ -5,9 +5,11 @@ import { ServerSelector } from '/@/renderer/features/sidebar/components/server-s
import { Box } from '/@/shared/components/box/box';
import { Button } from '/@/shared/components/button/button';
import { Center } from '/@/shared/components/center/center';
+import { Code } from '/@/shared/components/code/code';
import { Group } from '/@/shared/components/group/group';
import { Icon } from '/@/shared/components/icon/icon';
import { Stack } from '/@/shared/components/stack/stack';
+import { TextTitle } from '/@/shared/components/text-title/text-title';
import { Text } from '/@/shared/components/text/text';
interface RouterErrorFallbackProps {
@@ -41,32 +43,30 @@ const RouterErrorFallback = ({ error, resetErrorBoundary }: RouterErrorFallbackP
>
-
-
+
+
-
+
{t('error.genericError', { postProcess: 'sentenceCase' })}
-
+
-
+
{error?.message || t('error.genericError', { postProcess: 'sentenceCase' })}
{process.env.NODE_ENV === 'development' && error?.stack && (
-
{error.stack}
-
+
)}