|
@@ -1,6 +1,6 @@
|
|
|
'use client';
|
|
'use client';
|
|
|
|
|
|
|
|
-import React, { createContext, useContext, useState, useEffect, useCallback } from 'react';
|
|
|
|
|
|
|
+import React, { createContext, useContext, useState, useEffect, useCallback, useRef } from 'react';
|
|
|
import { authApi, usersApi, User } from './api';
|
|
import { authApi, usersApi, User } from './api';
|
|
|
|
|
|
|
|
interface AcceptedProject {
|
|
interface AcceptedProject {
|
|
@@ -31,22 +31,42 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|
|
const [acceptedProjects, setAcceptedProjects] = useState<AcceptedProject[]>([]);
|
|
const [acceptedProjects, setAcceptedProjects] = useState<AcceptedProject[]>([]);
|
|
|
const [justRegisteredName, setJustRegisteredName] = useState('');
|
|
const [justRegisteredName, setJustRegisteredName] = useState('');
|
|
|
|
|
|
|
|
|
|
+ // ── Init: load from localStorage then refresh from server ──────────────
|
|
|
|
|
+ const [initToken, setInitToken] = useState<string | null>(null);
|
|
|
|
|
+
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
const savedToken = localStorage.getItem('vidreview_token');
|
|
const savedToken = localStorage.getItem('vidreview_token');
|
|
|
const savedUser = localStorage.getItem('vidreview_user');
|
|
const savedUser = localStorage.getItem('vidreview_user');
|
|
|
|
|
|
|
|
if (savedToken && savedUser) {
|
|
if (savedToken && savedUser) {
|
|
|
setToken(savedToken);
|
|
setToken(savedToken);
|
|
|
|
|
+ setInitToken(savedToken);
|
|
|
try {
|
|
try {
|
|
|
setUser(JSON.parse(savedUser));
|
|
setUser(JSON.parse(savedUser));
|
|
|
} catch {
|
|
} catch {
|
|
|
localStorage.removeItem('vidreview_token');
|
|
localStorage.removeItem('vidreview_token');
|
|
|
localStorage.removeItem('vidreview_user');
|
|
localStorage.removeItem('vidreview_user');
|
|
|
|
|
+ setLoading(false);
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
|
|
+ } else {
|
|
|
|
|
+ setLoading(false);
|
|
|
}
|
|
}
|
|
|
- setLoading(false);
|
|
|
|
|
|
|
+ // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
}, []);
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
+ // After token is set from init, refresh from server for full user data (including storageQuota)
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (!initToken) return;
|
|
|
|
|
+ usersApi.getMe(initToken).then(({ user: u }) => {
|
|
|
|
|
+ setUser(u);
|
|
|
|
|
+ localStorage.setItem('vidreview_user', JSON.stringify(u));
|
|
|
|
|
+ }).catch(() => { /* ignore */ }).finally(() => {
|
|
|
|
|
+ setLoading(false);
|
|
|
|
|
+ });
|
|
|
|
|
+ // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
|
+ }, [initToken]);
|
|
|
|
|
+
|
|
|
const clearAcceptedProjects = useCallback(() => setAcceptedProjects([]), []);
|
|
const clearAcceptedProjects = useCallback(() => setAcceptedProjects([]), []);
|
|
|
|
|
|
|
|
const login = useCallback(async (email: string, password: string) => {
|
|
const login = useCallback(async (email: string, password: string) => {
|
|
@@ -108,3 +128,4 @@ export function useAuth() {
|
|
|
if (!ctx) throw new Error('useAuth must be used within AuthProvider');
|
|
if (!ctx) throw new Error('useAuth must be used within AuthProvider');
|
|
|
return ctx as Required<AuthContextValue>;
|
|
return ctx as Required<AuthContextValue>;
|
|
|
}
|
|
}
|
|
|
|
|
+
|