mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React from 'react'
2import {View} from 'react-native'
3import {AppBskyLabelerDefs} from '@atproto/api'
4import {msg, Trans} from '@lingui/macro'
5import {useLingui} from '@lingui/react'
6
7import {getLabelingServiceTitle} from '#/lib/moderation'
8import {ReportOption} from '#/lib/moderation/useReportOptions'
9import {useAgent} from '#/state/session'
10import {CharProgress} from '#/view/com/composer/char-progress/CharProgress'
11import * as Toast from '#/view/com/util/Toast'
12import {atoms as a, native, useTheme} from '#/alf'
13import {Button, ButtonIcon, ButtonText} from '#/components/Button'
14import * as Dialog from '#/components/Dialog'
15import * as Toggle from '#/components/forms/Toggle'
16import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
17import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron'
18import {KeyboardPadding} from '#/components/KeyboardPadding'
19import {Loader} from '#/components/Loader'
20import {Text} from '#/components/Typography'
21import {ReportDialogProps} from './types'
22
23export function SubmitView({
24 params,
25 labelers,
26 selectedLabeler,
27 selectedReportOption,
28 goBack,
29 onSubmitComplete,
30}: ReportDialogProps & {
31 labelers: AppBskyLabelerDefs.LabelerViewDetailed[]
32 selectedLabeler: string
33 selectedReportOption: ReportOption
34 goBack: () => void
35 onSubmitComplete: () => void
36}) {
37 const t = useTheme()
38 const {_} = useLingui()
39 const agent = useAgent()
40 const [details, setDetails] = React.useState<string>('')
41 const [submitting, setSubmitting] = React.useState<boolean>(false)
42 const [selectedServices, setSelectedServices] = React.useState<string[]>([
43 selectedLabeler,
44 ])
45 const [error, setError] = React.useState('')
46
47 const submit = React.useCallback(async () => {
48 setSubmitting(true)
49 setError('')
50
51 const $type =
52 params.type === 'account'
53 ? 'com.atproto.admin.defs#repoRef'
54 : 'com.atproto.repo.strongRef'
55 const report = {
56 reasonType: selectedReportOption.reason,
57 subject: {
58 $type,
59 ...params,
60 },
61 reason: details,
62 }
63 const results = await Promise.all(
64 selectedServices.map(did =>
65 agent
66 .withProxy('atproto_labeler', did)
67 .createModerationReport(report)
68 .then(
69 _ => true,
70 _ => false,
71 ),
72 ),
73 )
74
75 setSubmitting(false)
76
77 if (results.includes(true)) {
78 Toast.show(_(msg`Thank you. Your report has been sent.`))
79 onSubmitComplete()
80 } else {
81 setError(
82 _(
83 msg`There was an issue sending your report. Please check your internet connection.`,
84 ),
85 )
86 }
87 }, [
88 _,
89 params,
90 details,
91 selectedReportOption,
92 selectedServices,
93 onSubmitComplete,
94 setError,
95 agent,
96 ])
97
98 return (
99 <View style={[a.gap_2xl]}>
100 <Button
101 size="small"
102 variant="solid"
103 color="secondary"
104 shape="round"
105 label={_(msg`Go back to previous step`)}
106 onPress={goBack}>
107 <ButtonIcon icon={ChevronLeft} />
108 </Button>
109
110 <View
111 style={[
112 a.w_full,
113 a.flex_row,
114 a.align_center,
115 a.justify_between,
116 a.gap_lg,
117 a.p_md,
118 a.rounded_md,
119 a.border,
120 t.atoms.border_contrast_low,
121 ]}>
122 <View style={[a.flex_1, a.gap_xs]}>
123 <Text style={[a.text_md, a.font_bold]}>
124 {selectedReportOption.title}
125 </Text>
126 <Text style={[a.leading_tight, {maxWidth: 400}]}>
127 {selectedReportOption.description}
128 </Text>
129 </View>
130
131 <Check size="md" style={[a.pr_sm, t.atoms.text_contrast_low]} />
132 </View>
133
134 <View style={[a.gap_md]}>
135 <Text style={[t.atoms.text_contrast_medium]}>
136 <Trans>Select the moderation service(s) to report to</Trans>
137 </Text>
138
139 <Toggle.Group
140 label="Select mod services"
141 values={selectedServices}
142 onChange={setSelectedServices}>
143 <View style={[a.flex_row, a.gap_md, a.flex_wrap]}>
144 {labelers.map(labeler => {
145 const title = getLabelingServiceTitle({
146 displayName: labeler.creator.displayName,
147 handle: labeler.creator.handle,
148 })
149 return (
150 <Toggle.Item
151 key={labeler.creator.did}
152 name={labeler.creator.did}
153 label={title}>
154 <LabelerToggle title={title} />
155 </Toggle.Item>
156 )
157 })}
158 </View>
159 </Toggle.Group>
160 </View>
161 <View style={[a.gap_md]}>
162 <Text style={[t.atoms.text_contrast_medium]}>
163 <Trans>Optionally provide additional information below:</Trans>
164 </Text>
165
166 <View style={[a.relative, a.w_full]}>
167 <Dialog.Input
168 multiline
169 value={details}
170 onChangeText={setDetails}
171 label="Text field"
172 style={{paddingRight: 60}}
173 numberOfLines={6}
174 />
175
176 <View
177 style={[
178 a.absolute,
179 a.flex_row,
180 a.align_center,
181 a.pr_md,
182 a.pb_sm,
183 {
184 bottom: 0,
185 right: 0,
186 },
187 ]}>
188 <CharProgress count={details?.length || 0} />
189 </View>
190 </View>
191 </View>
192
193 <View style={[a.flex_row, a.align_center, a.justify_end, a.gap_lg]}>
194 {!selectedServices.length ||
195 (error && (
196 <Text
197 style={[
198 a.flex_1,
199 a.italic,
200 a.leading_snug,
201 t.atoms.text_contrast_medium,
202 ]}>
203 {error ? (
204 error
205 ) : (
206 <Trans>You must select at least one labeler for a report</Trans>
207 )}
208 </Text>
209 ))}
210
211 <Button
212 testID="sendReportBtn"
213 size="large"
214 variant="solid"
215 color="negative"
216 label={_(msg`Send report`)}
217 onPress={submit}
218 disabled={!selectedServices.length}>
219 <ButtonText>
220 <Trans>Send report</Trans>
221 </ButtonText>
222 {submitting && <ButtonIcon icon={Loader} />}
223 </Button>
224 </View>
225 <KeyboardPadding />
226 </View>
227 )
228}
229
230function LabelerToggle({title}: {title: string}) {
231 const t = useTheme()
232 const ctx = Toggle.useItemContext()
233
234 return (
235 <View
236 style={[
237 a.flex_row,
238 a.align_center,
239 a.gap_md,
240 a.p_md,
241 a.pr_lg,
242 a.rounded_sm,
243 a.overflow_hidden,
244 t.atoms.bg_contrast_25,
245 ctx.selected && [t.atoms.bg_contrast_50],
246 ]}>
247 <Toggle.Checkbox />
248 <View
249 style={[
250 a.flex_row,
251 a.align_center,
252 a.justify_between,
253 a.gap_lg,
254 a.z_10,
255 ]}>
256 <Text
257 style={[
258 native({marginTop: 2}),
259 t.atoms.text_contrast_medium,
260 ctx.selected && t.atoms.text,
261 ]}>
262 {title}
263 </Text>
264 </View>
265 </View>
266 )
267}