132 lines
3.3 KiB
TypeScript
132 lines
3.3 KiB
TypeScript
import React from 'react';
|
|
import { View, Text, StyleSheet } from 'react-native';
|
|
import Animated, {
|
|
useSharedValue,
|
|
useAnimatedStyle,
|
|
withSpring,
|
|
withTiming,
|
|
runOnJS,
|
|
} from 'react-native-reanimated';
|
|
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
import { AppColors } from '@/constants/theme';
|
|
import { Conversation } from '@/data/mockData';
|
|
|
|
interface ConversationItemProps {
|
|
conversation: Conversation;
|
|
onPress: () => void;
|
|
}
|
|
|
|
export function ConversationItem({ conversation, onPress }: ConversationItemProps) {
|
|
const scale = useSharedValue(1);
|
|
const opacity = useSharedValue(1);
|
|
|
|
const animatedStyle = useAnimatedStyle(() => ({
|
|
transform: [{ scale: scale.value }],
|
|
opacity: opacity.value,
|
|
}));
|
|
|
|
const tapGesture = Gesture.Tap()
|
|
.onBegin(() => {
|
|
scale.value = withSpring(0.97, { damping: 15, stiffness: 400 });
|
|
opacity.value = withTiming(0.8, { duration: 100 });
|
|
})
|
|
.onFinalize((_, success) => {
|
|
scale.value = withSpring(1, { damping: 15, stiffness: 400 });
|
|
opacity.value = withTiming(1, { duration: 150 });
|
|
if (success) {
|
|
runOnJS(onPress)();
|
|
}
|
|
});
|
|
|
|
return (
|
|
<GestureDetector gesture={tapGesture}>
|
|
<Animated.View style={[styles.container, animatedStyle]}>
|
|
<View style={styles.avatar}>
|
|
<Text style={styles.avatarEmoji}>{conversation.avatarEmoji}</Text>
|
|
</View>
|
|
|
|
<View style={styles.content}>
|
|
<Text style={styles.title} numberOfLines={1}>
|
|
{conversation.title}
|
|
</Text>
|
|
<Text style={styles.preview} numberOfLines={1}>
|
|
{conversation.lastMessage}
|
|
</Text>
|
|
</View>
|
|
|
|
<View style={styles.meta}>
|
|
<Text style={styles.timestamp}>{conversation.timestamp}</Text>
|
|
{conversation.unreadCount && conversation.unreadCount > 0 && (
|
|
<View style={styles.badge}>
|
|
<Text style={styles.badgeText}>{conversation.unreadCount}</Text>
|
|
</View>
|
|
)}
|
|
</View>
|
|
</Animated.View>
|
|
</GestureDetector>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
paddingVertical: 14,
|
|
paddingHorizontal: 20,
|
|
backgroundColor: AppColors.cream,
|
|
},
|
|
avatar: {
|
|
width: 56,
|
|
height: 56,
|
|
borderRadius: 14,
|
|
backgroundColor: AppColors.lavender,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
marginRight: 14,
|
|
shadowColor: AppColors.deepPurple,
|
|
shadowOffset: { width: 0, height: 2 },
|
|
shadowOpacity: 0.08,
|
|
shadowRadius: 8,
|
|
elevation: 2,
|
|
},
|
|
avatarEmoji: {
|
|
fontSize: 26,
|
|
},
|
|
content: {
|
|
flex: 1,
|
|
marginRight: 12,
|
|
},
|
|
title: {
|
|
fontSize: 17,
|
|
fontWeight: '500',
|
|
color: AppColors.deepPurple,
|
|
marginBottom: 4,
|
|
},
|
|
preview: {
|
|
fontSize: 14,
|
|
color: AppColors.slatePurple,
|
|
lineHeight: 20,
|
|
},
|
|
meta: {
|
|
alignItems: 'flex-end',
|
|
},
|
|
timestamp: {
|
|
fontSize: 12,
|
|
color: AppColors.slatePurple,
|
|
marginBottom: 6,
|
|
},
|
|
badge: {
|
|
width: 20,
|
|
height: 20,
|
|
borderRadius: 10,
|
|
backgroundColor: AppColors.mediumPurple,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
},
|
|
badgeText: {
|
|
fontSize: 12,
|
|
color: AppColors.white,
|
|
fontWeight: '500',
|
|
},
|
|
});
|