140 lines
4.3 KiB
TypeScript
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',
|
|
},
|
|
})
|
|
|