mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 22:06:38 +08:00
9f15f42b47
Document that NEXT_PUBLIC_WAVELET_BACKEND_URL must stay unset for local next dev so axios hits /api/* and Next rewrites proxy to the backend.
161 lines
3.8 KiB
TypeScript
161 lines
3.8 KiB
TypeScript
'use client';
|
|
|
|
import {
|
|
createContext,
|
|
ReactNode,
|
|
useCallback,
|
|
useContext,
|
|
useEffect,
|
|
useRef,
|
|
useState,
|
|
} from 'react';
|
|
|
|
import { AuthService } from '@/lib/services/auth';
|
|
import { User } from '@/lib/services/auth/types';
|
|
import { useTranslations } from 'next-intl';
|
|
|
|
/** 用户状态接口 */
|
|
interface UserState {
|
|
user: User | null;
|
|
loading: boolean;
|
|
error: string | null;
|
|
}
|
|
|
|
/** 用户上下文接口 */
|
|
interface UserContextValue extends UserState {
|
|
setUser: (user: User) => void;
|
|
refetch: () => Promise<void>;
|
|
logout: () => Promise<void>;
|
|
}
|
|
|
|
/** 用户上下文 */
|
|
const UserContext = createContext<UserContextValue | undefined>(undefined);
|
|
|
|
/**
|
|
* 用户Provider组件
|
|
*
|
|
* @param {React.ReactNode} children - 用户 Provider 的子元素
|
|
* @returns {React.ReactNode} 用户 Provider 组件
|
|
* @example
|
|
* ```tsx
|
|
* <UserProvider>
|
|
* <UserContext.Provider value={{ user, loading, error, refetch, updatePayKey, getTrustLevelLabel, getPayLevelLabel, logout }}>
|
|
* {children}
|
|
* </UserContext.Provider>
|
|
* </UserProvider>
|
|
* ```
|
|
*/
|
|
export function UserProvider({ children }: { children: ReactNode }) {
|
|
const [state, setState] = useState<UserState>({
|
|
user: null,
|
|
loading: true,
|
|
error: null,
|
|
});
|
|
|
|
const isMountedRef = useRef(true);
|
|
const t = useTranslations('contexts.user');
|
|
|
|
/** 获取用户信息 */
|
|
const fetchUser = useCallback(async () => {
|
|
try {
|
|
setState((prev) => ({ ...prev, loading: true, error: null }));
|
|
const user = await AuthService.getUserInfo();
|
|
|
|
if (!isMountedRef.current) return;
|
|
|
|
setState({ user, loading: false, error: null });
|
|
} catch (error) {
|
|
if (!isMountedRef.current) return;
|
|
|
|
setState({
|
|
user: null,
|
|
loading: false,
|
|
error: error instanceof Error ? error.message : t('getUserInfoFailed'),
|
|
});
|
|
}
|
|
}, [t]);
|
|
|
|
/** 重新获取用户信息 */
|
|
const refetch = useCallback(async () => {
|
|
await fetchUser();
|
|
}, [fetchUser]);
|
|
|
|
/** 直接设置用户信息(登录/注册后免二次请求) */
|
|
const setUser = useCallback((user: User) => {
|
|
setState({ user, loading: false, error: null });
|
|
}, []);
|
|
|
|
/** 用户登出 */
|
|
const logout = useCallback(async () => {
|
|
try {
|
|
await AuthService.logout();
|
|
|
|
if (!isMountedRef.current) return;
|
|
|
|
setState({ user: null, loading: false, error: null });
|
|
window.location.href = '/login';
|
|
} catch (error) {
|
|
if (!isMountedRef.current) {
|
|
throw error;
|
|
}
|
|
|
|
const errorMessage =
|
|
error instanceof Error ? error.message : t('logoutFailed');
|
|
setState((prev) => ({
|
|
...prev,
|
|
error: errorMessage,
|
|
}));
|
|
throw new Error(errorMessage);
|
|
}
|
|
}, [t]);
|
|
|
|
/** 组件挂载时获取用户信息(登录/注册页跳过,避免无意义请求) */
|
|
useEffect(() => {
|
|
isMountedRef.current = true;
|
|
|
|
const path = window.location.pathname;
|
|
if (path === '/login' || path === '/register') {
|
|
setState({ user: null, loading: false, error: null });
|
|
return () => {
|
|
isMountedRef.current = false;
|
|
};
|
|
}
|
|
|
|
fetchUser();
|
|
|
|
return () => {
|
|
isMountedRef.current = false;
|
|
};
|
|
}, [fetchUser]);
|
|
|
|
return (
|
|
<UserContext.Provider
|
|
value={{
|
|
...state,
|
|
setUser,
|
|
refetch,
|
|
logout,
|
|
}}
|
|
>
|
|
{children}
|
|
</UserContext.Provider>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 使用用户上下文的Hook
|
|
*
|
|
* @returns {UserContextValue} 用户上下文值
|
|
* @example
|
|
* ```tsx
|
|
* const { user, loading, error, refetch, updatePayKey, getTrustLevelLabel, getPayLevelLabel, logout } = useUser()
|
|
* ```
|
|
*/
|
|
export function useUser(): UserContextValue {
|
|
const context = useContext(UserContext);
|
|
if (context === undefined) {
|
|
throw new Error('useUser must be used within a UserProvider');
|
|
}
|
|
return context;
|
|
}
|