diff --git a/src/App.tsx b/src/App.tsx index 39c4b830..f98754da 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,7 +7,8 @@ */ 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 { loadErrorMessages, loadDevMessages } from '@apollo/client/dev'; 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 { MigrationFABIndicator } from '@/features/migration/components/MigrationFABIndicator.tsx'; 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 { DownloadQueue } = loadable(() => import('@/features/downloads/screens/DownloadQueue.tsx'), lazyLoadFallback); @@ -114,6 +117,55 @@ const ScrollToTop = () => { return null; }; +const InitializeGuard = ({ children }: PropsWithChildren) => { + const [isInitialized, setIsInitialized] = useState(false); + + useEffect(() => { + type RequestConfig = [string, () => Promise][]; + type InFlightRequest = [string, Promise]; + + 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 ; +}; + const InitialBackgroundRequests = () => { // Load the full download status once on startup to fill the cache requestManager.useGetDownloadStatus({ nextFetchPolicy: 'standby' }); @@ -349,22 +401,24 @@ export const App: React.FC = () => ( - - - - - + + + + + + - - - + + + + + + } /> + } /> + - - } /> - } /> - - - + + ); diff --git a/src/lib/requests/RequestManager.ts b/src/lib/requests/RequestManager.ts index 58f8d9b7..2443c89d 100644 --- a/src/lib/requests/RequestManager.ts +++ b/src/lib/requests/RequestManager.ts @@ -3562,6 +3562,12 @@ export class RequestManager { ) as useSubscription.Result; } + public getServerSettings( + options?: QueryOptions, + ): AbortabaleApolloQueryResponse { + return this.doRequest(GQLMethod.QUERY, GET_SERVER_SETTINGS, undefined, options); + } + public useGetServerSettings( options?: QueryHookOptions, ): AbortableApolloUseQueryResponse {