Include app source and update .gitignore to exclude local release artifacts and signing files. Made-with: Cursor
148 lines
3.9 KiB
TypeScript
148 lines
3.9 KiB
TypeScript
/**
|
|
* NotificationItem 通知项组件
|
|
* 根据通知类型显示不同图标和内容
|
|
*
|
|
* @deprecated 请使用 SystemMessageItem 组件代替
|
|
* 该组件使用旧的 Notification 类型,新功能请使用 SystemMessageItem
|
|
*/
|
|
|
|
import React from 'react';
|
|
import { View, TouchableOpacity, StyleSheet } from 'react-native';
|
|
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
|
import { formatDistanceToNow } from 'date-fns';
|
|
import { zhCN } from 'date-fns/locale';
|
|
import { colors, spacing, borderRadius } from '../../theme';
|
|
import { Notification, NotificationType } from '../../types';
|
|
import Text from '../common/Text';
|
|
import Avatar from '../common/Avatar';
|
|
|
|
interface NotificationItemProps {
|
|
notification: Notification;
|
|
onPress: () => void;
|
|
}
|
|
|
|
// 通知类型到图标和颜色的映射
|
|
const getNotificationIcon = (type: NotificationType): { icon: string; color: string } => {
|
|
switch (type) {
|
|
case 'like_post':
|
|
return { icon: 'heart', color: colors.error.main };
|
|
case 'like_comment':
|
|
return { icon: 'heart', color: colors.error.main };
|
|
case 'comment':
|
|
return { icon: 'comment', color: colors.info.main };
|
|
case 'reply':
|
|
return { icon: 'reply', color: colors.info.main };
|
|
case 'follow':
|
|
return { icon: 'account-plus', color: colors.primary.main };
|
|
case 'mention':
|
|
return { icon: 'at', color: colors.warning.main };
|
|
case 'system':
|
|
return { icon: 'information', color: colors.text.secondary };
|
|
default:
|
|
return { icon: 'bell', color: colors.text.secondary };
|
|
}
|
|
};
|
|
|
|
const NotificationItem: React.FC<NotificationItemProps> = ({
|
|
notification,
|
|
onPress,
|
|
}) => {
|
|
// 格式化时间
|
|
const formatTime = (dateString: string): string => {
|
|
try {
|
|
return formatDistanceToNow(new Date(dateString), {
|
|
addSuffix: true,
|
|
locale: zhCN,
|
|
});
|
|
} catch {
|
|
return '';
|
|
}
|
|
};
|
|
|
|
const { icon, color } = getNotificationIcon(notification.type);
|
|
|
|
return (
|
|
<TouchableOpacity
|
|
style={[styles.container, !notification.isRead && styles.unread]}
|
|
onPress={onPress}
|
|
activeOpacity={0.7}
|
|
>
|
|
{/* 通知图标/头像 */}
|
|
<View style={styles.iconContainer}>
|
|
{notification.type === 'follow' && notification.data.userId ? (
|
|
<Avatar
|
|
source={null}
|
|
size={40}
|
|
name={notification.title}
|
|
/>
|
|
) : (
|
|
<View style={[styles.iconWrapper, { backgroundColor: color + '20' }]}>
|
|
<MaterialCommunityIcons name={icon as any} size={20} color={color} />
|
|
</View>
|
|
)}
|
|
</View>
|
|
|
|
{/* 通知内容 */}
|
|
<View style={styles.content}>
|
|
<View style={styles.textContainer}>
|
|
<Text
|
|
variant="body"
|
|
numberOfLines={2}
|
|
style={!notification.isRead ? styles.unreadText : undefined}
|
|
>
|
|
{notification.content}
|
|
</Text>
|
|
</View>
|
|
<Text variant="caption" color={colors.text.secondary}>
|
|
{formatTime(notification.createdAt)}
|
|
</Text>
|
|
</View>
|
|
|
|
{/* 未读标记 */}
|
|
{!notification.isRead && <View style={styles.unreadDot} />}
|
|
</TouchableOpacity>
|
|
);
|
|
};
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
padding: spacing.lg,
|
|
backgroundColor: colors.background.paper,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: colors.divider,
|
|
},
|
|
unread: {
|
|
backgroundColor: colors.primary.light + '10',
|
|
},
|
|
iconContainer: {
|
|
marginRight: spacing.md,
|
|
},
|
|
iconWrapper: {
|
|
width: 40,
|
|
height: 40,
|
|
borderRadius: 20,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
content: {
|
|
flex: 1,
|
|
},
|
|
textContainer: {
|
|
marginBottom: spacing.xs,
|
|
},
|
|
unreadText: {
|
|
fontWeight: '600',
|
|
},
|
|
unreadDot: {
|
|
width: 8,
|
|
height: 8,
|
|
borderRadius: 4,
|
|
backgroundColor: colors.primary.main,
|
|
marginLeft: spacing.sm,
|
|
},
|
|
});
|
|
|
|
export default NotificationItem;
|