expo-popcore-app/app/(tabs)/index.tsx

198 lines
6.6 KiB
TypeScript

import { useLocalSearchParams, useRouter } from 'expo-router'
import { StatusBar } from 'expo-status-bar'
import { useEffect } from 'react'
import {
Platform,
ScrollView,
StyleSheet,
StatusBar as RNStatusBar,
View
} from 'react-native'
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
import { TitleBar, HeroSlider, TabNavigation, TemplateGrid } from '@/components/blocks/home'
import ErrorState from '@/components/ErrorState'
import LoadingState from '@/components/LoadingState'
import { useActivates } from '@/hooks/use-activates'
import { useCategories } from '@/hooks/use-categories'
import { useStickyTabs } from '@/hooks/use-sticky-tabs'
import { useTabNavigation } from '@/hooks/use-tab-navigation'
import { useTemplateFilter } from '@/hooks/use-template-filter'
export default function HomeScreen() {
const insets = useSafeAreaInsets()
const router = useRouter()
const params = useLocalSearchParams()
// 数据加载
const { load: loadCategories, data: categoriesData, loading: categoriesLoading, error: categoriesError } = useCategories()
const { load: loadActivates, data: activatesData } = useActivates()
// 标签导航状态
const {
activeIndex,
selectedCategoryId,
currentCategory,
tabs,
selectTab,
selectCategoryById,
} = useTabNavigation({
categories: categoriesData?.categories || [],
initialCategoryId: params.categoryId as string | undefined,
})
// 吸顶状态
const {
isSticky,
tabsHeight,
titleBarHeightRef,
handleScroll,
handleTabsLayout,
handleTitleBarLayout,
} = useStickyTabs()
// 模板过滤
const { filteredTemplates } = useTemplateFilter({
templates: currentCategory?.templates || [],
excludeVideo: true,
})
// 初始化加载
useEffect(() => {
loadCategories()
loadActivates()
}, [])
// 路由参数同步
useEffect(() => {
if (params.categoryId) {
selectCategoryById(params.categoryId as string)
}
}, [params.categoryId])
// 状态判断
const showLoading = categoriesLoading
const showEmptyState = !categoriesLoading && categoriesData?.categories?.length === 0
const showEmptyTemplates = !categoriesLoading && !showEmptyState && filteredTemplates.length === 0
// 导航处理
const handlePointsPress = () => router.push('/membership' as any)
const handleSearchPress = () => router.push('/searchTemplate')
const handleActivityPress = (link: string) => router.push(link as any)
const handleArrowPress = () => router.push({
pathname: '/channels' as any,
params: selectedCategoryId ? { categoryId: selectedCategoryId } : undefined,
})
const handleTemplatePress = (id: string) => router.push({
pathname: '/templateDetail' as any,
params: { id },
})
return (
<SafeAreaView style={styles.container} edges={['top']}>
<StatusBar style="light" />
<RNStatusBar barStyle="light-content" />
{/* 标题栏 */}
<TitleBar
onPointsPress={handlePointsPress}
onSearchPress={handleSearchPress}
onLayout={handleTitleBarLayout}
/>
{/* 吸顶标签导航 */}
{isSticky && !showLoading && !showEmptyState && (
<View style={[styles.stickyTabsWrapper, { top: titleBarHeightRef.current + insets.top }]}>
<TabNavigation
tabs={tabs}
activeIndex={activeIndex}
onTabPress={selectTab}
showArrow={true}
onArrowPress={handleArrowPress}
isSticky={true}
/>
</View>
)}
<ScrollView
style={styles.scrollView}
contentContainerStyle={styles.scrollContent}
showsVerticalScrollIndicator={false}
onScroll={(e) => handleScroll(e.nativeEvent.contentOffset.y)}
scrollEventThrottle={Platform.OS === 'ios' ? 16 : 50}
>
{/* 活动轮播图 */}
<HeroSlider
activities={activatesData?.activities || []}
onActivityPress={handleActivityPress}
/>
{/* 标签导航 */}
{!showLoading && !showEmptyState && (
<View
onLayout={(e) => handleTabsLayout(e.nativeEvent.layout.y, e.nativeEvent.layout.height)}
style={isSticky ? { opacity: 0, height: tabsHeight } : undefined}
>
<TabNavigation
tabs={tabs}
activeIndex={activeIndex}
onTabPress={selectTab}
showArrow={true}
onArrowPress={handleArrowPress}
/>
</View>
)}
{/* 加载状态 */}
{showLoading && <LoadingState />}
{/* 错误状态 */}
{categoriesError && (
<ErrorState message="加载分类失败" onRetry={() => loadCategories()} />
)}
{/* 空状态 - 分类数据为空 */}
{showEmptyState && !categoriesError && (
<ErrorState message="暂无分类数据" onRetry={() => loadCategories()} />
)}
{/* 空状态 - 当前分类下没有模板 */}
{showEmptyTemplates && (
<ErrorState message="该分类暂无模板" onRetry={() => loadCategories()} />
)}
{/* 模板网格 */}
{!showLoading && !showEmptyState && !showEmptyTemplates && (
<TemplateGrid
templates={filteredTemplates}
onTemplatePress={handleTemplatePress}
/>
)}
</ScrollView>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#090A0B',
},
scrollView: {
flex: 1,
backgroundColor: '#090A0B',
},
scrollContent: {
flexGrow: 1,
backgroundColor: '#090A0B',
},
stickyTabsWrapper: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
zIndex: 100,
backgroundColor: '#090A0B',
},
})