diff --git a/src/components/dms/BlockOrDeleteDialog.tsx b/src/components/dms/BlockOrDeleteDialog.tsx
new file mode 100644
index 0000000000..96120ab2d6
--- /dev/null
+++ b/src/components/dms/BlockOrDeleteDialog.tsx
@@ -0,0 +1,187 @@
+import {memo, useState} from 'react'
+import {View} from 'react-native'
+import {type AppBskyActorDefs, type ChatBskyConvoDefs} from '@atproto/api'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+import {StackActions, useNavigation} from '@react-navigation/native'
+import type React from 'react'
+
+import {type NavigationProp} from '#/lib/routes/types'
+import {isNative} from '#/platform/detection'
+import {useProfileShadow} from '#/state/cache/profile-shadow'
+import {useLeaveConvo} from '#/state/queries/messages/leave-conversation'
+import {
+ useProfileBlockMutationQueue,
+ useProfileQuery,
+} from '#/state/queries/profile'
+import * as Toast from '#/view/com/util/Toast'
+import {atoms as a, platform, useBreakpoints, useTheme, web} from '#/alf'
+import {Button, ButtonText} from '#/components/Button'
+import * as Dialog from '#/components/Dialog'
+import * as Toggle from '#/components/forms/Toggle'
+import {Loader} from '#/components/Loader'
+import {Text} from '#/components/Typography'
+
+type ReportDialogParams = {
+ convoId: string
+ message: ChatBskyConvoDefs.MessageView
+}
+
+export const BlockOrDeleteDialog = memo(function BlockOrDeleteDialogInner({
+ control,
+ params,
+ currentScreen,
+}: {
+ control: Dialog.DialogControlProps
+ params: ReportDialogParams
+ currentScreen: 'list' | 'conversation'
+}): React.ReactNode {
+ const {_} = useLingui()
+ return (
+
+
+
+
+
+
+
+ )
+})
+
+function DialogInner({
+ params,
+ currentScreen,
+}: {
+ params: ReportDialogParams
+ currentScreen: 'list' | 'conversation'
+}) {
+ const {data: profile} = useProfileQuery({
+ did: params.message.sender.did,
+ })
+
+ return profile ? (
+
+ ) : (
+
+
+
+ )
+}
+
+function DoneStep({
+ convoId,
+ currentScreen,
+ profile,
+}: {
+ convoId: string
+ currentScreen: 'list' | 'conversation'
+ profile: AppBskyActorDefs.ProfileViewDetailed
+}) {
+ const {_} = useLingui()
+ const navigation = useNavigation()
+ const control = Dialog.useDialogContext()
+ const {gtMobile} = useBreakpoints()
+ const t = useTheme()
+ const [actions, setActions] = useState(['block', 'leave'])
+ const shadow = useProfileShadow(profile)
+ const [queueBlock] = useProfileBlockMutationQueue(shadow)
+
+ const {mutate: leaveConvo} = useLeaveConvo(convoId, {
+ onMutate: () => {
+ if (currentScreen === 'conversation') {
+ navigation.dispatch(
+ StackActions.replace('Messages', isNative ? {animation: 'pop'} : {}),
+ )
+ }
+ },
+ onError: () => {
+ Toast.show(_(msg`Could not leave chat`), 'xmark')
+ },
+ })
+
+ let btnText = _(msg`Done`)
+ let toastMsg: string | undefined
+ if (actions.includes('leave') && actions.includes('block')) {
+ btnText = _(msg`Block and Delete`)
+ toastMsg = _(msg({message: 'Conversation deleted', context: 'toast'}))
+ } else if (actions.includes('leave')) {
+ btnText = _(msg`Delete Conversation`)
+ toastMsg = _(msg({message: 'Conversation deleted', context: 'toast'}))
+ } else if (actions.includes('block')) {
+ btnText = _(msg`Block User`)
+ toastMsg = _(msg({message: 'User blocked', context: 'toast'}))
+ }
+
+ const onPressPrimaryAction = () => {
+ control.close(() => {
+ if (actions.includes('block')) {
+ queueBlock()
+ }
+ if (actions.includes('leave')) {
+ leaveConvo()
+ }
+ if (toastMsg) {
+ Toast.show(toastMsg, 'check')
+ }
+ })
+ }
+
+ return (
+
+
+
+ Report submitted
+
+
+ Our moderation team has received your report.
+
+
+
+
+
+
+
+ Block user
+
+
+
+
+
+ Delete conversation
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/components/dms/ConvoMenu.tsx b/src/components/dms/ConvoMenu.tsx
index 152b686f86..c952c7ae22 100644
--- a/src/components/dms/ConvoMenu.tsx
+++ b/src/components/dms/ConvoMenu.tsx
@@ -18,6 +18,7 @@ import {type ViewStyleProp} from '#/alf'
import {atoms as a} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button'
import {BlockedByListDialog} from '#/components/dms/BlockedByListDialog'
+import {BlockOrDeleteDialog} from '#/components/dms/BlockOrDeleteDialog'
import {LeaveConvoPrompt} from '#/components/dms/LeaveConvoPrompt'
import {ReportConversationPrompt} from '#/components/dms/ReportConversationPrompt'
import {ArrowBoxLeft_Stroke2_Corner0_Rounded as ArrowBoxLeft} from '#/components/icons/ArrowBoxLeft'
@@ -65,6 +66,7 @@ let ConvoMenu = ({
const leaveConvoControl = Prompt.usePromptControl()
const reportControl = Prompt.usePromptControl()
const blockedByListControl = Prompt.usePromptControl()
+ const blockOrDeleteControl = Prompt.usePromptControl()
const {listBlocks} = blockInfo
@@ -113,14 +115,27 @@ let ConvoMenu = ({
currentScreen={currentScreen}
/>
{latestReportableMessage ? (
-
+ <>
+ {
+ blockOrDeleteControl.open()
+ }}
+ />
+
+ >
) : (
)}
diff --git a/src/components/dms/MessageContextMenu.tsx b/src/components/dms/MessageContextMenu.tsx
index cbf8798a89..5f81c383ab 100644
--- a/src/components/dms/MessageContextMenu.tsx
+++ b/src/components/dms/MessageContextMenu.tsx
@@ -15,6 +15,7 @@ import {useSession} from '#/state/session'
import * as Toast from '#/view/com/util/Toast'
import * as ContextMenu from '#/components/ContextMenu'
import {type TriggerProps} from '#/components/ContextMenu/types'
+import {BlockOrDeleteDialog} from '#/components/dms/BlockOrDeleteDialog'
import {BubbleQuestion_Stroke2_Corner0_Rounded as Translate} from '#/components/icons/Bubble'
import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard'
import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
@@ -37,6 +38,7 @@ export let MessageContextMenu = ({
const convo = useConvoActive()
const deleteControl = usePromptControl()
const reportControl = usePromptControl()
+ const blockOrDeleteControl = usePromptControl()
const langPrefs = useLanguagePrefs()
const translate = useTranslate()
@@ -178,6 +180,17 @@ export let MessageContextMenu = ({
convoId: convo.convo.id,
message,
}}
+ onClose={() => {
+ blockOrDeleteControl.open()
+ }}
+ />
+
{
logger.metric('reportDialog:close', {}, {statsig: false})
- }, [])
+ props.onClose?.()
+ }, [props])
return (
diff --git a/src/components/moderation/ReportDialog/types.ts b/src/components/moderation/ReportDialog/types.ts
index 3691b6cd65..c5d0f1a7eb 100644
--- a/src/components/moderation/ReportDialog/types.ts
+++ b/src/components/moderation/ReportDialog/types.ts
@@ -68,4 +68,5 @@ export type ParsedReportSubject =
export type ReportDialogProps = {
control: Dialog.DialogOuterProps['control']
subject: ParsedReportSubject
+ onClose?: () => void
}
diff --git a/src/screens/Messages/components/RequestButtons.tsx b/src/screens/Messages/components/RequestButtons.tsx
index 41e92be6a3..1d11fcca74 100644
--- a/src/screens/Messages/components/RequestButtons.tsx
+++ b/src/screens/Messages/components/RequestButtons.tsx
@@ -25,6 +25,7 @@ import {
EmailDialogScreenID,
useEmailDialogControl,
} from '#/components/dialogs/EmailDialog'
+import {BlockOrDeleteDialog} from '#/components/dms/BlockOrDeleteDialog'
import {CircleX_Stroke2_Corner0_Rounded} from '#/components/icons/CircleX'
import {Flag_Stroke2_Corner0_Rounded as FlagIcon} from '#/components/icons/Flag'
import {PersonX_Stroke2_Corner0_Rounded as PersonXIcon} from '#/components/icons/Person'
@@ -100,6 +101,7 @@ export function RejectMenu({
}, [queueBlock, leaveConvo, _])
const reportControl = useDialogControl()
+ const blockOrDeleteControl = useDialogControl()
const lastMessage = ChatBskyConvoDefs.isMessageView(convo.lastMessage)
? convo.lastMessage
@@ -162,14 +164,27 @@ export function RejectMenu({
{lastMessage && (
-
+ <>
+ {
+ blockOrDeleteControl.open()
+ }}
+ />
+
+ >
)}
>
)