Report dialog step 2
This commit is contained in:
@@ -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]}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -5,14 +5,23 @@ import {useLingui} from '@lingui/react'
|
|||||||
import {LABEL_GROUPS, LabelGroupDefinition} from '@atproto/api'
|
import {LABEL_GROUPS, LabelGroupDefinition} from '@atproto/api'
|
||||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
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 {Text} from '#/components/Typography'
|
||||||
import * as Dialog from '#/components/Dialog'
|
import * as Dialog from '#/components/Dialog'
|
||||||
import {GlobalDialogProps} from '#/components/dialogs'
|
import {GlobalDialogProps} from '#/components/dialogs'
|
||||||
import {Button, useButtonContext} from '#/components/Button'
|
import {Button, ButtonIcon, useButtonContext} from '#/components/Button'
|
||||||
import {Divider} from '#/components/Divider'
|
import {Divider} from '#/components/Divider'
|
||||||
import {useLabelGroupStrings} from '#/lib/moderation'
|
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 =
|
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 copyright link out to DMCA
|
||||||
* TODO add "other" option
|
* TODO add "other" option
|
||||||
@@ -92,7 +262,8 @@ export function ReportDialog({
|
|||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const insets = useSafeAreaInsets()
|
const insets = useSafeAreaInsets()
|
||||||
const control = Dialog.useDialogControl()
|
const control = Dialog.useDialogControl()
|
||||||
const [_labelGroup, setLabelGroup] = React.useState<string>('')
|
const [selectedLabelGroup, setSelectedLabelGroup] =
|
||||||
|
React.useState<string>('nudity')
|
||||||
const labelGroupStrings = useLabelGroupStrings()
|
const labelGroupStrings = useLabelGroupStrings()
|
||||||
|
|
||||||
// REQUIRED CLEANUP
|
// REQUIRED CLEANUP
|
||||||
@@ -132,30 +303,37 @@ export function ReportDialog({
|
|||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
|
|
||||||
<Dialog.ScrollableInner label="Report Dialog">
|
<Dialog.ScrollableInner label="Report Dialog">
|
||||||
<View style={[a.gap_lg]}>
|
{selectedLabelGroup ? (
|
||||||
<View style={[a.justify_center, a.gap_sm]}>
|
<SubmitView
|
||||||
<Text style={[a.text_2xl, a.font_bold]}>{i18n.title}</Text>
|
selectedLabelGroup={selectedLabelGroup}
|
||||||
<Text style={[a.text_md, t.atoms.text_contrast_700]}>
|
goBack={() => setSelectedLabelGroup('')}
|
||||||
{i18n.description}
|
/>
|
||||||
</Text>
|
) : (
|
||||||
</View>
|
<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}]}>
|
<View style={[a.gap_sm, {marginHorizontal: a.p_md.padding * -1}]}>
|
||||||
{groups.map(([name, def]) => {
|
{groups.map(([name, def]) => {
|
||||||
const groupStrings = labelGroupStrings[name]
|
const groupStrings = labelGroupStrings[name]
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
key={def.id}
|
key={def.id}
|
||||||
label={_(msg`Create report for ${groupStrings.name}`)}
|
label={_(msg`Create report for ${groupStrings.name}`)}
|
||||||
onPress={() => setLabelGroup(name)}>
|
onPress={() => setSelectedLabelGroup(name)}>
|
||||||
<LabelGroupButton labelGroup={name} />
|
<LabelGroupButton labelGroup={name} />
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
)}
|
||||||
</Dialog.ScrollableInner>
|
</Dialog.ScrollableInner>
|
||||||
</Dialog.Outer>
|
</Dialog.Outer>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import {
|
|||||||
|
|
||||||
import {HITSLOP_20} from 'lib/constants'
|
import {HITSLOP_20} from 'lib/constants'
|
||||||
import {isWeb} from '#/platform/detection'
|
import {isWeb} from '#/platform/detection'
|
||||||
import {useTheme, atoms as a, web, tokens, android} from '#/alf'
|
import {useTheme, atoms as a, web, tokens, android, flatten} from '#/alf'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text, leading} from '#/components/Typography'
|
||||||
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
||||||
import {Props as SVGIconProps} from '#/components/icons/common'
|
import {Props as SVGIconProps} from '#/components/icons/common'
|
||||||
|
|
||||||
@@ -208,10 +208,13 @@ export function createInput(Component: typeof TextInput) {
|
|||||||
paddingBottom: 2,
|
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,
|
textAlignVertical: rest.multiline ? 'top' : undefined,
|
||||||
minHeight: rest.multiline ? 60 : undefined,
|
minHeight: rest.multiline ? 60 : undefined,
|
||||||
},
|
},
|
||||||
|
flatten(rest.style),
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user