|
1 | 1 | import 'regenerator-runtime/runtime'; |
2 | 2 |
|
3 | 3 | import mountReactComponents from '../mountReactComponents'; |
4 | | -import { StrictMode, Suspense, use, useMemo } from 'react'; |
| 4 | +import { StrictMode, Suspense, use, useLayoutEffect, useMemo, useState } from 'react'; |
5 | 5 | import AuthenticityTokensManager, { getAuthenticityTokensURL } from 'AuthenticityTokensContext'; |
6 | 6 | import { createBrowserRouter, RouterContextProvider, RouterProvider } from 'react-router'; |
7 | 7 | import { buildBrowserApolloClient, GraphQLNotAuthenticatedErrorEvent } from 'useIntercodeApolloClient'; |
@@ -70,6 +70,35 @@ const bootstrapPromise: Promise<Bootstrap> = (async () => { |
70 | 70 | return { clientConfiguration, authenticityTokensManager, authManager, client }; |
71 | 71 | })(); |
72 | 72 |
|
| 73 | +type BrowserRouter = ReturnType<typeof createBrowserRouter>; |
| 74 | + |
| 75 | +// createBrowserRouter calls router.initialize() internally, which starts the |
| 76 | +// initial navigation asynchronously. By the time our useEffect subscribes, |
| 77 | +// the navigation may already be done — router.subscribe fires the callback |
| 78 | +// synchronously (via a buffered state update) with the final state. Watching |
| 79 | +// router.state.initialized (set to true once initial data loading completes) |
| 80 | +// handles both "already done" and "still in progress" correctly. |
| 81 | +function RouterLoadingOverlay({ router }: { router: BrowserRouter }) { |
| 82 | + const [ready, setReady] = useState(() => router.state.initialized); |
| 83 | + |
| 84 | + useLayoutEffect(() => { |
| 85 | + return router.subscribe((state) => { |
| 86 | + if (state.initialized) { |
| 87 | + setReady(true); |
| 88 | + } |
| 89 | + }); |
| 90 | + }, [router]); |
| 91 | + |
| 92 | + if (ready) return null; |
| 93 | + |
| 94 | + return ( |
| 95 | + <div className="text-center mt-5 custom-loading-indicator"> |
| 96 | + <div className="spinner-border" role="status" /> |
| 97 | + <span className="visually-hidden">Loading...</span> |
| 98 | + </div> |
| 99 | + ); |
| 100 | +} |
| 101 | + |
73 | 102 | function DataModeApplicationEntry() { |
74 | 103 | const bootstrap = use(bootstrapPromise); |
75 | 104 |
|
@@ -101,6 +130,7 @@ function DataModeApplicationEntry() { |
101 | 130 | return ( |
102 | 131 | <StrictMode> |
103 | 132 | <AuthenticationManagerContext.Provider value={bootstrap.authManager}> |
| 133 | + <RouterLoadingOverlay router={router} /> |
104 | 134 | <RouterProvider router={router} /> |
105 | 135 | </AuthenticationManagerContext.Provider> |
106 | 136 | </StrictMode> |
|
0 commit comments