/** * Mobile auth store — Zustand. Logic mirrors packages/core/auth/store.ts: * - Token written ONLY on successful verifyCode * - 401 → clear token; non-401 (5xx / network blip) → preserve token so * the next launch can retry * - logout = clear token + clear in-memory user + setToken(null) * * NOT shared with web/desktop (per Sharing Principles in root CLAUDE.md). * Storage backend is expo-secure-store (mobile only); web uses HttpOnly * cookies, desktop uses localStorage via StorageAdapter. */ import { create } from "zustand"; import type { User } from "@multica/core/types"; import { api, ApiError } from "./api"; import { clearToken, getToken, setToken } from "./secure-storage"; import { useWorkspaceStore } from "./workspace-store"; interface AuthState { user: User | null; isLoading: boolean; initialize: () => Promise; sendCode: (email: string) => Promise; verifyCode: (email: string, code: string) => Promise; logout: () => Promise; /** Overwrite the in-memory user — call after PATCH /api/me so name/avatar * edits land without a refetch. Server response is the source of truth. */ setUser: (user: User) => void; } export const useAuthStore = create((set) => ({ user: null, isLoading: true, initialize: async () => { // Restore the persisted workspace slug alongside the auth token so the // entry redirect (app/index.tsx) can route directly to the last-used // workspace without flashing /select-workspace. await useWorkspaceStore.getState().restoreSlug(); const token = await getToken(); if (!token) { set({ isLoading: false }); return; } api.setToken(token); try { const user = await api.getMe(); set({ user, isLoading: false }); } catch (err) { // Only clear token on a genuine 401. Network blips / 5xx keep the // token so the next launch (or a manual refresh) can retry. if (err instanceof ApiError && err.status === 401) { await clearToken(); api.setToken(null); } set({ user: null, isLoading: false }); } }, sendCode: async (email) => { await api.sendCode(email); }, verifyCode: async (email, code) => { const { token, user } = await api.verifyCode(email, code); await setToken(token); api.setToken(token); set({ user }); return user; }, logout: async () => { await clearToken(); api.setToken(null); set({ user: null }); }, setUser: (user) => set({ user }), }));