111 lines
2.4 KiB
TypeScript
111 lines
2.4 KiB
TypeScript
import React from 'react';
|
||
import { View, StyleSheet } from 'react-native';
|
||
import { useAuth } from '@/hooks/use-auth';
|
||
import { useAuthGuard } from '@/hooks/use-auth-guard';
|
||
import { ThemedText } from '@/components/themed-text';
|
||
|
||
interface AuthGuardProps {
|
||
children: React.ReactNode;
|
||
fallback?: React.ReactNode;
|
||
showLoginPrompt?: boolean;
|
||
title?: string;
|
||
subtitle?: string;
|
||
}
|
||
|
||
/**
|
||
* 认证守卫组件
|
||
* 用于包装需要登录才能访问的内容
|
||
*
|
||
* @param children - 需要保护的内容
|
||
* @param fallback - 未登录时显示的自定义内容(可选)
|
||
* @param showLoginPrompt - 是否显示默认的登录提示(默认true)
|
||
* @param title - 自定义提示标题
|
||
* @param subtitle - 自定义提示副标题
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* <AuthGuard>
|
||
* <ProtectedComponent />
|
||
* </AuthGuard>
|
||
*
|
||
* // 自定义提示
|
||
* <AuthGuard
|
||
* title="需要登录"
|
||
* subtitle="登录后即可使用此功能"
|
||
* >
|
||
* <ProtectedComponent />
|
||
* </AuthGuard>
|
||
* ```
|
||
*/
|
||
export function AuthGuard({
|
||
children,
|
||
fallback,
|
||
showLoginPrompt = true,
|
||
title = '需要登录',
|
||
subtitle = '请先登录以继续使用',
|
||
}: AuthGuardProps) {
|
||
const { isAuthenticated, isLoading } = useAuth();
|
||
|
||
if (isLoading) {
|
||
return <View style={styles.loadingContainer} />;
|
||
}
|
||
|
||
if (!isAuthenticated) {
|
||
if (fallback) {
|
||
return <>{fallback}</>;
|
||
}
|
||
|
||
if (showLoginPrompt) {
|
||
return (
|
||
<View style={styles.promptContainer}>
|
||
<View style={styles.iconContainer}>
|
||
<ThemedText style={styles.lockIcon}>🔒</ThemedText>
|
||
</View>
|
||
<ThemedText style={styles.title}>{title}</ThemedText>
|
||
<ThemedText style={styles.subtitle}>{subtitle}</ThemedText>
|
||
</View>
|
||
);
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
return <>{children}</>;
|
||
}
|
||
|
||
const styles = StyleSheet.create({
|
||
loadingContainer: {
|
||
flex: 1,
|
||
},
|
||
promptContainer: {
|
||
flex: 1,
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
padding: 32,
|
||
},
|
||
iconContainer: {
|
||
width: 80,
|
||
height: 80,
|
||
borderRadius: 40,
|
||
backgroundColor: 'rgba(0, 122, 255, 0.1)',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
marginBottom: 24,
|
||
},
|
||
lockIcon: {
|
||
fontSize: 40,
|
||
},
|
||
title: {
|
||
fontSize: 24,
|
||
fontWeight: '700',
|
||
marginBottom: 12,
|
||
textAlign: 'center',
|
||
},
|
||
subtitle: {
|
||
fontSize: 16,
|
||
opacity: 0.7,
|
||
textAlign: 'center',
|
||
lineHeight: 24,
|
||
},
|
||
});
|