expo-popcore-app/app/worksList.tsx

140 lines
4.3 KiB
TypeScript

import { useState, useMemo } from 'react'
import {
View,
Text,
StyleSheet,
StatusBar as RNStatusBar,
Pressable,
} from 'react-native'
import { StatusBar } from 'expo-status-bar'
import { SafeAreaView } from 'react-native-safe-area-context'
import { useRouter } from 'expo-router'
import { useTranslation } from 'react-i18next'
import { LeftArrowIcon, SearchIcon } from '@/components/icon'
import WorksGallery, { type Category, type WorkItem } from '@/components/WorksGallery'
import LoadingState from '@/components/LoadingState'
import ErrorState from '@/components/ErrorState'
import PaginationLoader from '@/components/PaginationLoader'
import { useWorksList } from '@/hooks/useWorksList'
export default function WorksListScreen() {
const { t } = useTranslation()
const router = useRouter()
const { works, loading, error, refreshing, hasMore, loadMore, refresh } = useWorksList()
const categories: Category[] = [
t('worksList.all') as Category,
t('worksList.pets') as Category,
t('worksList.portrait') as Category,
t('worksList.together') as Category,
]
const [selectedCategory, setSelectedCategory] = useState<Category>(categories[0])
const filteredWorks = useMemo(() =>
selectedCategory === categories[0]
? works
: works.filter(work => work.category === selectedCategory),
[selectedCategory, works, categories]
)
const filteredGroupedWorks = useMemo(() =>
filteredWorks.reduce((acc, work) => {
const dateKey = work.date instanceof Date
? work.date.toISOString().split('T')[0]
: new Date(work.date).toISOString().split('T')[0]
if (!acc[dateKey]) {
acc[dateKey] = []
}
acc[dateKey].push(work)
return acc
}, {} as Record<string, WorkItem[]>),
[filteredWorks]
)
return (
<SafeAreaView style={styles.container} edges={['top']}>
<StatusBar style="light" />
<RNStatusBar barStyle="light-content" />
{/* 顶部导航栏 */}
<View style={styles.header}>
<Pressable
style={styles.backButton}
onPress={() => router.push('/(tabs)/my')}
>
<LeftArrowIcon />
</Pressable>
<Text style={styles.headerTitle}>{t('worksList.title')}</Text>
<Pressable
style={styles.searchButton}
onPress={() => {
router.push('/searchWorks')
}}
>
<SearchIcon />
</Pressable>
</View>
{loading ? (
<LoadingState />
) : error ? (
<ErrorState message={error} onRetry={refresh} />
) : (
<WorksGallery
categories={categories}
selectedCategory={selectedCategory}
onCategoryChange={setSelectedCategory}
groupedWorks={filteredGroupedWorks}
onWorkPress={(id) => {
router.push({
pathname: '/generationRecord' as any,
params: { id: id.toString() },
})
}}
onEndReached={loadMore}
ListFooterComponent={
hasMore ? <PaginationLoader /> : undefined
}
/>
)}
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#090A0B',
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 12,
paddingTop: 13,
},
backButton: {
width: 22,
height: 22,
alignItems: 'center',
justifyContent: 'center',
},
headerTitle: {
color: '#F5F5F5',
fontSize: 14,
fontWeight: '500',
flex: 1,
textAlign: 'center',
},
searchButton: {
width: 20,
height: 20,
alignItems: 'center',
justifyContent: 'center',
},
})