feat: integrate UI components into Works List with pagination and refresh

- Add useWorksList hook for data fetching with pagination support
- Integrate RefreshControl for pull-to-refresh functionality
- Add LoadingState for initial loading display
- Add ErrorState with retry functionality
- Add PaginationLoader for load more indication
- Update WorksGallery to support refresh control and pagination
- Add comprehensive tests for worksList screen and hook
- Use useMemo for performance optimization

TODO: Replace mock API with actual backend endpoint when available

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
imeepos
2026-01-21 12:15:53 +08:00
parent beba2f2428
commit 10ee380051
5 changed files with 307 additions and 41 deletions

View File

@@ -1,4 +1,4 @@
import { useState } from 'react'
import { useState, useMemo } from 'react'
import {
View,
Text,
@@ -13,47 +13,47 @@ import { useTranslation } from 'react-i18next'
import { LeftArrowIcon, SearchIcon } from '@/components/icon'
import WorksGallery, { type Category, type WorkItem } from '@/components/WorksGallery'
const worksData: WorkItem[] = [
{ id: 1, date: new Date(2025, 10, 28), duration: '00:05', category: '萌宠' },
{ id: 2, date: new Date(2025, 10, 28), duration: '00:05', category: '写真' },
{ id: 3, date: new Date(2025, 10, 27), duration: '00:05', category: '萌宠' },
{ id: 4, date: new Date(2025, 10, 27), duration: '00:05', category: '合拍' },
{ id: 5, date: new Date(2025, 10, 27), duration: '00:05', category: '写真' },
{ id: 6, date: new Date(2025, 10, 27), duration: '00:05', category: '萌宠' },
{ id: 7, date: new Date(2025, 10, 26), duration: '00:05', category: '合拍' },
{ id: 8, date: new Date(2025, 10, 26), duration: '00:05', category: '写真' },
]
import RefreshControl from '@/components/RefreshControl'
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 = selectedCategory === categories[0]
? worksData
: worksData.filter(work => work.category === selectedCategory)
const filteredWorks = useMemo(() =>
selectedCategory === categories[0]
? works
: works.filter(work => work.category === selectedCategory),
[selectedCategory, works, categories]
)
const filteredGroupedWorks = filteredWorks.reduce((acc, work) => {
// 使用日期作为分组键,格式化为 YYYY-MM-DD 以便正确分组
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[]>)
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']}>
@@ -79,18 +79,31 @@ export default function WorksListScreen() {
</Pressable>
</View>
<WorksGallery
categories={categories}
selectedCategory={selectedCategory}
onCategoryChange={setSelectedCategory}
groupedWorks={filteredGroupedWorks}
onWorkPress={(id) => {
router.push({
pathname: '/generationRecord' as any,
params: { id: id.toString() },
})
}}
/>
{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() },
})
}}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={refresh} />
}
onEndReached={loadMore}
ListFooterComponent={
hasMore ? <PaginationLoader /> : undefined
}
/>
)}
</SafeAreaView>
)
}