fix user refresher
This commit is contained in:
parent
17c9885ccc
commit
5f47162a50
6 changed files with 85 additions and 36 deletions
65
src/App.tsx
65
src/App.tsx
|
@ -10,37 +10,60 @@ import LogoutPage from './app/auth/pages/LogoutPage.tsx'
|
||||||
import UnauthorizedHandler from './app/auth/components/UnauthorizedHandler.tsx'
|
import UnauthorizedHandler from './app/auth/components/UnauthorizedHandler.tsx'
|
||||||
import AdminPage from './app/admin/pages/AdminPage.tsx'
|
import AdminPage from './app/admin/pages/AdminPage.tsx'
|
||||||
import SignupCodesManagementPage from './app/admin/pages/subpages/SignupCodesManagementPage.tsx'
|
import SignupCodesManagementPage from './app/admin/pages/subpages/SignupCodesManagementPage.tsx'
|
||||||
import RefreshUser from './app/auth/components/RefreshUser.tsx'
|
import { useUser } from './app/user/userStore.ts'
|
||||||
|
import { useEffect, useMemo } from 'react'
|
||||||
|
|
||||||
function App() {
|
export default function App() {
|
||||||
const postService = new PostsService()
|
const postService = new PostsService()
|
||||||
const mediaService = new MediaService()
|
const mediaService = new MediaService()
|
||||||
const authService = new AuthService()
|
const authService = useMemo(() => new AuthService(), [])
|
||||||
|
|
||||||
|
const { user, setUser } = useUser()
|
||||||
|
|
||||||
|
const userId = user?.userId ?? null
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (userId == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeouts: number[] = []
|
||||||
|
|
||||||
|
timeouts.push(
|
||||||
|
setTimeout(async function refreshUser() {
|
||||||
|
const userInfo = await authService.refreshUser(userId)
|
||||||
|
|
||||||
|
setUser(userInfo)
|
||||||
|
|
||||||
|
timeouts.push(setTimeout(refreshUser, 60_000))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
timeouts.forEach(clearTimeout)
|
||||||
|
}
|
||||||
|
}, [authService, setUser, userId])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<UnauthorizedHandler>
|
<UnauthorizedHandler>
|
||||||
<RefreshUser authService={authService}>
|
<Routes>
|
||||||
<Routes>
|
<Route
|
||||||
|
path={'/'}
|
||||||
|
element={<HomePage postsService={postService} mediaService={mediaService} />}
|
||||||
|
/>
|
||||||
|
<Route path="/u/:username" element={<AuthorPage postsService={postService} />} />
|
||||||
|
<Route path="/login" element={<LoginPage authService={authService} />} />
|
||||||
|
<Route path="/logout" element={<LogoutPage authService={authService} />} />
|
||||||
|
<Route path="/signup/:code?" element={<SignupPage authService={authService} />} />
|
||||||
|
<Route path={'/admin'} element={<AdminPage />}>
|
||||||
<Route
|
<Route
|
||||||
path={'/'}
|
path={'codes'}
|
||||||
element={<HomePage postsService={postService} mediaService={mediaService} />}
|
element={<SignupCodesManagementPage authService={authService} />}
|
||||||
/>
|
/>
|
||||||
<Route path="/u/:username" element={<AuthorPage postsService={postService} />} />
|
</Route>
|
||||||
<Route path="/login" element={<LoginPage authService={authService} />} />
|
</Routes>
|
||||||
<Route path="/logout" element={<LogoutPage authService={authService} />} />
|
|
||||||
<Route path="/signup/:code?" element={<SignupPage authService={authService} />} />
|
|
||||||
<Route path={'/admin'} element={<AdminPage />}>
|
|
||||||
<Route
|
|
||||||
path={'codes'}
|
|
||||||
element={<SignupCodesManagementPage authService={authService} />}
|
|
||||||
/>
|
|
||||||
</Route>
|
|
||||||
</Routes>
|
|
||||||
</RefreshUser>
|
|
||||||
</UnauthorizedHandler>
|
</UnauthorizedHandler>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { dispatchMessage } from '../messageBus/messageBus.ts'
|
||||||
import client from '../api/client.ts'
|
import client from '../api/client.ts'
|
||||||
import { ProblemDetails } from '../../types'
|
import { ProblemDetails } from '../../types'
|
||||||
import { SignupCode } from './signupCode.ts'
|
import { SignupCode } from './signupCode.ts'
|
||||||
|
import { User } from '../user/userStore.ts'
|
||||||
|
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
@ -64,9 +65,9 @@ export class AuthService {
|
||||||
return res.data.signupCodes.map(SignupCode.fromDto)
|
return res.data.signupCodes.map(SignupCode.fromDto)
|
||||||
}
|
}
|
||||||
|
|
||||||
async refreshUser(userId: string) {
|
async refreshUser(userId: string): Promise<User | null> {
|
||||||
if (this.getCookie('hasSession') !== 'true') {
|
if (this.getCookie('hasSession') !== 'true') {
|
||||||
return
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await client.GET(`/auth/user/{userId}`, {
|
const res = await client.GET(`/auth/user/{userId}`, {
|
||||||
|
@ -76,11 +77,7 @@ export class AuthService {
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!res.data) {
|
return res.data ?? null
|
||||||
dispatchMessage('auth:user-refresh-failed', null)
|
|
||||||
} else {
|
|
||||||
dispatchMessage('auth:user-refreshed', { ...res.data })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getCookie(cookieName: string): string | undefined {
|
private getCookie(cookieName: string): string | undefined {
|
||||||
|
|
|
@ -52,9 +52,7 @@ export default function SignupPage({ authService }: SignupPageProps) {
|
||||||
}
|
}
|
||||||
}, [code, signupCode])
|
}, [code, signupCode])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {}, [signupCode])
|
||||||
console.debug('signup code', signupCode)
|
|
||||||
}, [signupCode])
|
|
||||||
|
|
||||||
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
|
@ -23,9 +23,9 @@ addMessageListener('auth:registered', setUser)
|
||||||
addMessageListener('auth:logged-out', setUser)
|
addMessageListener('auth:logged-out', setUser)
|
||||||
|
|
||||||
export const useUser = () => {
|
export const useUser = () => {
|
||||||
const [user] = useStore(userStore)
|
const [user, setUser] = useStore(userStore)
|
||||||
|
|
||||||
return { user }
|
return { user, setUser }
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadStoredUser(): User | null {
|
function loadStoredUser(): User | null {
|
||||||
|
|
17
src/hooks/useOnMounted.ts
Normal file
17
src/hooks/useOnMounted.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { useEffect, useRef } from 'react'
|
||||||
|
|
||||||
|
export function useOnMounted(callback: () => void | Promise<void>) {
|
||||||
|
const isMounted = useRef(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isMounted.current) return
|
||||||
|
isMounted.current = true
|
||||||
|
|
||||||
|
const timeoutId = setTimeout(callback)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
isMounted.current = false
|
||||||
|
clearTimeout(timeoutId)
|
||||||
|
}
|
||||||
|
}, [callback])
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import { useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
|
|
||||||
export interface Store<T> {
|
export interface Store<T> {
|
||||||
getState: () => T
|
getState: () => T
|
||||||
|
@ -37,7 +37,21 @@ export function createStore<T extends object | null>(initialState: T): Store<T>
|
||||||
export function useStore<T>(store: Store<T>) {
|
export function useStore<T>(store: Store<T>) {
|
||||||
const [selectedState, setSelectedState] = useState(() => store.getState())
|
const [selectedState, setSelectedState] = useState(() => store.getState())
|
||||||
|
|
||||||
useEffect(() => store.subscribe((newState) => setSelectedState(newState)), [store])
|
useEffect(() => {
|
||||||
|
const unsubscribe = store.subscribe((newState) => setSelectedState(newState))
|
||||||
|
|
||||||
return [selectedState, setSelectedState] as const
|
return () => {
|
||||||
|
unsubscribe()
|
||||||
|
}
|
||||||
|
}, [store])
|
||||||
|
|
||||||
|
const setState = useCallback(
|
||||||
|
(nextState: T | ((prevState: T) => T)) => {
|
||||||
|
setSelectedState(nextState)
|
||||||
|
store.setState(nextState)
|
||||||
|
},
|
||||||
|
[store],
|
||||||
|
)
|
||||||
|
|
||||||
|
return [selectedState, setState] as const
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue