Display system messages in chats (#10312)

Co-authored-by: Samuel Newman <mozzius@protonmail.com>
This commit is contained in:
DS Boyce
2026-04-21 10:43:22 -07:00
committed by GitHub
parent 0df1d6f53e
commit 2ab1e2c9e9
16 changed files with 219 additions and 11 deletions
-1
View File
@@ -61,7 +61,6 @@ let DateDivider = ({date: dateStr}: {date: string}): React.ReactNode => {
style={[
a.text_xs,
a.text_center,
t.atoms.bg,
t.atoms.text_contrast_medium,
a.px_md,
]}>
+17 -1
View File
@@ -474,7 +474,23 @@ let MessageItem = ({
]}>
<RichText
value={rt}
style={[a.text_md, isFromSelf && {color: t.palette.white}]}
style={[
a.text_md,
isFromSelf && {color: t.palette.white},
// Emoji-only: add top leading to avoid clipping the
// glyph, then pull the bottom up by the same amount so
// the glyph bottom-aligns with the avatar instead of
// sitting above its line-box baseline.
isOnlyEmoji(message.text) && [
a.leading_tight,
// Visually align bottom of the emoji with the avatar
!isFromSelf &&
platform({
android: {marginTop: a.mt_2xs.marginTop},
default: {marginBottom: -a.mb_sm.marginBottom},
}),
],
]}
interactiveStyle={a.underline}
enableTags
emojiMultiplier={3}
+44
View File
@@ -0,0 +1,44 @@
import {View} from 'react-native'
import {useLingui} from '@lingui/react/macro'
import {type ConvoItem} from '#/state/messages/convo/types'
import {atoms as a, useTheme} from '#/alf'
import {getSystemMessageInfo} from '#/components/dms/systemMessage'
import {Text} from '#/components/Typography'
export function SystemMessageItem({
item,
}: {
item: ConvoItem & {type: 'system-message'}
}) {
const t = useTheme()
const {i18n} = useLingui()
const info = getSystemMessageInfo(item.message.data)
if (!info) return null
const {Icon, message} = info
return (
<View
style={[
a.w_full,
a.flex_row,
a.align_center,
a.justify_center,
a.px_md,
a.mt_md,
]}>
<Icon size="xs" style={[a.mr_2xs, t.atoms.text_contrast_medium]} />
<Text
style={[
a.text_xs,
a.text_center,
t.atoms.text_contrast_medium,
{includeFontPadding: false, textAlignVertical: 'center'},
]}>
{i18n._(message)}
</Text>
</View>
)
}
+68
View File
@@ -0,0 +1,68 @@
import {ChatBskyConvoDefs} from '@atproto/api'
import {type MessageDescriptor} from '@lingui/core'
import {msg} from '@lingui/core/macro'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {ArrowBoxLeft_Stroke2_Corner0_Rounded as LeaveIcon} from '#/components/icons/ArrowBoxLeft'
import {ArrowBoxRight_Stroke2_Corner3_Rounded as JoinIcon} from '#/components/icons/ArrowBoxRight'
import {
ChainLink_Stroke2_Corner0_Rounded as ChainLinkIcon,
ChainLinkBroken_Stroke2_Corner0_Rounded as ChainLinkBrokenIcon,
} from '#/components/icons/ChainLink'
import {type Props as SVGIconProps} from '#/components/icons/common'
import {
Lock_Stroke2_Corner0_Rounded as LockIcon,
Unlock_Stroke2_Corner2_Rounded as UnlockIcon,
} from '#/components/icons/Lock'
import {PencilLine_Stroke2_Corner0_Rounded as PencilIcon} from '#/components/icons/Pencil'
export type SystemMessageInfo = {
message: MessageDescriptor
Icon: React.ComponentType<SVGIconProps>
}
export function getSystemMessageInfo(
data: ChatBskyConvoDefs.SystemMessageView['data'],
): SystemMessageInfo | null {
if (ChatBskyConvoDefs.isSystemMessageDataAddMember(data)) {
return {
Icon: JoinIcon,
message: msg`${createSanitizedDisplayName(data.member)} was added to the group`,
}
} else if (ChatBskyConvoDefs.isSystemMessageDataRemoveMember(data)) {
return {
Icon: LeaveIcon,
message: msg`${createSanitizedDisplayName(data.member)} was removed from the group`,
}
} else if (ChatBskyConvoDefs.isSystemMessageDataMemberJoin(data)) {
return {
Icon: JoinIcon,
message: msg`${createSanitizedDisplayName(data.member)} joined the group`,
}
} else if (ChatBskyConvoDefs.isSystemMessageDataMemberLeave(data)) {
return {
Icon: LeaveIcon,
message: msg`${createSanitizedDisplayName(data.member)} left the group`,
}
} else if (ChatBskyConvoDefs.isSystemMessageDataLockConvo(data)) {
return {Icon: LockIcon, message: msg`Chat locked`}
} else if (ChatBskyConvoDefs.isSystemMessageDataUnlockConvo(data)) {
return {Icon: UnlockIcon, message: msg`Chat unlocked`}
} else if (ChatBskyConvoDefs.isSystemMessageDataLockConvoPermanently(data)) {
return {Icon: LockIcon, message: msg`Chat locked permanently`}
} else if (ChatBskyConvoDefs.isSystemMessageDataEditGroup(data)) {
return {
Icon: PencilIcon,
message: msg`Chat title changed to ${data.newName ?? ''}`,
}
} else if (ChatBskyConvoDefs.isSystemMessageDataCreateJoinLink(data)) {
return {Icon: ChainLinkIcon, message: msg`Invite link created`}
} else if (ChatBskyConvoDefs.isSystemMessageDataEditJoinLink(data)) {
return {Icon: ChainLinkIcon, message: msg`Invite link edited`}
} else if (ChatBskyConvoDefs.isSystemMessageDataEnableJoinLink(data)) {
return {Icon: ChainLinkIcon, message: msg`Invite link enabled`}
} else if (ChatBskyConvoDefs.isSystemMessageDataDisableJoinLink(data)) {
return {Icon: ChainLinkBrokenIcon, message: msg`Invite link disabled`}
}
return null
}