import React, { createContext, useContext, useEffect, useState } from "react"; import AsyncStorage from "@react-native-async-storage/async-storage"; import Toast from "react-native-toast-message"; import { authApi, LoginPayload, RegisterPayload, setUnauthorizedHandler, subscriptionsApi, userApi, UserUpdateDTO, } from "@/services/api"; type User = { id: string; email: string; token: string; subscribedSubjectIds: string[]; notificationType: 'PUSH_NOTIFICATION' | 'NO_NOTIFICATION'; }; type AuthContextValue = { user: User | null; loading: boolean; login: (p: LoginPayload) => Promise; register: (p: RegisterPayload) => Promise; logout: () => Promise; subscribe: (subjectId: string) => Promise; unsubscribe: (subjectId: string) => Promise; isSubscribed: (subjectId: string) => boolean; updateUser: (newEmail?: string, newPassword?: string) => Promise; updateNotificationType: (type: 'PUSH_NOTIFICATION' | 'NO_NOTIFICATION') => Promise; }; const AuthContext = createContext(null); export function AuthProvider({ children }: { children: React.ReactNode }) { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { AsyncStorage.getItem("auth_user") .then((raw) => { if (raw) setUser(JSON.parse(raw)); }) .finally(() => setLoading(false)); }, []); const persist = async (u: User | null) => { if (u) await AsyncStorage.setItem("auth_user", JSON.stringify(u)); else await AsyncStorage.removeItem("auth_user"); setUser(u); }; useEffect(() => { setUnauthorizedHandler(() => { persist(null); Toast.show({ type: 'info', text1: 'Session expired', text2: 'Please sign in again.', position: 'top', visibilityTime: 4000, }); }); return () => setUnauthorizedHandler(null); }, []); const updateNotificationType = async (type: 'PUSH_NOTIFICATION' | 'NO_NOTIFICATION') => { if (!user) return; await userApi.updateNotificationType(type, user.token); await persist({ ...user, notificationType: type }); }; const login = async (payload: LoginPayload) => { const res = await authApi.login(payload); let subscribedSubjectIds: string[] = []; let notificationType: 'PUSH_NOTIFICATION' | 'NO_NOTIFICATION' = 'PUSH_NOTIFICATION'; if (res.userId) { try { const dto = await authApi.getUser(res.token); subscribedSubjectIds = dto.subjectSet?.map((s) => s.id) ?? []; notificationType = dto.notificationType ?? 'PUSH_NOTIFICATION'; } catch (e) { console.error('Failed to load subscriptions on login:', e); } } await persist({ id: res.userId ?? "", email: res.email, token: res.token, subscribedSubjectIds, notificationType, }); }; const register = async (payload: RegisterPayload) => { await authApi.register(payload); await login({ email: payload.email.trim(), password: payload.password.trim(), }); }; const updateUser = async (newEmail?: string, newPassword?: string) => { if (!user) return; const body: UserUpdateDTO = { email: user.email, newEmail: newEmail || undefined, password: newPassword || undefined, subjectSet: user.subscribedSubjectIds.map((id) => ({ id })), }; const dto = await authApi.updateUser(body, user.token); const updated: User = { ...user, email: newEmail ?? user.email, subscribedSubjectIds: dto.subjectSet?.map((s: any) => s.id) ?? user.subscribedSubjectIds, }; await persist(updated); }; const logout = () => persist(null); const subscribe = async (subjectId: string) => { if (!user) return; const dto = await subscriptionsApi.subscribe( subjectId, user.token); const updated = { ...user, subscribedSubjectIds: dto.subjectSet?.map((s) => s.id) ?? [...user.subscribedSubjectIds, subjectId], }; await persist(updated); }; const unsubscribe = async (subjectId: string) => { if (!user) return; const dto = await subscriptionsApi.unsubscribe( subjectId, user.token); const updated = { ...user, subscribedSubjectIds: dto.subjectSet?.map((s) => s.id) ?? user.subscribedSubjectIds.filter((id) => id !== subjectId), }; await persist(updated); }; const isSubscribed = (subjectId: string) => user?.subscribedSubjectIds.includes(subjectId) ?? false; return ( {children} ); } export function useAuth() { const ctx = useContext(AuthContext); if (!ctx) throw new Error("useAuth must be used inside AuthProvider"); return ctx; }