mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at ruby-v 273 lines 7.5 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 {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}