Clean up minor issues in MessgeItem (#10338)

This commit is contained in:
DS Boyce
2026-04-23 09:30:50 -07:00
committed by GitHub
parent bf371d7ecc
commit 6fc31eeb48
+69 -81
View File
@@ -1,4 +1,4 @@
import {memo, useCallback, useEffect, useMemo, useRef} from 'react' import {memo, useEffect, useMemo, useRef} from 'react'
import { import {
type GestureResponderEvent, type GestureResponderEvent,
LayoutAnimation, LayoutAnimation,
@@ -28,9 +28,8 @@ import {plural} from '@lingui/core/macro'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {makeProfileLink} from '#/lib/routes/links' import {makeProfileLink} from '#/lib/routes/links'
import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {sanitizeHandle} from '#/lib/strings/handles'
import {useConvoActive} from '#/state/messages/convo' import {useConvoActive} from '#/state/messages/convo'
import {type ConvoItem} from '#/state/messages/convo/types' import {type ConvoItem} from '#/state/messages/convo/types'
import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useModerationOpts} from '#/state/preferences/moderation-opts'
@@ -59,7 +58,9 @@ const DISPLAY_NAME_INSET = 22
const CLUSTERED_MESSAGE_THRESHOLD_MS = 5 * 60 * 1000 const CLUSTERED_MESSAGE_THRESHOLD_MS = 5 * 60 * 1000
const MESSAGE_GAP_THRESHOLD_MS = 60 * 60 * 1000 const MESSAGE_GAP_THRESHOLD_MS = 60 * 60 * 1000
function isWithinCluster({ const TAP_AND_DRAG_DELAY_MS = 100
function isWithinClusterBoundary({
isPending, isPending,
adjacentMessage, adjacentMessage,
isFromSameSender, isFromSameSender,
@@ -111,43 +112,38 @@ let MessageItem = ({
const {message, nextMessage, prevMessage} = item const {message, nextMessage, prevMessage} = item
const isPending = item.type === 'pending-message' const isPending = item.type === 'pending-message'
const displayName = sanitizeDisplayName( const displayName = profile ? createSanitizedDisplayName(profile) : null
profile?.displayName || sanitizeHandle(profile?.handle ?? ''),
)
const isFromSelf = message.sender?.did === currentAccount?.did const isFromSelf =
message.sender?.did != null && message.sender.did === currentAccount?.did
const prevIsMessage = ChatBskyConvoDefs.isMessageView(prevMessage) const prevIsMessage = ChatBskyConvoDefs.isMessageView(prevMessage)
const nextIsMessage = ChatBskyConvoDefs.isMessageView(nextMessage) const nextIsMessage = ChatBskyConvoDefs.isMessageView(nextMessage)
const isPrevFromSameSender = const isPrevFromSameSender =
prevIsMessage && prevMessage.sender?.did === message.sender?.did prevIsMessage &&
prevMessage.sender?.did === message.sender?.did &&
message.sender?.did != null
const isNextFromSameSender = const isNextFromSameSender =
nextIsMessage && nextMessage.sender?.did === message.sender?.did nextIsMessage &&
nextMessage.sender?.did === message.sender?.did &&
message.sender?.did != null
const isFirstInCluster = useMemo( const isFirstInCluster = isWithinClusterBoundary({
() => isPending,
isWithinCluster({ adjacentMessage: prevMessage,
isPending, isFromSameSender: isPrevFromSameSender,
adjacentMessage: prevMessage, currentSentAt: message.sentAt,
isFromSameSender: isPrevFromSameSender, direction: 'prev',
currentSentAt: message.sentAt, })
direction: 'prev',
}),
[isPending, prevMessage, isPrevFromSameSender, message.sentAt],
)
const isLastInCluster = useMemo( const isLastInCluster = isWithinClusterBoundary({
() => isPending,
isWithinCluster({ adjacentMessage: nextMessage,
isPending, isFromSameSender: isNextFromSameSender,
adjacentMessage: nextMessage, currentSentAt: message.sentAt,
isFromSameSender: isNextFromSameSender, direction: 'next',
currentSentAt: message.sentAt, })
direction: 'next',
}),
[isPending, nextMessage, isNextFromSameSender, message.sentAt],
)
const hasLargeGapFromPrev = const hasLargeGapFromPrev =
!ChatBskyConvoDefs.isMessageView(prevMessage) || !ChatBskyConvoDefs.isMessageView(prevMessage) ||
@@ -159,7 +155,6 @@ let MessageItem = ({
const isDateDividerToggled = isDividerToggled(message.id) const isDateDividerToggled = isDividerToggled(message.id)
const isNextDateDividerToggled = const isNextDateDividerToggled =
nextMessage != null && isDividerToggled(nextMessage.id) nextMessage != null && isDividerToggled(nextMessage.id)
const showDateDivider = hasLargeGapFromPrev
const effectiveFirstInCluster = isFirstInCluster || isDateDividerToggled const effectiveFirstInCluster = isFirstInCluster || isDateDividerToggled
const effectiveLastInCluster = isLastInCluster || isNextDateDividerToggled const effectiveLastInCluster = isLastInCluster || isNextDateDividerToggled
@@ -169,9 +164,7 @@ let MessageItem = ({
const hasReactions = message.reactions && message.reactions.length > 0 const hasReactions = message.reactions && message.reactions.length > 0
const prevHasReactions = const prevHasReactions =
prevIsMessage && prevIsMessage && prevMessage.reactions && prevMessage.reactions.length > 0
prevMessage.reactions != null &&
prevMessage.reactions.length > 0
const squaredBottomCorner = const squaredBottomCorner =
!hasReactions && !hasReactions &&
isInCluster && isInCluster &&
@@ -183,9 +176,7 @@ let MessageItem = ({
const pendingColor = t.palette.primary_300 const pendingColor = t.palette.primary_300
const rt = useMemo(() => { const rt = new RichTextAPI({text: message.text, facets: message.facets})
return new RichTextAPI({text: message.text, facets: message.facets})
}, [message.text, message.facets])
const hasEmbedAndText = const hasEmbedAndText =
AppBskyEmbedRecord.isView(message.embed) && rt.text.length > 0 AppBskyEmbedRecord.isView(message.embed) && rt.text.length > 0
@@ -225,27 +216,26 @@ let MessageItem = ({
}, },
) )
const avatar = profile ? ( const avatar =
<Link profile && moderationOpts ? (
label={l`${sanitizeDisplayName( <Link
profile.displayName || sanitizeHandle(profile.handle), label={l`${createSanitizedDisplayName(profile)}s avatar`}
)}s avatar`} accessibilityHint={l`Opens this profile`}
accessibilityHint={l`Opens this profile`} to={makeProfileLink({
to={makeProfileLink({ did: profile.did,
did: profile.did, handle: profile.handle,
handle: profile.handle, })}
})} onPress={() => unstableCacheProfileView(queryClient, profile)}>
onPress={() => unstableCacheProfileView(queryClient, profile)}> <ProfileCard.Avatar
<ProfileCard.Avatar profile={profile}
profile={profile} size={AVATAR_SIZE}
size={AVATAR_SIZE} moderationOpts={moderationOpts}
moderationOpts={moderationOpts!} disabledPreview
disabledPreview />
/> </Link>
</Link> ) : (
) : ( <ProfileCard.AvatarPlaceholder size={AVATAR_SIZE} />
<ProfileCard.AvatarPlaceholder size={AVATAR_SIZE} /> )
)
const groupedReactions = useMemo(() => { const groupedReactions = useMemo(() => {
const reactions = message.reactions ?? [] const reactions = message.reactions ?? []
@@ -287,11 +277,11 @@ let MessageItem = ({
return l`You reacted ${reaction.value}` return l`You reacted ${reaction.value}`
} else { } else {
const senderDid = reaction.sender.did const senderDid = reaction.sender.did
const sender = convo.members.find(member => member.did === senderDid) const memberSender = convo.members.find(
if (sender) { member => member.did === senderDid,
return l`${sanitizeDisplayName( )
sender.displayName || sender.handle, if (memberSender) {
)} reacted ${reaction.value}` return l`${createSanitizedDisplayName(memberSender)} reacted ${reaction.value}`
} }
return l`Someone reacted ${reaction.value}` return l`Someone reacted ${reaction.value}`
} }
@@ -343,14 +333,16 @@ let MessageItem = ({
// Include a delay here to account for tap-and-drag before release. // Include a delay here to account for tap-and-drag before release.
setTimeout(() => { setTimeout(() => {
reactionTapRef.current = false reactionTapRef.current = false
}, 100) }, TAP_AND_DRAG_DELAY_MS)
}} }}
onPress={() => (isGroupChat ? reactionsControl.open() : undefined)}> onPress={isGroupChat ? reactionsControl.open : undefined}>
{groupedReactions.map(group => ( {groupedReactions.map(group => (
<Animated.View <Animated.View
entering={native(ZoomIn.springify(200).delay(400))} entering={native(ZoomIn.springify(200).delay(400))}
exiting={ exiting={
groupedReactions.length > 1 && native(ZoomOut.delay(200)) groupedReactions.length > 1
? native(ZoomOut.delay(200))
: undefined
} }
layout={native(LinearTransition.delay(300))} layout={native(LinearTransition.delay(300))}
key={group.value} key={group.value}
@@ -399,7 +391,7 @@ let MessageItem = ({
return ( return (
<> <>
{(showDateDivider || isDateDividerToggled) && ( {(hasLargeGapFromPrev || isDateDividerToggled) && (
<Animated.View entering={native(FadeIn)} exiting={native(FadeOut)}> <Animated.View entering={native(FadeIn)} exiting={native(FadeOut)}>
<DateDivider date={message.sentAt} /> <DateDivider date={message.sentAt} />
</Animated.View> </Animated.View>
@@ -424,7 +416,7 @@ let MessageItem = ({
a.flex_grow, a.flex_grow,
!isFromSelf && isGroupChat && {paddingLeft: AVATAR_SIZE}, !isFromSelf && isGroupChat && {paddingLeft: AVATAR_SIZE},
]}> ]}>
{showDisplayName ? ( {displayName && showDisplayName ? (
<Text <Text
style={[ style={[
a.text_xs, a.text_xs,
@@ -459,7 +451,6 @@ let MessageItem = ({
...(isOnlyEmoji(message.text) ...(isOnlyEmoji(message.text)
? [] ? []
: [ : [
a.rounded_md,
a.rounded_xl, a.rounded_xl,
a.py_sm, a.py_sm,
a.px_md, a.px_md,
@@ -538,16 +529,13 @@ let MessageItemMetadata = ({
const t = useTheme() const t = useTheme()
const {t: l} = useLingui() const {t: l} = useLingui()
const handleRetry = useCallback( const handleRetry = (e: GestureResponderEvent) => {
(e: GestureResponderEvent) => { if (item.type === 'pending-message' && item.retry) {
if (item.type === 'pending-message' && item.retry) { e.preventDefault()
e.preventDefault() item.retry()
item.retry() return false
return false }
} }
},
[item],
)
const errorColor = t.palette.negative_400 const errorColor = t.palette.negative_400