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