98 lines
3.6 KiB
TypeScript
98 lines
3.6 KiB
TypeScript
import { QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
|
|
import { useRouter } from '@tanstack/react-router'
|
|
import React, { useState } from 'react'
|
|
import { ApiError } from './api/ApiError'
|
|
import ExpiredTokenError from './api/ExpiredTokenError'
|
|
import refresh, { RefreshTokenError } from './api/refresh'
|
|
import LoginView from './api/types/LoginView'
|
|
import { useAuth } from './auth'
|
|
|
|
const AuthAwareQueryClientProvider = ({ children }: React.PropsWithChildren) => {
|
|
const { putToken, clearToken } = useAuth()
|
|
const router = useRouter()
|
|
const [currentlyRefreshing, setCurrentlyRefreshing] = useState(false)
|
|
|
|
const doRefresh = async () => {
|
|
if (!currentlyRefreshing) {
|
|
console.log('starting refresh')
|
|
setCurrentlyRefreshing(true)
|
|
let refreshResult: LoginView
|
|
try {
|
|
refreshResult = await refresh()
|
|
} catch (error) {
|
|
if (error instanceof RefreshTokenError) {
|
|
console.log(`RefreshTokenError: ${error.reason}`)
|
|
setCurrentlyRefreshing(false)
|
|
clearToken()
|
|
await router.navigate({
|
|
to: '/login',
|
|
search: {
|
|
reason: error.reason,
|
|
redirect: router.state.location.href
|
|
}
|
|
})
|
|
console.log('post-navigate')
|
|
return
|
|
} else {
|
|
setCurrentlyRefreshing(false)
|
|
throw error
|
|
}
|
|
}
|
|
putToken(refreshResult.accessToken, refreshResult.username)
|
|
setCurrentlyRefreshing(false)
|
|
console.log('refresh done')
|
|
}
|
|
}
|
|
|
|
const [queryClient] = useState<QueryClient>(
|
|
() =>
|
|
new QueryClient({
|
|
defaultOptions: {
|
|
mutations: {
|
|
onError(error) {
|
|
if (error instanceof ExpiredTokenError) {
|
|
doRefresh()
|
|
}
|
|
}
|
|
},
|
|
queries: {
|
|
retry(failureCount, error) {
|
|
if (
|
|
error instanceof ExpiredTokenError ||
|
|
(error instanceof ApiError && error.status === 404)
|
|
) {
|
|
return false
|
|
} else {
|
|
return failureCount <= 3
|
|
}
|
|
},
|
|
retryDelay(failureCount, error) {
|
|
if (error instanceof ExpiredTokenError) {
|
|
return 0
|
|
} else {
|
|
return failureCount * 1000
|
|
}
|
|
}
|
|
}
|
|
},
|
|
queryCache: new QueryCache({
|
|
onError(error) {
|
|
if (error instanceof ExpiredTokenError) {
|
|
doRefresh()
|
|
}
|
|
}
|
|
})
|
|
})
|
|
)
|
|
|
|
return (
|
|
<QueryClientProvider client={queryClient}>
|
|
{children}
|
|
<ReactQueryDevtools position="right" buttonPosition="top-right" />
|
|
</QueryClientProvider>
|
|
)
|
|
}
|
|
|
|
export default AuthAwareQueryClientProvider
|