import React from 'react' import { StyleSheet, View, ViewStyle } from 'react-native' import { useTheme, useAtFontScaleBreakpoint, useFontScale, useScalableNumberOfLines, } from '../../hooks' import { ConversationPreviewData, ConversationResource } from '../../types' import { REACTION_EMOJIS, isArchivedConversation, platformFontWeightBold } from '../../utils' import { formatDatePreview } from '../../utils/date' import { faBoxArchive } from '../../vendor/font_awesome_pro_solid_icons' import { tokens } from '../../vendor/tapestry/tokens' import { AvatarGroup, ConversationAvatar, Heading, IconTile, Text, Badge, type BadgeProps, } from '../display' import { ConversationActions } from './conversation_actions' import { MuteIndicator } from './mute_indicator' import { UnreadCountBadge } from './unread_count_badge' const EMPTY_CONVERSATION_PREVIEW_TEXT = 'Send your first message' interface ConversationPreviewProps { conversation: ConversationResource onPress: () => void showBadges?: boolean style?: ViewStyle } export const ConversationPreview = ({ conversation, onPress, showBadges = true, style, }: ConversationPreviewProps) => { const styles = useStyles() const { conversationPreview, lastMessageCreatedAt, title, unreadCount, badges, muted, disabled, unreadReaction, } = conversation const archived = isArchivedConversation(conversation) const emptyConversation = !lastMessageCreatedAt const fallbackIconName = emptyConversation ? 'people.noTextMessage' : 'general.person' const hasMetaContent = lastMessageCreatedAt || unreadCount > 0 || muted const isUnread = unreadCount > 0 || unreadReaction const numberOfLinesScaledTitle = useScalableNumberOfLines(1) const numberOfLinesScaledPreviewText = useScalableNumberOfLines(2) const conversationPreviewText = conversationPreview ? formatConversationPreviewText(conversationPreview) : EMPTY_CONVERSATION_PREVIEW_TEXT const conversationAccessibilityLabel = getConversationAccessibilityLabel({ conversation, showBadges, archived, }) return ( {archived ? ( ) : ( )} {title} {conversationPreviewText} {hasMetaContent && ( {formatDatePreview(conversationPreview?.createdAt)} )} ) } interface BadgeShape { appName?: BadgeProps['productLogoName'] pcoResourceType: string text: string | null } const ConversationBadges = ({ visible, badges }: { visible: boolean; badges?: BadgeShape[] }) => { const styles = useStyles() const numberOfLinesScaled = useScalableNumberOfLines(1) if (!visible || !badges || badges.length === 0) { return null } return ( {badges.map(badge => ( ))} ) } const skeletonBadges: BadgeShape[] = [ { pcoResourceType: ' ', text: ' ', }, ] export const ConversationPreviewSkeleton = () => { const styles = useStyles() return ( ) } function reactionEmoji(value: ConversationPreviewData['value']): string | null { if (!value) return null return REACTION_EMOJIS[value] ?? null } function formatConversationPreviewText(preview: ConversationPreviewData) { if (preview.kind === 'system') return preview.text if (preview.kind === 'reaction') { const emoji = reactionEmoji(preview.value) return `${preview.authorName}${emoji ? ` ${emoji}` : ' reacted'} to: "${preview.text}"` } return `${preview.authorName}: ${preview.text}` } function formatConversationPreviewAccessibilityLabel(preview: ConversationPreviewData) { const date = formatDatePreview(preview.createdAt) if (preview.kind === 'system') return `Latest activity: ${preview.text} at ${date}` if (preview.kind === 'reaction') { return `${preview.authorName} reacted to your message sent ${date} and says ${preview.text}` } return `Last message sent ${date} from ${preview.authorName} and says ${preview.text}` } interface GetConversationAccessibilityLabelProps { conversation: ConversationResource showBadges: boolean archived: boolean } const getConversationAccessibilityLabel = ({ conversation, showBadges, archived, }: GetConversationAccessibilityLabelProps) => { const { badges, title, conversationPreview, muted, disabled, unreadCount, unreadReaction } = conversation const hasBadges = badges && showBadges const hasUnreadMessages = unreadCount > 0 const label = { title, badge: hasBadges ? `for ${badges[0]?.pcoResourceType} ${badges[0]?.text || ''}` : '', disabled: archived ? 'Archived' : disabled ? 'Disabled' : '', muted: muted ? 'Muted' : '', readStatus: hasUnreadMessages ? `${unreadCount} unread messages` : unreadReaction ? 'unread reaction' : '', lastMessage: conversationPreview ? formatConversationPreviewAccessibilityLabel(conversationPreview) : EMPTY_CONVERSATION_PREVIEW_TEXT, } return [ `Select ${label.title}`, label.badge, label.disabled, label.muted, label.readStatus, label.lastMessage, ] .filter(Boolean) .join(', ') } const useStyles = () => { const { colors } = useTheme() const atFontScaleBreakpoint = useAtFontScaleBreakpoint() const fontScale = useFontScale() return StyleSheet.create({ previewRow: { flexDirection: atFontScaleBreakpoint ? 'column' : 'row', gap: 8, borderBottomWidth: 1, borderBottomColor: atFontScaleBreakpoint ? colors.borderColorDefaultDark : colors.borderColorDefaultDim, paddingTop: 12, paddingBottom: 12, paddingHorizontal: 16, }, disabled: { opacity: 0.4, }, title: { fontSize: 16, color: colors.textColorDefaultPrimary, }, archivedTitle: { color: colors.textColorDefaultSecondary, }, unreadTitle: { fontWeight: tokens.fontWeightBold, }, unreadText: { fontWeight: platformFontWeightBold, }, titleLoading: { width: 120, height: 20, backgroundColor: colors.fillColorNeutral080, borderRadius: 4, }, conversationBody: { flex: 1, rowGap: 2, }, emptyConversationPreviewText: { color: colors.interaction, }, subtitleLoading: { width: 80, height: 16, backgroundColor: colors.fillColorNeutral090, borderRadius: 4, }, metaContainer: { gap: 4, alignItems: atFontScaleBreakpoint ? 'center' : 'flex-end', justifyContent: atFontScaleBreakpoint ? 'space-between' : 'flex-start', flexDirection: atFontScaleBreakpoint ? 'row' : 'column', }, dateLoading: { width: 60, height: 16, backgroundColor: colors.fillColorNeutral090, borderRadius: 4, }, badges: { marginTop: 4, alignItems: 'flex-start', }, statusContainer: { flexDirection: 'row', alignItems: 'center', gap: 4 * fontScale, }, }) }