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