Replace DoneStep with separate dialog that opens after reporting
This commit is contained in:
@@ -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 (
|
||||||
|
<Dialog.Outer control={control}>
|
||||||
|
<Dialog.Handle />
|
||||||
|
<Dialog.ScrollableInner
|
||||||
|
label={_(
|
||||||
|
msg`Would you like to block this account or delete this conversation?`,
|
||||||
|
)}
|
||||||
|
style={[web({maxWidth: 400})]}>
|
||||||
|
<DialogInner params={params} currentScreen={currentScreen} />
|
||||||
|
<Dialog.Close />
|
||||||
|
</Dialog.ScrollableInner>
|
||||||
|
</Dialog.Outer>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
function DialogInner({
|
||||||
|
params,
|
||||||
|
currentScreen,
|
||||||
|
}: {
|
||||||
|
params: ReportDialogParams
|
||||||
|
currentScreen: 'list' | 'conversation'
|
||||||
|
}) {
|
||||||
|
const {data: profile} = useProfileQuery({
|
||||||
|
did: params.message.sender.did,
|
||||||
|
})
|
||||||
|
|
||||||
|
return profile ? (
|
||||||
|
<DoneStep
|
||||||
|
convoId={params.convoId}
|
||||||
|
currentScreen={currentScreen}
|
||||||
|
profile={profile}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<View style={[a.w_full, a.py_5xl, a.align_center]}>
|
||||||
|
<Loader />
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function DoneStep({
|
||||||
|
convoId,
|
||||||
|
currentScreen,
|
||||||
|
profile,
|
||||||
|
}: {
|
||||||
|
convoId: string
|
||||||
|
currentScreen: 'list' | 'conversation'
|
||||||
|
profile: AppBskyActorDefs.ProfileViewDetailed
|
||||||
|
}) {
|
||||||
|
const {_} = useLingui()
|
||||||
|
const navigation = useNavigation<NavigationProp>()
|
||||||
|
const control = Dialog.useDialogContext()
|
||||||
|
const {gtMobile} = useBreakpoints()
|
||||||
|
const t = useTheme()
|
||||||
|
const [actions, setActions] = useState<string[]>(['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 (
|
||||||
|
<View style={a.gap_2xl}>
|
||||||
|
<View style={[a.justify_center, gtMobile ? a.gap_sm : a.gap_xs]}>
|
||||||
|
<Text style={[a.text_2xl, a.font_semi_bold]}>
|
||||||
|
<Trans>Report submitted</Trans>
|
||||||
|
</Text>
|
||||||
|
<Text style={[a.text_md, t.atoms.text_contrast_medium]}>
|
||||||
|
<Trans>Our moderation team has received your report.</Trans>
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<Toggle.Group
|
||||||
|
label={_(msg`Block and/or delete this conversation`)}
|
||||||
|
values={actions}
|
||||||
|
onChange={setActions}>
|
||||||
|
<View style={[a.gap_md]}>
|
||||||
|
<Toggle.Item name="block" label={_(msg`Block user`)}>
|
||||||
|
<Toggle.Checkbox />
|
||||||
|
<Toggle.LabelText style={[a.text_md]}>
|
||||||
|
<Trans>Block user</Trans>
|
||||||
|
</Toggle.LabelText>
|
||||||
|
</Toggle.Item>
|
||||||
|
<Toggle.Item name="leave" label={_(msg`Delete conversation`)}>
|
||||||
|
<Toggle.Checkbox />
|
||||||
|
<Toggle.LabelText style={[a.text_md]}>
|
||||||
|
<Trans>Delete conversation</Trans>
|
||||||
|
</Toggle.LabelText>
|
||||||
|
</Toggle.Item>
|
||||||
|
</View>
|
||||||
|
</Toggle.Group>
|
||||||
|
|
||||||
|
<View style={[a.gap_sm]}>
|
||||||
|
<Button
|
||||||
|
label={btnText}
|
||||||
|
onPress={onPressPrimaryAction}
|
||||||
|
size="large"
|
||||||
|
color={actions.length > 0 ? 'negative' : 'primary'}>
|
||||||
|
<ButtonText>{btnText}</ButtonText>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
label={_(msg`Close`)}
|
||||||
|
onPress={() => control.close()}
|
||||||
|
size={platform({native: 'small', web: 'large'})}
|
||||||
|
color="secondary">
|
||||||
|
<ButtonText>
|
||||||
|
<Trans>Close</Trans>
|
||||||
|
</ButtonText>
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ 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 {BlockedByListDialog} from '#/components/dms/BlockedByListDialog'
|
import {BlockedByListDialog} from '#/components/dms/BlockedByListDialog'
|
||||||
|
import {BlockOrDeleteDialog} from '#/components/dms/BlockOrDeleteDialog'
|
||||||
import {LeaveConvoPrompt} from '#/components/dms/LeaveConvoPrompt'
|
import {LeaveConvoPrompt} from '#/components/dms/LeaveConvoPrompt'
|
||||||
import {ReportConversationPrompt} from '#/components/dms/ReportConversationPrompt'
|
import {ReportConversationPrompt} from '#/components/dms/ReportConversationPrompt'
|
||||||
import {ArrowBoxLeft_Stroke2_Corner0_Rounded as ArrowBoxLeft} from '#/components/icons/ArrowBoxLeft'
|
import {ArrowBoxLeft_Stroke2_Corner0_Rounded as ArrowBoxLeft} from '#/components/icons/ArrowBoxLeft'
|
||||||
@@ -65,6 +66,7 @@ let ConvoMenu = ({
|
|||||||
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 {listBlocks} = blockInfo
|
const {listBlocks} = blockInfo
|
||||||
|
|
||||||
@@ -113,14 +115,27 @@ let ConvoMenu = ({
|
|||||||
currentScreen={currentScreen}
|
currentScreen={currentScreen}
|
||||||
/>
|
/>
|
||||||
{latestReportableMessage ? (
|
{latestReportableMessage ? (
|
||||||
<ReportDialog
|
<>
|
||||||
subject={{
|
<ReportDialog
|
||||||
view: 'convo',
|
subject={{
|
||||||
convoId: convo.id,
|
view: 'convo',
|
||||||
message: latestReportableMessage,
|
convoId: convo.id,
|
||||||
}}
|
message: latestReportableMessage,
|
||||||
control={reportControl}
|
}}
|
||||||
/>
|
control={reportControl}
|
||||||
|
onClose={() => {
|
||||||
|
blockOrDeleteControl.open()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<BlockOrDeleteDialog
|
||||||
|
control={blockOrDeleteControl}
|
||||||
|
currentScreen={currentScreen}
|
||||||
|
params={{
|
||||||
|
convoId: convo.id,
|
||||||
|
message: latestReportableMessage,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<ReportConversationPrompt control={reportControl} />
|
<ReportConversationPrompt control={reportControl} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {useSession} from '#/state/session'
|
|||||||
import * as Toast from '#/view/com/util/Toast'
|
import * as Toast from '#/view/com/util/Toast'
|
||||||
import * as ContextMenu from '#/components/ContextMenu'
|
import * as ContextMenu from '#/components/ContextMenu'
|
||||||
import {type TriggerProps} from '#/components/ContextMenu/types'
|
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 {BubbleQuestion_Stroke2_Corner0_Rounded as Translate} from '#/components/icons/Bubble'
|
||||||
import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard'
|
import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard'
|
||||||
import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
|
import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
|
||||||
@@ -37,6 +38,7 @@ export let MessageContextMenu = ({
|
|||||||
const convo = useConvoActive()
|
const convo = useConvoActive()
|
||||||
const deleteControl = usePromptControl()
|
const deleteControl = usePromptControl()
|
||||||
const reportControl = usePromptControl()
|
const reportControl = usePromptControl()
|
||||||
|
const blockOrDeleteControl = usePromptControl()
|
||||||
const langPrefs = useLanguagePrefs()
|
const langPrefs = useLanguagePrefs()
|
||||||
const translate = useTranslate()
|
const translate = useTranslate()
|
||||||
|
|
||||||
@@ -178,6 +180,17 @@ export let MessageContextMenu = ({
|
|||||||
convoId: convo.convo.id,
|
convoId: convo.convo.id,
|
||||||
message,
|
message,
|
||||||
}}
|
}}
|
||||||
|
onClose={() => {
|
||||||
|
blockOrDeleteControl.open()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<BlockOrDeleteDialog
|
||||||
|
control={blockOrDeleteControl}
|
||||||
|
currentScreen="conversation"
|
||||||
|
params={{
|
||||||
|
convoId: convo.convo.id,
|
||||||
|
message,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Prompt.Basic
|
<Prompt.Basic
|
||||||
|
|||||||
@@ -60,7 +60,8 @@ export function ReportDialog(
|
|||||||
)
|
)
|
||||||
const onClose = React.useCallback(() => {
|
const onClose = React.useCallback(() => {
|
||||||
logger.metric('reportDialog:close', {}, {statsig: false})
|
logger.metric('reportDialog:close', {}, {statsig: false})
|
||||||
}, [])
|
props.onClose?.()
|
||||||
|
}, [props])
|
||||||
return (
|
return (
|
||||||
<Dialog.Outer control={props.control} onClose={onClose}>
|
<Dialog.Outer control={props.control} onClose={onClose}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
|
|||||||
@@ -68,4 +68,5 @@ export type ParsedReportSubject =
|
|||||||
export type ReportDialogProps = {
|
export type ReportDialogProps = {
|
||||||
control: Dialog.DialogOuterProps['control']
|
control: Dialog.DialogOuterProps['control']
|
||||||
subject: ParsedReportSubject
|
subject: ParsedReportSubject
|
||||||
|
onClose?: () => void
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import {
|
|||||||
EmailDialogScreenID,
|
EmailDialogScreenID,
|
||||||
useEmailDialogControl,
|
useEmailDialogControl,
|
||||||
} from '#/components/dialogs/EmailDialog'
|
} from '#/components/dialogs/EmailDialog'
|
||||||
|
import {BlockOrDeleteDialog} from '#/components/dms/BlockOrDeleteDialog'
|
||||||
import {CircleX_Stroke2_Corner0_Rounded} from '#/components/icons/CircleX'
|
import {CircleX_Stroke2_Corner0_Rounded} from '#/components/icons/CircleX'
|
||||||
import {Flag_Stroke2_Corner0_Rounded as FlagIcon} from '#/components/icons/Flag'
|
import {Flag_Stroke2_Corner0_Rounded as FlagIcon} from '#/components/icons/Flag'
|
||||||
import {PersonX_Stroke2_Corner0_Rounded as PersonXIcon} from '#/components/icons/Person'
|
import {PersonX_Stroke2_Corner0_Rounded as PersonXIcon} from '#/components/icons/Person'
|
||||||
@@ -100,6 +101,7 @@ export function RejectMenu({
|
|||||||
}, [queueBlock, leaveConvo, _])
|
}, [queueBlock, leaveConvo, _])
|
||||||
|
|
||||||
const reportControl = useDialogControl()
|
const reportControl = useDialogControl()
|
||||||
|
const blockOrDeleteControl = useDialogControl()
|
||||||
|
|
||||||
const lastMessage = ChatBskyConvoDefs.isMessageView(convo.lastMessage)
|
const lastMessage = ChatBskyConvoDefs.isMessageView(convo.lastMessage)
|
||||||
? convo.lastMessage
|
? convo.lastMessage
|
||||||
@@ -162,14 +164,27 @@ export function RejectMenu({
|
|||||||
</Menu.Outer>
|
</Menu.Outer>
|
||||||
</Menu.Root>
|
</Menu.Root>
|
||||||
{lastMessage && (
|
{lastMessage && (
|
||||||
<ReportDialog
|
<>
|
||||||
subject={{
|
<ReportDialog
|
||||||
view: 'convo',
|
subject={{
|
||||||
convoId: convo.id,
|
view: 'convo',
|
||||||
message: lastMessage,
|
convoId: convo.id,
|
||||||
}}
|
message: lastMessage,
|
||||||
control={reportControl}
|
}}
|
||||||
/>
|
control={reportControl}
|
||||||
|
onClose={() => {
|
||||||
|
blockOrDeleteControl.open()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<BlockOrDeleteDialog
|
||||||
|
control={blockOrDeleteControl}
|
||||||
|
currentScreen={currentScreen}
|
||||||
|
params={{
|
||||||
|
convoId: convo.id,
|
||||||
|
message: lastMessage,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user