diff --git a/src/App.tsx b/src/App.tsx index 6f04dca..f7a78c6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,37 +10,60 @@ import LogoutPage from './app/auth/pages/LogoutPage.tsx' import UnauthorizedHandler from './app/auth/components/UnauthorizedHandler.tsx' import AdminPage from './app/admin/pages/AdminPage.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 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 ( - - + + } + /> + } /> + } /> + } /> + } /> + }> } + path={'codes'} + element={} /> - } /> - } /> - } /> - } /> - }> - } - /> - - - + + ) } - -export default App diff --git a/src/app/auth/authService.ts b/src/app/auth/authService.ts index 293c242..bdd721c 100644 --- a/src/app/auth/authService.ts +++ b/src/app/auth/authService.ts @@ -2,6 +2,7 @@ import { dispatchMessage } from '../messageBus/messageBus.ts' import client from '../api/client.ts' import { ProblemDetails } from '../../types' import { SignupCode } from './signupCode.ts' +import { User } from '../user/userStore.ts' export class AuthService { constructor() {} @@ -64,9 +65,9 @@ export class AuthService { return res.data.signupCodes.map(SignupCode.fromDto) } - async refreshUser(userId: string) { + async refreshUser(userId: string): Promise { if (this.getCookie('hasSession') !== 'true') { - return + return null } const res = await client.GET(`/auth/user/{userId}`, { @@ -76,11 +77,7 @@ export class AuthService { credentials: 'include', }) - if (!res.data) { - dispatchMessage('auth:user-refresh-failed', null) - } else { - dispatchMessage('auth:user-refreshed', { ...res.data }) - } + return res.data ?? null } private getCookie(cookieName: string): string | undefined { diff --git a/src/app/auth/pages/SignupPage.tsx b/src/app/auth/pages/SignupPage.tsx index ba3b994..19b2ab4 100644 --- a/src/app/auth/pages/SignupPage.tsx +++ b/src/app/auth/pages/SignupPage.tsx @@ -52,9 +52,7 @@ export default function SignupPage({ authService }: SignupPageProps) { } }, [code, signupCode]) - useEffect(() => { - console.debug('signup code', signupCode) - }, [signupCode]) + useEffect(() => {}, [signupCode]) const onSubmit = async (e: FormEvent) => { e.preventDefault() diff --git a/src/app/user/userStore.ts b/src/app/user/userStore.ts index 49107fc..f52d391 100644 --- a/src/app/user/userStore.ts +++ b/src/app/user/userStore.ts @@ -23,9 +23,9 @@ addMessageListener('auth:registered', setUser) addMessageListener('auth:logged-out', setUser) export const useUser = () => { - const [user] = useStore(userStore) + const [user, setUser] = useStore(userStore) - return { user } + return { user, setUser } } function loadStoredUser(): User | null { diff --git a/src/hooks/useOnMounted.ts b/src/hooks/useOnMounted.ts new file mode 100644 index 0000000..2053977 --- /dev/null +++ b/src/hooks/useOnMounted.ts @@ -0,0 +1,17 @@ +import { useEffect, useRef } from 'react' + +export function useOnMounted(callback: () => void | Promise) { + const isMounted = useRef(false) + + useEffect(() => { + if (isMounted.current) return + isMounted.current = true + + const timeoutId = setTimeout(callback) + + return () => { + isMounted.current = false + clearTimeout(timeoutId) + } + }, [callback]) +} diff --git a/src/utils/store.ts b/src/utils/store.ts index 5db61dc..ae99ef7 100644 --- a/src/utils/store.ts +++ b/src/utils/store.ts @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' export interface Store { getState: () => T @@ -37,7 +37,21 @@ export function createStore(initialState: T): Store export function useStore(store: Store) { 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 }