80 lines
1.7 KiB
TypeScript
80 lines
1.7 KiB
TypeScript
import React, { memo, useCallback, useMemo } from 'react'
|
|
import { Pressable, StyleSheet, Text, View } from 'react-native'
|
|
import { Ionicons } from '@expo/vector-icons'
|
|
|
|
export interface LikeButtonProps {
|
|
liked?: boolean
|
|
loading?: boolean
|
|
count?: number
|
|
size?: number
|
|
onPress?: () => void
|
|
testID?: string
|
|
}
|
|
|
|
const LikeButtonComponent: React.FC<LikeButtonProps> = ({
|
|
liked = false,
|
|
loading = false,
|
|
count,
|
|
size = 24,
|
|
onPress,
|
|
testID,
|
|
}) => {
|
|
// 使用 useCallback 缓存 onPress 回调
|
|
const handlePress = useCallback(() => {
|
|
if (!loading && onPress) {
|
|
onPress()
|
|
}
|
|
}, [loading, onPress])
|
|
|
|
// 缓存样式计算
|
|
const containerStyle = useMemo(() => [styles.container, loading && styles.disabled], [loading])
|
|
|
|
// 根据点赞状态选择图标名称和颜色
|
|
const iconName = liked ? 'heart' : 'heart-outline'
|
|
const iconColor = liked ? '#FF3B30' : '#8E8E93'
|
|
|
|
return (
|
|
<Pressable
|
|
onPress={handlePress}
|
|
disabled={loading}
|
|
testID={testID}
|
|
style={containerStyle}
|
|
>
|
|
<View style={styles.content}>
|
|
<Ionicons
|
|
name={iconName}
|
|
size={size}
|
|
color={iconColor}
|
|
/>
|
|
{count !== undefined && (
|
|
<Text style={[styles.count, { fontSize: size * 0.7 }]}>
|
|
{count}
|
|
</Text>
|
|
)}
|
|
</View>
|
|
</Pressable>
|
|
)
|
|
}
|
|
|
|
export const LikeButton = memo(LikeButtonComponent)
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
padding: 8,
|
|
},
|
|
disabled: {
|
|
opacity: 0.5,
|
|
},
|
|
content: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
},
|
|
count: {
|
|
color: '#8E8E93',
|
|
fontWeight: '600',
|
|
marginLeft: 4,
|
|
},
|
|
})
|