eletrotupi / tcc/ frontend/context/AuthContext.tsx8b7a4383c6c39de79612d786a2c836f1b01221b8
4.2 KB | 179 lines Raw
1
import React, { createContext, useContext, useEffect, useState } from "react";
2
import { apiClient, User } from "@/lib/api";
3
import { queryClient, storage } from "@/lib/queryClient";
4

5
interface AuthProviderProps {
6
  children: React.ReactNode;
7
}
8

9
interface AuthContextType {
10
  user: User | null;
11
  isLoading: boolean;
12
  isAuthenticated: boolean;
13
  updateAuthUser: (user: User) => void;
14
  login: (email: string, password: string) => Promise<{ user: User } | null>;
15
  signup: (userData: {
16
    firstName: string;
17
    lastName?: string;
18
    email: string;
19
    password: string;
20
  }) => Promise<void>;
21
  logout: () => Promise<void>;
22
  forgotPassword: (
23
    email: string,
24
  ) => Promise<{ message: string; token?: string }>;
25
  resetPassword: (token: string, password: string) => Promise<void>;
26
  activate: (code: number) => Promise<void>;
27
  requestActivateCode: () => Promise<void>;
28
}
29

30
export function sanitizeUser(data: any): User {
31
  return {
32
    id: data.id,
33
    email: data.email,
34
    firstName: data.firstName,
35
    lastName: data.lastName,
36
    updatedAt: data.updatedAt,
37
    avatarKey: data.avatarKey,
38
    avatarURL: data.avatarURL,
39
    active: data.active,
40
    notificationsEnabled: data.notificationsEnabled,
41
    dailyReminderTime: data.dailyReminderTime,
42
  };
43
}
44

45
const AuthContext = createContext<AuthContextType | null>(null);
46

47
export function useAuth() {
48
  const context = useContext(AuthContext);
49
  if (!context) {
50
    throw new Error("useAuth must be used within an AuthProvider");
51
  }
52

53
  return context;
54
}
55

56
export const AuthProvider = ({ children }: AuthProviderProps) => {
57
  const [user, setUser] = useState<User | null>(null);
58
  const [isLoading, setIsLoading] = useState(true);
59
  const isAuthenticated = !!user;
60

61
  useEffect(() => {
62
    checkAuthState();
63
  }, []);
64

65
  useEffect(() => {
66
    apiClient.registerUnauthorizedHandler(() => {
67
      logout();
68
    });
69
  }, []);
70

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

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

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

95
  const login = async (
96
    email: string,
97
    password: string,
98
  ): Promise<{ user: User } | null> => {
99
    setIsLoading(true);
100

101
    try {
102
      const response = await apiClient.login(email, password);
103

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

108
      return response;
109
    } finally {
110
      setIsLoading(false);
111
    }
112
  };
113

114
  const signup = async (userData: {
115
    firstName: string;
116
    lastName?: string;
117
    email: string;
118
    password: string;
119
  }) => {
120
    setIsLoading(true);
121
    try {
122
      const response = await apiClient.signup(userData);
123
      setUser(response.user);
124
    } finally {
125
      setIsLoading(false);
126
    }
127
  };
128

129
  const logout = async () => {
130
    setIsLoading(true);
131
    try {
132
      await apiClient.logout();
133

134
      setUser(null);
135
      // Clear both the in-memory cache and the persisted MMKV cache
136
      queryClient.clear();
137
      storage.clearAll();
138
    } finally {
139
      setIsLoading(false);
140
    }
141
  };
142

143
  const forgotPassword = async (email: string) => {
144
    return apiClient.forgotPassword(email);
145
  };
146

147
  const resetPassword = async (token: string, password: string) => {
148
    await apiClient.resetPassword(token, password);
149
  };
150

151
  const activate = async (code: number) => {
152
    const response = await apiClient.activate(code);
153
    setUser(response.user);
154
  };
155

156
  const requestActivateCode = async () => {
157
    await apiClient.requestActivateCode();
158
  };
159

160
  const updateAuthUser = (user: User) => {
161
    setUser(user);
162
  };
163

164
  const value: AuthContextType = {
165
    user,
166
    isLoading,
167
    isAuthenticated,
168
    login,
169
    signup,
170
    logout,
171
    forgotPassword,
172
    resetPassword,
173
    updateAuthUser,
174
    activate,
175
    requestActivateCode,
176
  };
177

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