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,
},
})
}