expo-popcore-old/components/auth/auth-provider.tsx

94 lines
2.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}