diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx index 6d7f4956..8e9b7ea4 100644 --- a/src/frontend/src/App.tsx +++ b/src/frontend/src/App.tsx @@ -6,11 +6,11 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' import { useLang } from 'hoofd' import { Route, Switch } from 'wouter' -import { Screen } from './layout/Screen' import { HomeRoute } from '@/features/home' import { RoomRoute, roomIdRegex } from '@/features/rooms' import { NotFound } from './routes/NotFound' import './i18n/init' +import { RenderIfUserFetched } from './features/auth' const queryClient = new QueryClient() @@ -19,12 +19,14 @@ function App() { useLang(i18n.language) return ( - }> - - - - - + + + + + + + + diff --git a/src/frontend/src/features/auth/api/useUser.tsx b/src/frontend/src/features/auth/api/useUser.tsx index 6286ca12..84f5e601 100644 --- a/src/frontend/src/features/auth/api/useUser.tsx +++ b/src/frontend/src/features/auth/api/useUser.tsx @@ -2,16 +2,25 @@ import { useQuery } from '@tanstack/react-query' import { keys } from '@/api/queryKeys' import { fetchUser } from './fetchUser' +/** + * returns info about currently logged in user + * + * `isLoggedIn` is undefined while query is loading and true/false when it's done + */ export const useUser = () => { const query = useQuery({ queryKey: [keys.user], queryFn: fetchUser, }) + let isLoggedIn = undefined + if (query.data !== undefined) { + isLoggedIn = query.data !== false + } return { ...query, // if fetchUser returns false, it means the user is not logged in: expose that user: query.data === false ? undefined : query.data, - isLoggedIn: query.data !== undefined && query.data !== false, + isLoggedIn, } } diff --git a/src/frontend/src/features/auth/components/RenderIfUserFetched.tsx b/src/frontend/src/features/auth/components/RenderIfUserFetched.tsx new file mode 100644 index 00000000..276a0133 --- /dev/null +++ b/src/frontend/src/features/auth/components/RenderIfUserFetched.tsx @@ -0,0 +1,17 @@ +import { type ReactNode } from 'react' +import { useUser } from '@/features/auth' +import { LoadingScreen } from '@/layout/LoadingScreen' + +/** + * wrapper that renders children only when user info has been actually fetched + * + * this is helpful to prevent flash of logged-out content for a few milliseconds when user is actually logged in + */ +export const RenderIfUserFetched = ({ children }: { children: ReactNode }) => { + const { isLoggedIn } = useUser() + return isLoggedIn !== undefined ? ( + children + ) : ( + + ) +} diff --git a/src/frontend/src/features/auth/index.ts b/src/frontend/src/features/auth/index.ts index d298a6cb..0b533fea 100644 --- a/src/frontend/src/features/auth/index.ts +++ b/src/frontend/src/features/auth/index.ts @@ -1,3 +1,4 @@ export { useUser } from './api/useUser' export { authUrl } from './utils/authUrl' export { logoutUrl } from './utils/logoutUrl' +export { RenderIfUserFetched } from './components/RenderIfUserFetched' diff --git a/src/frontend/src/layout/LoadingScreen.tsx b/src/frontend/src/layout/LoadingScreen.tsx index 40053ba8..2d58c9e4 100644 --- a/src/frontend/src/layout/LoadingScreen.tsx +++ b/src/frontend/src/layout/LoadingScreen.tsx @@ -3,14 +3,20 @@ import { useTranslation } from 'react-i18next' import { BoxScreen } from './BoxScreen' import { Screen } from './Screen' -export const LoadingScreen = ({ asBox = false }: { asBox?: boolean }) => { +export const LoadingScreen = ({ + asBox = false, + renderTimeout = 500, +}: { + asBox?: boolean + renderTimeout?: number +}) => { const { t } = useTranslation() // show the loading screen only after a little while to prevent flash of texts const [show, setShow] = useState(false) useEffect(() => { - const timeout = setTimeout(() => setShow(true), 500) + const timeout = setTimeout(() => setShow(true), renderTimeout) return () => clearTimeout(timeout) - }, []) + }, [renderTimeout]) if (!show) { return null }