fix user refresher

This commit is contained in:
john 2025-05-19 17:52:18 +02:00
parent 17c9885ccc
commit 5f47162a50
6 changed files with 85 additions and 36 deletions

View file

@ -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

View file

@ -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 {

View file

@ -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()

View file

@ -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
View 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])
}

View file

@ -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
} }