Files
bw-expo-app/app/(tabs)/explore.tsx
imeepos 668caaa91f 🔐 feat: 认证系统升级 - 从重定向到底部Modal的完整改造
## 核心功能

### 1. 全局认证架构
- 新增 AuthProvider:全局认证上下文,管理登录状态和Modal
- 新增 useAuthGuard Hook:提供 requireAuth 认证拦截功能
- 新增 AuthGuard 组件:包装需要认证的组件
- 新增 AuthPrompt 组件:多种样式的登录引导组件

### 2. 登录体验优化
- LoginModal 已优化为底部抽屉式设计
- 登录成功后自动关闭Modal并恢复用户操作
- 支持队列机制,多个认证检查依次执行

### 3. 页面架构调整
- 根布局集成 AuthProvider
- 移除 Tabs 布局的重定向逻辑
- Explore 页面添加认证保护,显示登录提示

### 4. API安全增强
- 新增 TokenManager:完整的Token生命周期管理
- 新增 SecureClient:安全API客户端,自动处理认证
- 更新 API 客户端:支持认证头和自动Token刷新

### 5. 文档完善
- AUTH_SYSTEM.md:完整的认证系统使用指南
- AUTH_UPGRADE_SUMMARY.md:升级总结文档

## 技术亮点

 TypeScript 完整支持,编译时类型检查
 React 最佳实践,合理使用 Hooks 和 Context
 优雅的错误处理,友好的用户提示
 流畅的动画效果,原生级用户体验

## 核心优势

- 流畅:无需页面跳转,Modal即用即走
- 智能:自动检测登录需求,无需手动判断
- 安全:完整Token管理和自动刷新机制
- 灵活:多种认证组件满足不同场景
- 易用:详细文档和示例,快速上手

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 11:18:20 +08:00

206 lines
5.6 KiB
TypeScript

import { useMemo, useState } from 'react';
import { ScrollView, StyleSheet, View, TouchableOpacity, Text } from 'react-native';
import {
CategoryTabs,
CommunityGrid,
FeatureCarousel,
Header,
PageLayout,
SectionHeader,
StatusBarSpacer,
type CommunityItem,
type FeatureItem,
} from '@/components/bestai';
import { useAuth } from '@/hooks/use-auth';
import { useAuthGuard } from '@/hooks/use-auth-guard';
const categories = [
{ id: 'visual-effects', label: 'Visual Effects' },
{ id: 'higgsfield-soul', label: 'Higgsfield Soul' },
{ id: 'higgsfield-apps', label: 'Higgsfield Apps' },
{ id: 'king', label: 'King' },
];
const baseFeatureItems: FeatureItem[] = [
{
id: 'sketch-video',
title: 'UNLIMITED SKETCH TO VIDEO',
subtitle: 'Bring your imagination to life with Sora 2',
image: 'https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?auto=format&fit=crop&w=800&q=80',
},
{
id: 'portrait-video',
title: 'UNLIMITED PORTRAIT TO VIDEO',
subtitle: 'Transform portraits into motion-driven stories',
image: 'https://images.unsplash.com/photo-1524504388940-b1c1722653e1?auto=format&fit=crop&w=800&q=80',
},
{
id: 'water-simulation',
title: 'FLUID SIMULATION PACK',
subtitle: 'Realistic water physics for cinematic scenes',
image: 'https://images.unsplash.com/photo-1505744386214-51dba16a26fc?auto=format&fit=crop&w=800&q=80',
},
];
const baseCommunityItems: CommunityItem[] = [
{
id: 'community-1',
chip: '64/10',
title: 'OBJECTS AROUND',
image: 'https://images.unsplash.com/photo-1524504388940-b1c1722653e1?auto=format&fit=crop&w=800&q=80',
actionLabel: 'Generate',
},
{
id: 'community-2',
chip: '64/10',
title: 'OBJECTS AROUND',
image: 'https://images.unsplash.com/photo-1544723795-3fb6469f5b39?auto=format&fit=crop&w=800&q=80',
actionLabel: 'Generate',
},
{
id: 'community-3',
chip: '64/10',
title: 'OBJECTS AROUND',
image: 'https://images.unsplash.com/photo-1524504388940-b1c1722653e1?auto=format&fit=crop&w=800&q=80',
actionLabel: 'Generate',
},
{
id: 'community-4',
chip: '64/10',
title: 'OBJECTS AROUND',
image: 'https://images.unsplash.com/photo-1531259683007-016a7b628fc4?auto=format&fit=crop&w=800&q=80',
actionLabel: 'Generate',
},
];
export default function ExploreScreen() {
const { isAuthenticated } = useAuth();
const { requireAuth } = useAuthGuard();
const [activeCategory, setActiveCategory] = useState(categories[0]?.id ?? 'visual-effects');
const featureItems = useMemo(() => {
return baseFeatureItems.map((item, index) => ({
...item,
id: `${activeCategory}-${index}`,
}));
}, [activeCategory]);
const communityItems = useMemo(() => {
return baseCommunityItems.map((item, index) => ({
...item,
id: `${activeCategory}-${index}`,
}));
}, [activeCategory]);
const handleGeneratePress = () => {
requireAuth(() => {
console.log('用户已登录,执行生成操作');
});
};
if (!isAuthenticated) {
return (
<PageLayout backgroundColor="#050505">
<ScrollView
style={styles.scroll}
contentContainerStyle={styles.contentContainer}
showsVerticalScrollIndicator={false}
>
<StatusBarSpacer />
<Header />
<View style={styles.loginPrompt}>
<View style={styles.lockIconContainer}>
<Text style={styles.lockIcon}>🔒</Text>
</View>
<Text style={styles.loginTitle}></Text>
<Text style={styles.loginSubtitle}>
使
</Text>
<TouchableOpacity
style={styles.loginButton}
onPress={() => requireAuth()}
activeOpacity={0.8}
>
<Text style={styles.loginButtonText}></Text>
</TouchableOpacity>
</View>
<View style={styles.bottomSpacer} />
</ScrollView>
</PageLayout>
);
}
return (
<PageLayout backgroundColor="#050505">
<ScrollView
style={styles.scroll}
contentContainerStyle={styles.contentContainer}
showsVerticalScrollIndicator={false}
>
<StatusBarSpacer />
<Header />
<CategoryTabs categories={categories} activeId={activeCategory} onChange={setActiveCategory} />
<FeatureCarousel items={featureItems} />
<SectionHeader title="WAN 2.5 COMMUNITY" />
<CommunityGrid items={communityItems} />
<View style={styles.bottomSpacer} />
</ScrollView>
</PageLayout>
);
}
const styles = StyleSheet.create({
scroll: {
flex: 1,
},
contentContainer: {
paddingTop: 16,
paddingBottom: 48,
},
bottomSpacer: {
height: 32,
},
loginPrompt: {
alignItems: 'center',
paddingVertical: 60,
paddingHorizontal: 32,
},
lockIconContainer: {
width: 80,
height: 80,
borderRadius: 40,
backgroundColor: 'rgba(255, 255, 255, 0.1)',
alignItems: 'center',
justifyContent: 'center',
marginBottom: 24,
},
lockIcon: {
fontSize: 40,
},
loginTitle: {
fontSize: 24,
fontWeight: '700',
color: '#ffffff',
marginBottom: 12,
},
loginSubtitle: {
fontSize: 16,
color: 'rgba(255, 255, 255, 0.6)',
textAlign: 'center',
lineHeight: 24,
marginBottom: 32,
},
loginButton: {
backgroundColor: '#d7ff1f',
paddingHorizontal: 32,
paddingVertical: 16,
borderRadius: 12,
},
loginButtonText: {
color: '#050505',
fontSize: 16,
fontWeight: '700',
},
});