Bluesky app fork with some witchin' additions ๐Ÿ’ซ
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

[๐Ÿด] Appeal form for disabled DMs (#4126)

* add appeal dialog

* use useMutation for the labels on me dialog

* replace text button with small button

authored by

Samuel Newman and committed by
GitHub
d3d2dc8a e5aa8c08

+158 -17
+11 -5
src/components/dms/NewChatDialog/index.tsx
··· 1 - import React, {useCallback, useMemo, useRef, useState} from 'react' 1 + import React, { 2 + useCallback, 3 + useLayoutEffect, 4 + useMemo, 5 + useRef, 6 + useState, 7 + } from 'react' 2 8 import type {TextInput as TextInputType} from 'react-native' 3 9 import {View} from 'react-native' 4 10 import {AppBskyActorDefs, moderateProfile, ModerationOpts} from '@atproto/api' ··· 293 299 const control = Dialog.useDialogContext() 294 300 const listRef = useRef<BottomSheetFlatListMethods>(null) 295 301 const {currentAccount} = useSession() 296 - const inputRef = React.useRef<TextInputType>(null) 302 + const inputRef = useRef<TextInputType>(null) 297 303 298 304 const [searchText, setSearchText] = useState('') 299 305 ··· 306 312 limit: 12, 307 313 }) 308 314 309 - const items = React.useMemo(() => { 315 + const items = useMemo(() => { 310 316 let _items: Item[] = [] 311 317 312 318 if (isError) { ··· 368 374 items.push({type: 'empty', key: 'empty', message: _(msg`No results`)}) 369 375 } 370 376 371 - const renderItems = React.useCallback( 377 + const renderItems = useCallback( 372 378 ({item}: {item: Item}) => { 373 379 switch (item.type) { 374 380 case 'profile': { ··· 395 401 [moderationOpts, onCreateChat], 396 402 ) 397 403 398 - React.useLayoutEffect(() => { 404 + useLayoutEffect(() => { 399 405 if (isWeb) { 400 406 setImmediate(() => { 401 407 inputRef?.current?.focus()
+19 -9
src/components/moderation/LabelsOnMeDialog.tsx
··· 3 3 import {ComAtprotoLabelDefs, ComAtprotoModerationDefs} from '@atproto/api' 4 4 import {msg, Trans} from '@lingui/macro' 5 5 import {useLingui} from '@lingui/react' 6 + import {useMutation} from '@tanstack/react-query' 6 7 7 8 import {useLabelInfo} from '#/lib/moderation/useLabelInfo' 8 9 import {makeProfileLink} from '#/lib/routes/links' 9 10 import {sanitizeHandle} from '#/lib/strings/handles' 11 + import {logger} from '#/logger' 10 12 import {useAgent, useSession} from '#/state/session' 11 13 import * as Toast from '#/view/com/util/Toast' 12 14 import {atoms as a, useBreakpoints, useTheme} from '#/alf' 13 - import {Button, ButtonText} from '#/components/Button' 15 + import {Button, ButtonIcon, ButtonText} from '#/components/Button' 14 16 import * as Dialog from '#/components/Dialog' 15 17 import {InlineLinkText} from '#/components/Link' 16 18 import {Text} from '#/components/Typography' 17 19 import {Divider} from '../Divider' 18 - 20 + import {Loader} from '../Loader' 19 21 export {useDialogControl as useLabelsOnMeDialogControl} from '#/components/Dialog' 20 22 21 23 type Subject = ··· 100 102 label={label} 101 103 isSelfLabel={label.src === currentAccount?.did} 102 104 control={props.control} 103 - onPressAppeal={label => setAppealingLabel(label)} 105 + onPressAppeal={setAppealingLabel} 104 106 /> 105 107 ))} 106 108 </View> ··· 201 203 const isAccountReport = 'did' in subject 202 204 const {getAgent} = useAgent() 203 205 204 - const onSubmit = async () => { 205 - try { 206 + const {mutate, isPending} = useMutation({ 207 + mutationFn: async () => { 206 208 const $type = !isAccountReport 207 209 ? 'com.atproto.repo.strongRef' 208 210 : 'com.atproto.admin.defs#repoRef' ··· 216 218 }, 217 219 reason: details, 218 220 }) 219 - Toast.show(_(msg`Appeal submitted`)) 220 - } finally { 221 + }, 222 + onError: err => { 223 + logger.error('Failed to submit label appeal', {message: err}) 224 + Toast.show(_(msg`Failed to submit appeal, please try again.`)) 225 + }, 226 + onSuccess: () => { 221 227 control.close() 222 - } 223 - } 228 + Toast.show(_(msg`Appeal submitted`)) 229 + }, 230 + }) 231 + 232 + const onSubmit = React.useCallback(() => mutate(), [mutate]) 224 233 225 234 return ( 226 235 <> ··· 281 290 onPress={onSubmit} 282 291 label={_(msg`Submit`)}> 283 292 <ButtonText>{_(msg`Submit`)}</ButtonText> 293 + {isPending && <ButtonIcon icon={Loader} />} 284 294 </Button> 285 295 </View> 286 296 </>
+128 -3
src/screens/Messages/Conversation/ChatDisabled.tsx
··· 1 - import React from 'react' 1 + import React, {useCallback, useState} from 'react' 2 2 import {View} from 'react-native' 3 - import {Trans} from '@lingui/macro' 3 + import {ComAtprotoModerationDefs} from '@atproto/api' 4 + import {msg, Trans} from '@lingui/macro' 5 + import {useLingui} from '@lingui/react' 6 + import {useMutation} from '@tanstack/react-query' 4 7 5 - import {atoms as a, useTheme} from '#/alf' 8 + import {logger} from '#/logger' 9 + import {useAgent, useSession} from '#/state/session' 10 + import * as Toast from '#/view/com/util/Toast' 11 + import {atoms as a, useBreakpoints, useTheme} from '#/alf' 12 + import {Button, ButtonIcon, ButtonText} from '#/components/Button' 13 + import * as Dialog from '#/components/Dialog' 14 + import {Loader} from '#/components/Loader' 6 15 import {Text} from '#/components/Typography' 7 16 8 17 export function ChatDisabled() { ··· 20 29 access to chats on Bluesky. 21 30 </Trans> 22 31 </Text> 32 + <AppealDialog /> 23 33 </View> 24 34 </View> 25 35 ) 26 36 } 37 + 38 + function AppealDialog() { 39 + const control = Dialog.useDialogControl() 40 + const {_} = useLingui() 41 + 42 + return ( 43 + <> 44 + <Button 45 + testID="appealDisabledChatBtn" 46 + variant="solid" 47 + color="secondary" 48 + size="small" 49 + onPress={control.open} 50 + label={_(msg`Appeal this decision`)} 51 + style={a.mt_sm}> 52 + <ButtonText>{_(msg`Appeal this decision`)}</ButtonText> 53 + </Button> 54 + 55 + <Dialog.Outer control={control}> 56 + <Dialog.Handle /> 57 + <DialogInner /> 58 + </Dialog.Outer> 59 + </> 60 + ) 61 + } 62 + 63 + function DialogInner() { 64 + const {_} = useLingui() 65 + const control = Dialog.useDialogContext() 66 + const [details, setDetails] = useState('') 67 + const {gtMobile} = useBreakpoints() 68 + const {getAgent} = useAgent() 69 + const {currentAccount} = useSession() 70 + 71 + const {mutate, isPending} = useMutation({ 72 + mutationFn: async () => { 73 + if (!currentAccount) 74 + throw new Error('No current account, should be unreachable') 75 + await getAgent().createModerationReport({ 76 + reasonType: ComAtprotoModerationDefs.REASONAPPEAL, 77 + subject: { 78 + $type: 'com.atproto.admin.defs#repoRef', 79 + did: currentAccount.did, 80 + }, 81 + reason: details, 82 + }) 83 + }, 84 + onError: err => { 85 + logger.error('Failed to submit chat appeal', {message: err}) 86 + Toast.show(_(msg`Failed to submit appeal, please try again.`)) 87 + }, 88 + onSuccess: () => { 89 + control.close() 90 + Toast.show(_(msg`Appeal submitted`)) 91 + }, 92 + }) 93 + 94 + const onSubmit = useCallback(() => mutate(), [mutate]) 95 + const onBack = useCallback(() => control.close(), [control]) 96 + 97 + return ( 98 + <Dialog.ScrollableInner label={_(msg`Appeal this decision`)}> 99 + <Text style={[a.text_2xl, a.font_bold, a.pb_xs, a.leading_tight]}> 100 + <Trans>Appeal this decision</Trans> 101 + </Text> 102 + <Text style={[a.text_md, a.leading_snug]}> 103 + <Trans> 104 + This appeal will be sent to the Bluesky moderation service. 105 + </Trans> 106 + </Text> 107 + <View style={[a.my_md]}> 108 + <Dialog.Input 109 + label={_(msg`Text input field`)} 110 + placeholder={_( 111 + msg`Please explain why you think your chats were incorrectly disabled`, 112 + )} 113 + value={details} 114 + onChangeText={setDetails} 115 + autoFocus={true} 116 + numberOfLines={3} 117 + multiline 118 + maxLength={300} 119 + /> 120 + </View> 121 + 122 + <View 123 + style={ 124 + gtMobile 125 + ? [a.flex_row, a.justify_between] 126 + : [{flexDirection: 'column-reverse'}, a.gap_sm] 127 + }> 128 + <Button 129 + testID="backBtn" 130 + variant="solid" 131 + color="secondary" 132 + size="medium" 133 + onPress={onBack} 134 + label={_(msg`Back`)}> 135 + <ButtonText>{_(msg`Back`)}</ButtonText> 136 + </Button> 137 + <Button 138 + testID="submitBtn" 139 + variant="solid" 140 + color="primary" 141 + size="medium" 142 + onPress={onSubmit} 143 + label={_(msg`Submit`)}> 144 + <ButtonText>{_(msg`Submit`)}</ButtonText> 145 + {isPending && <ButtonIcon icon={Loader} />} 146 + </Button> 147 + </View> 148 + <Dialog.Close /> 149 + </Dialog.ScrollableInner> 150 + ) 151 + }