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