mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-06 20:10:12 +02:00
refactor app error boundaries
This commit is contained in:
+1
-1
@@ -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",
|
||||
|
||||
Generated
+27
-8
@@ -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:
|
||||
|
||||
@@ -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 (
|
||||
<div style={{ backgroundColor: 'var(--theme-colors-background)' }}>
|
||||
<Center style={{ height: '100vh' }}>
|
||||
<Stack style={{ maxWidth: '50%' }}>
|
||||
<Group>
|
||||
<ActionIcon
|
||||
icon="arrowLeftS"
|
||||
onClick={handleReturn}
|
||||
px={10}
|
||||
variant="subtle"
|
||||
/>
|
||||
<Icon fill="error" icon="error" size="lg" />
|
||||
<Text size="lg">{t('error.genericError')}</Text>
|
||||
</Group>
|
||||
<Divider my={5} />
|
||||
<Text size="sm">{error?.message}</Text>
|
||||
<Group gap="sm" grow>
|
||||
<Button
|
||||
leftSection={<Icon icon="home" />}
|
||||
onClick={handleHome}
|
||||
size="md"
|
||||
style={{ flex: 0.5 }}
|
||||
variant="default"
|
||||
>
|
||||
{t('page.home.title')}
|
||||
</Button>
|
||||
<DropdownMenu position="bottom-start">
|
||||
<DropdownMenu.Target>
|
||||
<Button
|
||||
leftSection={<Icon icon="menu" />}
|
||||
size="md"
|
||||
style={{ flex: 0.5 }}
|
||||
variant="default"
|
||||
>
|
||||
{t('common.menu')}
|
||||
</Button>
|
||||
</DropdownMenu.Target>
|
||||
<DropdownMenu.Dropdown>
|
||||
<AppMenu />
|
||||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
</Group>
|
||||
<Group grow>
|
||||
<Button onClick={handleReload} size="md" variant="filled">
|
||||
{t('common.reload')}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Center>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RouteErrorBoundary;
|
||||
@@ -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 (
|
||||
<PageErrorBoundary>
|
||||
<ActionRequiredRoute />
|
||||
</PageErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default ActionRequiredRouteWithBoundary;
|
||||
|
||||
@@ -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 (
|
||||
<PageErrorBoundary>
|
||||
<InvalidRoute />
|
||||
</PageErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default InvalidRouteWithBoundary;
|
||||
|
||||
@@ -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 (
|
||||
<PageErrorBoundary>
|
||||
<AlbumDetailRoute />
|
||||
</PageErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default AlbumDetailRouteWithBoundary;
|
||||
|
||||
@@ -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 (
|
||||
<PageErrorBoundary>
|
||||
<AlbumListRoute />
|
||||
</PageErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default AlbumListRouteWithBoundary;
|
||||
|
||||
@@ -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 (
|
||||
<PageErrorBoundary>
|
||||
<DummyAlbumDetailRoute />
|
||||
</PageErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default DummyAlbumDetailRouteWithBoundary;
|
||||
|
||||
@@ -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 (
|
||||
<PageErrorBoundary>
|
||||
<AlbumArtistDetailRoute />
|
||||
</PageErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default AlbumArtistDetailRouteWithBoundary;
|
||||
|
||||
@@ -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 (
|
||||
<PageErrorBoundary>
|
||||
<AlbumArtistDetailTopSongsListRoute />
|
||||
</PageErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default AlbumArtistDetailTopSongsListRouteWithBoundary;
|
||||
|
||||
@@ -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 (
|
||||
<PageErrorBoundary>
|
||||
<AlbumArtistListRoute />
|
||||
</PageErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default AlbumArtistListRouteWithBoundary;
|
||||
|
||||
@@ -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 (
|
||||
<PageErrorBoundary>
|
||||
<ArtistListRoute />
|
||||
</PageErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default ArtistListRouteWithBoundary;
|
||||
|
||||
@@ -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 (
|
||||
<PageErrorBoundary>
|
||||
<GenreListRoute />
|
||||
</PageErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default GenreListRouteWithBoundary;
|
||||
|
||||
@@ -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 (
|
||||
<Suspense fallback={<Spinner container />}>
|
||||
<HomeRoute />
|
||||
</Suspense>
|
||||
<PageErrorBoundary>
|
||||
<Suspense fallback={<Spinner container />}>
|
||||
<HomeRoute />
|
||||
</Suspense>
|
||||
</PageErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default SuspensedHomeRoute;
|
||||
export default HomeRouteWithBoundary;
|
||||
|
||||
@@ -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 (
|
||||
<PageErrorBoundary>
|
||||
<LoginRoute />
|
||||
</PageErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginRouteWithBoundary;
|
||||
|
||||
@@ -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 (
|
||||
<PageErrorBoundary>
|
||||
<NowPlayingRoute />
|
||||
</PageErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default NowPlayingRouteWithBoundary;
|
||||
|
||||
@@ -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 (
|
||||
<PageErrorBoundary>
|
||||
<PlaylistDetailSongListRoute />
|
||||
</PageErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlaylistDetailSongListRouteWithBoundary;
|
||||
|
||||
@@ -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 (
|
||||
<PageErrorBoundary>
|
||||
<PlaylistListRoute />
|
||||
</PageErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlaylistListRouteWithBoundary;
|
||||
|
||||
@@ -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 (
|
||||
<PageErrorBoundary>
|
||||
<SearchRoute />
|
||||
</PageErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchRouteWithBoundary;
|
||||
|
||||
@@ -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 (
|
||||
<Box h="100%" pos="relative" w="100%">
|
||||
<Box
|
||||
style={{
|
||||
padding: 'var(--theme-spacing-md)',
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0,
|
||||
zIndex: 1000,
|
||||
}}
|
||||
>
|
||||
<ServerSelector />
|
||||
</Box>
|
||||
<Center h="100%" p="md" w="100%">
|
||||
<Stack maw="800px">
|
||||
<Group gap="xs">
|
||||
<Icon fill="error" icon="error" size="lg" />
|
||||
<TextTitle fw={700} order={3}>
|
||||
{t('error.genericError', { postProcess: 'sentenceCase' })}
|
||||
</TextTitle>
|
||||
</Group>
|
||||
<Text style={{ wordBreak: 'break-word' }}>
|
||||
{error?.message || t('error.genericError', { postProcess: 'sentenceCase' })}
|
||||
</Text>
|
||||
{process.env.NODE_ENV === 'development' && error?.stack && (
|
||||
<Code
|
||||
p="md"
|
||||
style={{
|
||||
backgroundColor: 'var(--theme-colors-surface)',
|
||||
fontFamily: 'monospace',
|
||||
maxHeight: '300px',
|
||||
overflow: 'auto',
|
||||
wordBreak: 'break-word',
|
||||
}}
|
||||
>
|
||||
{error.stack}
|
||||
</Code>
|
||||
)}
|
||||
<Group grow>
|
||||
<Button onClick={resetErrorBoundary} size="md" variant="default">
|
||||
{t('common.reload', { postProcess: 'sentenceCase' })}
|
||||
</Button>
|
||||
<Button onClick={handleRefresh} size="md" variant="filled">
|
||||
{t('common.refresh', { postProcess: 'sentenceCase' })}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Center>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
interface PageErrorBoundaryProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const PageErrorBoundary = ({ children }: PageErrorBoundaryProps) => {
|
||||
return (
|
||||
<ErrorBoundary
|
||||
FallbackComponent={PageErrorFallback}
|
||||
onError={(error, errorInfo) => {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.error('Page error boundary caught an error:', error, errorInfo);
|
||||
}
|
||||
}}
|
||||
onReset={() => {}}
|
||||
>
|
||||
{children}
|
||||
</ErrorBoundary>
|
||||
);
|
||||
};
|
||||
+11
-11
@@ -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
|
||||
>
|
||||
<ServerSelector />
|
||||
</Box>
|
||||
<Center style={{ height: '100vh' }}>
|
||||
<Stack style={{ maxWidth: '50%' }}>
|
||||
<Center h="100vh" p="md" w="100%">
|
||||
<Stack maw="800px">
|
||||
<Group gap="xs">
|
||||
<Icon fill="error" icon="error" size="lg" />
|
||||
<Text size="lg">
|
||||
<TextTitle fw={700} order={3}>
|
||||
{t('error.genericError', { postProcess: 'sentenceCase' })}
|
||||
</Text>
|
||||
</TextTitle>
|
||||
</Group>
|
||||
<Text size="sm" style={{ wordBreak: 'break-word' }}>
|
||||
<Text style={{ wordBreak: 'break-word' }}>
|
||||
{error?.message || t('error.genericError', { postProcess: 'sentenceCase' })}
|
||||
</Text>
|
||||
{process.env.NODE_ENV === 'development' && error?.stack && (
|
||||
<Text
|
||||
size="xs"
|
||||
<Code
|
||||
p="md"
|
||||
style={{
|
||||
backgroundColor: 'var(--theme-colors-error)',
|
||||
color: 'var(--theme-colors-errorText)',
|
||||
backgroundColor: 'var(--theme-colors-surface)',
|
||||
fontFamily: 'monospace',
|
||||
maxHeight: '300px',
|
||||
overflow: 'auto',
|
||||
padding: '10px',
|
||||
wordBreak: 'break-word',
|
||||
}}
|
||||
>
|
||||
{error.stack}
|
||||
</Text>
|
||||
</Code>
|
||||
)}
|
||||
<Group grow>
|
||||
<Button onClick={resetErrorBoundary} size="md" variant="default">
|
||||
@@ -6,12 +6,13 @@ import { ListContext } from '/@/renderer/context/list-context';
|
||||
import { genresQueries } from '/@/renderer/features/genres/api/genres-api';
|
||||
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 { SongListContent } from '/@/renderer/features/songs/components/song-list-content';
|
||||
import { SongListHeader } from '/@/renderer/features/songs/components/song-list-header';
|
||||
import { useCurrentServer } from '/@/renderer/store';
|
||||
import { GenreListSort, SortOrder } from '/@/shared/types/domain-types';
|
||||
|
||||
const TrackListRoute = () => {
|
||||
const SongListRoute = () => {
|
||||
const server = useCurrentServer();
|
||||
const [searchParams] = useSearchParams();
|
||||
const { albumArtistId, genreId } = useParams();
|
||||
@@ -83,4 +84,12 @@ const TrackListRoute = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default TrackListRoute;
|
||||
const SongListRouteWithBoundary = () => {
|
||||
return (
|
||||
<PageErrorBoundary>
|
||||
<SongListRoute />
|
||||
</PageErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default SongListRouteWithBoundary;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { HashRouter, Route, Routes } from 'react-router';
|
||||
|
||||
import { RouterErrorBoundary } from '/@/renderer/components/error-boundary/router-error-boundary';
|
||||
import { AddToPlaylistContextModal } from '/@/renderer/features/playlists/components/add-to-playlist-context-modal';
|
||||
import { SettingsModal } from '/@/renderer/features/settings/components/settings-modal';
|
||||
import { RouterErrorBoundary } from '/@/renderer/features/shared/components/router-error-boundary';
|
||||
import { ShareItemContextModal } from '/@/renderer/features/sharing/components/share-item-context-modal';
|
||||
import { ResponsiveLayout } from '/@/renderer/layouts/responsive-layout';
|
||||
import { AppOutlet } from '/@/renderer/router/app-outlet';
|
||||
@@ -65,52 +65,31 @@ const GenreListRoute = lazy(() => import('/@/renderer/features/genres/routes/gen
|
||||
|
||||
const SearchRoute = lazy(() => import('/@/renderer/features/search/routes/search-route'));
|
||||
|
||||
const RouteErrorBoundary = lazy(
|
||||
() => import('/@/renderer/features/action-required/components/route-error-boundary'),
|
||||
);
|
||||
|
||||
export const AppRouter = () => {
|
||||
const router = (
|
||||
<HashRouter>
|
||||
<RouterErrorBoundary>
|
||||
<ModalsProvider
|
||||
modals={{
|
||||
addToPlaylist: AddToPlaylistContextModal,
|
||||
base: BaseContextModal,
|
||||
settings: SettingsModal,
|
||||
shareItem: ShareItemContextModal,
|
||||
}}
|
||||
>
|
||||
<ModalsProvider
|
||||
modals={{
|
||||
addToPlaylist: AddToPlaylistContextModal,
|
||||
base: BaseContextModal,
|
||||
settings: SettingsModal,
|
||||
shareItem: ShareItemContextModal,
|
||||
}}
|
||||
>
|
||||
<RouterErrorBoundary>
|
||||
<Routes>
|
||||
<Route element={<TitlebarOutlet />}>
|
||||
<Route element={<AppOutlet />} errorElement={<RouteErrorBoundary />}>
|
||||
<Route element={<AppOutlet />}>
|
||||
<Route element={<ResponsiveLayout />}>
|
||||
<Route
|
||||
element={<HomeRoute />}
|
||||
errorElement={<RouteErrorBoundary />}
|
||||
index
|
||||
/>
|
||||
<Route
|
||||
element={<HomeRoute />}
|
||||
errorElement={<RouteErrorBoundary />}
|
||||
path={AppRoute.HOME}
|
||||
/>
|
||||
<Route
|
||||
element={<SearchRoute />}
|
||||
errorElement={<RouteErrorBoundary />}
|
||||
path={AppRoute.SEARCH}
|
||||
/>
|
||||
<Route element={<HomeRoute />} index />
|
||||
<Route element={<HomeRoute />} path={AppRoute.HOME} />
|
||||
<Route element={<SearchRoute />} path={AppRoute.SEARCH} />
|
||||
<Route
|
||||
element={<NowPlayingRoute />}
|
||||
errorElement={<RouteErrorBoundary />}
|
||||
path={AppRoute.NOW_PLAYING}
|
||||
/>
|
||||
<Route path={AppRoute.LIBRARY_GENRES}>
|
||||
<Route
|
||||
element={<GenreListRoute />}
|
||||
errorElement={<RouteErrorBoundary />}
|
||||
index
|
||||
/>
|
||||
<Route element={<GenreListRoute />} index />
|
||||
<Route
|
||||
element={<AlbumListRoute />}
|
||||
path={AppRoute.LIBRARY_GENRES_ALBUMS}
|
||||
@@ -122,17 +101,14 @@ export const AppRouter = () => {
|
||||
</Route>
|
||||
<Route
|
||||
element={<AlbumListRoute />}
|
||||
errorElement={<RouteErrorBoundary />}
|
||||
path={AppRoute.LIBRARY_ALBUMS}
|
||||
/>
|
||||
<Route
|
||||
element={<AlbumDetailRoute />}
|
||||
errorElement={<RouteErrorBoundary />}
|
||||
path={AppRoute.LIBRARY_ALBUMS_DETAIL}
|
||||
/>
|
||||
<Route
|
||||
element={<ArtistListRoute />}
|
||||
errorElement={<RouteErrorBoundary />}
|
||||
path={AppRoute.LIBRARY_ARTISTS}
|
||||
/>
|
||||
<Route path={AppRoute.LIBRARY_ARTISTS_DETAIL}>
|
||||
@@ -152,28 +128,21 @@ export const AppRouter = () => {
|
||||
</Route>
|
||||
<Route
|
||||
element={<DummyAlbumDetailRoute />}
|
||||
errorElement={<RouteErrorBoundary />}
|
||||
path={AppRoute.FAKE_LIBRARY_ALBUM_DETAILS}
|
||||
/>
|
||||
<Route
|
||||
element={<SongListRoute />}
|
||||
errorElement={<RouteErrorBoundary />}
|
||||
path={AppRoute.LIBRARY_SONGS}
|
||||
/>
|
||||
<Route
|
||||
element={<PlaylistListRoute />}
|
||||
errorElement={<RouteErrorBoundary />}
|
||||
path={AppRoute.PLAYLISTS}
|
||||
/>
|
||||
<Route
|
||||
element={<PlaylistDetailSongListRoute />}
|
||||
errorElement={<RouteErrorBoundary />}
|
||||
path={AppRoute.PLAYLISTS_DETAIL_SONGS}
|
||||
/>
|
||||
<Route
|
||||
errorElement={<RouteErrorBoundary />}
|
||||
path={AppRoute.LIBRARY_ALBUM_ARTISTS}
|
||||
>
|
||||
<Route path={AppRoute.LIBRARY_ALBUM_ARTISTS}>
|
||||
<Route element={<AlbumArtistListRoute />} index />
|
||||
<Route path={AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL}>
|
||||
<Route element={<AlbumArtistDetailRoute />} index />
|
||||
@@ -209,8 +178,8 @@ export const AppRouter = () => {
|
||||
</Route>
|
||||
</Route>
|
||||
</Routes>
|
||||
</ModalsProvider>
|
||||
</RouterErrorBoundary>
|
||||
</RouterErrorBoundary>
|
||||
</ModalsProvider>
|
||||
</HashRouter>
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user