auth-context.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. 'use client';
  2. import React, { createContext, useContext, useState, useEffect, useCallback } from 'react';
  3. import { authApi, usersApi, User } from './api';
  4. interface AcceptedProject {
  5. projectId: string;
  6. projectName: string;
  7. }
  8. interface AuthContextValue {
  9. user: User | null;
  10. token: string | null;
  11. loading: boolean;
  12. acceptedProjects: AcceptedProject[];
  13. justRegisteredName: string;
  14. clearAcceptedProjects: () => void;
  15. login: (email: string, password: string) => Promise<void>;
  16. register: (email: string, name: string, password: string, inviteToken?: string) => Promise<void>;
  17. logout: () => Promise<void>;
  18. refreshUser: () => Promise<void>;
  19. updateUserData: (data: Partial<User>) => void;
  20. }
  21. const AuthContext = createContext<AuthContextValue | null>(null);
  22. export function AuthProvider({ children }: { children: React.ReactNode }) {
  23. const [user, setUser] = useState<User | null>(null);
  24. const [token, setToken] = useState<string | null>(null);
  25. const [loading, setLoading] = useState(true);
  26. const [acceptedProjects, setAcceptedProjects] = useState<AcceptedProject[]>([]);
  27. const [justRegisteredName, setJustRegisteredName] = useState('');
  28. useEffect(() => {
  29. const savedToken = localStorage.getItem('vidreview_token');
  30. const savedUser = localStorage.getItem('vidreview_user');
  31. if (savedToken && savedUser) {
  32. setToken(savedToken);
  33. try {
  34. setUser(JSON.parse(savedUser));
  35. } catch {
  36. localStorage.removeItem('vidreview_token');
  37. localStorage.removeItem('vidreview_user');
  38. }
  39. }
  40. setLoading(false);
  41. }, []);
  42. const clearAcceptedProjects = useCallback(() => setAcceptedProjects([]), []);
  43. const login = useCallback(async (email: string, password: string) => {
  44. const { user: u, token: t, acceptedProjects: ap } = await authApi.login({ email, password });
  45. localStorage.setItem('vidreview_token', t);
  46. localStorage.setItem('vidreview_user', JSON.stringify(u));
  47. setToken(t);
  48. setUser(u);
  49. setAcceptedProjects(ap ?? []);
  50. }, []);
  51. const register = useCallback(async (email: string, name: string, password: string, inviteToken?: string) => {
  52. const result = await authApi.register({ email, name, password, inviteToken });
  53. const { user: u, token: t, acceptedProjects: ap, userName } = result;
  54. localStorage.setItem('vidreview_token', t);
  55. localStorage.setItem('vidreview_user', JSON.stringify(u));
  56. setToken(t);
  57. setUser(u);
  58. setAcceptedProjects(ap ?? []);
  59. setJustRegisteredName(userName ?? name);
  60. }, []);
  61. const logout = useCallback(async () => {
  62. try { await authApi.logout(); } catch { /* ignore */ }
  63. localStorage.removeItem('vidreview_token');
  64. localStorage.removeItem('vidreview_user');
  65. setToken(null);
  66. setUser(null);
  67. setAcceptedProjects([]);
  68. }, []);
  69. const refreshUser = useCallback(async () => {
  70. if (!token) return;
  71. try {
  72. const { user: u } = await usersApi.getMe(token);
  73. setUser(u);
  74. localStorage.setItem('vidreview_user', JSON.stringify(u));
  75. } catch { /* ignore refresh errors */ }
  76. }, [token]);
  77. const updateUserData = useCallback((data: Partial<User>) => {
  78. setUser(prev => {
  79. if (!prev) return prev;
  80. const updated = { ...prev, ...data };
  81. localStorage.setItem('vidreview_user', JSON.stringify(updated));
  82. return updated;
  83. });
  84. }, []);
  85. return (
  86. <AuthContext.Provider value={{ user, token, loading, acceptedProjects, justRegisteredName, clearAcceptedProjects, login, register, logout, refreshUser, updateUserData }}>
  87. {children}
  88. </AuthContext.Provider>
  89. );
  90. }
  91. export function useAuth() {
  92. const ctx = useContext(AuthContext);
  93. if (!ctx) throw new Error('useAuth must be used within AuthProvider');
  94. return ctx as Required<AuthContextValue>;
  95. }