eletrotupi / tcc/ frontend/context/AuthContext.tsxc732afc720dd4f3c2603ff1f301efdf4cb1f8431
4.3 KB | 179 lines Raw
1
import React, {
2
  createContext,
3
  useCallback,
4
  useContext,
5
  useEffect,
6
  useMemo,
7
  useState,
8
} from "react";
9
import { apiClient, User } from "@/lib/api";
10
import { queryClient, storage } from "@/lib/queryClient";
11
import { AuthStatus, deriveStatus } from "@/context/authRouting";
12

13
interface AuthProviderProps {
14
  children: React.ReactNode;
15
}
16

17
interface SignUpInput {
18
  firstName: string;
19
  lastName?: string;
20
  email: string;
21
  password: string;
22
  acceptedTerms: boolean;
23
}
24

25
interface AuthContextType {
26
  user: User | null;
27
  status: AuthStatus;
28
  isLoading: boolean;
29
  updateAuthUser: (user: User) => void;
30
  login: (email: string, password: string) => Promise<{ user: User } | null>;
31
  signup: (userData: SignUpInput) => Promise<void>;
32
  logout: () => Promise<void>;
33
  forgotPassword: (email: string) => Promise<{ success: boolean }>;
34
  resetPassword: (token: string, password: string) => Promise<void>;
35
  activate: (code: string) => Promise<void>;
36
  requestActivateCode: () => Promise<void>;
37
  completeOnboarding: () => Promise<void>;
38
}
39

40
const AuthContext = createContext<AuthContextType | null>(null);
41

42
export function useAuth() {
43
  const context = useContext(AuthContext);
44
  if (!context) {
45
    throw new Error("useAuth must be used within an AuthProvider");
46
  }
47

48
  return context;
49
}
50

51
export const AuthProvider = ({ children }: AuthProviderProps) => {
52
  const [user, setUser] = useState<User | null>(null);
53
  const [isLoading, setIsLoading] = useState(true);
54
  const status = deriveStatus(isLoading, user);
55

56
  const logout = useCallback(async () => {
57
    setIsLoading(true);
58
    try {
59
      await apiClient.logout();
60

61
      setUser(null);
62
      // Clear both the in-memory cache and the persisted MMKV cache
63
      queryClient.clear();
64
      storage.clearAll();
65
    } finally {
66
      setIsLoading(false);
67
    }
68
  }, []);
69

70
  const checkAuthState = useCallback(async () => {
71
    const doAuthCheck = async () => {
72
      const { token } = await apiClient.getStoredAuthData();
73
      if (token) {
74
        const { user } = await apiClient.getMe();
75
        setUser(user);
76
      }
77
    };
78

79
    const timeout = new Promise<never>((_, reject) =>
80
      setTimeout(() => reject(new Error("Auth check timed out")), 10_000),
81
    );
82

83
    try {
84
      await Promise.race([doAuthCheck(), timeout]);
85
    } catch (error) {
86
      console.error("Auth check failed:", error);
87
      await apiClient.logout();
88
    } finally {
89
      setIsLoading(false);
90
    }
91
  }, []);
92

93
  useEffect(() => {
94
    checkAuthState();
95
  }, [checkAuthState]);
96

97
  useEffect(() => {
98
    apiClient.registerUnauthorizedHandler(logout);
99
  }, [logout]);
100

101
  const login = useCallback(async (email: string, password: string) => {
102
    const response = await apiClient.login(email, password);
103

104
    if (response.user) {
105
      setUser(response.user);
106
    }
107

108
    return response;
109
  }, []);
110

111
  const signup = useCallback(async (userData: SignUpInput) => {
112
    const response = await apiClient.signup(userData);
113

114
    if (response.user) {
115
      setUser(response.user);
116
    }
117
  }, []);
118

119
  const forgotPassword = useCallback(
120
    (email: string) => apiClient.forgotPassword(email),
121
    [],
122
  );
123

124
  const resetPassword = useCallback(async (token: string, password: string) => {
125
    await apiClient.resetPassword(token, password);
126
  }, []);
127

128
  const activate = useCallback(async (code: string) => {
129
    const response = await apiClient.activate(code);
130
    setUser(response.user);
131
  }, []);
132

133
  const requestActivateCode = useCallback(
134
    () => apiClient.requestActivateCode(),
135
    [],
136
  );
137

138
  const completeOnboarding = useCallback(async () => {
139
    const { user } = await apiClient.completeOnboarding();
140
    setUser(user);
141
  }, []);
142

143
  const updateAuthUser = useCallback((user: User) => {
144
    setUser(user);
145
  }, []);
146

147
  const value = useMemo<AuthContextType>(
148
    () => ({
149
      user,
150
      status,
151
      isLoading,
152
      login,
153
      signup,
154
      logout,
155
      forgotPassword,
156
      resetPassword,
157
      updateAuthUser,
158
      activate,
159
      requestActivateCode,
160
      completeOnboarding,
161
    }),
162
    [
163
      user,
164
      status,
165
      isLoading,
166
      login,
167
      signup,
168
      logout,
169
      forgotPassword,
170
      resetPassword,
171
      updateAuthUser,
172
      activate,
173
      requestActivateCode,
174
      completeOnboarding,
175
    ],
176
  );
177

178
  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
179
};