diff --git a/src/common/providers/ProviderBackendCapabilities.tsx b/src/common/providers/ProviderBackendCapabilities.tsx index c9ce72347..aab84d7ec 100644 --- a/src/common/providers/ProviderBackendCapabilities.tsx +++ b/src/common/providers/ProviderBackendCapabilities.tsx @@ -2,12 +2,70 @@ import * as React from 'react'; import { useKnowledgeOfBackendCaps } from '~/modules/backend/store-backend-capabilities'; +import { Release } from '~/common/app.release'; import { apiQuery } from '~/common/util/trpc.client'; import { preloadTiktokenLibrary } from '~/common/tokens/tokens.text'; +import { themeFontFamilyCss } from '~/common/app.theme'; -// configuration -const BACKEND_WARNING_TIMEOUT = 5000; +// Configuration +const BACKEND_WARNING_TIMEOUT = 6000; + + +// Styles, all manual without depending on Emotion/Joy UI +const styles: Record = { + container: { + // looks + background: 'var(--joy-palette-background-level1)', + color: 'var(--joy-palette-text-primary)', + fontFamily: themeFontFamilyCss, + minHeight: '100vh', + // layout + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + }, + content: { + display: 'flex', + alignItems: 'center', + }, + content2: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + gap: '0.25rem', + }, + leftbox: { + borderRight: `1px solid var(--joy-palette-neutral-400)`, + margin: '0 1.5rem 0 0', + padding: '0 1.5rem 0 0', + }, + heading: { + margin: 0, + fontSize: '1.5rem', + fontWeight: 500, + }, + version: { + fontSize: '12px', + }, + message: { + margin: 0, + fontSize: '14px', + fontWeight: 400, + }, + button: { + color: 'var(--joy-palette-neutral-solidColor, #fff)', + background: 'var(--joy-palette-neutral-solidBg, #000)', + padding: '0.75rem 1.25rem', + fontSize: '14px', + fontWeight: 500, + border: 'none', + borderRadius: '4px', + cursor: 'pointer', + transition: 'opacity 0.2s ease', + }, +}; /** @@ -18,6 +76,7 @@ export function ProviderBackendCapabilities(props: { children: React.ReactNode } // state const [backendTimeout, setBackendTimeout] = React.useState(false); + const [versionVerified, setVersionVerified] = React.useState(null); // external state const [haveCapabilities, storeBackendCapabilities] = useKnowledgeOfBackendCaps(); @@ -31,10 +90,17 @@ export function ProviderBackendCapabilities(props: { children: React.ReactNode } // [effect] copy from the backend capabilities payload to the frontend state store React.useEffect(() => { - if (data) + if (data) { storeBackendCapabilities(data); + + // Compare client and server build info + const clientBuildInfo = Release.buildInfo('frontend'); + const serverBuildInfo = data.build || {}; + setVersionVerified(clientBuildInfo.gitSha === serverBuildInfo.gitSha && clientBuildInfo.pkgVersion === serverBuildInfo.pkgVersion); + } }, [data, storeBackendCapabilities]); + // [effect] warn if the backend is not available React.useEffect(() => { if (!haveCapabilities) { @@ -51,8 +117,66 @@ export function ProviderBackendCapabilities(props: { children: React.ReactNode } }, [haveCapabilities]); - // create components after the capabilities are loaded - return haveCapabilities ? props.children - : backendTimeout ?
Cannot esablish a connection with the application server.
- : null; + // + // Rendering Gates + // + + // Version mismatch notice + if (versionVerified === false) { + return ( +
+
+
+

Updated

+ {/*
*/} + {/* version. {Release.buildInfo('frontend').pkgVersion}*/} + {/*
*/} +
+ +
+
+ ); + } + + // Backend timeout notice + if (backendTimeout && versionVerified !== true) { + return ( +
+
+

Connection Error

+

Unable to connect to the server.

+ +
+
+ ); + } + + // Wait for the backend to respond + if (versionVerified === null) { + return null; + // return ( + //
+ //

+ // Loading application... + //

+ //
+ // ); + } + + // Render the children when ready + return versionVerified ? props.children : null; } \ No newline at end of file