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

Configure Feed

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

at c540dae4e7db67031ee5f67feb076927999e364d 342 lines 11 kB view raw
1import React from 'react' 2import { 3 ActivityIndicator, 4 SafeAreaView, 5 StyleSheet, 6 TouchableOpacity, 7 View, 8} from 'react-native' 9import {LinearGradient} from 'expo-linear-gradient' 10import {msg, Trans} from '@lingui/macro' 11import {useLingui} from '@lingui/react' 12 13import {DM_SERVICE_HEADERS} from '#/lib/constants' 14import {usePalette} from '#/lib/hooks/usePalette' 15import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 16import {cleanError} from '#/lib/strings/errors' 17import {colors, gradients, s} from '#/lib/styles' 18import {useTheme} from '#/lib/ThemeContext' 19import {isAndroid, isWeb} from '#/platform/detection' 20import {useModalControls} from '#/state/modals' 21import {useAgent, useSession, useSessionApi} from '#/state/session' 22import {atoms as a, useTheme as useNewTheme} from '#/alf' 23import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' 24import {Text as NewText} from '#/components/Typography' 25import {resetToTab} from '../../../Navigation' 26import {ErrorMessage} from '../util/error/ErrorMessage' 27import {Text} from '../util/text/Text' 28import * as Toast from '../util/Toast' 29import {ScrollView, TextInput} from './util' 30 31export const snapPoints = isAndroid ? ['90%'] : ['55%'] 32 33export function Component({}: {}) { 34 const pal = usePalette('default') 35 const theme = useTheme() 36 const t = useNewTheme() 37 const {currentAccount} = useSession() 38 const agent = useAgent() 39 const {removeAccount} = useSessionApi() 40 const {_} = useLingui() 41 const {closeModal} = useModalControls() 42 const {isMobile} = useWebMediaQueries() 43 const [isEmailSent, setIsEmailSent] = React.useState<boolean>(false) 44 const [confirmCode, setConfirmCode] = React.useState<string>('') 45 const [password, setPassword] = React.useState<string>('') 46 const [isProcessing, setIsProcessing] = React.useState<boolean>(false) 47 const [error, setError] = React.useState<string>('') 48 const onPressSendEmail = async () => { 49 setError('') 50 setIsProcessing(true) 51 try { 52 await agent.com.atproto.server.requestAccountDelete() 53 setIsEmailSent(true) 54 } catch (e: any) { 55 setError(cleanError(e)) 56 } 57 setIsProcessing(false) 58 } 59 const onPressConfirmDelete = async () => { 60 if (!currentAccount?.did) { 61 throw new Error(`DeleteAccount modal: currentAccount.did is undefined`) 62 } 63 64 setError('') 65 setIsProcessing(true) 66 const token = confirmCode.replace(/\s/g, '') 67 68 try { 69 // inform chat service of intent to delete account 70 const {success} = await agent.api.chat.bsky.actor.deleteAccount( 71 undefined, 72 { 73 headers: DM_SERVICE_HEADERS, 74 }, 75 ) 76 if (!success) { 77 throw new Error('Failed to inform chat service of account deletion') 78 } 79 await agent.com.atproto.server.deleteAccount({ 80 did: currentAccount.did, 81 password, 82 token, 83 }) 84 Toast.show(_(msg`Your account has been deleted`)) 85 resetToTab('HomeTab') 86 removeAccount(currentAccount) 87 closeModal() 88 } catch (e: any) { 89 setError(cleanError(e)) 90 } 91 setIsProcessing(false) 92 } 93 const onCancel = () => { 94 closeModal() 95 } 96 return ( 97 <SafeAreaView style={[s.flex1]}> 98 <ScrollView style={[pal.view]} keyboardShouldPersistTaps="handled"> 99 <View style={[styles.titleContainer, pal.view]}> 100 <Text type="title-xl" style={[s.textCenter, pal.text]}> 101 <Trans> 102 Delete Account{' '} 103 <Text type="title-xl" style={[pal.text, s.bold]}> 104 " 105 </Text> 106 <Text 107 type="title-xl" 108 numberOfLines={1} 109 style={[ 110 isMobile ? styles.titleMobile : styles.titleDesktop, 111 pal.text, 112 s.bold, 113 ]}> 114 {currentAccount?.handle} 115 </Text> 116 <Text type="title-xl" style={[pal.text, s.bold]}> 117 " 118 </Text> 119 </Trans> 120 </Text> 121 </View> 122 {!isEmailSent ? ( 123 <> 124 <Text type="lg" style={[styles.description, pal.text]}> 125 <Trans> 126 For security reasons, we'll need to send a confirmation code to 127 your email address. 128 </Trans> 129 </Text> 130 {error ? ( 131 <View style={s.mt10}> 132 <ErrorMessage message={error} /> 133 </View> 134 ) : undefined} 135 {isProcessing ? ( 136 <View style={[styles.btn, s.mt10]}> 137 <ActivityIndicator /> 138 </View> 139 ) : ( 140 <> 141 <TouchableOpacity 142 style={styles.mt20} 143 onPress={onPressSendEmail} 144 accessibilityRole="button" 145 accessibilityLabel={_(msg`Send email`)} 146 accessibilityHint={_( 147 msg`Sends email with confirmation code for account deletion`, 148 )}> 149 <LinearGradient 150 colors={[ 151 gradients.blueLight.start, 152 gradients.blueLight.end, 153 ]} 154 start={{x: 0, y: 0}} 155 end={{x: 1, y: 1}} 156 style={[styles.btn]}> 157 <Text type="button-lg" style={[s.white, s.bold]}> 158 <Trans context="action">Send Email</Trans> 159 </Text> 160 </LinearGradient> 161 </TouchableOpacity> 162 <TouchableOpacity 163 style={[styles.btn, s.mt10]} 164 onPress={onCancel} 165 accessibilityRole="button" 166 accessibilityLabel={_(msg`Cancel account deletion`)} 167 accessibilityHint="" 168 onAccessibilityEscape={onCancel}> 169 <Text type="button-lg" style={pal.textLight}> 170 <Trans context="action">Cancel</Trans> 171 </Text> 172 </TouchableOpacity> 173 </> 174 )} 175 176 <View style={[!isWeb && a.px_xl]}> 177 <View 178 style={[ 179 a.w_full, 180 a.flex_row, 181 a.gap_sm, 182 a.mt_lg, 183 a.p_lg, 184 a.rounded_sm, 185 t.atoms.bg_contrast_25, 186 ]}> 187 <CircleInfo 188 size="md" 189 style={[ 190 a.relative, 191 { 192 top: -1, 193 }, 194 ]} 195 /> 196 197 <NewText style={[a.leading_snug, a.flex_1]}> 198 <Trans> 199 You can also temporarily deactivate your account instead, 200 and reactivate it at any time. 201 </Trans> 202 </NewText> 203 </View> 204 </View> 205 </> 206 ) : ( 207 <> 208 {/* TODO: Update this label to be more concise */} 209 <Text 210 type="lg" 211 style={[pal.text, styles.description]} 212 nativeID="confirmationCode"> 213 <Trans> 214 Check your inbox for an email with the confirmation code to 215 enter below: 216 </Trans> 217 </Text> 218 <TextInput 219 style={[styles.textInput, pal.borderDark, pal.text, styles.mb20]} 220 placeholder={_(msg`Confirmation code`)} 221 placeholderTextColor={pal.textLight.color} 222 keyboardAppearance={theme.colorScheme} 223 value={confirmCode} 224 onChangeText={setConfirmCode} 225 accessibilityLabelledBy="confirmationCode" 226 accessibilityLabel={_(msg`Confirmation code`)} 227 accessibilityHint={_( 228 msg`Input confirmation code for account deletion`, 229 )} 230 /> 231 <Text 232 type="lg" 233 style={[pal.text, styles.description]} 234 nativeID="password"> 235 <Trans>Please enter your password as well:</Trans> 236 </Text> 237 <TextInput 238 style={[styles.textInput, pal.borderDark, pal.text]} 239 placeholder={_(msg`Password`)} 240 placeholderTextColor={pal.textLight.color} 241 keyboardAppearance={theme.colorScheme} 242 secureTextEntry 243 value={password} 244 onChangeText={setPassword} 245 accessibilityLabelledBy="password" 246 accessibilityLabel={_(msg`Password`)} 247 accessibilityHint={_(msg`Input password for account deletion`)} 248 /> 249 {error ? ( 250 <View style={styles.mt20}> 251 <ErrorMessage message={error} /> 252 </View> 253 ) : undefined} 254 {isProcessing ? ( 255 <View style={[styles.btn, s.mt10]}> 256 <ActivityIndicator /> 257 </View> 258 ) : ( 259 <> 260 <TouchableOpacity 261 style={[styles.btn, styles.evilBtn, styles.mt20]} 262 onPress={onPressConfirmDelete} 263 accessibilityRole="button" 264 accessibilityLabel={_(msg`Confirm delete account`)} 265 accessibilityHint=""> 266 <Text type="button-lg" style={[s.white, s.bold]}> 267 <Trans>Delete my account</Trans> 268 </Text> 269 </TouchableOpacity> 270 <TouchableOpacity 271 style={[styles.btn, s.mt10]} 272 onPress={onCancel} 273 accessibilityRole="button" 274 accessibilityLabel={_(msg`Cancel account deletion`)} 275 accessibilityHint={_(msg`Exits account deletion process`)} 276 onAccessibilityEscape={onCancel}> 277 <Text type="button-lg" style={pal.textLight}> 278 <Trans context="action">Cancel</Trans> 279 </Text> 280 </TouchableOpacity> 281 </> 282 )} 283 </> 284 )} 285 </ScrollView> 286 </SafeAreaView> 287 ) 288} 289 290const styles = StyleSheet.create({ 291 titleContainer: { 292 display: 'flex', 293 flexDirection: 'row', 294 justifyContent: 'center', 295 flexWrap: 'wrap', 296 marginTop: 12, 297 marginBottom: 12, 298 marginLeft: 20, 299 marginRight: 20, 300 }, 301 titleMobile: { 302 textAlign: 'center', 303 }, 304 titleDesktop: { 305 textAlign: 'center', 306 overflow: 'hidden', 307 whiteSpace: 'nowrap', 308 textOverflow: 'ellipsis', 309 // @ts-ignore only rendered on web 310 maxWidth: '400px', 311 }, 312 description: { 313 textAlign: 'center', 314 paddingHorizontal: 22, 315 marginBottom: 10, 316 }, 317 mt20: { 318 marginTop: 20, 319 }, 320 mb20: { 321 marginBottom: 20, 322 }, 323 textInput: { 324 borderWidth: 1, 325 borderRadius: 6, 326 paddingHorizontal: 16, 327 paddingVertical: 12, 328 fontSize: 20, 329 marginHorizontal: 20, 330 }, 331 btn: { 332 flexDirection: 'row', 333 alignItems: 'center', 334 justifyContent: 'center', 335 borderRadius: 32, 336 padding: 14, 337 marginHorizontal: 20, 338 }, 339 evilBtn: { 340 backgroundColor: colors.red4, 341 }, 342})