mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React, {memo, useMemo, useState} from 'react'
2import {View} from 'react-native'
3import {
4 ChatBskyConvoDefs,
5 ComAtprotoModerationCreateReport,
6 RichText as RichTextAPI,
7} from '@atproto/api'
8import {msg, Trans} from '@lingui/macro'
9import {useLingui} from '@lingui/react'
10import {useMutation} from '@tanstack/react-query'
11
12import {ReportOption} from '#/lib/moderation/useReportOptions'
13import {isAndroid} from '#/platform/detection'
14import {useAgent} from '#/state/session'
15import {CharProgress} from '#/view/com/composer/char-progress/CharProgress'
16import * as Toast from '#/view/com/util/Toast'
17import {atoms as a, useBreakpoints, useTheme} from '#/alf'
18import * as Dialog from '#/components/Dialog'
19import {KeyboardControllerPadding} from '#/components/KeyboardControllerPadding'
20import {Button, ButtonIcon, ButtonText} from '../Button'
21import {Divider} from '../Divider'
22import {ChevronLeft_Stroke2_Corner0_Rounded as Chevron} from '../icons/Chevron'
23import {Loader} from '../Loader'
24import {SelectReportOptionView} from '../ReportDialog/SelectReportOptionView'
25import {RichText} from '../RichText'
26import {Text} from '../Typography'
27import {MessageItemMetadata} from './MessageItem'
28
29type ReportDialogParams = {
30 type: 'convoMessage'
31 convoId: string
32 message: ChatBskyConvoDefs.MessageView
33}
34
35let ReportDialog = ({
36 control,
37 params,
38}: {
39 control: Dialog.DialogControlProps
40 params: ReportDialogParams
41}): React.ReactNode => {
42 const {_} = useLingui()
43 return (
44 <Dialog.Outer
45 control={control}
46 nativeOptions={isAndroid ? {sheet: {snapPoints: ['100%']}} : {}}>
47 <Dialog.Handle />
48 <Dialog.ScrollableInner label={_(msg`Report this message`)}>
49 <DialogInner params={params} />
50 <Dialog.Close />
51 <KeyboardControllerPadding />
52 </Dialog.ScrollableInner>
53 </Dialog.Outer>
54 )
55}
56ReportDialog = memo(ReportDialog)
57export {ReportDialog}
58
59function DialogInner({params}: {params: ReportDialogParams}) {
60 const [reportOption, setReportOption] = useState<ReportOption | null>(null)
61
62 return reportOption ? (
63 <SubmitStep
64 params={params}
65 reportOption={reportOption}
66 goBack={() => setReportOption(null)}
67 />
68 ) : (
69 <ReasonStep params={params} setReportOption={setReportOption} />
70 )
71}
72
73function ReasonStep({
74 setReportOption,
75}: {
76 setReportOption: (reportOption: ReportOption) => void
77 params: ReportDialogParams
78}) {
79 const control = Dialog.useDialogContext()
80
81 return (
82 <SelectReportOptionView
83 labelers={[]}
84 goBack={control.close}
85 params={{
86 type: 'convoMessage',
87 }}
88 onSelectReportOption={setReportOption}
89 />
90 )
91}
92
93function SubmitStep({
94 params,
95 reportOption,
96 goBack,
97}: {
98 params: ReportDialogParams
99 reportOption: ReportOption
100 goBack: () => void
101}) {
102 const {_} = useLingui()
103 const {gtMobile} = useBreakpoints()
104 const t = useTheme()
105 const [details, setDetails] = useState('')
106 const control = Dialog.useDialogContext()
107 const agent = useAgent()
108
109 const {
110 mutate: submit,
111 error,
112 isPending: submitting,
113 } = useMutation({
114 mutationFn: async () => {
115 if (params.type === 'convoMessage') {
116 const {convoId, message} = params
117
118 const report = {
119 reasonType: reportOption.reason,
120 subject: {
121 $type: 'chat.bsky.convo.defs#messageRef',
122 messageId: message.id,
123 convoId,
124 did: message.sender.did,
125 } satisfies ChatBskyConvoDefs.MessageRef,
126 reason: details,
127 } satisfies ComAtprotoModerationCreateReport.InputSchema
128
129 await agent.createModerationReport(report)
130 }
131 },
132 onSuccess: () => {
133 control.close(() => {
134 Toast.show(_(msg`Thank you. Your report has been sent.`))
135 })
136 },
137 })
138
139 const copy = useMemo(() => {
140 return {
141 convoMessage: {
142 title: _(msg`Report this message`),
143 },
144 }[params.type]
145 }, [_, params])
146
147 return (
148 <View style={a.gap_lg}>
149 <Button
150 size="small"
151 variant="solid"
152 color="secondary"
153 shape="round"
154 label={_(msg`Go back to previous step`)}
155 onPress={goBack}>
156 <ButtonIcon icon={Chevron} />
157 </Button>
158
159 <View style={[a.justify_center, gtMobile ? a.gap_sm : a.gap_xs]}>
160 <Text style={[a.text_2xl, a.font_bold]}>{copy.title}</Text>
161 <Text style={[a.text_md, t.atoms.text_contrast_medium]}>
162 <Trans>
163 Your report will be sent to the Bluesky Moderation Service
164 </Trans>
165 </Text>
166 </View>
167
168 {params.type === 'convoMessage' && (
169 <PreviewMessage message={params.message} />
170 )}
171
172 <Text style={[a.text_md, t.atoms.text_contrast_medium]}>
173 <Text style={[a.font_bold, a.text_md, t.atoms.text_contrast_medium]}>
174 <Trans>Reason:</Trans>
175 </Text>{' '}
176 <Text style={[a.font_bold, a.text_md]}>{reportOption.title}</Text>
177 </Text>
178
179 <Divider />
180
181 <View style={[a.gap_md]}>
182 <Text style={[t.atoms.text_contrast_medium]}>
183 <Trans>Optionally provide additional information below:</Trans>
184 </Text>
185
186 <View style={[a.relative, a.w_full]}>
187 <Dialog.Input
188 multiline
189 value={details}
190 onChangeText={setDetails}
191 label="Text field"
192 style={{paddingRight: 60}}
193 numberOfLines={6}
194 />
195
196 <View
197 style={[
198 a.absolute,
199 a.flex_row,
200 a.align_center,
201 a.pr_md,
202 a.pb_sm,
203 {
204 bottom: 0,
205 right: 0,
206 },
207 ]}>
208 <CharProgress count={details?.length || 0} />
209 </View>
210 </View>
211 </View>
212
213 <View style={[a.flex_row, a.align_center, a.justify_end, a.gap_lg]}>
214 {error && (
215 <Text
216 style={[
217 a.flex_1,
218 a.italic,
219 a.leading_snug,
220 t.atoms.text_contrast_medium,
221 ]}>
222 <Trans>
223 There was an issue sending your report. Please check your internet
224 connection.
225 </Trans>
226 </Text>
227 )}
228
229 <Button
230 testID="sendReportBtn"
231 size="large"
232 variant="solid"
233 color="negative"
234 label={_(msg`Send report`)}
235 onPress={() => submit()}>
236 <ButtonText>
237 <Trans>Send report</Trans>
238 </ButtonText>
239 {submitting && <ButtonIcon icon={Loader} />}
240 </Button>
241 </View>
242 </View>
243 )
244}
245
246function PreviewMessage({message}: {message: ChatBskyConvoDefs.MessageView}) {
247 const t = useTheme()
248 const rt = useMemo(() => {
249 return new RichTextAPI({text: message.text, facets: message.facets})
250 }, [message.text, message.facets])
251
252 return (
253 <View style={a.align_start}>
254 <View
255 style={[
256 a.py_sm,
257 a.my_2xs,
258 a.rounded_md,
259 {
260 paddingLeft: 14,
261 paddingRight: 14,
262 backgroundColor: t.palette.contrast_50,
263 borderRadius: 17,
264 },
265 {borderBottomLeftRadius: 2},
266 ]}>
267 <RichText
268 value={rt}
269 style={[a.text_md, a.leading_snug]}
270 interactiveStyle={a.underline}
271 enableTags
272 />
273 </View>
274 <MessageItemMetadata
275 item={{
276 type: 'message',
277 message,
278 key: '',
279 nextMessage: null,
280 }}
281 style={[a.text_left, a.mb_0]}
282 />
283 </View>
284 )
285}