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 {isAndroid} from '#/platform/detection'
10import {useAgent} from '#/state/session'
11import {CharProgress} from '#/view/com/composer/char-progress/CharProgress'
12import * as Toast from '#/view/com/util/Toast'
13import {atoms as a, native, useTheme} from '#/alf'
14import {Button, ButtonIcon, ButtonText} from '#/components/Button'
15import * as Dialog from '#/components/Dialog'
16import * as Toggle from '#/components/forms/Toggle'
17import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
18import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron'
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 return agent
66 .createModerationReport(report, {
67 encoding: 'application/json',
68 headers: {
69 'atproto-proxy': `${did}#atproto_labeler`,
70 },
71 })
72 .then(
73 _ => true,
74 _ => false,
75 )
76 }),
77 )
78
79 setSubmitting(false)
80
81 if (results.includes(true)) {
82 Toast.show(_(msg`Thank you. Your report has been sent.`))
83 onSubmitComplete()
84 } else {
85 setError(
86 _(
87 msg`There was an issue sending your report. Please check your internet connection.`,
88 ),
89 )
90 }
91 }, [
92 _,
93 params,
94 details,
95 selectedReportOption,
96 selectedServices,
97 onSubmitComplete,
98 setError,
99 agent,
100 ])
101
102 return (
103 <View style={[a.gap_2xl]}>
104 <Button
105 size="small"
106 variant="solid"
107 color="secondary"
108 shape="round"
109 label={_(msg`Go back to previous step`)}
110 onPress={goBack}>
111 <ButtonIcon icon={ChevronLeft} />
112 </Button>
113
114 <View
115 style={[
116 a.w_full,
117 a.flex_row,
118 a.align_center,
119 a.justify_between,
120 a.gap_lg,
121 a.p_md,
122 a.rounded_md,
123 a.border,
124 t.atoms.border_contrast_low,
125 ]}>
126 <View style={[a.flex_1, a.gap_xs]}>
127 <Text style={[a.text_md, a.font_bold]}>
128 {selectedReportOption.title}
129 </Text>
130 <Text style={[a.leading_tight, {maxWidth: 400}]}>
131 {selectedReportOption.description}
132 </Text>
133 </View>
134
135 <Check size="md" style={[a.pr_sm, t.atoms.text_contrast_low]} />
136 </View>
137
138 <View style={[a.gap_md]}>
139 <Text style={[t.atoms.text_contrast_medium]}>
140 <Trans>Select the moderation service(s) to report to</Trans>
141 </Text>
142
143 <Toggle.Group
144 label="Select mod services"
145 values={selectedServices}
146 onChange={setSelectedServices}>
147 <View style={[a.flex_row, a.gap_md, a.flex_wrap]}>
148 {labelers.map(labeler => {
149 const title = getLabelingServiceTitle({
150 displayName: labeler.creator.displayName,
151 handle: labeler.creator.handle,
152 })
153 return (
154 <Toggle.Item
155 key={labeler.creator.did}
156 name={labeler.creator.did}
157 label={title}>
158 <LabelerToggle title={title} />
159 </Toggle.Item>
160 )
161 })}
162 </View>
163 </Toggle.Group>
164 </View>
165 <View style={[a.gap_md]}>
166 <Text style={[t.atoms.text_contrast_medium]}>
167 <Trans>Optionally provide additional information below:</Trans>
168 </Text>
169
170 <View style={[a.relative, a.w_full]}>
171 <Dialog.Input
172 multiline
173 value={details}
174 onChangeText={setDetails}
175 label="Text field"
176 style={{paddingRight: 60}}
177 numberOfLines={6}
178 />
179
180 <View
181 style={[
182 a.absolute,
183 a.flex_row,
184 a.align_center,
185 a.pr_md,
186 a.pb_sm,
187 {
188 bottom: 0,
189 right: 0,
190 },
191 ]}>
192 <CharProgress count={details?.length || 0} />
193 </View>
194 </View>
195 </View>
196
197 <View style={[a.flex_row, a.align_center, a.justify_end, a.gap_lg]}>
198 {!selectedServices.length ||
199 (error && (
200 <Text
201 style={[
202 a.flex_1,
203 a.italic,
204 a.leading_snug,
205 t.atoms.text_contrast_medium,
206 ]}>
207 {error ? (
208 error
209 ) : (
210 <Trans>You must select at least one labeler for a report</Trans>
211 )}
212 </Text>
213 ))}
214
215 <Button
216 testID="sendReportBtn"
217 size="large"
218 variant="solid"
219 color="negative"
220 label={_(msg`Send report`)}
221 onPress={submit}
222 disabled={!selectedServices.length}>
223 <ButtonText>
224 <Trans>Send report</Trans>
225 </ButtonText>
226 {submitting && <ButtonIcon icon={Loader} />}
227 </Button>
228 </View>
229 {/* Maybe fix this later -h */}
230 {isAndroid ? <View style={{height: 300}} /> : null}
231 </View>
232 )
233}
234
235function LabelerToggle({title}: {title: string}) {
236 const t = useTheme()
237 const ctx = Toggle.useItemContext()
238
239 return (
240 <View
241 style={[
242 a.flex_row,
243 a.align_center,
244 a.gap_md,
245 a.p_md,
246 a.pr_lg,
247 a.rounded_sm,
248 a.overflow_hidden,
249 t.atoms.bg_contrast_25,
250 ctx.selected && [t.atoms.bg_contrast_50],
251 ]}>
252 <Toggle.Checkbox />
253 <View
254 style={[
255 a.flex_row,
256 a.align_center,
257 a.justify_between,
258 a.gap_lg,
259 a.z_10,
260 ]}>
261 <Text
262 emoji
263 style={[
264 native({marginTop: 2}),
265 t.atoms.text_contrast_medium,
266 ctx.selected && t.atoms.text,
267 ]}>
268 {title}
269 </Text>
270 </View>
271 </View>
272 )
273}