expo-popcore-app/components/SearchResultsGrid.tsx

170 lines
4.6 KiB
TypeScript

import { useState } from 'react'
import {
View,
Text,
StyleSheet,
ScrollView,
Dimensions,
Pressable,
} from 'react-native'
import { Image } from 'expo-image'
import { useRouter } from 'expo-router'
import { useTranslation } from 'react-i18next'
import { WhiteStarIcon } from '@/components/icon'
import type { TemplateGeneration } from '@/hooks'
const { width: screenWidth } = Dimensions.get('window')
interface SearchResultItem extends TemplateGeneration {
height: number
title: string
image: string
}
interface SearchResultsGridProps {
results: SearchResultItem[]
}
export default function SearchResultsGrid({ results }: SearchResultsGridProps) {
const { t } = useTranslation()
const router = useRouter()
const [gridWidth, setGridWidth] = useState(screenWidth)
const horizontalPadding = 8 * 2
const cardGap = 5
const cardWidth = (gridWidth - horizontalPadding - cardGap) / 2
const handleSameStylePress = (item: SearchResultItem) => {
const templateData = {
id: item.template?.id,
videoUrl: item.resultUrl?.[0] || item.originalUrl,
thumbnailUrl: item.template?.coverImageUrl,
title: item.template?.title || item.template?.titleEn || '',
}
router.push({
pathname: '/generateVideo' as any,
params: { template: JSON.stringify(templateData) },
} as any)
}
if (results.length === 0) {
return (
<View style={styles.emptyContainer}>
<Text style={styles.emptyText}>{t('searchResults.noResults')}</Text>
</View>
)
}
return (
<ScrollView
style={styles.scrollView}
showsVerticalScrollIndicator={false}
>
<View
style={styles.gridContainer}
onLayout={(event) => {
const { width } = event.nativeEvent.layout
setGridWidth(width)
}}
>
{results.map((item, index) => (
<View
key={item.id}
style={[
styles.card,
{ width: cardWidth },
index % 2 === 0 ? styles.cardLeft : styles.cardRight,
]}
>
<View
style={[styles.cardImageContainer, { height: item.height }]}
>
<Image source={item.image} style={styles.cardImage} contentFit="cover" />
</View>
<Text style={styles.cardTitle} numberOfLines={1}>
{item.title}
</Text>
<Pressable
style={styles.sameStyleButton}
onPress={() => handleSameStylePress(item)}
>
<WhiteStarIcon />
<Text style={styles.sameStyleText}>{t('searchResults.makeSame')}</Text>
</Pressable>
</View>
))}
</View>
</ScrollView>
)
}
const styles = StyleSheet.create({
scrollView: {
flex: 1,
},
gridContainer: {
flexDirection: 'row',
flexWrap: 'wrap',
paddingHorizontal: 8,
justifyContent: 'space-between',
},
card: {
backgroundColor: '#16181B',
borderBottomLeftRadius: 12,
borderBottomRightRadius: 12,
marginBottom: 12,
},
cardLeft: {
marginRight: 0,
},
cardRight: {
marginLeft: 0,
},
cardImageContainer: {
width: '100%',
borderRadius: 12,
overflow: 'hidden',
marginBottom: 8,
},
cardImage: {
width: '100%',
height: '100%',
},
cardTitle: {
color: '#FFFFFF',
fontSize: 12,
fontWeight: '500',
paddingHorizontal: 8,
},
sameStyleButton: {
flexDirection: 'row',
alignItems: 'center',
gap: 4,
backgroundColor: '#262A31',
height: 32,
marginHorizontal: 8,
marginTop: 10,
marginBottom: 8,
borderRadius: 100,
justifyContent: 'center',
},
sameStyleText: {
color: '#FFFFFF',
fontSize: 12,
fontWeight: '500',
lineHeight: 17,
},
emptyContainer: {
flex: 1,
backgroundColor: '#090A0B',
justifyContent: 'center',
alignItems: 'center',
},
emptyText: {
color: '#8A8A8A',
fontSize: 14,
},
})