Fix chat message spacing and alignment (#10579)

This commit is contained in:
DS Boyce
2026-05-22 11:08:03 -07:00
committed by GitHub
parent 4bdedc55be
commit 96ae32b78e
2 changed files with 18 additions and 15 deletions
+11 -8
View File
@@ -68,7 +68,6 @@ function isWithinClusterBoundary({
direction: 'prev' | 'next' direction: 'prev' | 'next'
}): boolean { }): boolean {
if (!isFromSameSender) return true if (!isFromSameSender) return true
if (isPending && adjacentMessage) return false
if (ChatBskyConvoDefs.isMessageView(adjacentMessage)) { if (ChatBskyConvoDefs.isMessageView(adjacentMessage)) {
const thisDate = new Date(currentSentAt) const thisDate = new Date(currentSentAt)
const adjDate = new Date(adjacentMessage.sentAt) const adjDate = new Date(adjacentMessage.sentAt)
@@ -76,7 +75,10 @@ function isWithinClusterBoundary({
direction === 'next' direction === 'next'
? adjDate.getTime() - thisDate.getTime() ? adjDate.getTime() - thisDate.getTime()
: thisDate.getTime() - adjDate.getTime() : thisDate.getTime() - adjDate.getTime()
return diff > CLUSTERED_MESSAGE_THRESHOLD_MS const isOutsideThreshold = diff > CLUSTERED_MESSAGE_THRESHOLD_MS
// For pending messages, still check the time threshold
if (isPending) return isOutsideThreshold
return isOutsideThreshold
} }
return true return true
} }
@@ -386,7 +388,11 @@ let MessageItem = ({
return ( return (
<> <>
{hasLargeGapFromPrev && <DateDivider date={message.sentAt} />} {hasLargeGapFromPrev && <DateDivider date={message.sentAt} />}
<View style={[messageInset, isFirstInCluster && a.mt_md]}> <View
style={[
messageInset,
isFirstInCluster ? a.mt_md : {marginTop: CLUSTERED_MESSAGE_GAP},
]}>
<View style={[a.relative]}> <View style={[a.relative]}>
{showAvatar ? ( {showAvatar ? (
<View <View
@@ -435,9 +441,9 @@ let MessageItem = ({
<MessageItemEmbed <MessageItemEmbed
embed={message.embed} embed={message.embed}
isFromSelf={isFromSelf} isFromSelf={isFromSelf}
isGroupChat={isGroupChat}
squaredBottomCorner={squaredBottomCorner || hasEmbedAndText} squaredBottomCorner={squaredBottomCorner || hasEmbedAndText}
squaredTopCorner={squaredTopCorner} squaredTopCorner={squaredTopCorner}
isFirstInCluster={isFirstInCluster}
/> />
)} )}
{rt.text.length > 0 && ( {rt.text.length > 0 && (
@@ -450,10 +456,7 @@ let MessageItem = ({
a.py_sm, a.py_sm,
a.px_md, a.px_md,
{ {
marginTop: marginTop: hasEmbedAndText ? CLUSTERED_MESSAGE_GAP : 0,
hasEmbedAndText || !isFirstInCluster
? CLUSTERED_MESSAGE_GAP
: 0,
backgroundColor: isFromSelf backgroundColor: isFromSelf
? isPending ? isPending
? pendingColor ? pendingColor
+7 -7
View File
@@ -6,22 +6,21 @@ import {atoms as a, native, useTheme, web} from '#/alf'
import {Embed, PostEmbedViewContext} from '#/components/Post/Embed' import {Embed, PostEmbedViewContext} from '#/components/Post/Embed'
import {MessageContextProvider} from './MessageContext' import {MessageContextProvider} from './MessageContext'
const CLUSTERED_MESSAGE_GAP = 2
const BORDER_RADIUS = 20 const BORDER_RADIUS = 20
const SQUARED_BORDER_RADIUS = 4 const SQUARED_BORDER_RADIUS = 4
let MessageItemEmbed = ({ let MessageItemEmbed = ({
embed, embed,
isFromSelf, isFromSelf,
isGroupChat,
squaredTopCorner, squaredTopCorner,
squaredBottomCorner, squaredBottomCorner,
isFirstInCluster,
}: { }: {
embed: $Typed<AppBskyEmbedRecord.View> embed: $Typed<AppBskyEmbedRecord.View>
isFromSelf: boolean isFromSelf: boolean
isGroupChat: boolean
squaredTopCorner: boolean squaredTopCorner: boolean
squaredBottomCorner: boolean squaredBottomCorner: boolean
isFirstInCluster: boolean
}): React.ReactNode => { }): React.ReactNode => {
const t = useTheme() const t = useTheme()
const screen = useWindowDimensions() const screen = useWindowDimensions()
@@ -30,18 +29,19 @@ let MessageItemEmbed = ({
<MessageContextProvider> <MessageContextProvider>
<View <View
style={[ style={[
!isFromSelf && a.ml_sm, !isFromSelf && isGroupChat && a.ml_sm,
native({ native({
flexBasis: 0, flexBasis: 0,
width: Math.min(screen.width, 600) / 1.4, width: Math.min(screen.width, 600) / 1.4,
}), }),
web({ web({
width: '100%',
minWidth: 280, minWidth: 280,
maxWidth: 360, maxWidth: 360,
}), }),
{ // Cancel out the embed's internal a.mt_sm so the container's
marginTop: isFirstInCluster ? 0 : CLUSTERED_MESSAGE_GAP, // CLUSTERED_MESSAGE_GAP (2px) is the only spacing applied
}, {marginTop: -a.mt_sm.marginTop},
]}> ]}>
<View> <View>
<Embed <Embed