expo-popcore-app/app/searchWorksResults.tsx

238 lines
7.5 KiB
TypeScript

import { useState } from 'react'
import {
StyleSheet,
StatusBar as RNStatusBar,
View,
Text,
ActivityIndicator,
} from 'react-native'
import { StatusBar } from 'expo-status-bar'
import { SafeAreaView } from 'react-native-safe-area-context'
import { useRouter, useLocalSearchParams } from 'expo-router'
import { useTranslation } from 'react-i18next'
import SearchBar from '@/components/SearchBar'
import WorksGallery, { type Category, type WorkItem } from '@/components/WorksGallery'
import { useWorksSearch, type WorksSearchResult } from '@/hooks/use-works-search'
/**
* 将 API 返回的作品数据转换为 WorkItem 格式
*/
function convertToWorkItem(apiWork: WorksSearchResult): WorkItem {
return {
id: parseInt(apiWork.id, 10),
date: apiWork.createdAt,
duration: `${String(Math.floor(apiWork.duration / 60)).padStart(2, '0')}:${String(
apiWork.duration % 60
).padStart(2, '0')}`,
// TODO: 从 API 响应中获取分类信息
category: '写真' as Category,
}
}
/**
* 按日期分组作品
*/
function groupWorksByDate(works: WorkItem[]): Record<string, WorkItem[]> {
return works.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[]>)
}
export default function SearchWorksResultsScreen() {
const { t } = useTranslation()
const router = useRouter()
const params = useLocalSearchParams()
const [searchText, setSearchText] = useState((params.q as string) || '')
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 { data, works, isLoading, error } = useWorksSearch({
keyword: searchText,
category: selectedCategory,
page: 1,
limit: 20,
})
// 根据分类过滤结果(如果 API 不支持分类筛选,则在前端过滤)
const filteredWorks =
selectedCategory === categories[0]
? works
: works.filter((work) => {
const convertedWork = convertToWorkItem(work)
return convertedWork.category === selectedCategory
})
// 转换为 WorkItem 格式
const workItems: WorkItem[] = filteredWorks.map(convertToWorkItem)
// 按日期分组
const groupedWorks = groupWorksByDate(workItems)
// 处理错误状态
if (error) {
return (
<SafeAreaView style={styles.container} edges={['top']}>
<StatusBar style="light" />
<RNStatusBar barStyle="light-content" />
<SearchBar
searchText={searchText}
onSearchTextChange={setSearchText}
onSearch={(text) => {
router.push({
pathname: '/searchWorksResults',
params: { q: text },
})
}}
onBack={() => router.back()}
placeholder={t('search.searchWorks')}
marginBottom={0}
readOnly={true}
onInputPress={() => {
router.push({
pathname: '/searchWorks',
params: { q: searchText },
})
}}
onClearPress={() => {
router.push({
pathname: '/searchWorks',
params: { q: '' },
})
}}
/>
<View style={styles.errorContainer}>
<Text style={styles.errorText}>
{error instanceof Error ? error.message : t('search.errorOccurred')}
</Text>
</View>
</SafeAreaView>
)
}
return (
<SafeAreaView style={styles.container} edges={['top']}>
<StatusBar style="light" />
<RNStatusBar barStyle="light-content" />
{/* Top Bar with Search */}
<SearchBar
searchText={searchText}
onSearchTextChange={setSearchText}
onSearch={(text) => {
router.push({
pathname: '/searchWorksResults',
params: { q: text },
})
}}
onBack={() => router.back()}
placeholder={t('search.searchWorks')}
marginBottom={0}
readOnly={true}
onInputPress={() => {
router.push({
pathname: '/searchWorks',
params: { q: searchText },
})
}}
onClearPress={() => {
router.push({
pathname: '/searchWorks',
params: { q: '' },
})
}}
/>
{/* Loading State */}
{isLoading && (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#FF6699" />
<Text style={styles.loadingText}>{t('search.loading')}</Text>
</View>
)}
{/* Empty State */}
{!isLoading && workItems.length === 0 && searchText.trim() && (
<View style={styles.emptyContainer}>
<Text style={styles.emptyText}>{t('search.noResults')}</Text>
</View>
)}
{/* Results */}
{!isLoading && workItems.length > 0 && (
<WorksGallery
categories={categories}
selectedCategory={selectedCategory}
onCategoryChange={setSelectedCategory}
groupedWorks={groupedWorks}
onWorkPress={(id) => {
router.push({
pathname: '/generationRecord' as any,
params: { id: id.toString() },
})
}}
/>
)}
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#090A0B',
},
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#090A0B',
},
loadingText: {
color: '#F5F5F5',
fontSize: 14,
marginTop: 12,
},
errorContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#090A0B',
paddingHorizontal: 20,
},
errorText: {
color: '#FF6666',
fontSize: 14,
textAlign: 'center',
},
emptyContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#090A0B',
},
emptyText: {
color: '#8A8A8A',
fontSize: 14,
},
})