import { useNavigation } from '@react-navigation/native' import React from 'react' import { Pressable, StyleSheet, View } from 'react-native' import Animated from 'react-native-reanimated' import { useAnimatedMessageBackgroundColor, useTheme } from '../../hooks' import { ReactionCountResource } from '../../types/resources/reaction' import { formatEventDateTime } from '../../utils/date' import { Haptic } from '../../utils/native_adapters' import { SystemMessageResource } from '../../utils/system_messages' import { Text } from '../display' import { MessageReaction } from './message_reaction' interface SystemMessageProps { message: SystemMessageResource conversationId: number conversationDisabled: boolean } export function SystemMessage({ message, conversationId, conversationDisabled, }: SystemMessageProps) { const { reactionCounts } = message const styles = useStyles() const navigation = useNavigation() const { colors } = useTheme() const text = systemMessageText(message) const isTruncated = message.systemTextFormat === 'people' && message.systemTextParts.overflowCount > 0 const pressable = isTruncated || !conversationDisabled const hasReactions = reactionCounts.length > 0 const { animatedBackgroundColor, handleMessagePressIn, handleMessagePressOut } = useAnimatedMessageBackgroundColor() const handlePress = () => { if (message.systemTextFormat !== 'people') return navigation.navigate('SystemMessagePeople', { conversation_id: conversationId, person_ids: message.personIdsForSystemEvent, }) } const handleLongPress = () => { Haptic.impactLight() navigation.navigate('MessageActions', { message_id: message.id, conversation_id: conversationId, isSystemMessage: true, }) } const handleReactionLongPress = (reaction: ReactionCountResource) => { Haptic.impactLight() navigation.navigate('Reactions', { message_id: message.id, conversation_id: conversationId, reaction_value: reaction.value, }) } return ( {text} {hasReactions && ( {message.reactionCounts.map(reaction => ( ))} )} ) } function systemMessageHint(isTruncated: boolean, conversationDisabled: boolean) { if (conversationDisabled) return isTruncated ? 'Tap to see people.' : undefined return isTruncated ? 'Tap to see people. Long press for message actions.' : 'Long press for message actions.' } function systemMessageText(message: SystemMessageResource): string { switch (message.systemTextFormat) { case 'people': { const { names, overflowCount, action } = message.systemTextParts return peopleSystemMessageText(names, overflowCount, action) } case 'actor': return `${message.text} on ${formatEventDateTime(message.createdAt)}` } } function peopleSystemMessageText(names: string[], overflowCount: number, action: string): string { if (overflowCount > 0) { const overflowLabel = `${overflowCount} ${overflowCount === 1 ? 'other' : 'others'}` return `${names.join(', ')}, and ${overflowLabel} ${action}` } switch (names.length) { case 0: return `Someone ${action}` case 1: return `${names[0]} ${action}` case 2: return `${names[0]} and ${names[1]} ${action}` // Backend guarantees names.length <= 3 when overflowCount is 0; // overflow branch handles 4+ people. default: return `${names[0]}, ${names[1]}, and ${names[2]} ${action}` } } const useStyles = () => { const { colors } = useTheme() return StyleSheet.create({ container: { paddingVertical: 12, paddingHorizontal: 16, alignItems: 'center', }, text: { color: colors.textColorDefaultSecondary, textAlign: 'center', }, reactions: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'center', gap: 4, marginTop: 8, }, }) }