mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at tooltip 7.6 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 {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}