import {memo, useMemo, useState} from 'react'
import {View} from 'react-native'
import {
type $Typed,
type AppBskyActorDefs,
type ChatBskyConvoDefs,
type ComAtprotoModerationCreateReport,
RichText as RichTextAPI,
} from '@atproto/api'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {StackActions, useNavigation} from '@react-navigation/native'
import {useMutation} from '@tanstack/react-query'
import type React from 'react'
import {BLUESKY_MOD_SERVICE_HEADERS} from '#/lib/constants'
import {type ReportOption} from '#/lib/moderation/useReportOptions'
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 {useAgent} from '#/state/session'
import {CharProgress} from '#/view/com/composer/char-progress/CharProgress'
import * as Toast from '#/view/com/util/Toast'
import {atoms as a, platform, useBreakpoints, useTheme, web} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {Divider} from '#/components/Divider'
import * as Toggle from '#/components/forms/Toggle'
import {ChevronLeft_Stroke2_Corner0_Rounded as Chevron} from '#/components/icons/Chevron'
import {PaperPlane_Stroke2_Corner0_Rounded as SendIcon} from '#/components/icons/PaperPlane'
import {Loader} from '#/components/Loader'
import {SelectReportOptionView} from '#/components/ReportDialog/SelectReportOptionView'
import {RichText} from '#/components/RichText'
import {Text} from '#/components/Typography'
import {MessageItemMetadata} from './MessageItem'
type ReportDialogParams = {
type: 'convoMessage'
convoId: string
message: ChatBskyConvoDefs.MessageView
}
let ReportDialog = ({
control,
params,
currentScreen,
}: {
control: Dialog.DialogControlProps
params: ReportDialogParams
currentScreen: 'list' | 'conversation'
}): React.ReactNode => {
const {_} = useLingui()
return (
)
}
ReportDialog = memo(ReportDialog)
export {ReportDialog}
function DialogInner({
params,
currentScreen,
}: {
params: ReportDialogParams
currentScreen: 'list' | 'conversation'
}) {
const {data: profile, isError} = useProfileQuery({
did: params.message.sender.did,
})
const [reportOption, setReportOption] = useState(null)
const [done, setDone] = useState(false)
const control = Dialog.useDialogContext()
return done ? (
profile ? (
) : (
)
) : reportOption ? (
setReportOption(null)}
onComplete={() => {
if (isError) {
control.close()
} else {
setDone(true)
}
}}
/>
) : (
)
}
function ReasonStep({
setReportOption,
}: {
setReportOption: (reportOption: ReportOption) => void
params: ReportDialogParams
}) {
const control = Dialog.useDialogContext()
return (
)
}
function SubmitStep({
params,
reportOption,
goBack,
onComplete,
}: {
params: ReportDialogParams
reportOption: ReportOption
goBack: () => void
onComplete: () => void
}) {
const {_} = useLingui()
const {gtMobile} = useBreakpoints()
const t = useTheme()
const [details, setDetails] = useState('')
const agent = useAgent()
const {
mutate: submit,
error,
isPending: submitting,
} = useMutation({
mutationFn: async () => {
if (params.type === 'convoMessage') {
const {convoId, message} = params
const subject: $Typed = {
$type: 'chat.bsky.convo.defs#messageRef',
messageId: message.id,
convoId,
did: message.sender.did,
}
const report = {
reasonType: reportOption.reason,
subject,
reason: details,
} satisfies ComAtprotoModerationCreateReport.InputSchema
await agent.createModerationReport(report, {
encoding: 'application/json',
headers: BLUESKY_MOD_SERVICE_HEADERS,
})
}
},
onSuccess: onComplete,
})
const copy = useMemo(() => {
return {
convoMessage: {
title: _(msg`Report this message`),
},
}[params.type]
}, [_, params])
return (
{copy.title}
Your report will be sent to the Bluesky Moderation Service
{params.type === 'convoMessage' && (
)}
Reason:
{' '}
{reportOption.title}
Optionally provide additional information below:
{error && (
There was an issue sending your report. Please check your internet
connection.
)}
)
}
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
)
}
function PreviewMessage({message}: {message: ChatBskyConvoDefs.MessageView}) {
const t = useTheme()
const rt = useMemo(() => {
return new RichTextAPI({text: message.text, facets: message.facets})
}, [message.text, message.facets])
return (
)
}