Display sender name in last message for group clip clops (#10320)

Co-authored-by: Samuel Newman <mozzius@protonmail.com>
This commit is contained in:
DS Boyce
2026-04-22 10:03:53 -07:00
committed by GitHub
parent c7e9efbf99
commit dcc06a90a0
11 changed files with 253 additions and 134 deletions
+6 -1
View File
@@ -45,7 +45,12 @@ export function AvatarBubbles({
: size === 'medium'
? 56 / 120
: 1
const marginOffset = size === 'small' || size === 'medium' ? -2 : 0
const marginOffset =
(typeof size === 'number' && size < 120) ||
size === 'small' ||
size === 'medium'
? -2
: 0
const initialValue = animate ? 0 : 1
const p0 = useSharedValue(initialValue)
+3 -2
View File
@@ -3,7 +3,7 @@ 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 {getSystemMessageInfo} from '#/components/dms/getSystemMessageInfo'
import {Text} from '#/components/Typography'
export function SystemMessageItem({
@@ -14,7 +14,7 @@ export function SystemMessageItem({
const t = useTheme()
const {i18n} = useLingui()
const info = getSystemMessageInfo(item.message.data)
const info = getSystemMessageInfo(item.message.data, item.relatedProfiles)
if (!info) return null
const {Icon, message} = info
@@ -28,6 +28,7 @@ export function SystemMessageItem({
a.justify_center,
a.px_md,
a.mt_md,
a.mb_xs,
]}>
<Icon size="xs" style={[a.mr_2xs, t.atoms.text_contrast_medium]} />
<Text
+93
View File
@@ -0,0 +1,93 @@
import {AppBskyEmbedRecord, ChatBskyConvoDefs} from '@atproto/api'
import {type I18n} from '@lingui/core'
import {msg} from '@lingui/core/macro'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {
postUriToRelativePath,
toBskyAppUrl,
toShortUrl,
} from '#/lib/strings/url-helpers'
export type UserMessageInfo = {
message: string | null
sentAt: string
reportableMessage?: ChatBskyConvoDefs.MessageView
}
export function getMessageInfo({
convo,
currentAccountDid,
i18n,
}: {
convo: ChatBskyConvoDefs.ConvoView
currentAccountDid: string | undefined
i18n: I18n
}): UserMessageInfo | null {
if (!ChatBskyConvoDefs.isMessageView(convo.lastMessage)) {
return null
}
const lastMessage = convo.lastMessage
const isFromMe = lastMessage.sender?.did === currentAccountDid
const senderDid = lastMessage.sender?.did
const sender = convo.members.find(m => m.did === senderDid)
const name = sender ? createSanitizedDisplayName(sender) : null
const isGroup = ChatBskyConvoDefs.isGroupConvo(convo.kind)
const reportableMessage = isFromMe ? undefined : lastMessage
const prefix = (message: string) => {
if (isFromMe) {
return i18n._(
msg({
message: `You: ${message}`,
comment: 'When the last message in a chat was made by you.',
}),
)
} else if (isGroup && name) {
return i18n._(
msg({
message: `${name}: ${message}`,
comment:
'When the last message in a group chat came from someone other than you.',
}),
)
}
return message
}
let message: string | null = null
if (lastMessage.text) {
message = prefix(lastMessage.text)
} else if (lastMessage.embed) {
const defaultEmbeddedContentMessage = i18n._(
msg`(contains embedded content)`,
)
if (AppBskyEmbedRecord.isView(lastMessage.embed)) {
const embed = lastMessage.embed
if (AppBskyEmbedRecord.isViewRecord(embed.record)) {
const record = embed.record
const path = postUriToRelativePath(record.uri, {
handle: record.author.handle,
})
const href = path ? toBskyAppUrl(path) : undefined
const short = href ? toShortUrl(href) : defaultEmbeddedContentMessage
message = prefix(short)
} else {
message = prefix(defaultEmbeddedContentMessage)
}
} else {
message = prefix(defaultEmbeddedContentMessage)
}
}
return {
message,
sentAt: lastMessage.sentAt,
reportableMessage,
}
}
+54
View File
@@ -0,0 +1,54 @@
import {ChatBskyConvoDefs} from '@atproto/api'
import {type I18n} from '@lingui/core'
import {msg} from '@lingui/core/macro'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
export type UserReactionInfo = {
message: string
createdAt: string
}
export function getReactionInfo({
convo,
currentAccountDid,
i18n,
}: {
convo: ChatBskyConvoDefs.ConvoView
currentAccountDid: string | undefined
i18n: I18n
}): UserReactionInfo | null {
if (!ChatBskyConvoDefs.isMessageAndReactionView(convo.lastReaction)) {
return null
}
const {reaction, message: reactedTo} = convo.lastReaction
const isFromMe = reaction.sender.did === currentAccountDid
const senderDid = reaction.sender.did
const sender = convo.members.find(m => m.did === senderDid)
const name = sender ? createSanitizedDisplayName(sender) : null
const lastMessageText = reactedTo.text
const fallbackMessage = i18n._(
msg({
message: 'a message',
comment:
'If last message does not contain text, fall back to "{user} reacted to {a message}"',
}),
)
const target = lastMessageText ? `"${lastMessageText}"` : fallbackMessage
let message: string
if (isFromMe) {
message = i18n._(msg`You reacted ${reaction.value} to ${target}`)
} else if (name) {
message = i18n._(msg`${name} reacted ${reaction.value} to ${target}`)
} else {
message = i18n._(msg`Someone reacted ${reaction.value} to ${target}`)
}
return {
message,
createdAt: reaction.createdAt,
}
}
@@ -1,4 +1,4 @@
import {ChatBskyConvoDefs} from '@atproto/api'
import {type ChatBskyActorDefs, ChatBskyConvoDefs} from '@atproto/api'
import {type MessageDescriptor} from '@lingui/core'
import {msg} from '@lingui/core/macro'
@@ -21,28 +21,47 @@ export type SystemMessageInfo = {
Icon: React.ComponentType<SVGIconProps>
}
function getReferredDisplayName(
user: ChatBskyConvoDefs.SystemMessageReferredUser,
relatedProfiles: ChatBskyActorDefs.ProfileViewBasic[],
): string | null {
const profile = relatedProfiles.find(p => p.did === user.did)
return profile ? createSanitizedDisplayName(profile) : null
}
export function getSystemMessageInfo(
data: ChatBskyConvoDefs.SystemMessageView['data'],
relatedProfiles: ChatBskyActorDefs.ProfileViewBasic[],
): SystemMessageInfo | null {
if (ChatBskyConvoDefs.isSystemMessageDataAddMember(data)) {
const name = getReferredDisplayName(data.member, relatedProfiles)
return {
Icon: JoinIcon,
message: msg`${createSanitizedDisplayName(data.member)} was added to the group`,
message: name
? msg`${name} was added to the group`
: msg`Someone was added to the group`,
}
} else if (ChatBskyConvoDefs.isSystemMessageDataRemoveMember(data)) {
const name = getReferredDisplayName(data.member, relatedProfiles)
return {
Icon: LeaveIcon,
message: msg`${createSanitizedDisplayName(data.member)} was removed from the group`,
message: name
? msg`${name} was removed from the group`
: msg`Someone was removed from the group`,
}
} else if (ChatBskyConvoDefs.isSystemMessageDataMemberJoin(data)) {
const name = getReferredDisplayName(data.member, relatedProfiles)
return {
Icon: JoinIcon,
message: msg`${createSanitizedDisplayName(data.member)} joined the group`,
message: name
? msg`${name} joined the group`
: msg`Someone joined the group`,
}
} else if (ChatBskyConvoDefs.isSystemMessageDataMemberLeave(data)) {
const name = getReferredDisplayName(data.member, relatedProfiles)
return {
Icon: LeaveIcon,
message: msg`${createSanitizedDisplayName(data.member)} left the group`,
message: name ? msg`${name} left the group` : msg`Someone left the group`,
}
} else if (ChatBskyConvoDefs.isSystemMessageDataLockConvo(data)) {
return {Icon: LockIcon, message: msg`Chat locked`}
@@ -53,7 +72,9 @@ export function getSystemMessageInfo(
} else if (ChatBskyConvoDefs.isSystemMessageDataEditGroup(data)) {
return {
Icon: PencilIcon,
message: msg`Chat title changed to ${data.newName ?? ''}`,
message: data.newName
? msg`Chat title changed to ${data.newName}`
: msg`Chat title changed`,
}
} else if (ChatBskyConvoDefs.isSystemMessageDataCreateJoinLink(data)) {
return {Icon: ChainLinkIcon, message: msg`Invite link created`}
+6 -3
View File
@@ -110,14 +110,16 @@ export function parseConvoView(
owner = member as GroupConvoMember
}
} else {
throw new Error(
logger.warn(
'Expected a GroupConvoMember, got an unknown kind of member',
)
return null
}
}
if (!owner) {
throw new Error('No owner found in group convo')
logger.warn('No owner found in group convo')
return null
}
return {
@@ -136,7 +138,8 @@ export function parseConvoView(
const otherUser = convoView.members.find(m => m.did !== ownDid)
if (!otherUser) {
throw new Error('No other user found in direct convo')
logger.warn('No other user found in direct convo')
return null
}
return {