🚸(frontend) prevent flash of "logged out" user content when loading
until now, we concluded that is `isLoggedIn` !== true meant the user wasn't logged in. While it also meant that we are currently loading user info. wrap the whole in something that doesn't render anything until we made the first user request to prevent this behavior.
This commit is contained in:
@@ -6,11 +6,11 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useLang } from 'hoofd'
|
import { useLang } from 'hoofd'
|
||||||
import { Route, Switch } from 'wouter'
|
import { Route, Switch } from 'wouter'
|
||||||
import { Screen } from './layout/Screen'
|
|
||||||
import { HomeRoute } from '@/features/home'
|
import { HomeRoute } from '@/features/home'
|
||||||
import { RoomRoute, roomIdRegex } from '@/features/rooms'
|
import { RoomRoute, roomIdRegex } from '@/features/rooms'
|
||||||
import { NotFound } from './routes/NotFound'
|
import { NotFound } from './routes/NotFound'
|
||||||
import './i18n/init'
|
import './i18n/init'
|
||||||
|
import { RenderIfUserFetched } from './features/auth'
|
||||||
|
|
||||||
const queryClient = new QueryClient()
|
const queryClient = new QueryClient()
|
||||||
|
|
||||||
@@ -19,12 +19,14 @@ function App() {
|
|||||||
useLang(i18n.language)
|
useLang(i18n.language)
|
||||||
return (
|
return (
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<Suspense fallback={<Screen />}>
|
<Suspense fallback={null}>
|
||||||
<Switch>
|
<RenderIfUserFetched>
|
||||||
<Route path="/" component={HomeRoute} />
|
<Switch>
|
||||||
<Route path={roomIdRegex} component={RoomRoute} />
|
<Route path="/" component={HomeRoute} />
|
||||||
<Route component={NotFound} />
|
<Route path={roomIdRegex} component={RoomRoute} />
|
||||||
</Switch>
|
<Route component={NotFound} />
|
||||||
|
</Switch>
|
||||||
|
</RenderIfUserFetched>
|
||||||
<ReactQueryDevtools initialIsOpen={false} />
|
<ReactQueryDevtools initialIsOpen={false} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
|
|||||||
@@ -2,16 +2,25 @@ import { useQuery } from '@tanstack/react-query'
|
|||||||
import { keys } from '@/api/queryKeys'
|
import { keys } from '@/api/queryKeys'
|
||||||
import { fetchUser } from './fetchUser'
|
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 = () => {
|
export const useUser = () => {
|
||||||
const query = useQuery({
|
const query = useQuery({
|
||||||
queryKey: [keys.user],
|
queryKey: [keys.user],
|
||||||
queryFn: fetchUser,
|
queryFn: fetchUser,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let isLoggedIn = undefined
|
||||||
|
if (query.data !== undefined) {
|
||||||
|
isLoggedIn = query.data !== false
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
...query,
|
...query,
|
||||||
// if fetchUser returns false, it means the user is not logged in: expose that
|
// if fetchUser returns false, it means the user is not logged in: expose that
|
||||||
user: query.data === false ? undefined : query.data,
|
user: query.data === false ? undefined : query.data,
|
||||||
isLoggedIn: query.data !== undefined && query.data !== false,
|
isLoggedIn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
) : (
|
||||||
|
<LoadingScreen renderTimeout={1000} />
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
export { useUser } from './api/useUser'
|
export { useUser } from './api/useUser'
|
||||||
export { authUrl } from './utils/authUrl'
|
export { authUrl } from './utils/authUrl'
|
||||||
export { logoutUrl } from './utils/logoutUrl'
|
export { logoutUrl } from './utils/logoutUrl'
|
||||||
|
export { RenderIfUserFetched } from './components/RenderIfUserFetched'
|
||||||
|
|||||||
@@ -3,14 +3,20 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { BoxScreen } from './BoxScreen'
|
import { BoxScreen } from './BoxScreen'
|
||||||
import { Screen } from './Screen'
|
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()
|
const { t } = useTranslation()
|
||||||
// show the loading screen only after a little while to prevent flash of texts
|
// show the loading screen only after a little while to prevent flash of texts
|
||||||
const [show, setShow] = useState(false)
|
const [show, setShow] = useState(false)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timeout = setTimeout(() => setShow(true), 500)
|
const timeout = setTimeout(() => setShow(true), renderTimeout)
|
||||||
return () => clearTimeout(timeout)
|
return () => clearTimeout(timeout)
|
||||||
}, [])
|
}, [renderTimeout])
|
||||||
if (!show) {
|
if (!show) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user