diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-01-04 17:43:59 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-01-04 19:09:58 +0900 |
| commit | f8e4be9b36a16969ac53bd9ce12ce8064be10196 (patch) | |
| tree | b2cf350d2e2e52803ff809311effb40da767d859 /src/client/atoms/auth.ts | |
| parent | e1c9e5e89bb91bca2586470c786510c3e1c03826 (diff) | |
| download | kioku-f8e4be9b36a16969ac53bd9ce12ce8064be10196.tar.gz kioku-f8e4be9b36a16969ac53bd9ce12ce8064be10196.tar.zst kioku-f8e4be9b36a16969ac53bd9ce12ce8064be10196.zip | |
refactor(client): migrate state management from React Context to Jotai
Replace AuthProvider and SyncProvider with Jotai atoms for more granular
state management and better performance. This migration:
- Creates atoms for auth, sync, decks, cards, noteTypes, and study state
- Uses atomFamily for parameterized state (e.g., cards by deckId)
- Introduces StoreInitializer component for subscription initialization
- Updates all components and pages to use useAtomValue/useSetAtom
- Updates all tests to use Jotai Provider with createStore pattern
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'src/client/atoms/auth.ts')
| -rw-r--r-- | src/client/atoms/auth.ts | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/src/client/atoms/auth.ts b/src/client/atoms/auth.ts new file mode 100644 index 0000000..f618ccf --- /dev/null +++ b/src/client/atoms/auth.ts @@ -0,0 +1,57 @@ +import { atom, useSetAtom } from "jotai"; +import { useEffect } from "react"; +import { apiClient, type User } from "../api/client"; + +// Primitive atoms +export const userAtom = atom<User | null>(null); +export const authLoadingAtom = atom<boolean>(true); + +// Derived atom - checks if user is authenticated via apiClient +export const isAuthenticatedAtom = atom<boolean>((get) => { + // We need to trigger re-evaluation when user changes + get(userAtom); + return apiClient.isAuthenticated(); +}); + +// Action atom - login +export const loginAtom = atom( + null, + async ( + _get, + set, + { username, password }: { username: string; password: string }, + ) => { + const response = await apiClient.login(username, password); + set(userAtom, response.user); + }, +); + +// Action atom - logout +export const logoutAtom = atom(null, (_get, set) => { + apiClient.logout(); + set(userAtom, null); +}); + +// Hook to initialize auth state and subscribe to session expiration +export function useAuthInit() { + const setAuthLoading = useSetAtom(authLoadingAtom); + const setUser = useSetAtom(userAtom); + + useEffect(() => { + // Check for existing auth on mount + const tokens = apiClient.getTokens(); + if (tokens) { + // We have tokens stored, but we don't have user info cached + // For now, just set authenticated state. User info will be fetched when needed. + } + setAuthLoading(false); + + // Subscribe to session expired events from the API client + const unsubscribe = apiClient.onSessionExpired(() => { + apiClient.logout(); + setUser(null); + }); + + return unsubscribe; + }, [setAuthLoading, setUser]); +} |
