This commit is contained in:
Eric Bailey
2025-02-23 17:49:35 -06:00
parent 6f077c16ae
commit bb1c482259
2 changed files with 32 additions and 24 deletions
@@ -13,9 +13,9 @@ import {isNative} from '#/platform/detection'
import {useMyLabelersQuery} from '#/state/queries/preferences' import {useMyLabelersQuery} from '#/state/queries/preferences'
import {CharProgress} from '#/view/com/composer/char-progress/CharProgress' import {CharProgress} from '#/view/com/composer/char-progress/CharProgress'
import {UserAvatar} from '#/view/com/util/UserAvatar' import {UserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, useGutters,useTheme} from '#/alf' import {atoms as a, useGutters, useTheme} from '#/alf'
import {Admonition} from '#/components/Admonition' import {Admonition} from '#/components/Admonition'
import {Button, ButtonIcon,ButtonText} from '#/components/Button' import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog' import * as Dialog from '#/components/Dialog'
import {useDelayedLoading} from '#/components/hooks/useDelayedLoading' import {useDelayedLoading} from '#/components/hooks/useDelayedLoading'
import { import {
@@ -24,7 +24,7 @@ import {
} from '#/components/icons/Check' } from '#/components/icons/Check'
import {PaperPlane_Stroke2_Corner0_Rounded as PaperPlane} from '#/components/icons/PaperPlane' import {PaperPlane_Stroke2_Corner0_Rounded as PaperPlane} from '#/components/icons/PaperPlane'
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
import {createStaticClick,InlineLinkText} from '#/components/Link' import {createStaticClick, InlineLinkText} from '#/components/Link'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {useSubmitReportMutation} from './action' import {useSubmitReportMutation} from './action'
@@ -32,7 +32,7 @@ import {useCopyForSubject} from './copy'
import {initialState, reducer} from './state' import {initialState, reducer} from './state'
import {ReportDialogProps, ReportSubject} from './types' import {ReportDialogProps, ReportSubject} from './types'
import {parseReportSubject} from './utils/parseReportSubject' import {parseReportSubject} from './utils/parseReportSubject'
import {ReportOption,useReportOptions} from './utils/useReportOptions' import {ReportOption, useReportOptions} from './utils/useReportOptions'
export {useDialogControl as useReportDialogControl} from '#/components/Dialog' export {useDialogControl as useReportDialogControl} from '#/components/Dialog'
@@ -48,21 +48,27 @@ export function ReportDialog(
return ( return (
<Dialog.Outer control={props.control}> <Dialog.Outer control={props.control}>
<Dialog.Handle /> <Dialog.Handle />
{subject ? <Inner {...props} subject={subject} /> : <Invalid />} {subject && false ? <Inner {...props} subject={subject} /> : <Invalid />}
</Dialog.Outer> </Dialog.Outer>
) )
} }
// TODO /**
* This should only be shown if the dialog is configured incorrectly by a
* developer, but nevertheless we should have a graceful fallback.
*/
function Invalid() { function Invalid() {
const {_} = useLingui() const {_} = useLingui()
return ( return (
<Dialog.ScrollableInner label={_(msg`Report dialog`)}> <Dialog.ScrollableInner label={_(msg`Report dialog`)}>
<Text style={[a.font_heavy, a.text_xl]}> <Text style={[a.font_heavy, a.text_xl, a.leading_snug, a.pb_xs]}>
<Trans>Invalid report subject</Trans> <Trans>Invalid report subject</Trans>
</Text> </Text>
<Text style={[a.text_md]}> <Text style={[a.text_md, a.leading_snug]}>
<Trans>This is a developer error.</Trans> <Trans>
Something wasn't quite right with the data you're trying to report.
Please contact support.
</Trans>
</Text> </Text>
<Dialog.Close /> <Dialog.Close />
</Dialog.ScrollableInner> </Dialog.ScrollableInner>
@@ -78,13 +84,17 @@ function Inner(props: ReportDialogProps) {
error: labelersLoadError, error: labelersLoadError,
} = useMyLabelersQuery({excludeNonConfigurableLabelers: true}) } = useMyLabelersQuery({excludeNonConfigurableLabelers: true})
const isLoading = useDelayedLoading(500, isLabelerLoading) const isLoading = useDelayedLoading(500, isLabelerLoading)
const copy = useCopyForSubject(props.subject) const copy = useCopyForSubject(props.subject)
const allOptions = useReportOptions() const reportOptions = useReportOptions()
const options = allOptions[props.subject.type]
const [state, dispatch] = React.useReducer(reducer, initialState) const [state, dispatch] = React.useReducer(reducer, initialState)
/**
* Submission handling
*/
const {mutateAsync: submitReport} = useSubmitReportMutation()
const [isPending, setPending] = React.useState(false)
const [isSuccess, setSuccess] = React.useState(false)
/** /**
* Labelers that support this `subject` and its NSID collection * Labelers that support this `subject` and its NSID collection
*/ */
@@ -92,7 +102,7 @@ function Inner(props: ReportDialogProps) {
if (!allLabelers) return [] if (!allLabelers) return []
return allLabelers return allLabelers
.filter(l => { .filter(l => {
// @ts-ignore // @ts-expect-error TODO
const subjectTypes: string[] | undefined = l.subjectTypes const subjectTypes: string[] | undefined = l.subjectTypes
if (subjectTypes === undefined) return true if (subjectTypes === undefined) return true
if (props.subject.type === 'account') { if (props.subject.type === 'account') {
@@ -104,7 +114,7 @@ function Inner(props: ReportDialogProps) {
} }
}) })
.filter(l => { .filter(l => {
// @ts-ignore // @ts-expect-error TODO
const collections: string[] | undefined = l.subjectCollections const collections: string[] | undefined = l.subjectCollections
if (collections === undefined) return true if (collections === undefined) return true
// all chat collections accepted, since only Bluesky handles chats // all chat collections accepted, since only Bluesky handles chats
@@ -113,22 +123,19 @@ function Inner(props: ReportDialogProps) {
}) })
.filter(l => { .filter(l => {
if (!state.selectedOption) return true if (!state.selectedOption) return true
// @ts-ignore // @ts-expect-error TODO
const reasonTypes: string[] | undefined = l.reasonTypes const reasonTypes: string[] | undefined = l.reasonTypes
if (reasonTypes === undefined) return true if (reasonTypes === undefined) return true
return reasonTypes.includes(state.selectedOption.reason) return reasonTypes.includes(state.selectedOption.reason)
}) })
}, [props, allLabelers, state.selectedOption]) }, [props, allLabelers, state.selectedOption])
const {mutateAsync: submitReport} = useSubmitReportMutation()
const [isPending, setPending] = React.useState(false)
const [isSuccess, setSuccess] = React.useState(false)
const onSubmit = React.useCallback(async () => { const onSubmit = React.useCallback(async () => {
dispatch({type: 'clearError'}) dispatch({type: 'clearError'})
try { try {
setPending(true) setPending(true)
// wait at least 1s, make it feel substantial
await wait( await wait(
1e3, 1e3,
submitReport({ submitReport({
@@ -137,6 +144,7 @@ function Inner(props: ReportDialogProps) {
}), }),
) )
setSuccess(true) setSuccess(true)
// give time for user feedback
setTimeout(() => { setTimeout(() => {
props.control.close() props.control.close()
}, 1e3) }, 1e3)
@@ -198,7 +206,7 @@ function Inner(props: ReportDialogProps) {
</View> </View>
) : ( ) : (
<View style={[a.gap_sm]}> <View style={[a.gap_sm]}>
{options.map(o => ( {reportOptions[props.subject.type].map(o => (
<OptionCard <OptionCard
key={o.reason} key={o.reason}
option={o} option={o}
+3 -3
View File
@@ -86,18 +86,18 @@ export function useLabelersDetailedInfoQuery({dids}: {dids: string[]}) {
const decorated = res.data.views.map(view => ({ const decorated = res.data.views.map(view => ({
...view, ...view,
reasonTypes: REASON_TYPES, reasonTypes: REASON_TYPES,
// @ts-ignore TODO
subjectTypes: subjectTypes:
// @ts-expect-error TODO
view.creator.did === BSKY_MOD_SERVICE view.creator.did === BSKY_MOD_SERVICE
? ['account', 'record', 'chat'] ? ['account', 'record', 'chat']
: ['account', 'record'], : ['account', 'record'],
// @ts-ignore TODO
subjectCollections: subjectCollections:
// @ts-expect-error TODO
view.creator.did === BSKY_MOD_SERVICE view.creator.did === BSKY_MOD_SERVICE
? undefined ? undefined
: ['app.bsky.feed.post'], : ['app.bsky.feed.post'],
})) }))
return decorated return decorated as AppBskyLabelerDefs.LabelerViewDetailed[]
}, },
}) })
} }