Update the reporting flow to first select a recipient if the user has multiple labelers (#3258)

zio/stable
Paul Frazee 2024-03-18 16:15:57 -07:00 committed by GitHub
parent 1b10c7bc08
commit 959121f394
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 175 additions and 20 deletions

View File

@ -0,0 +1,115 @@
import React from 'react'
import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {AppBskyLabelerDefs} from '@atproto/api'
export {useDialogControl as useReportDialogControl} from '#/components/Dialog'
import {atoms as a, useTheme} from '#/alf'
import {Text} from '#/components/Typography'
import {Button, useButtonContext} from '#/components/Button'
import {Divider} from '#/components/Divider'
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron'
import {ReportDialogProps} from './types'
export function SelectLabelerView({
...props
}: ReportDialogProps & {
labelers: AppBskyLabelerDefs.LabelerViewDetailed[]
onSelectLabeler: (v: string) => void
}) {
const t = useTheme()
const {_} = useLingui()
return (
<View style={[a.gap_lg]}>
<View style={[a.justify_center, a.gap_sm]}>
<Text style={[a.text_2xl, a.font_bold]}>
<Trans>Select moderation service</Trans>
</Text>
<Text style={[a.text_md, t.atoms.text_contrast_medium]}>
<Trans>Who do you want to send this report to?</Trans>
</Text>
</View>
<Divider />
<View style={[a.gap_sm, {marginHorizontal: a.p_md.padding * -1}]}>
{props.labelers.map(labeler => {
return (
<Button
key={labeler.creator.did}
label={_(msg`Send report to ${labeler.creator.displayName}`)}
onPress={() => props.onSelectLabeler(labeler.creator.did)}>
<LabelerButton
title={labeler.creator.displayName || labeler.creator.handle}
description={labeler.creator.description || ''}
/>
</Button>
)
})}
</View>
</View>
)
}
function LabelerButton({
title,
description,
}: {
title: string
description: string
}) {
const t = useTheme()
const {hovered, pressed} = useButtonContext()
const interacted = hovered || pressed
const styles = React.useMemo(() => {
return {
interacted: {
backgroundColor: t.palette.contrast_50,
},
}
}, [t])
return (
<View
style={[
a.w_full,
a.flex_row,
a.align_center,
a.justify_between,
a.p_md,
a.rounded_md,
{paddingRight: 70},
interacted && styles.interacted,
]}>
<View style={[a.flex_1, a.gap_xs]}>
<Text style={[a.text_md, a.font_bold, t.atoms.text_contrast_medium]}>
{title}
</Text>
<Text style={[a.leading_tight, {maxWidth: 400}]} numberOfLines={3}>
{description}
</Text>
</View>
<View
style={[
a.absolute,
a.inset_0,
a.justify_center,
a.pr_md,
{left: 'auto'},
]}>
<ChevronRight
size="md"
fill={
hovered ? t.palette.primary_500 : t.atoms.text_contrast_low.color
}
/>
</View>
</View>
)
}

View File

@ -18,7 +18,10 @@ import {
useButtonContext, useButtonContext,
} from '#/components/Button' } from '#/components/Button'
import {Divider} from '#/components/Divider' import {Divider} from '#/components/Divider'
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron' import {
ChevronRight_Stroke2_Corner0_Rounded as ChevronRight,
ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft,
} from '#/components/icons/Chevron'
import {SquareArrowTopRight_Stroke2_Corner0_Rounded as SquareArrowTopRight} from '#/components/icons/SquareArrowTopRight' import {SquareArrowTopRight_Stroke2_Corner0_Rounded as SquareArrowTopRight} from '#/components/icons/SquareArrowTopRight'
import {ReportDialogProps} from './types' import {ReportDialogProps} from './types'
@ -28,6 +31,7 @@ export function SelectReportOptionView({
}: ReportDialogProps & { }: ReportDialogProps & {
labelers: AppBskyLabelerDefs.LabelerViewDetailed[] labelers: AppBskyLabelerDefs.LabelerViewDetailed[]
onSelectReportOption: (reportOption: ReportOption) => void onSelectReportOption: (reportOption: ReportOption) => void
goBack: () => void
}) { }) {
const t = useTheme() const t = useTheme()
const {_} = useLingui() const {_} = useLingui()
@ -60,6 +64,18 @@ export function SelectReportOptionView({
return ( return (
<View style={[a.gap_lg]}> <View style={[a.gap_lg]}>
{props.labelers?.length > 1 ? (
<Button
size="small"
variant="solid"
color="secondary"
shape="round"
label={_(msg`Go back to previous step`)}
onPress={props.goBack}>
<ButtonIcon icon={ChevronLeft} />
</Button>
) : null}
<View style={[a.justify_center, a.gap_sm]}> <View style={[a.justify_center, a.gap_sm]}>
<Text style={[a.text_2xl, a.font_bold]}>{i18n.title}</Text> <Text style={[a.text_2xl, a.font_bold]}>{i18n.title}</Text>
<Text style={[a.text_md, t.atoms.text_contrast_medium]}> <Text style={[a.text_md, t.atoms.text_contrast_medium]}>

View File

@ -24,11 +24,13 @@ import {getAgent} from '#/state/session'
export function SubmitView({ export function SubmitView({
params, params,
labelers, labelers,
selectedLabeler,
selectedReportOption, selectedReportOption,
goBack, goBack,
onSubmitComplete, onSubmitComplete,
}: ReportDialogProps & { }: ReportDialogProps & {
labelers: AppBskyLabelerDefs.LabelerViewDetailed[] labelers: AppBskyLabelerDefs.LabelerViewDetailed[]
selectedLabeler: string
selectedReportOption: ReportOption selectedReportOption: ReportOption
goBack: () => void goBack: () => void
onSubmitComplete: () => void onSubmitComplete: () => void
@ -37,9 +39,9 @@ export function SubmitView({
const {_} = useLingui() const {_} = useLingui()
const [details, setDetails] = React.useState<string>('') const [details, setDetails] = React.useState<string>('')
const [submitting, setSubmitting] = React.useState<boolean>(false) const [submitting, setSubmitting] = React.useState<boolean>(false)
const [selectedServices, setSelectedServices] = React.useState<string[]>( const [selectedServices, setSelectedServices] = React.useState<string[]>([
labelers?.map(labeler => labeler.creator.did) || [], selectedLabeler,
) ])
const [error, setError] = React.useState('') const [error, setError] = React.useState('')
const submit = React.useCallback(async () => { const submit = React.useCallback(async () => {

View File

@ -12,9 +12,11 @@ import * as Dialog from '#/components/Dialog'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {ReportDialogProps} from './types' import {ReportDialogProps} from './types'
import {SelectLabelerView} from './SelectLabelerView'
import {SelectReportOptionView} from './SelectReportOptionView' import {SelectReportOptionView} from './SelectReportOptionView'
import {SubmitView} from './SubmitView' import {SubmitView} from './SubmitView'
import {useDelayedLoading} from '#/components/hooks/useDelayedLoading' import {useDelayedLoading} from '#/components/hooks/useDelayedLoading'
import {AppBskyLabelerDefs} from '@atproto/api'
export function ReportDialog(props: ReportDialogProps) { export function ReportDialog(props: ReportDialogProps) {
return ( return (
@ -33,9 +35,6 @@ function ReportDialogInner(props: ReportDialogProps) {
error, error,
} = useMyLabelersQuery() } = useMyLabelersQuery()
const isLoading = useDelayedLoading(500, isLabelerLoading) const isLoading = useDelayedLoading(500, isLabelerLoading)
const [selectedReportOption, setSelectedReportOption] = React.useState<
ReportOption | undefined
>()
return ( return (
<Dialog.ScrollableInner label="Report Dialog"> <Dialog.ScrollableInner label="Report Dialog">
@ -51,23 +50,46 @@ function ReportDialogInner(props: ReportDialogProps) {
<Trans>Something went wrong, please try again.</Trans> <Trans>Something went wrong, please try again.</Trans>
</Text> </Text>
</View> </View>
) : selectedReportOption ? (
<SubmitView
{...props}
labelers={labelers}
selectedReportOption={selectedReportOption}
goBack={() => setSelectedReportOption(undefined)}
onSubmitComplete={() => props.control.close()}
/>
) : ( ) : (
<SelectReportOptionView <ReportDialogLoaded labelers={labelers} {...props} />
{...props}
labelers={labelers}
onSelectReportOption={setSelectedReportOption}
/>
)} )}
<Dialog.Close /> <Dialog.Close />
</Dialog.ScrollableInner> </Dialog.ScrollableInner>
) )
} }
function ReportDialogLoaded(
props: ReportDialogProps & {
labelers: AppBskyLabelerDefs.LabelerViewDetailed[]
},
) {
const [selectedLabeler, setSelectedLabeler] = React.useState<
string | undefined
>(props.labelers.length === 1 ? props.labelers[0].creator.did : undefined)
const [selectedReportOption, setSelectedReportOption] = React.useState<
ReportOption | undefined
>()
if (selectedReportOption && selectedLabeler) {
return (
<SubmitView
{...props}
selectedLabeler={selectedLabeler}
selectedReportOption={selectedReportOption}
goBack={() => setSelectedReportOption(undefined)}
onSubmitComplete={() => props.control.close()}
/>
)
}
if (selectedLabeler) {
return (
<SelectReportOptionView
{...props}
goBack={() => setSelectedLabeler(undefined)}
onSelectReportOption={setSelectedReportOption}
/>
)
}
return <SelectLabelerView {...props} onSelectLabeler={setSelectedLabeler} />
}