[Chat] Centralize conversation report-subject resolution (#10754)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-06-08 20:07:35 +03:00
committed by GitHub
parent 0090285fc0
commit 9b5fc19613
7 changed files with 151 additions and 148 deletions
+38 -46
View File
@@ -1,6 +1,6 @@
import {memo, useCallback} from 'react' import {memo, useCallback} from 'react'
import {Keyboard, View} from 'react-native' import {Keyboard, View} from 'react-native'
import {ChatBskyConvoDefs, type ModerationCause} from '@atproto/api' import {type ModerationCause} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
@@ -16,13 +16,18 @@ import {
unstableCacheProfileView, unstableCacheProfileView,
useProfileBlockMutationQueue, useProfileBlockMutationQueue,
} from '#/state/queries/profile' } from '#/state/queries/profile'
import {useSession} from '#/state/session'
import {type ViewStyleProp} from '#/alf' import {type ViewStyleProp} from '#/alf'
import {atoms as a} from '#/alf' import {atoms as a} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button' import {Button, ButtonIcon} from '#/components/Button'
import {AfterReportConversationDialog} from '#/components/dms/AfterReportConversationDialog'
import {AfterReportDialog} from '#/components/dms/AfterReportDialog' import {AfterReportDialog} from '#/components/dms/AfterReportDialog'
import {BlockedByListDialog} from '#/components/dms/BlockedByListDialog' import {BlockedByListDialog} from '#/components/dms/BlockedByListDialog'
import {LeaveConvoPrompt} from '#/components/dms/LeaveConvoPrompt' import {LeaveConvoPrompt} from '#/components/dms/LeaveConvoPrompt'
import {ReportConversationDialog} from '#/components/dms/ReportConversationDialog' import {
type ConvoWithDetails,
getConvoReportSubject,
} from '#/components/dms/util'
import {ArrowBoxLeft_Stroke2_Corner0_Rounded as ArrowBoxLeftIcon} from '#/components/icons/ArrowBoxLeft' import {ArrowBoxLeft_Stroke2_Corner0_Rounded as ArrowBoxLeftIcon} from '#/components/icons/ArrowBoxLeft'
import {Bubble_Stroke2_Corner2_Rounded as BubbleIcon} from '#/components/icons/Bubble' import {Bubble_Stroke2_Corner2_Rounded as BubbleIcon} from '#/components/icons/Bubble'
import {DotGrid3x1_Stroke2_Corner0_Rounded as DotsHorizontalIcon} from '#/components/icons/DotGrid' import {DotGrid3x1_Stroke2_Corner0_Rounded as DotsHorizontalIcon} from '#/components/icons/DotGrid'
@@ -39,7 +44,6 @@ import {ReportDialog} from '#/components/moderation/ReportDialog'
import * as Prompt from '#/components/Prompt' import * as Prompt from '#/components/Prompt'
import * as Toast from '#/components/Toast' import * as Toast from '#/components/Toast'
import type * as bsky from '#/types/bsky' import type * as bsky from '#/types/bsky'
import {AfterReportConversationDialog} from './AfterReportConversationDialog'
let ConvoMenu = ({ let ConvoMenu = ({
convo, convo,
@@ -49,10 +53,9 @@ let ConvoMenu = ({
showMarkAsRead, showMarkAsRead,
hideTrigger, hideTrigger,
blockInfo, blockInfo,
latestReportableMessage,
style, style,
}: { }: {
convo: ChatBskyConvoDefs.ConvoView convo: ConvoWithDetails
profile: Shadow<bsky.profile.AnyProfileView> profile: Shadow<bsky.profile.AnyProfileView>
control?: Menu.MenuControlProps control?: Menu.MenuControlProps
currentScreen: 'list' | 'conversation' currentScreen: 'list' | 'conversation'
@@ -62,20 +65,21 @@ let ConvoMenu = ({
listBlocks: ModerationCause[] listBlocks: ModerationCause[]
userBlock?: ModerationCause userBlock?: ModerationCause
} }
latestReportableMessage?: ChatBskyConvoDefs.MessageView
style?: ViewStyleProp['style'] style?: ViewStyleProp['style']
}): React.ReactNode => { }): React.ReactNode => {
const {t: l} = useLingui() const {t: l} = useLingui()
const queryClient = useQueryClient() const queryClient = useQueryClient()
const {currentAccount} = useSession()
const leaveConvoControl = Prompt.usePromptControl() const leaveConvoControl = Prompt.usePromptControl()
const reportControl = Prompt.usePromptControl() const reportControl = Prompt.usePromptControl()
const blockedByListControl = Prompt.usePromptControl() const blockedByListControl = Prompt.usePromptControl()
const blockOrDeleteControl = Prompt.usePromptControl() const afterReportControl = Prompt.usePromptControl()
const deleteControl = Prompt.usePromptControl()
const {listBlocks} = blockInfo const {listBlocks} = blockInfo
const reportSubject = getConvoReportSubject(convo, currentAccount?.did)
return ( return (
<> <>
<Menu.Root control={control}> <Menu.Root control={control}>
@@ -108,6 +112,7 @@ let ConvoMenu = ({
showMarkAsRead={showMarkAsRead} showMarkAsRead={showMarkAsRead}
blockInfo={blockInfo} blockInfo={blockInfo}
convo={convo} convo={convo}
canReport={!!reportSubject}
leaveConvoControl={leaveConvoControl} leaveConvoControl={leaveConvoControl}
reportControl={reportControl} reportControl={reportControl}
blockedByListControl={blockedByListControl} blockedByListControl={blockedByListControl}
@@ -116,54 +121,37 @@ let ConvoMenu = ({
</Menu.Root> </Menu.Root>
<LeaveConvoPrompt <LeaveConvoPrompt
control={leaveConvoControl} control={leaveConvoControl}
convoId={convo.id} convoId={convo.view.id}
currentScreen={currentScreen} currentScreen={currentScreen}
/> />
{latestReportableMessage ? ( {reportSubject && (
<>
<ReportDialog <ReportDialog
subject={{ subject={reportSubject}
view: 'convo',
convoId: convo.id,
message: latestReportableMessage,
}}
control={reportControl} control={reportControl}
onAfterSubmit={() => { onAfterSubmit={() => {
const sender = convo.members.find( unstableCacheProfileView(queryClient, profile)
member => member.did === latestReportableMessage.sender.did, afterReportControl.open()
)
if (sender) {
unstableCacheProfileView(queryClient, sender)
}
blockOrDeleteControl.open()
}} }}
/> />
<AfterReportDialog )}
control={blockOrDeleteControl} {convo.kind === 'group' ? (
currentScreen={currentScreen}
params={{
convoId: convo.id,
did: latestReportableMessage.sender.did,
}}
/>
</>
) : (
<>
<ReportConversationDialog
control={reportControl}
convoId={convo.id}
did={profile.did}
onAfterSubmit={deleteControl.open}
/>
<AfterReportConversationDialog <AfterReportConversationDialog
control={deleteControl} control={afterReportControl}
currentScreen={currentScreen} currentScreen={currentScreen}
params={{ params={{
convoId: convo.id, convoId: convo.view.id,
did: profile.did,
}}
/>
) : (
<AfterReportDialog
control={afterReportControl}
currentScreen={currentScreen}
params={{
convoId: convo.view.id,
did: profile.did, did: profile.did,
}} }}
/> />
</>
)} )}
<BlockedByListDialog <BlockedByListDialog
control={blockedByListControl} control={blockedByListControl}
@@ -177,14 +165,16 @@ ConvoMenu = memo(ConvoMenu)
function MenuContent({ function MenuContent({
convo: initialConvo, convo: initialConvo,
profile, profile,
canReport,
showMarkAsRead, showMarkAsRead,
blockInfo, blockInfo,
leaveConvoControl, leaveConvoControl,
reportControl, reportControl,
blockedByListControl, blockedByListControl,
}: { }: {
convo: ChatBskyConvoDefs.ConvoView convo: ConvoWithDetails
profile: Shadow<bsky.profile.AnyProfileView> profile: Shadow<bsky.profile.AnyProfileView>
canReport: boolean
showMarkAsRead?: boolean showMarkAsRead?: boolean
blockInfo: { blockInfo: {
listBlocks: ModerationCause[] listBlocks: ModerationCause[]
@@ -201,9 +191,9 @@ function MenuContent({
const {listBlocks, userBlock} = blockInfo const {listBlocks, userBlock} = blockInfo
const isBlocking = userBlock || !!listBlocks.length const isBlocking = userBlock || !!listBlocks.length
const isDeletedAccount = profile.handle === 'missing.invalid' const isDeletedAccount = profile.handle === 'missing.invalid'
const isGroupConvo = ChatBskyConvoDefs.isGroupConvo(initialConvo.kind) const isGroupConvo = initialConvo.kind === 'group'
const convoId = initialConvo.id const convoId = initialConvo.view.id
const {data: convo} = useConvoQuery({convoId}) const {data: convo} = useConvoQuery({convoId})
const onNavigateToProfile = useCallback(() => { const onNavigateToProfile = useCallback(() => {
@@ -299,6 +289,7 @@ function MenuContent({
</Menu.ItemText> </Menu.ItemText>
</Menu.Item> </Menu.Item>
)} )}
{canReport && (
<Menu.Item <Menu.Item
destructive destructive
label={l`Report conversation`} label={l`Report conversation`}
@@ -308,6 +299,7 @@ function MenuContent({
<Trans>Report conversation</Trans> <Trans>Report conversation</Trans>
</Menu.ItemText> </Menu.ItemText>
</Menu.Item> </Menu.Item>
)}
</Menu.Group> </Menu.Group>
<Menu.Divider /> <Menu.Divider />
<Menu.Group> <Menu.Group>
+2 -15
View File
@@ -1,10 +1,6 @@
import {useMemo} from 'react' import {useMemo} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import { import {moderateProfile, type ModerationOpts} from '@atproto/api'
ChatBskyConvoDefs,
moderateProfile,
type ModerationOpts,
} from '@atproto/api'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name' import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
@@ -12,7 +8,6 @@ import {makeProfileLink} from '#/lib/routes/links'
import {sanitizeHandle} from '#/lib/strings/handles' import {sanitizeHandle} from '#/lib/strings/handles'
import {useProfileShadow} from '#/state/cache/profile-shadow' import {useProfileShadow} from '#/state/cache/profile-shadow'
import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useSession} from '#/state/session'
import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
import {useIsWithinSplitView} from '#/screens/Messages/components/splitView/context' import {useIsWithinSplitView} from '#/screens/Messages/components/splitView/context'
import {atoms as a, useTheme, web} from '#/alf' import {atoms as a, useTheme, web} from '#/alf'
@@ -88,7 +83,6 @@ function ProfileHeaderReady({
}) { }) {
const t = useTheme() const t = useTheme()
const {t: l} = useLingui() const {t: l} = useLingui()
const {currentAccount} = useSession()
const profile = useProfileShadow(convo.primaryMember) const profile = useProfileShadow(convo.primaryMember)
const moderation = moderateProfile(profile, moderationOpts) const moderation = moderateProfile(profile, moderationOpts)
@@ -110,12 +104,6 @@ function ProfileHeaderReady({
: createSanitizedDisplayName(profile, true, moderation.ui('displayName')) : createSanitizedDisplayName(profile, true, moderation.ui('displayName'))
const handle = isDeletedAccount ? null : sanitizeHandle(profile.handle, '@') const handle = isDeletedAccount ? null : sanitizeHandle(profile.handle, '@')
const latestReportableMessage =
ChatBskyConvoDefs.isMessageView(convo.view.lastMessage) &&
convo.view.lastMessage.sender?.did !== currentAccount?.did
? convo.view.lastMessage
: undefined
return ( return (
<Wrapper <Wrapper
heading={ heading={
@@ -151,11 +139,10 @@ function ProfileHeaderReady({
} }
settings={ settings={
<ConvoMenu <ConvoMenu
convo={convo.view} convo={convo}
profile={profile} profile={profile}
currentScreen="conversation" currentScreen="conversation"
blockInfo={blockInfo} blockInfo={blockInfo}
latestReportableMessage={latestReportableMessage}
/> />
} }
/> />
+39
View File
@@ -10,6 +10,7 @@ import {EMOJI_REACTION_LIMIT} from '#/lib/constants'
import {logger} from '#/logger' import {logger} from '#/logger'
import {type Shadow} from '#/state/cache/profile-shadow' import {type Shadow} from '#/state/cache/profile-shadow'
import {type ConvoState, ConvoStatus} from '#/state/messages/convo/types' import {type ConvoState, ConvoStatus} from '#/state/messages/convo/types'
import {type ReportSubject} from '#/components/moderation/ReportDialog/types'
import * as bsky from '#/types/bsky' import * as bsky from '#/types/bsky'
export const MESSAGE_GAP_THRESHOLD_MS = 60 * 60 * 1000 export const MESSAGE_GAP_THRESHOLD_MS = 60 * 60 * 1000
@@ -240,3 +241,41 @@ export function parseConvoView(
return null return null
} }
} }
/**
* Resolves the report subject for a conversation-level "Report conversation"
* action (as opposed to reporting an individual message, which always reports
* that message + its sender).
*
* - group: always report the whole convo, targeting the owner. Returns null if
* the owner has left, in which case there is nothing to report against.
* - direct: report the last reportable message if there is one (i.e. the last
* message exists and wasn't sent by us), otherwise report the whole convo
* targeting the other user.
*/
export function getConvoReportSubject(
convo: ConvoWithDetails,
ownDid: string | undefined,
): ReportSubject | null {
if (convo.kind === 'group') {
if (!convo.primaryMember) return null
return {convoId: convo.view.id, did: convo.primaryMember.did}
}
const lastMessage = convo.view.lastMessage
const reportableMessage =
ChatBskyConvoDefs.isMessageView(lastMessage) &&
lastMessage.sender?.did !== ownDid
? lastMessage
: null
if (reportableMessage) {
return {
view: 'convo',
convoId: convo.view.id,
message: reportableMessage,
}
}
return {convoId: convo.view.id, did: convo.primaryMember.did}
}
@@ -307,20 +307,13 @@ function BaseChatItem({
isDeletedAccount || isDeletedAccount ||
(convo.kind === 'group' && convo.details.lockStatus !== 'unlocked') (convo.kind === 'group' && convo.details.lockStatus !== 'unlocked')
const { const {lastMessage, LastMessageIcon, lastMessageSentAt} = useMemo(() => {
lastMessage,
LastMessageIcon,
lastMessageSentAt,
latestReportableMessage,
} = useMemo(() => {
let lastMessage = l`No messages yet` let lastMessage = l`No messages yet`
let LastMessageIcon: React.ComponentType<SVGIconProps> | null = null let LastMessageIcon: React.ComponentType<SVGIconProps> | null = null
let lastMessageSentAt: string | null = null let lastMessageSentAt: string | null = null
let latestReportableMessage: ChatBskyConvoDefs.MessageView | undefined
// Deleted message // Deleted message
if (ChatBskyConvoDefs.isDeletedMessageView(convo.view.lastMessage)) { if (ChatBskyConvoDefs.isDeletedMessageView(convo.view.lastMessage)) {
lastMessageSentAt = convo.view.lastMessage.sentAt lastMessageSentAt = convo.view.lastMessage.sentAt
@@ -340,7 +333,6 @@ function BaseChatItem({
if (info) { if (info) {
lastMessage = info.message ?? lastMessage lastMessage = info.message ?? lastMessage
lastMessageSentAt = info.sentAt lastMessageSentAt = info.sentAt
latestReportableMessage = info.reportableMessage
} }
} }
@@ -385,7 +377,6 @@ function BaseChatItem({
lastMessage, lastMessage,
LastMessageIcon, LastMessageIcon,
lastMessageSentAt, lastMessageSentAt,
latestReportableMessage,
} }
}, [l, convo, currentAccount?.did, isDeletedAccount, i18n]) }, [l, convo, currentAccount?.did, isDeletedAccount, i18n])
@@ -663,7 +654,7 @@ function BaseChatItem({
{/* TODO: Allow showing menu for groups where the owner has left! */} {/* TODO: Allow showing menu for groups where the owner has left! */}
{showMenu && primaryProfile && ( {showMenu && primaryProfile && (
<ConvoMenu <ConvoMenu
convo={convo.view} convo={convo}
profile={primaryProfile} profile={primaryProfile}
control={menuControl} control={menuControl}
currentScreen="list" currentScreen="list"
@@ -681,7 +672,6 @@ function BaseChatItem({
!gtMobile || showActions || menuControl.isOpen ? 1 : 0, !gtMobile || showActions || menuControl.isOpen ? 1 : 0,
}, },
]} ]}
latestReportableMessage={latestReportableMessage}
/> />
)} )}
@@ -1,7 +1,7 @@
import {useCallback, useMemo} from 'react' import {useCallback, useMemo} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {LinearGradient} from 'expo-linear-gradient' import {LinearGradient} from 'expo-linear-gradient'
import {ChatBskyConvoDefs, moderateProfile} from '@atproto/api' import {moderateProfile} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name' import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
@@ -33,12 +33,6 @@ export function ChatStatusInfo({convoState}: {convoState: ActiveConvoStates}) {
// if we ever allow someone other than the owner to invite people, this will need to change // if we ever allow someone other than the owner to invite people, this will need to change
const otherUser = convoState.convo.primaryMember const otherUser = convoState.convo.primaryMember
const lastMessage = ChatBskyConvoDefs.isMessageView(
convoState.convo.view.lastMessage,
)
? convoState.convo.view.lastMessage
: null
if (!moderationOpts) { if (!moderationOpts) {
return null return null
} }
@@ -64,9 +58,9 @@ export function ChatStatusInfo({convoState}: {convoState: ActiveConvoStates}) {
<View style={[a.flex_row, a.gap_md, a.w_full, otherUser && a.pt_sm]}> <View style={[a.flex_row, a.gap_md, a.w_full, otherUser && a.pt_sm]}>
{otherUser && ( {otherUser && (
<RejectMenu <RejectMenu
label={lastMessage ? l`Block or report` : l`Block`} label={l`Block or report`}
icon={true} icon={true}
convo={convoState.convo.view} convo={convoState.convo}
profile={otherUser} profile={otherUser}
color="negative_subtle" color="negative_subtle"
size="large" size="large"
@@ -72,7 +72,7 @@ export function IncomingRequestListItem({
<AcceptChatButton convo={convo.view} currentScreen="list" /> <AcceptChatButton convo={convo.view} currentScreen="list" />
) : null} ) : null}
<RejectMenu <RejectMenu
convo={convo.view} convo={convo}
profile={convo.primaryMember} profile={convo.primaryMember}
showDeleteConvo showDeleteConvo
currentScreen="list" currentScreen="list"
@@ -14,6 +14,7 @@ import {
unstableCacheProfileView, unstableCacheProfileView,
useProfileBlockMutationQueue, useProfileBlockMutationQueue,
} from '#/state/queries/profile' } from '#/state/queries/profile'
import {useSession} from '#/state/session'
import { import {
Button, Button,
ButtonIcon, ButtonIcon,
@@ -25,7 +26,12 @@ import {
EmailDialogScreenID, EmailDialogScreenID,
useEmailDialogControl, useEmailDialogControl,
} from '#/components/dialogs/EmailDialog' } from '#/components/dialogs/EmailDialog'
import {AfterReportConversationDialog} from '#/components/dms/AfterReportConversationDialog'
import {AfterReportDialog} from '#/components/dms/AfterReportDialog' import {AfterReportDialog} from '#/components/dms/AfterReportDialog'
import {
type ConvoWithDetails,
getConvoReportSubject,
} from '#/components/dms/util'
import {ArrowBoxLeft_Stroke2_Corner0_Rounded as LeaveIcon} from '#/components/icons/ArrowBoxLeft' import {ArrowBoxLeft_Stroke2_Corner0_Rounded as LeaveIcon} from '#/components/icons/ArrowBoxLeft'
import {Check_Stroke2_Corner0_Rounded as CheckIcon} from '#/components/icons/Check' import {Check_Stroke2_Corner0_Rounded as CheckIcon} from '#/components/icons/Check'
import {CircleX_Stroke2_Corner0_Rounded} from '#/components/icons/CircleX' import {CircleX_Stroke2_Corner0_Rounded} from '#/components/icons/CircleX'
@@ -49,17 +55,18 @@ export function RejectMenu({
}: Omit<ButtonProps, 'onPress' | 'children' | 'label'> & { }: Omit<ButtonProps, 'onPress' | 'children' | 'label'> & {
label?: string label?: string
icon?: boolean icon?: boolean
convo: ChatBskyConvoDefs.ConvoView convo: ConvoWithDetails
profile: ChatBskyActorDefs.ProfileViewBasic profile: ChatBskyActorDefs.ProfileViewBasic
showDeleteConvo?: boolean showDeleteConvo?: boolean
currentScreen: 'list' | 'conversation' currentScreen: 'list' | 'conversation'
}) { }) {
const {t: l} = useLingui() const {t: l} = useLingui()
const {currentAccount} = useSession()
const shadowedProfile = useProfileShadow(profile) const shadowedProfile = useProfileShadow(profile)
const navigation = useNavigation<NavigationProp>() const navigation = useNavigation<NavigationProp>()
const queryClient = useQueryClient() const queryClient = useQueryClient()
const {mutate: leaveConvo} = useLeaveConvo(convo.id, { const {mutate: leaveConvo} = useLeaveConvo(convo.view.id, {
onMutate: () => { onMutate: () => {
if (currentScreen === 'conversation') { if (currentScreen === 'conversation') {
navigation.dispatch(StackActions.pop()) navigation.dispatch(StackActions.pop())
@@ -110,9 +117,7 @@ export function RejectMenu({
const reportControl = useDialogControl() const reportControl = useDialogControl()
const blockOrDeleteControl = useDialogControl() const blockOrDeleteControl = useDialogControl()
const lastMessage = ChatBskyConvoDefs.isMessageView(convo.lastMessage) const reportSubject = getConvoReportSubject(convo, currentAccount?.did)
? convo.lastMessage
: null
return ( return (
<> <>
@@ -152,10 +157,6 @@ export function RejectMenu({
</Menu.ItemText> </Menu.ItemText>
<Menu.ItemIcon icon={PersonXIcon} /> <Menu.ItemIcon icon={PersonXIcon} />
</Menu.Item> </Menu.Item>
{/* note: last message will almost certainly be defined, since you can't
delete messages for other people and it's impossible for a convo on this
screen to have a message sent by you */}
{lastMessage && (
<Menu.Item <Menu.Item
label={l`Report conversation`} label={l`Report conversation`}
onPress={reportControl.open}> onPress={reportControl.open}>
@@ -164,38 +165,38 @@ export function RejectMenu({
</Menu.ItemText> </Menu.ItemText>
<Menu.ItemIcon icon={FlagIcon} /> <Menu.ItemIcon icon={FlagIcon} />
</Menu.Item> </Menu.Item>
)}
</Menu.Group> </Menu.Group>
</Menu.Outer> </Menu.Outer>
</Menu.Root> </Menu.Root>
{lastMessage && (
<> {reportSubject && (
<ReportDialog <ReportDialog
subject={{ subject={reportSubject}
view: 'convo',
convoId: convo.id,
message: lastMessage,
}}
control={reportControl} control={reportControl}
onAfterSubmit={() => { onAfterSubmit={() => {
const sender = convo.members.find( unstableCacheProfileView(queryClient, profile)
member => member.did === lastMessage.sender.did,
)
if (sender) {
unstableCacheProfileView(queryClient, sender)
}
blockOrDeleteControl.open() blockOrDeleteControl.open()
}} }}
/> />
)}
{convo.kind === 'group' ? (
<AfterReportConversationDialog
control={blockOrDeleteControl}
currentScreen={currentScreen}
params={{
convoId: convo.view.id,
did: profile.did,
}}
/>
) : (
<AfterReportDialog <AfterReportDialog
control={blockOrDeleteControl} control={blockOrDeleteControl}
currentScreen={currentScreen} currentScreen={currentScreen}
params={{ params={{
convoId: convo.id, convoId: convo.view.id,
did: lastMessage.sender.did, did: profile.did,
}} }}
/> />
</>
)} )}
</> </>
) )