import React, {useCallback, useMemo, useRef} from 'react' import {LayoutAnimation, StyleProp, TextStyle, View} from 'react-native' import {ChatBskyConvoDefs, RichText as RichTextAPI} from '@atproto/api' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useSession} from '#/state/session' import {TimeElapsed} from 'view/com/util/TimeElapsed' import {atoms as a, useTheme} from '#/alf' import {ActionsWrapper} from '#/components/dms/ActionsWrapper' import {Text} from '#/components/Typography' import {RichText} from '../RichText' let MessageItem = ({ item, next, pending, }: { item: ChatBskyConvoDefs.MessageView next: | ChatBskyConvoDefs.MessageView | ChatBskyConvoDefs.DeletedMessageView | null pending?: boolean }): React.ReactNode => { const t = useTheme() const {currentAccount} = useSession() const isFromSelf = item.sender?.did === currentAccount?.did const isNextFromSelf = ChatBskyConvoDefs.isMessageView(next) && next.sender?.did === currentAccount?.did const isLastInGroup = useMemo(() => { // TODO this means it's a placeholder. Let's figure out the right way to do this though! if (item.id.length > 13) { return false } // if the next message is from a different sender, then it's the last in the group if (isFromSelf ? !isNextFromSelf : isNextFromSelf) { return true } // or, if there's a 3 minute gap between this message and the next if (ChatBskyConvoDefs.isMessageView(next)) { const thisDate = new Date(item.sentAt) const nextDate = new Date(next.sentAt) const diff = nextDate.getTime() - thisDate.getTime() // 3 minutes return diff > 3 * 60 * 1000 } return true }, [item, next, isFromSelf, isNextFromSelf]) const lastInGroupRef = useRef(isLastInGroup) if (lastInGroupRef.current !== isLastInGroup) { LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) lastInGroupRef.current = isLastInGroup } const pendingColor = t.name === 'light' ? t.palette.primary_200 : t.palette.primary_800 const rt = useMemo(() => { return new RichTextAPI({text: item.text, facets: item.facets}) }, [item.text, item.facets]) return ( ) } MessageItem = React.memo(MessageItem) export {MessageItem} let MessageItemMetadata = ({ message, isLastInGroup, style, }: { message: ChatBskyConvoDefs.MessageView isLastInGroup: boolean style: StyleProp }): React.ReactNode => { const t = useTheme() const {_} = useLingui() const relativeTimestamp = useCallback( (timestamp: string) => { const date = new Date(timestamp) const now = new Date() const time = new Intl.DateTimeFormat(undefined, { hour: 'numeric', minute: 'numeric', }).format(date) const diff = now.getTime() - date.getTime() // if under 1 minute if (diff < 1000 * 60) { return _(msg`Now`) } // if in the last day if (localDateString(now) === localDateString(date)) { return time } // if yesterday const yesterday = new Date(now) yesterday.setDate(yesterday.getDate() - 1) if (localDateString(yesterday) === localDateString(date)) { return _(msg`Yesterday, ${time}`) } return new Intl.DateTimeFormat(undefined, { hour: 'numeric', minute: 'numeric', day: 'numeric', month: 'numeric', year: 'numeric', }).format(date) }, [_], ) if (!isLastInGroup) { return null } return ( {({timeElapsed}) => ( {timeElapsed} )} ) } MessageItemMetadata = React.memo(MessageItemMetadata) export {MessageItemMetadata} function localDateString(date: Date) { // can't use toISOString because it should be in local time const mm = date.getMonth() const dd = date.getDate() const yyyy = date.getFullYear() // not padding with 0s because it's not necessary, it's just used for comparison return `${yyyy}-${mm}-${dd}` }