Require server settings and global metadata to be loaded

This commit is contained in:
schroda
2026-04-27 15:38:49 +02:00
parent a7dcdd5651
commit 9440326b72
2 changed files with 75 additions and 15 deletions

View File

@@ -7,7 +7,8 @@
*/ */
import CssBaseline from '@mui/material/CssBaseline'; import CssBaseline from '@mui/material/CssBaseline';
import React, { useEffect, useLayoutEffect } from 'react'; import type { PropsWithChildren } from 'react';
import React, { useEffect, useLayoutEffect, useState } from 'react';
import { Navigate, Outlet, Route, Routes, useLocation, useNavigate } from 'react-router-dom'; import { Navigate, Outlet, Route, Routes, useLocation, useNavigate } from 'react-router-dom';
import { loadErrorMessages, loadDevMessages } from '@apollo/client/dev'; import { loadErrorMessages, loadDevMessages } from '@apollo/client/dev';
import { loadable } from 'react-lazily/loadable'; import { loadable } from 'react-lazily/loadable';
@@ -34,6 +35,8 @@ import { AuthManager } from '@/features/authentication/AuthManager.ts';
import { ImageProcessingType } from '@/features/settings/Settings.types.ts'; import { ImageProcessingType } from '@/features/settings/Settings.types.ts';
import { MigrationFABIndicator } from '@/features/migration/components/MigrationFABIndicator.tsx'; import { MigrationFABIndicator } from '@/features/migration/components/MigrationFABIndicator.tsx';
import { MigrationManager } from '@/features/migration/MigrationManager.ts'; import { MigrationManager } from '@/features/migration/MigrationManager.ts';
import { SplashScreen } from '@/features/authentication/components/SplashScreen.tsx';
import { d } from 'koration';
const { Browse } = loadable(() => import('@/features/browse/screens/Browse.tsx'), lazyLoadFallback); const { Browse } = loadable(() => import('@/features/browse/screens/Browse.tsx'), lazyLoadFallback);
const { DownloadQueue } = loadable(() => import('@/features/downloads/screens/DownloadQueue.tsx'), lazyLoadFallback); const { DownloadQueue } = loadable(() => import('@/features/downloads/screens/DownloadQueue.tsx'), lazyLoadFallback);
@@ -114,6 +117,55 @@ const ScrollToTop = () => {
return null; return null;
}; };
const InitializeGuard = ({ children }: PropsWithChildren) => {
const [isInitialized, setIsInitialized] = useState(false);
useEffect(() => {
type RequestConfig = [string, () => Promise<unknown>][];
type InFlightRequest = [string, Promise<unknown>];
const initialRequests: RequestConfig = [
['globalMeta', () => requestManager.getGlobalMeta().response],
['serverSettings', () => requestManager.getServerSettings().response],
];
const executeRequests = async (requests: RequestConfig, timeout: number = d(5).seconds.inWholeMilliseconds) => {
const runningRequests = requests.map(([key, fn]) => [key, fn()] satisfies InFlightRequest);
const failedRequests = runningRequests.filter(async ([_, request]) => {
try {
await request;
return false;
} catch (e) {
return true;
}
});
if (failedRequests.length) {
await new Promise((resolve) => {
setTimeout(resolve, timeout);
});
return executeRequests(
requests.filter(([key]) => !failedRequests.some(([k]) => k === key)),
(timeout * 1.5) % d(2).minutes.inWholeMilliseconds,
);
}
};
executeRequests(initialRequests).catch(defaultPromiseErrorHandler('InitializeGuard'));
setIsInitialized(true);
}, []);
if (isInitialized) {
return children;
}
return <SplashScreen />;
};
const InitialBackgroundRequests = () => { const InitialBackgroundRequests = () => {
// Load the full download status once on startup to fill the cache // Load the full download status once on startup to fill the cache
requestManager.useGetDownloadStatus({ nextFetchPolicy: 'standby' }); requestManager.useGetDownloadStatus({ nextFetchPolicy: 'standby' });
@@ -349,22 +401,24 @@ export const App: React.FC = () => (
<CssBaseline enableColorScheme /> <CssBaseline enableColorScheme />
<AuthGuard> <AuthGuard>
<ServerUpdateChecker /> <InitializeGuard>
<WebUIUpdateChecker /> <ServerUpdateChecker />
<InitialBackgroundRequests /> <WebUIUpdateChecker />
<BackgroundSubscriptions /> <InitialBackgroundRequests />
<ResumeMigration /> <BackgroundSubscriptions />
<ResumeMigration />
<Box sx={{ display: 'flex' }}> <Box sx={{ display: 'flex' }}>
<Box sx={{ flexShrink: 0, position: 'relative', height: '100vh' }}> <Box sx={{ flexShrink: 0, position: 'relative', height: '100vh' }}>
<DefaultNavBar /> <DefaultNavBar />
</Box>
<Routes>
<Route path={AppRoutes.matchAll.match} element={<MainApp />} />
<Route path={AppRoutes.reader.match} element={<ReaderApp />} />
</Routes>
</Box> </Box>
<Routes> <MigrationFABIndicator />
<Route path={AppRoutes.matchAll.match} element={<MainApp />} /> </InitializeGuard>
<Route path={AppRoutes.reader.match} element={<ReaderApp />} />
</Routes>
</Box>
<MigrationFABIndicator />
</AuthGuard> </AuthGuard>
</AppContext> </AppContext>
); );

View File

@@ -3562,6 +3562,12 @@ export class RequestManager {
) as useSubscription.Result<UpdaterSubscription>; ) as useSubscription.Result<UpdaterSubscription>;
} }
public getServerSettings(
options?: QueryOptions<GetServerSettingsQueryVariables>,
): AbortabaleApolloQueryResponse<GetServerSettingsQuery> {
return this.doRequest(GQLMethod.QUERY, GET_SERVER_SETTINGS, undefined, options);
}
public useGetServerSettings( public useGetServerSettings(
options?: QueryHookOptions<GetServerSettingsQuery, GetServerSettingsQueryVariables>, options?: QueryHookOptions<GetServerSettingsQuery, GetServerSettingsQueryVariables>,
): AbortableApolloUseQueryResponse<GetServerSettingsQuery, GetServerSettingsQueryVariables> { ): AbortableApolloUseQueryResponse<GetServerSettingsQuery, GetServerSettingsQueryVariables> {