XuqmGroup-RNChatDemo/src/components/ConversationItem.tsx

68 行
2.8 KiB
TypeScript

import React from 'react'
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'
import type { ConversationData } from '@xuqm/rn-sdk'
import { formatTime } from '../utils/format'
interface Props {
item: ConversationData & { targetName?: string; targetAvatar?: string }
onPress(): void
}
function Avatar({ name, uri }: { name: string; uri?: string }) {
const letter = (name || '?').charAt(0).toUpperCase()
return (
<View style={styles.avatar}>
<Text style={styles.avatarText}>{letter}</Text>
</View>
)
}
function lastMsgPreview(item: ConversationData): string {
switch (item.lastMsgType) {
case 'IMAGE': return '[图片]'
case 'VIDEO': return '[视频]'
case 'AUDIO': return '[语音]'
case 'FILE': return '[文件]'
default: return item.lastMsgContent
}
}
export default function ConversationItem({ item, onPress }: Props) {
return (
<TouchableOpacity style={styles.row} onPress={onPress} activeOpacity={0.7}>
<Avatar name={item.targetName ?? item.targetId} uri={item.targetAvatar} />
<View style={styles.body}>
<View style={styles.topRow}>
<Text style={styles.name} numberOfLines={1}>{item.targetName ?? item.targetId}</Text>
<Text style={styles.time}>{item.lastMsgTime ? formatTime(item.lastMsgTime) : ''}</Text>
</View>
<View style={styles.bottomRow}>
<Text style={[styles.preview, item.isMuted && styles.muted]} numberOfLines={1}>
{item.isMuted ? '[已静音] ' : ''}{lastMsgPreview(item)}
</Text>
{item.unreadCount > 0 && (
<View style={styles.badge}>
<Text style={styles.badgeText}>{item.unreadCount > 99 ? '99+' : item.unreadCount}</Text>
</View>
)}
</View>
</View>
</TouchableOpacity>
)
}
const styles = StyleSheet.create({
row: { flexDirection: 'row', padding: 12, alignItems: 'center', backgroundColor: '#fff' },
avatar: { width: 48, height: 48, borderRadius: 8, backgroundColor: '#07C160', alignItems: 'center', justifyContent: 'center', marginRight: 12 },
avatarText: { color: '#fff', fontSize: 20, fontWeight: '600' },
body: { flex: 1 },
topRow: { flexDirection: 'row', justifyContent: 'space-between', marginBottom: 4 },
name: { flex: 1, fontSize: 16, fontWeight: '500', color: '#111', marginRight: 8 },
time: { fontSize: 12, color: '#999' },
bottomRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },
preview: { flex: 1, fontSize: 14, color: '#888', marginRight: 8 },
muted: { color: '#bbb' },
badge: { backgroundColor: '#ff3b30', borderRadius: 10, minWidth: 20, height: 20, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 4 },
badgeText: { color: '#fff', fontSize: 11, fontWeight: '700' },
})