| /** | |
| * Authentication & user state store. | |
| * | |
| * Manages the current user session, authentication status, and | |
| * onboarding completion. Uses Zustand v5 with immer middleware | |
| * for ergonomic partial updates. | |
| */ | |
| import { create } from 'zustand'; | |
| import { immer } from 'zustand/middleware/immer'; | |
| import type { User, UserRole } from '../types/user'; | |
| // βββ State Shape βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| interface AuthState { | |
| /** Whether the user has a valid auth session */ | |
| isAuthenticated: boolean; | |
| /** The currently logged-in user (null when logged out) */ | |
| user: User | null; | |
| /** Whether the user has completed the onboarding flow */ | |
| isOnboarded: boolean; | |
| /** Whether auth state is currently being restored (e.g., on app launch) */ | |
| isRestoring: boolean; | |
| } | |
| // βββ Actions βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| interface AuthActions { | |
| /** Mark the user as authenticated with their full profile */ | |
| setAuth: (user: User) => void; | |
| /** Set / replace the user object (e.g., after profile update) */ | |
| setUser: (user: User) => void; | |
| /** Mark onboarding as completed */ | |
| setOnboarded: (value: boolean) => void; | |
| /** Log out: clear all auth state */ | |
| logout: () => void; | |
| /** Apply partial updates to the current user */ | |
| updateUser: (partial: Partial<User>) => void; | |
| /** Set the restoring flag (shown during app startup) */ | |
| setRestoring: (value: boolean) => void; | |
| /** Check authentication state (e.g., on app launch from persisted data) */ | |
| checkAuth: () => void; | |
| } | |
| // βββ Store βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export const useAuthStore = create<AuthState & AuthActions>()( | |
| immer((set) => ({ | |
| // Initial state | |
| isAuthenticated: false, | |
| user: null, | |
| isOnboarded: false, | |
| isRestoring: true, | |
| // Actions | |
| setAuth: (user) => | |
| set((state) => { | |
| state.isAuthenticated = true; | |
| state.user = user; | |
| state.isRestoring = false; | |
| }), | |
| setUser: (user) => | |
| set((state) => { | |
| state.user = user; | |
| }), | |
| setOnboarded: (value) => | |
| set((state) => { | |
| state.isOnboarded = value; | |
| }), | |
| logout: () => | |
| set((state) => { | |
| state.isAuthenticated = false; | |
| state.user = null; | |
| state.isOnboarded = false; | |
| state.isRestoring = false; | |
| }), | |
| updateUser: (partial) => | |
| set((state) => { | |
| if (state.user) { | |
| Object.assign(state.user, partial); | |
| } | |
| }), | |
| setRestoring: (value) => | |
| set((state) => { | |
| state.isRestoring = value; | |
| }), | |
| checkAuth: () => | |
| set((state) => { | |
| // In a real app this would check SQLite / SecureStore for a persisted | |
| // user session. For now we simply mark restoring as complete. | |
| state.isRestoring = false; | |
| }), | |
| })), | |
| ); | |
| // βββ Selectors βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export const selectIsRider = (state: AuthState) => | |
| state.user?.currentRole === 'rider'; | |
| export const selectIsPassenger = (state: AuthState) => | |
| state.user?.currentRole === 'passenger'; | |
| export const selectUserDisplayName = (state: AuthState) => | |
| state.user?.displayName ?? ''; | |
| export const selectUserAvatar = (state: AuthState) => | |
| state.user?.avatarUrl ?? null; | |
| export const selectUserId = (state: AuthState) => state.user?.id ?? null; | |