From 6f670d5c3ef29e0c950fc02ec4aa34ae5426a4b8 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Wed, 12 Mar 2025 17:16:11 -0400 Subject: [PATCH 1/3] Add basic 404 page and *.html redirect --- src/apps/experimental/routes/routes.tsx | 9 +++- src/apps/stable/routes/routes.tsx | 9 +++- src/components/router/FallbackRoute.tsx | 57 +++++++++++++++++++++++++ src/strings/en-us.json | 2 + 4 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 src/components/router/FallbackRoute.tsx diff --git a/src/apps/experimental/routes/routes.tsx b/src/apps/experimental/routes/routes.tsx index 046f447d65..57161a9891 100644 --- a/src/apps/experimental/routes/routes.tsx +++ b/src/apps/experimental/routes/routes.tsx @@ -5,6 +5,7 @@ import ConnectionRequired from 'components/ConnectionRequired'; import { toAsyncPageRoute } from 'components/router/AsyncRoute'; import { toViewManagerPageRoute } from 'components/router/LegacyRoute'; import ErrorBoundary from 'components/router/ErrorBoundary'; +import FallbackRoute from 'components/router/FallbackRoute'; import { ASYNC_USER_ROUTES } from './asyncRoutes'; import { LEGACY_PUBLIC_ROUTES, LEGACY_USER_ROUTES } from './legacyRoutes'; @@ -33,7 +34,13 @@ export const EXPERIMENTAL_APP_ROUTES: RouteObject[] = [ /* Public routes */ { index: true, element: }, - ...LEGACY_PUBLIC_ROUTES.map(toViewManagerPageRoute) + ...LEGACY_PUBLIC_ROUTES.map(toViewManagerPageRoute), + + /* Fallback route for invalid paths */ + { + path: '*', + Component: FallbackRoute + } ] } ]; diff --git a/src/apps/stable/routes/routes.tsx b/src/apps/stable/routes/routes.tsx index 787d89ac7f..08bd56634e 100644 --- a/src/apps/stable/routes/routes.tsx +++ b/src/apps/stable/routes/routes.tsx @@ -5,6 +5,7 @@ import ConnectionRequired from 'components/ConnectionRequired'; import { toAsyncPageRoute } from 'components/router/AsyncRoute'; import { toViewManagerPageRoute } from 'components/router/LegacyRoute'; import ErrorBoundary from 'components/router/ErrorBoundary'; +import FallbackRoute from 'components/router/FallbackRoute'; import AppLayout from '../AppLayout'; @@ -28,7 +29,13 @@ export const STABLE_APP_ROUTES: RouteObject[] = [ /* Public routes */ { index: true, element: }, - ...LEGACY_PUBLIC_ROUTES.map(toViewManagerPageRoute) + ...LEGACY_PUBLIC_ROUTES.map(toViewManagerPageRoute), + + /* Fallback route for invalid paths */ + { + path: '*', + Component: FallbackRoute + } ] } ]; diff --git a/src/components/router/FallbackRoute.tsx b/src/components/router/FallbackRoute.tsx new file mode 100644 index 0000000000..f438d730ad --- /dev/null +++ b/src/components/router/FallbackRoute.tsx @@ -0,0 +1,57 @@ +import React, { useEffect, useMemo } from 'react'; +import { Navigate, useLocation } from 'react-router-dom'; + +import loading from 'components/loading/loading'; +import Page from 'components/Page'; +import globalize from 'lib/globalize'; +import LinkButton from 'elements/emby-button/LinkButton'; + +const FallbackRoute = () => { + const location = useLocation(); + + useEffect(() => { + loading.hide(); + }, []); + + // Check if the requested path should be redirected + const to = useMemo(() => { + const _to = { + search: location.search, + hash: location.hash + }; + + // If a path ends in ".html", redirect to the path with it removed + if (location.pathname.endsWith('.html')) { + return { ..._to, pathname: location.pathname.slice(0, -5) }; + } + }, [ location ]); + + if (to) { + console.warn('[FallbackRoute] You are using a deprecated URL format. This will stop working in a future Jellyfin update.'); + + return ( + + ); + } + + return ( + +

{globalize.translate('HeaderPageNotFound')}

+

{globalize.translate('PageNotFound')}

+ + {globalize.translate('GoHome')} + +
+ ); +}; + +export default FallbackRoute; diff --git a/src/strings/en-us.json b/src/strings/en-us.json index 71de76a2ab..bb5f9cf80a 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -473,6 +473,7 @@ "HeaderNoLyrics": "No lyrics found", "HeaderOnNow": "On Now", "HeaderOtherItems": "Other Items", + "HeaderPageNotFound": "Page not found", "HeaderParentalRatings": "Parental Ratings", "HeaderPassword": "Password", "HeaderPasswordReset": "Password Reset", @@ -1315,6 +1316,7 @@ "PackageInstallCancelled": "{0} (version {1}) installation cancelled.", "PackageInstallCompleted": "{0} (version {1}) installation completed.", "PackageInstallFailed": "{0} (version {1}) installation failed.", + "PageNotFound": "This is not the page you are looking for.", "ParentalRating": "Parental rating", "PasswordMatchError": "Password and password confirmation must match.", "PasswordRequiredForAdmin": "A password is required for admin accounts.", From 9c16515549ccf5ac42366faaa962c470c6a0d595 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Thu, 13 Mar 2025 11:21:50 -0400 Subject: [PATCH 2/3] Fix ConnectionRequired missing for public routes --- src/apps/experimental/AppOverrides.scss | 4 ++++ src/apps/experimental/routes/routes.tsx | 27 ++++++++++++++++--------- src/apps/stable/routes/routes.tsx | 22 ++++++++++++-------- src/components/router/FallbackRoute.tsx | 25 ++++++++++------------- 4 files changed, 46 insertions(+), 32 deletions(-) diff --git a/src/apps/experimental/AppOverrides.scss b/src/apps/experimental/AppOverrides.scss index fa586829e9..f6ea674428 100644 --- a/src/apps/experimental/AppOverrides.scss +++ b/src/apps/experimental/AppOverrides.scss @@ -17,6 +17,10 @@ $drawer-width: 240px; left: $drawer-width; } } +// The fallback page has no drawer +#fallbackPage { + left: 0; +} // Hide some items from the user "settings" page that are in the drawer #myPreferencesMenuPage { diff --git a/src/apps/experimental/routes/routes.tsx b/src/apps/experimental/routes/routes.tsx index 57161a9891..d0c5deab09 100644 --- a/src/apps/experimental/routes/routes.tsx +++ b/src/apps/experimental/routes/routes.tsx @@ -16,9 +16,11 @@ export const EXPERIMENTAL_APP_ROUTES: RouteObject[] = [ path: '/*', lazy: () => import('../AppLayout'), children: [ + { index: true, element: }, + { - /* User routes: Any child route of this layout is authenticated */ - element: , + /* User routes */ + Component: ConnectionRequired, children: [ ...ASYNC_USER_ROUTES.map(toAsyncPageRoute), ...LEGACY_USER_ROUTES.map(toViewManagerPageRoute), @@ -26,21 +28,26 @@ export const EXPERIMENTAL_APP_ROUTES: RouteObject[] = [ // The video page is special since it combines new controls with the legacy view { path: 'video', - element: + Component: VideoPage } ], ErrorBoundary }, - /* Public routes */ - { index: true, element: }, - ...LEGACY_PUBLIC_ROUTES.map(toViewManagerPageRoute), - - /* Fallback route for invalid paths */ { - path: '*', - Component: FallbackRoute + /* Public routes */ + element: , + children: [ + ...LEGACY_PUBLIC_ROUTES.map(toViewManagerPageRoute), + + /* Fallback route for invalid paths */ + { + path: '*', + Component: FallbackRoute + } + ] } + ] } ]; diff --git a/src/apps/stable/routes/routes.tsx b/src/apps/stable/routes/routes.tsx index 08bd56634e..f0e2718b27 100644 --- a/src/apps/stable/routes/routes.tsx +++ b/src/apps/stable/routes/routes.tsx @@ -17,9 +17,11 @@ export const STABLE_APP_ROUTES: RouteObject[] = [ path: '/*', Component: AppLayout, children: [ + { index: true, element: }, + { /* User routes */ - element: , + Component: ConnectionRequired, children: [ ...ASYNC_USER_ROUTES.map(toAsyncPageRoute), ...LEGACY_USER_ROUTES.map(toViewManagerPageRoute) @@ -27,15 +29,19 @@ export const STABLE_APP_ROUTES: RouteObject[] = [ ErrorBoundary }, - /* Public routes */ - { index: true, element: }, - ...LEGACY_PUBLIC_ROUTES.map(toViewManagerPageRoute), - - /* Fallback route for invalid paths */ { - path: '*', - Component: FallbackRoute + /* Public routes */ + element: , + children: [ + ...LEGACY_PUBLIC_ROUTES.map(toViewManagerPageRoute), + /* Fallback route for invalid paths */ + { + path: '*', + Component: FallbackRoute + } + ] } + ] } ]; diff --git a/src/components/router/FallbackRoute.tsx b/src/components/router/FallbackRoute.tsx index f438d730ad..24df3bb8d3 100644 --- a/src/components/router/FallbackRoute.tsx +++ b/src/components/router/FallbackRoute.tsx @@ -1,7 +1,6 @@ -import React, { useEffect, useMemo } from 'react'; +import React, { useMemo } from 'react'; import { Navigate, useLocation } from 'react-router-dom'; -import loading from 'components/loading/loading'; import Page from 'components/Page'; import globalize from 'lib/globalize'; import LinkButton from 'elements/emby-button/LinkButton'; @@ -9,10 +8,6 @@ import LinkButton from 'elements/emby-button/LinkButton'; const FallbackRoute = () => { const location = useLocation(); - useEffect(() => { - loading.hide(); - }, []); - // Check if the requested path should be redirected const to = useMemo(() => { const _to = { @@ -42,14 +37,16 @@ const FallbackRoute = () => { id='fallbackPage' className='mainAnimatedPage libraryPage' > -

{globalize.translate('HeaderPageNotFound')}

-

{globalize.translate('PageNotFound')}

- - {globalize.translate('GoHome')} - +
+

{globalize.translate('HeaderPageNotFound')}

+

{globalize.translate('PageNotFound')}

+ + {globalize.translate('GoHome')} + +
); }; From 6d77e1b4375bcf347f6baa71ff6d8726da76892b Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Thu, 13 Mar 2025 12:09:58 -0400 Subject: [PATCH 3/3] Add title to FallbackPage Co-authored-by: viown <48097677+viown@users.noreply.github.com> --- src/components/router/FallbackRoute.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/router/FallbackRoute.tsx b/src/components/router/FallbackRoute.tsx index 24df3bb8d3..ec91faa313 100644 --- a/src/components/router/FallbackRoute.tsx +++ b/src/components/router/FallbackRoute.tsx @@ -35,6 +35,7 @@ const FallbackRoute = () => { return (