Clean up minor issues in MessgeItem (#10338)
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user