94 lines
2.5 KiB
TypeScript
94 lines
2.5 KiB
TypeScript
import { useAuth } from '@/hooks/use-auth';
|
||
import { authEvents } from '@/lib/api/client';
|
||
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react';
|
||
import { LoginModal } from './login-modal';
|
||
|
||
export interface AuthContextType {
|
||
requireAuth: (callback?: () => void) => boolean;
|
||
showLoginModal: boolean;
|
||
setShowLoginModal: (show: boolean) => void;
|
||
pendingCallback: (() => void) | null;
|
||
}
|
||
|
||
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
||
|
||
export { AuthContext };
|
||
|
||
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||
const { isAuthenticated, isLoading } = useAuth();
|
||
const [showLoginModal, setShowLoginModal] = useState(false);
|
||
const [pendingCallback, setPendingCallback] = useState<(() => void) | null>(null);
|
||
|
||
// 认证拦截函数
|
||
const requireAuth = useCallback((callback?: () => void): boolean => {
|
||
if (isLoading) {
|
||
// 如果认证状态还在加载中,不执行任何操作
|
||
return false;
|
||
}
|
||
|
||
if (!isAuthenticated) {
|
||
// 未登录,保存回调并显示登录Modal
|
||
setPendingCallback(() => callback || null);
|
||
setShowLoginModal(true);
|
||
return false;
|
||
}
|
||
|
||
// 已登录,执行回调
|
||
if (callback) {
|
||
callback();
|
||
}
|
||
return true;
|
||
}, [isAuthenticated, isLoading]);
|
||
|
||
// 监听登录状态变化
|
||
useEffect(() => {
|
||
if (isAuthenticated && showLoginModal) {
|
||
// 登录成功,自动关闭Modal并执行回调
|
||
if (pendingCallback) {
|
||
pendingCallback();
|
||
setPendingCallback(null);
|
||
}
|
||
setShowLoginModal(false);
|
||
}
|
||
}, [isAuthenticated, showLoginModal, pendingCallback]);
|
||
|
||
// 监听 401 未授权事件
|
||
useEffect(() => {
|
||
const unsubscribe = authEvents.onUnauthorized(() => {
|
||
if (!isLoading && !isAuthenticated) {
|
||
setShowLoginModal(true);
|
||
}
|
||
});
|
||
|
||
return unsubscribe;
|
||
}, [isLoading, isAuthenticated]);
|
||
|
||
const value = {
|
||
requireAuth,
|
||
showLoginModal,
|
||
setShowLoginModal,
|
||
pendingCallback,
|
||
};
|
||
|
||
return (
|
||
<AuthContext.Provider value={value}>
|
||
{children}
|
||
<LoginModal
|
||
visible={showLoginModal}
|
||
onClose={() => {
|
||
setShowLoginModal(false);
|
||
setPendingCallback(null);
|
||
}}
|
||
/>
|
||
</AuthContext.Provider>
|
||
);
|
||
}
|
||
|
||
export function useAuthGuard() {
|
||
const context = useContext(AuthContext);
|
||
if (context === undefined) {
|
||
throw new Error('useAuthGuard must be used within an AuthProvider');
|
||
}
|
||
return context;
|
||
}
|