From 3b1ed1ace2bded0c4aefb0d65a2949f709e2a319 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Tue, 22 Apr 2025 17:23:06 -0400 Subject: [PATCH] Update server update required handling --- src/components/ConnectionErrorPage.tsx | 45 ++++++++++++++++++++++++++ src/components/ConnectionRequired.tsx | 42 +++++++++++------------- src/strings/en-us.json | 1 + 3 files changed, 65 insertions(+), 23 deletions(-) create mode 100644 src/components/ConnectionErrorPage.tsx diff --git a/src/components/ConnectionErrorPage.tsx b/src/components/ConnectionErrorPage.tsx new file mode 100644 index 000000000..2b7278eca --- /dev/null +++ b/src/components/ConnectionErrorPage.tsx @@ -0,0 +1,45 @@ +import React, { FC, useEffect, useState } from 'react'; + +import Page from 'components/Page'; +import globalize from 'lib/globalize'; +import { ConnectionState } from 'lib/jellyfin-apiclient'; + +interface ConnectionErrorPageProps { + state: ConnectionState +} + +const ConnectionErrorPage: FC = ({ + state +}) => { + const [ title, setTitle ] = useState(); + const [ htmlMessage, setHtmlMessage ] = useState(); + + useEffect(() => { + if (state === ConnectionState.ServerUpdateNeeded) { + setTitle(globalize.translate('HeaderUpdateRequired')); + setHtmlMessage(globalize.translate( + 'ServerUpdateNeeded', + 'jellyfin.org/downloads/server' + )); + } + }, [ state ]); + + if (!title) return; + + return ( + +
+

{title}

+ {htmlMessage && ( +

+ )} +

+
+ ); +}; + +export default ConnectionErrorPage; diff --git a/src/components/ConnectionRequired.tsx b/src/components/ConnectionRequired.tsx index e84e86de4..290a6c8d5 100644 --- a/src/components/ConnectionRequired.tsx +++ b/src/components/ConnectionRequired.tsx @@ -2,10 +2,9 @@ import React, { FunctionComponent, useCallback, useEffect, useState } from 'reac import { Outlet, useLocation, useNavigate } from 'react-router-dom'; import type { ApiClient, ConnectResponse } from 'jellyfin-apiclient'; -import globalize from 'lib/globalize'; import { ConnectionState, ServerConnections } from 'lib/jellyfin-apiclient'; -import alert from './alert'; +import ConnectionErrorPage from './ConnectionErrorPage'; import Loading from './loading/LoadingComponent'; enum AccessLevel { @@ -56,8 +55,16 @@ const ConnectionRequired: FunctionComponent = ({ const navigate = useNavigate(); const location = useLocation(); + const [ errorState, setErrorState ] = useState(); const [ isLoading, setIsLoading ] = useState(true); + const navigateIfNotThere = useCallback((route: BounceRoutes) => { + // If we try to navigate to the current route, just set isLoading = false + if (location.pathname === route) setIsLoading(false); + // Otherwise navigate to the route + else navigate(route); + }, [ location.pathname, navigate ]); + const bounce = useCallback(async (connectionResponse: ConnectResponse) => { switch (connectionResponse.State) { case ConnectionState.SignedIn: @@ -77,31 +84,14 @@ const ConnectionRequired: FunctionComponent = ({ return; case ConnectionState.ServerSelection: // Bounce to select server page - if (location.pathname === BounceRoutes.SelectServer) { - setIsLoading(false); - } else { - console.debug('[ConnectionRequired] redirecting to select server page'); - navigate(BounceRoutes.SelectServer); - } - return; - case ConnectionState.ServerUpdateNeeded: - // Show update needed message and bounce to select server page - try { - await alert({ - text: globalize.translate('ServerUpdateNeeded', 'https://github.com/jellyfin/jellyfin'), - html: globalize.translate('ServerUpdateNeeded', 'https://github.com/jellyfin/jellyfin') - }); - } catch (ex) { - console.warn('[ConnectionRequired] failed to show alert', ex); - } - console.debug('[ConnectionRequired] server update required, redirecting to select server page'); - navigate(BounceRoutes.SelectServer); + console.debug('[ConnectionRequired] redirecting to select server page'); + navigateIfNotThere(BounceRoutes.SelectServer); return; } console.warn('[ConnectionRequired] unhandled connection state', connectionResponse.State); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [location.pathname, navigate]); + }, [ navigateIfNotThere, location.pathname, navigate ]); const handleWizard = useCallback(async (firstConnection: ConnectResponse | null) => { const apiClient = firstConnection?.ApiClient || ServerConnections.currentApiClient(); @@ -194,7 +184,9 @@ const ConnectionRequired: FunctionComponent = ({ console.debug('[ConnectionRequired] connection state', firstConnection?.State); ServerConnections.firstConnection = true; - if (level === AccessLevel.Wizard) { + if (firstConnection?.State === ConnectionState.ServerUpdateNeeded) { + setErrorState(firstConnection.State); + } else if (level === AccessLevel.Wizard) { handleWizard(firstConnection) .catch(err => { console.error('[ConnectionRequired] could not validate wizard status', err); @@ -217,6 +209,10 @@ const ConnectionRequired: FunctionComponent = ({ }); }, [handleIncompleteWizard, handleWizard, level, validateUserAccess]); + if (errorState) { + return ; + } + if (isLoading) { return ; } diff --git a/src/strings/en-us.json b/src/strings/en-us.json index 9f6e69f1c..ab5b5f05c 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -538,6 +538,7 @@ "HeaderTypeText": "Enter Text", "HeaderUninstallPlugin": "Uninstall Plugin", "HeaderUpcomingOnTV": "Upcoming On TV", + "HeaderUpdateRequired": "Update Required", "HeaderUploadImage": "Upload Image", "HeaderUploadLyrics": "Upload Lyrics", "HeaderUploadSubtitle": "Upload Subtitle",