[Chat] Keep track of relatedProfiles in Convo agent (#10354)

This commit is contained in:
Samuel Newman
2026-04-24 14:55:04 +03:00
committed by GitHub
parent 2215e4461f
commit 263474a0b9
11 changed files with 82 additions and 71 deletions
+7 -1
View File
@@ -4,23 +4,29 @@ import {useLingui} from '@lingui/react/macro'
import {atoms as a} from '#/alf'
import {MessageContextMenu} from '#/components/dms/MessageContextMenu'
import type * as bsky from '#/types/bsky'
export function ActionsWrapper({
message,
isFromSelf,
senderProfile,
children,
onTap,
}: {
message: ChatBskyConvoDefs.MessageView
hasReactions?: boolean
isFromSelf: boolean
senderProfile?: bsky.profile.AnyProfileView
children: React.ReactNode
onTap?: () => void
}) {
const {t: l} = useLingui()
return (
<MessageContextMenu message={message} onTap={onTap}>
<MessageContextMenu
message={message}
senderProfile={senderProfile}
onTap={onTap}>
{trigger =>
// will always be true, since this file is platform split
trigger.IS_NATIVE && (
+4 -1
View File
@@ -10,6 +10,7 @@ import {MessageContextMenu} from '#/components/dms/MessageContextMenu'
import {DotGrid3x1_Stroke2_Corner0_Rounded as DotsHorizontalIcon} from '#/components/icons/DotGrid'
import {EmojiSmile_Stroke2_Corner0_Rounded as EmojiSmileIcon} from '#/components/icons/Emoji'
import * as Toast from '#/components/Toast'
import type * as bsky from '#/types/bsky'
import {EmojiReactionPicker} from './EmojiReactionPicker'
import {hasReachedReactionLimit} from './util'
@@ -17,12 +18,14 @@ export function ActionsWrapper({
message,
hasReactions,
isFromSelf,
senderProfile,
children,
onTap,
}: {
message: ChatBskyConvoDefs.MessageView
hasReactions?: boolean
isFromSelf: boolean
senderProfile?: bsky.profile.AnyProfileView
children: React.ReactNode
onTap?: () => void
}) {
@@ -114,7 +117,7 @@ export function ActionsWrapper({
)
}}
</EmojiReactionPicker>
<MessageContextMenu message={message}>
<MessageContextMenu message={message} senderProfile={senderProfile}>
{({props, state, IS_NATIVE, control}) => {
// always false, file is platform split
if (IS_NATIVE) return null
+1 -1
View File
@@ -4,7 +4,7 @@ import {Trans, useLingui} from '@lingui/react/macro'
import {subDays} from 'date-fns'
import {atoms as a, useTheme} from '#/alf'
import {Text} from '../Typography'
import {Text} from '#/components/Typography'
import {localDateString} from './util'
const timeFormatter = new Intl.DateTimeFormat(undefined, {
+4 -3
View File
@@ -25,15 +25,18 @@ import {usePromptControl} from '#/components/Prompt'
import * as Toast from '#/components/Toast'
import {useAnalytics} from '#/analytics'
import {IS_NATIVE} from '#/env'
import type * as bsky from '#/types/bsky'
import {EmojiReactionPicker} from './EmojiReactionPicker'
import {hasReachedReactionLimit} from './util'
export let MessageContextMenu = ({
message,
senderProfile,
children,
onTap,
}: {
message: ChatBskyConvoDefs.MessageView
senderProfile?: bsky.profile.AnyProfileView
children: TriggerProps['children']
onTap?: () => void
}): React.ReactNode => {
@@ -110,9 +113,7 @@ export let MessageContextMenu = ({
[l, convo, message, currentAccount?.did],
)
const sender = convo.convo.members.find(
member => member.did === message.sender.did,
)
const sender = senderProfile
return (
<>
+19 -15
View File
@@ -30,7 +30,6 @@ import {useQueryClient} from '@tanstack/react-query'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {makeProfileLink} from '#/lib/routes/links'
import {useConvoActive} from '#/state/messages/convo'
import {type ConvoItem} from '#/state/messages/convo/types'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {unstableCacheProfileView} from '#/state/queries/unstable-profile-cache'
@@ -43,7 +42,6 @@ import {InlineLinkText, Link} from '#/components/Link'
import * as ProfileCard from '#/components/ProfileCard'
import {RichText} from '#/components/RichText'
import {Text} from '#/components/Typography'
import type * as bsky from '#/types/bsky'
import {DateDivider} from './DateDivider'
import {useDateDividerToggle} from './DateDividerToggle'
import {MessageItemEmbed} from './MessageItemEmbed'
@@ -93,19 +91,18 @@ function isWithinClusterBoundary({
let MessageItem = ({
item,
isGroupChat = false,
profile,
}: {
item: ConvoItem & {type: 'message' | 'pending-message'}
isGroupChat?: boolean
profile?: bsky.profile.AnyProfileView
}): React.ReactNode => {
const t = useTheme()
const {currentAccount} = useSession()
const {t: l} = useLingui()
const {convo} = useConvoActive()
const moderationOpts = useModerationOpts()
const queryClient = useQueryClient()
const profile = item.relatedProfiles.get(item.message.sender.did)
const reactionsControl = useDialogControl()
const reactionTapRef = useRef(false)
@@ -277,9 +274,7 @@ let MessageItem = ({
return l`You reacted ${reaction.value}`
} else {
const senderDid = reaction.sender.did
const memberSender = convo.members.find(
member => member.did === senderDid,
)
const memberSender = item.relatedProfiles.get(senderDid)
if (memberSender) {
return l`${createSanitizedDisplayName(memberSender)} reacted ${reaction.value}`
}
@@ -290,7 +285,13 @@ let MessageItem = ({
one: '# person',
other: '# people',
})} reacted ${groupedReactions.map(g => g.value).join(' ')}`
}, [reactions, groupedReactions, currentAccount?.did, convo.members, l])
}, [
reactions,
groupedReactions,
currentAccount?.did,
item.relatedProfiles,
l,
])
const appliedReactions = (
<LayoutAnimationConfig skipEntering skipExiting>
@@ -375,7 +376,7 @@ let MessageItem = ({
) : null}
<ReactionsDialog
control={reactionsControl}
members={convo.members}
relatedProfiles={item.relatedProfiles}
message={message}
reactions={message.reactions}
groupedReactions={groupedReactions}
@@ -391,11 +392,13 @@ let MessageItem = ({
return (
<>
{(hasLargeGapFromPrev || isDateDividerToggled) && (
<Animated.View entering={native(FadeIn)} exiting={native(FadeOut)}>
<DateDivider date={message.sentAt} />
</Animated.View>
)}
<LayoutAnimationConfig skipExiting skipEntering>
{(hasLargeGapFromPrev || isDateDividerToggled) && (
<Animated.View entering={native(FadeIn)} exiting={native(FadeOut)}>
<DateDivider date={message.sentAt} />
</Animated.View>
)}
</LayoutAnimationConfig>
<View style={[messageInset, effectiveFirstInCluster && a.mt_md]}>
<View style={[a.relative]}>
{showAvatar ? (
@@ -434,6 +437,7 @@ let MessageItem = ({
hasReactions={hasReactions}
isFromSelf={isFromSelf}
message={message}
senderProfile={profile}
onTap={() => {
if (reactionTapRef.current) return
if (!hasLargeGapFromPrev) {
+4 -4
View File
@@ -7,7 +7,7 @@ import {
View,
} from 'react-native'
import Animated from 'react-native-reanimated'
import {type ChatBskyConvoDefs} from '@atproto/api'
import {type ChatBskyActorDefs, type ChatBskyConvoDefs} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro'
import {HITSLOP_10} from '#/lib/constants'
@@ -33,13 +33,13 @@ type Reaction = {
export function ReactionsDialog({
control,
members,
relatedProfiles,
message,
reactions,
groupedReactions,
}: {
control: Dialog.DialogControlProps
members: bsky.profile.AnyProfileView[]
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
message: ChatBskyConvoDefs.MessageView
reactions?: ChatBskyConvoDefs.ReactionView[]
groupedReactions?: Reaction[]
@@ -100,7 +100,7 @@ export function ReactionsDialog({
return 0
})
.map(reaction => {
const sender = members.find(m => m.did === reaction.sender.did)
const sender = relatedProfiles.get(reaction.sender.did)
if (!sender) return null
return (
<ReactionRow
+3 -3
View File
@@ -23,15 +23,15 @@ export type SystemMessageInfo = {
function getReferredDisplayName(
user: ChatBskyConvoDefs.SystemMessageReferredUser,
relatedProfiles: ChatBskyActorDefs.ProfileViewBasic[],
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>,
): string | null {
const profile = relatedProfiles.find(p => p.did === user.did)
const profile = relatedProfiles.get(user.did)
return profile ? createSanitizedDisplayName(profile) : null
}
export function getSystemMessageInfo(
data: ChatBskyConvoDefs.SystemMessageView['data'],
relatedProfiles: ChatBskyActorDefs.ProfileViewBasic[],
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>,
): SystemMessageInfo | null {
if (ChatBskyConvoDefs.isSystemMessageDataAddMember(data)) {
const name = getReferredDisplayName(data.member, relatedProfiles)
@@ -298,7 +298,10 @@ function BaseChatItem({
// System message
if (ChatBskyConvoDefs.isSystemMessageView(convo.lastMessage)) {
const info = getSystemMessageInfo(convo.lastMessage.data, convo.members)
const info = getSystemMessageInfo(
convo.lastMessage.data,
new Map(convo.members.map(m => [m.did, m])),
)
if (info) {
lastMessage = i18n._(info.message)
lastMessageSentAt = convo.lastMessage.sentAt
@@ -374,9 +374,6 @@ export function MessagesList({
return (
<MessageItem
item={item}
profile={convoState.convo.members.find(
member => member.did === item.message.sender.did,
)}
isGroupChat={convoState.convo.kind === 'group'}
/>
)
+25 -16
View File
@@ -107,10 +107,8 @@ export class Convo {
{id: string; message: ChatBskyConvoSendMessage.InputSchema['message']}
> = new Map()
private deletedMessages: Set<string> = new Set()
private systemMessageProfiles: Map<
string,
ChatBskyActorDefs.ProfileViewBasic
> = new Map()
private relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic> =
new Map()
private isProcessingPendingMessages = false
@@ -483,7 +481,7 @@ export class Convo {
this.newMessages = new Map()
this.pendingMessages = new Map()
this.deletedMessages = new Set()
this.systemMessageProfiles = new Map()
this.relatedProfiles = new Map()
this.pendingMessageFailure = null
this.fetchMessageHistoryError = undefined
@@ -510,6 +508,11 @@ export class Convo {
private setConvo(convo: ChatBskyConvoDefs.ConvoView) {
this.convo = parseConvoView(convo, this.senderUserDid) ?? this.convo
if (this.convo) {
for (const member of this.convo.members) {
this.relatedProfiles.set(member.did, member)
}
}
}
private updateConvo(convo: Partial<ChatBskyConvoDefs.ConvoView>) {
@@ -517,6 +520,9 @@ export class Convo {
this.convo =
parseConvoView({...this.convo.view, ...convo}, this.senderUserDid) ??
this.convo
for (const member of this.convo.members) {
this.relatedProfiles.set(member.did, member)
}
}
}
@@ -704,7 +710,7 @@ export class Convo {
if (relatedProfiles) {
for (const profile of relatedProfiles) {
this.systemMessageProfiles.set(profile.did, profile)
this.relatedProfiles.set(profile.did, profile)
}
}
@@ -824,6 +830,12 @@ export class Convo {
*/
this.latestRev = ev.rev
if ('relatedProfiles' in ev && Array.isArray(ev.relatedProfiles)) {
for (const profile of ev.relatedProfiles) {
this.relatedProfiles.set(profile.did, profile)
}
}
if (
ChatBskyConvoDefs.isLogCreateMessage(ev) &&
ChatBskyConvoDefs.isMessageView(ev.message)
@@ -876,14 +888,6 @@ export class Convo {
const systemView = toSystemMessageView(ev)
if (systemView) {
this.newMessages.set(systemView.id, systemView)
if (
'relatedProfiles' in ev &&
Array.isArray(ev.relatedProfiles)
) {
for (const profile of ev.relatedProfiles) {
this.systemMessageProfiles.set(profile.did, profile)
}
}
needsCommit = true
}
}
@@ -1180,6 +1184,7 @@ export class Convo {
type: 'message',
key: m.id,
message: m,
relatedProfiles: this.relatedProfiles,
nextMessage: null,
prevMessage: null,
})
@@ -1188,6 +1193,7 @@ export class Convo {
type: 'deleted-message',
key: m.id,
message: m,
relatedProfiles: this.relatedProfiles,
nextMessage: null,
prevMessage: null,
})
@@ -1196,7 +1202,7 @@ export class Convo {
type: 'system-message',
key: m.id,
message: m,
relatedProfiles: Array.from(this.systemMessageProfiles.values()),
relatedProfiles: this.relatedProfiles,
})
}
})
@@ -1218,6 +1224,7 @@ export class Convo {
type: 'message',
key: m.id,
message: m,
relatedProfiles: this.relatedProfiles,
nextMessage: null,
prevMessage: null,
})
@@ -1226,6 +1233,7 @@ export class Convo {
type: 'deleted-message',
key: m.id,
message: m,
relatedProfiles: this.relatedProfiles,
nextMessage: null,
prevMessage: null,
})
@@ -1234,7 +1242,7 @@ export class Convo {
type: 'system-message',
key: m.id,
message: m,
relatedProfiles: Array.from(this.systemMessageProfiles.values()),
relatedProfiles: this.relatedProfiles,
})
}
})
@@ -1255,6 +1263,7 @@ export class Convo {
did: this.senderUserDid,
},
},
relatedProfiles: this.relatedProfiles,
nextMessage: null,
prevMessage: null,
failed: this.pendingMessageFailure !== null,
+11 -23
View File
@@ -59,34 +59,20 @@ export enum ConvoDispatchEvent {
}
export type ConvoDispatch =
| {
event: ConvoDispatchEvent.Init
}
| {
event: ConvoDispatchEvent.Ready
}
| {
event: ConvoDispatchEvent.Resume
}
| {
event: ConvoDispatchEvent.Background
}
| {
event: ConvoDispatchEvent.Suspend
}
| {
event: ConvoDispatchEvent.Error
payload: ConvoError
}
| {
event: ConvoDispatchEvent.Disable
}
| {event: ConvoDispatchEvent.Init}
| {event: ConvoDispatchEvent.Ready}
| {event: ConvoDispatchEvent.Resume}
| {event: ConvoDispatchEvent.Background}
| {event: ConvoDispatchEvent.Suspend}
| {event: ConvoDispatchEvent.Error; payload: ConvoError}
| {event: ConvoDispatchEvent.Disable}
export type ConvoItem =
| {
type: 'message'
key: string
message: ChatBskyConvoDefs.MessageView
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
nextMessage:
| ChatBskyConvoDefs.MessageView
| ChatBskyConvoDefs.DeletedMessageView
@@ -100,6 +86,7 @@ export type ConvoItem =
type: 'pending-message'
key: string
message: ChatBskyConvoDefs.MessageView
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
nextMessage:
| ChatBskyConvoDefs.MessageView
| ChatBskyConvoDefs.DeletedMessageView
@@ -118,6 +105,7 @@ export type ConvoItem =
type: 'deleted-message'
key: string
message: ChatBskyConvoDefs.DeletedMessageView
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
nextMessage:
| ChatBskyConvoDefs.MessageView
| ChatBskyConvoDefs.DeletedMessageView
@@ -131,7 +119,7 @@ export type ConvoItem =
type: 'system-message'
key: string
message: ChatBskyConvoDefs.SystemMessageView
relatedProfiles: ChatBskyActorDefs.ProfileViewBasic[]
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
}
| {
type: 'error'