Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

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

at feat/tealfm 260 lines 8.6 kB view raw
1import {useState} from 'react' 2import {View} from 'react-native' 3import {KeyboardAwareScrollView} from 'react-native-keyboard-controller' 4import {useSafeAreaInsets} from 'react-native-safe-area-context' 5import {type ComAtprotoAdminDefs, ToolsOzoneReportDefs} from '@atproto/api' 6import {msg, Trans} from '@lingui/macro' 7import {useLingui} from '@lingui/react' 8import {useMutation} from '@tanstack/react-query' 9import {countGraphemes} from 'unicode-segmenter/grapheme' 10 11import { 12 BLUESKY_MOD_SERVICE_HEADERS, 13 MAX_REPORT_REASON_GRAPHEME_LENGTH, 14} from '#/lib/constants' 15import {useEnableKeyboardController} from '#/lib/hooks/useEnableKeyboardController' 16import {cleanError} from '#/lib/strings/errors' 17import {useAgent, useSession, useSessionApi} from '#/state/session' 18import {CharProgress} from '#/view/com/composer/char-progress/CharProgress' 19import {Logo} from '#/view/icons/Logo' 20import {atoms as a, useBreakpoints, useTheme} from '#/alf' 21import {Button, ButtonIcon, ButtonText} from '#/components/Button' 22import * as TextField from '#/components/forms/TextField' 23import {SimpleInlineLinkText} from '#/components/Link' 24import {Loader} from '#/components/Loader' 25import {P, Text} from '#/components/Typography' 26import {IS_WEB} from '#/env' 27 28const COL_WIDTH = 400 29 30export function Takendown() { 31 const {_} = useLingui() 32 const t = useTheme() 33 const insets = useSafeAreaInsets() 34 const {gtMobile} = useBreakpoints() 35 const {currentAccount} = useSession() 36 const {logoutCurrentAccount} = useSessionApi() 37 const agent = useAgent() 38 const [isAppealling, setIsAppealling] = useState(false) 39 const [reason, setReason] = useState('') 40 41 const reasonGraphemeLength = countGraphemes(reason) 42 const isOverMaxLength = 43 reasonGraphemeLength > MAX_REPORT_REASON_GRAPHEME_LENGTH 44 45 const { 46 mutate: submitAppeal, 47 isPending, 48 isSuccess, 49 error, 50 } = useMutation({ 51 mutationFn: async (appealText: string) => { 52 if (!currentAccount) throw new Error('No session') 53 await agent.com.atproto.moderation.createReport( 54 { 55 reasonType: ToolsOzoneReportDefs.REASONAPPEAL, 56 subject: { 57 $type: 'com.atproto.admin.defs#repoRef', 58 did: currentAccount.did, 59 } satisfies ComAtprotoAdminDefs.RepoRef, 60 reason: appealText, 61 }, 62 { 63 encoding: 'application/json', 64 headers: BLUESKY_MOD_SERVICE_HEADERS, 65 }, 66 ) 67 }, 68 onSuccess: () => setReason(''), 69 }) 70 71 const primaryBtn = 72 isAppealling && !isSuccess ? ( 73 <Button 74 color="primary" 75 size="large" 76 label={_(msg`Submit appeal`)} 77 onPress={() => submitAppeal(reason)} 78 disabled={isPending || isOverMaxLength}> 79 <ButtonText> 80 <Trans>Submit Appeal</Trans> 81 </ButtonText> 82 {isPending && <ButtonIcon icon={Loader} />} 83 </Button> 84 ) : ( 85 <Button 86 size="large" 87 color="secondary_inverted" 88 label={_(msg`Sign out`)} 89 onPress={() => logoutCurrentAccount('Takendown')}> 90 <ButtonText> 91 <Trans>Sign Out</Trans> 92 </ButtonText> 93 </Button> 94 ) 95 96 const secondaryBtn = isAppealling ? ( 97 !isSuccess && ( 98 <Button 99 variant="ghost" 100 size="large" 101 color="secondary" 102 label={_(msg`Cancel`)} 103 onPress={() => setIsAppealling(false)}> 104 <ButtonText> 105 <Trans>Cancel</Trans> 106 </ButtonText> 107 </Button> 108 ) 109 ) : ( 110 <Button 111 variant="ghost" 112 size="large" 113 color="secondary" 114 label={_(msg`Appeal suspension`)} 115 onPress={() => setIsAppealling(true)}> 116 <ButtonText> 117 <Trans>Appeal Suspension</Trans> 118 </ButtonText> 119 </Button> 120 ) 121 122 const webLayout = IS_WEB && gtMobile 123 124 useEnableKeyboardController(true) 125 126 return ( 127 <View style={[a.util_screen_outer, a.flex_1]}> 128 <KeyboardAwareScrollView style={[a.flex_1, t.atoms.bg]} centerContent> 129 <View 130 style={[ 131 a.flex_row, 132 a.justify_center, 133 gtMobile ? a.pt_4xl : [a.px_xl, a.pt_4xl], 134 ]}> 135 <View style={[a.flex_1, {maxWidth: COL_WIDTH, minHeight: COL_WIDTH}]}> 136 <View style={[a.pb_xl]}> 137 <Logo width={64} /> 138 </View> 139 140 <Text style={[a.text_4xl, a.font_bold, a.pb_md]}> 141 {isAppealling ? ( 142 <Trans>Appeal suspension</Trans> 143 ) : ( 144 <Trans>Your account has been suspended</Trans> 145 )} 146 </Text> 147 148 {isAppealling ? ( 149 <View style={[a.relative, a.w_full, a.mt_xl]}> 150 {isSuccess ? ( 151 <P style={[t.atoms.text_contrast_medium, a.text_center]}> 152 <Trans> 153 Your appeal has been submitted. If your appeal succeeds, 154 you will receive an email. 155 </Trans> 156 </P> 157 ) : ( 158 <> 159 <TextField.LabelText> 160 <Trans>Reason for appeal</Trans> 161 </TextField.LabelText> 162 <TextField.Root 163 isInvalid={ 164 reasonGraphemeLength > 165 MAX_REPORT_REASON_GRAPHEME_LENGTH || !!error 166 }> 167 <TextField.Input 168 label={_(msg`Reason for appeal`)} 169 defaultValue={reason} 170 onChangeText={setReason} 171 placeholder={_(msg`Why are you appealing?`)} 172 multiline 173 numberOfLines={5} 174 autoFocus 175 style={{paddingBottom: 40, minHeight: 150}} 176 maxLength={MAX_REPORT_REASON_GRAPHEME_LENGTH * 10} 177 /> 178 </TextField.Root> 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 192 count={reasonGraphemeLength} 193 max={MAX_REPORT_REASON_GRAPHEME_LENGTH} 194 /> 195 </View> 196 </> 197 )} 198 {error && ( 199 <Text 200 style={[ 201 a.text_md, 202 a.leading_snug, 203 {color: t.palette.negative_500}, 204 a.mt_lg, 205 ]}> 206 {cleanError(error)} 207 </Text> 208 )} 209 </View> 210 ) : ( 211 <P style={[t.atoms.text_contrast_medium, a.leading_snug]}> 212 <Trans> 213 Your account was found to be in violation of the{' '} 214 <SimpleInlineLinkText 215 label={_(msg`Bluesky Social Terms of Service`)} 216 to="https://bsky.social/about/support/tos" 217 style={[a.text_md, a.leading_snug]}> 218 Bluesky Social Terms of Service 219 </SimpleInlineLinkText> 220 . You have been sent an email outlining the specific violation 221 and suspension period, if applicable. You can appeal this 222 decision if you believe it was made in error. 223 </Trans> 224 </P> 225 )} 226 227 {webLayout && ( 228 <View 229 style={[ 230 a.w_full, 231 a.flex_row, 232 a.justify_between, 233 a.pt_5xl, 234 {paddingBottom: 200}, 235 ]}> 236 {secondaryBtn} 237 {primaryBtn} 238 </View> 239 )} 240 </View> 241 </View> 242 </KeyboardAwareScrollView> 243 244 {!webLayout && ( 245 <View 246 style={[ 247 a.align_center, 248 t.atoms.bg, 249 gtMobile ? a.px_5xl : a.px_xl, 250 {paddingBottom: Math.max(insets.bottom, a.pb_5xl.paddingBottom)}, 251 ]}> 252 <View style={[a.w_full, a.gap_sm, {maxWidth: COL_WIDTH}]}> 253 {primaryBtn} 254 {secondaryBtn} 255 </View> 256 </View> 257 )} 258 </View> 259 ) 260}