Report dialog step 2

This commit is contained in:
Eric Bailey
2024-02-08 18:47:48 -06:00
parent f71cccd620
commit e45cc6f774
3 changed files with 236 additions and 28 deletions
+27
View File
@@ -0,0 +1,27 @@
import React from 'react'
import LinearGradient from 'react-native-linear-gradient'
import {atoms as a, tokens} from '#/alf'
export function GradientFill({
gradient,
}: {
gradient:
| typeof tokens.gradients.sky
| typeof tokens.gradients.midnight
| typeof tokens.gradients.sunrise
| typeof tokens.gradients.sunset
| typeof tokens.gradients.bonfire
| typeof tokens.gradients.summer
| typeof tokens.gradients.nordic
}) {
return (
<LinearGradient
colors={gradient.values.map(c => c[1])}
locations={gradient.values.map(c => c[0])}
start={{x: 0, y: 0}}
end={{x: 1, y: 1}}
style={[a.absolute, a.inset_0]}
/>
)
}
+203 -25
View File
@@ -5,14 +5,23 @@ import {useLingui} from '@lingui/react'
import {LABEL_GROUPS, LabelGroupDefinition} from '@atproto/api'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {atoms as a, useTheme} from '#/alf'
import {atoms as a, useTheme, tokens, native} from '#/alf'
import {Text} from '#/components/Typography'
import * as Dialog from '#/components/Dialog'
import {GlobalDialogProps} from '#/components/dialogs'
import {Button, useButtonContext} from '#/components/Button'
import {Button, ButtonIcon, useButtonContext} from '#/components/Button'
import {Divider} from '#/components/Divider'
import {useLabelGroupStrings} from '#/lib/moderation'
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 {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
import * as Toggle from '#/components/forms/Toggle'
import {GradientFill} from '#/components/GradientFill'
import * as TextField from '#/components/forms/TextField'
import {CharProgress} from '#/view/com/composer/char-progress/CharProgress'
export type ReportDialogProps =
| {
@@ -80,6 +89,167 @@ function LabelGroupButton({labelGroup}: {labelGroup: string}) {
)
}
function ModServiceToggle({title}: {title: string}) {
const t = useTheme()
const ctx = Toggle.useItemContext()
return (
<View
style={[
a.py_md,
a.px_xl,
a.rounded_full,
a.overflow_hidden,
t.atoms.bg_contrast_25,
]}>
{ctx.selected && <GradientFill gradient={tokens.gradients.midnight} />}
<View
style={[
a.flex_row,
a.align_center,
a.justify_between,
a.gap_lg,
a.z_10,
]}>
<Text
style={[
native({marginTop: 2}),
{
color:
t.name === 'light' && ctx.selected
? t.palette.white
: t.atoms.text.color,
},
]}>
{title}
</Text>
<Plus
size="sm"
fill={
ctx.selected
? t.palette.primary_200
: t.atoms.text_contrast_400.color
}
/>
</View>
</View>
)
}
function SubmitView({
selectedLabelGroup,
goBack,
}: {
selectedLabelGroup: string
goBack: () => void
}) {
const t = useTheme()
const {_} = useLingui()
const labelGroupStrings = useLabelGroupStrings()
const groupInfoStrings = labelGroupStrings[selectedLabelGroup]
const [selectedServices, setSelectedServices] = React.useState<string[]>([])
const [details, setDetails] = React.useState<string>('')
return (
<View style={[a.gap_2xl]}>
<Button
size="small"
variant="solid"
color="secondary"
shape="round"
label={_(msg`Go back to previous step`)}
onPress={goBack}>
<ButtonIcon icon={ChevronLeft} />
</Button>
<View
style={[
a.w_full,
a.flex_row,
a.align_center,
a.justify_between,
a.gap_lg,
a.p_md,
a.rounded_md,
t.atoms.bg_contrast_25,
]}>
<View style={[a.flex_1, a.gap_xs]}>
<Text style={[a.text_md, a.font_bold]}>{groupInfoStrings.name}</Text>
<Text style={[a.leading_tight, {maxWidth: 400}]}>
{groupInfoStrings.description}
</Text>
</View>
<Check size="md" style={[a.pr_sm]} />
</View>
<View style={[a.gap_md]}>
<Text style={[t.atoms.text_contrast_700]}>
Select the moderation service(s) to report to
</Text>
<Toggle.Group
label="Select mod services"
values={selectedServices}
onChange={setSelectedServices}>
<View style={[a.flex_row, a.gap_sm, a.flex_wrap]}>
<Toggle.Item name="bluesky" label="Bluesky">
<ModServiceToggle title="Bluesky" />
</Toggle.Item>
<Toggle.Item name="contraption" label="The Contraption">
<ModServiceToggle title="The Contraption" />
</Toggle.Item>
<Toggle.Item name="safety" label="Safety Corp">
<ModServiceToggle title="Safety Corp" />
</Toggle.Item>
</View>
</Toggle.Group>
</View>
<View style={[a.gap_md]}>
<Text style={[t.atoms.text_contrast_700]}>
Optionally provide additional information below:
</Text>
<View style={[a.relative, a.w_full]}>
<TextField.Input
multiline
value={details}
onChangeText={setDetails}
label="Text field"
style={{paddingRight: 60}}
numberOfLines={6}
/>
<View
style={[
a.absolute,
a.flex_row,
a.align_center,
a.pr_md,
a.pb_sm,
{
bottom: 0,
right: 0,
},
]}>
<CharProgress count={details?.length || 0} />
</View>
</View>
</View>
<View style={[a.align_end]}>
<Button
size="large"
variant="gradient"
color="gradient_midnight"
label={_(msg`Submit`)}>
Submit
</Button>
</View>
</View>
)
}
/**
* TODO copyright link out to DMCA
* TODO add "other" option
@@ -92,7 +262,8 @@ export function ReportDialog({
const {_} = useLingui()
const insets = useSafeAreaInsets()
const control = Dialog.useDialogControl()
const [_labelGroup, setLabelGroup] = React.useState<string>('')
const [selectedLabelGroup, setSelectedLabelGroup] =
React.useState<string>('nudity')
const labelGroupStrings = useLabelGroupStrings()
// REQUIRED CLEANUP
@@ -132,30 +303,37 @@ export function ReportDialog({
<Dialog.Handle />
<Dialog.ScrollableInner label="Report Dialog">
<View style={[a.gap_lg]}>
<View style={[a.justify_center, a.gap_sm]}>
<Text style={[a.text_2xl, a.font_bold]}>{i18n.title}</Text>
<Text style={[a.text_md, t.atoms.text_contrast_700]}>
{i18n.description}
</Text>
</View>
{selectedLabelGroup ? (
<SubmitView
selectedLabelGroup={selectedLabelGroup}
goBack={() => setSelectedLabelGroup('')}
/>
) : (
<View style={[a.gap_lg]}>
<View style={[a.justify_center, a.gap_sm]}>
<Text style={[a.text_2xl, a.font_bold]}>{i18n.title}</Text>
<Text style={[a.text_md, t.atoms.text_contrast_700]}>
{i18n.description}
</Text>
</View>
<Divider />
<Divider />
<View style={[a.gap_sm, {marginHorizontal: a.p_md.padding * -1}]}>
{groups.map(([name, def]) => {
const groupStrings = labelGroupStrings[name]
return (
<Button
key={def.id}
label={_(msg`Create report for ${groupStrings.name}`)}
onPress={() => setLabelGroup(name)}>
<LabelGroupButton labelGroup={name} />
</Button>
)
})}
<View style={[a.gap_sm, {marginHorizontal: a.p_md.padding * -1}]}>
{groups.map(([name, def]) => {
const groupStrings = labelGroupStrings[name]
return (
<Button
key={def.id}
label={_(msg`Create report for ${groupStrings.name}`)}
onPress={() => setSelectedLabelGroup(name)}>
<LabelGroupButton labelGroup={name} />
</Button>
)
})}
</View>
</View>
</View>
)}
</Dialog.ScrollableInner>
</Dialog.Outer>
)
+6 -3
View File
@@ -12,8 +12,8 @@ import {
import {HITSLOP_20} from 'lib/constants'
import {isWeb} from '#/platform/detection'
import {useTheme, atoms as a, web, tokens, android} from '#/alf'
import {Text} from '#/components/Typography'
import {useTheme, atoms as a, web, tokens, android, flatten} from '#/alf'
import {Text, leading} from '#/components/Typography'
import {useInteractionState} from '#/components/hooks/useInteractionState'
import {Props as SVGIconProps} from '#/components/icons/common'
@@ -208,10 +208,13 @@ export function createInput(Component: typeof TextInput) {
paddingBottom: 2,
}),
{
lineHeight: a.text_md.fontSize * 1.1875,
lineHeight: rest.multiline
? leading(a.text_md, a.leading_normal)
: a.text_md.fontSize * 1.1875,
textAlignVertical: rest.multiline ? 'top' : undefined,
minHeight: rest.multiline ? 60 : undefined,
},
flatten(rest.style),
]}
/>