Bluesky app fork with some witchin' additions 馃挮
at main 170 lines 5.7 kB view raw
1import {useCallback, useState} from 'react' 2import {View} from 'react-native' 3import {msg} from '@lingui/core/macro' 4import {useLingui} from '@lingui/react' 5import {Trans} from '@lingui/react/macro' 6 7import {DM_SERVICE_HEADERS} from '#/lib/constants' 8import {saveBytesToDisk} from '#/lib/media/manip' 9import {logger} from '#/logger' 10import {useAgent} from '#/state/session' 11import {atoms as a, useTheme, web} from '#/alf' 12import {Button, ButtonIcon, ButtonText} from '#/components/Button' 13import * as Dialog from '#/components/Dialog' 14import {Download_Stroke2_Corner0_Rounded as DownloadIcon} from '#/components/icons/Download' 15import {InlineLinkText} from '#/components/Link' 16import {Loader} from '#/components/Loader' 17import * as Toast from '#/components/Toast' 18import {Text} from '#/components/Typography' 19 20export function ExportCarDialog({ 21 control, 22}: { 23 control: Dialog.DialogControlProps 24}) { 25 const {_} = useLingui() 26 const t = useTheme() 27 const agent = useAgent() 28 const [loading, setLoading] = useState<'repo' | 'chat' | false>(false) 29 30 const download = useCallback(async () => { 31 if (!agent.session) { 32 return // shouldnt ever happen 33 } 34 try { 35 setLoading('repo') 36 const did = agent.session.did 37 const downloadRes = await agent.com.atproto.sync.getRepo({did}) 38 const saveRes = await saveBytesToDisk( 39 'repo.car', 40 downloadRes.data, 41 downloadRes.headers['content-type'] || 'application/vnd.ipld.car', 42 ) 43 44 if (saveRes) { 45 Toast.show(_(msg`File saved successfully!`)) 46 } 47 } catch (e) { 48 logger.error('Error occurred while downloading CAR file', {message: e}) 49 Toast.show(_(msg`Error occurred while saving file`), {type: 'error'}) 50 } finally { 51 setLoading(false) 52 control.close() 53 } 54 }, [_, control, agent]) 55 56 const downloadChatData = useCallback(async () => { 57 if (!agent.session) { 58 return 59 } 60 try { 61 setLoading('chat') 62 // Using raw fetch because the XRPC client incorrectly tries to JSON-parse 63 // application/jsonl responses (substring match on application/json). 64 const res = await agent.sessionManager.fetchHandler( 65 '/xrpc/chat.bsky.actor.exportAccountData', 66 {headers: DM_SERVICE_HEADERS}, 67 ) 68 if (!res.ok) { 69 throw new Error(`HTTP ${res.status}`) 70 } 71 const data = new Uint8Array(await res.arrayBuffer()) 72 const saveRes = await saveBytesToDisk( 73 'chat.jsonl', 74 data, 75 res.headers.get('content-type') || 'application/jsonl', 76 ) 77 78 if (saveRes) { 79 Toast.show(_(msg`File saved successfully!`)) 80 } 81 } catch (e) { 82 logger.error('Error occurred while downloading chat data', {message: e}) 83 Toast.show(_(msg`Error occurred while saving file`), {type: 'error'}) 84 } finally { 85 setLoading(false) 86 control.close() 87 } 88 }, [_, control, agent]) 89 90 return ( 91 <Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}> 92 <Dialog.Handle /> 93 <Dialog.ScrollableInner 94 accessibilityDescribedBy="dialog-description" 95 accessibilityLabelledBy="dialog-title" 96 style={web({maxWidth: 500})}> 97 <View style={[a.relative, a.gap_lg, a.w_full]}> 98 <Text nativeID="dialog-title" style={[a.text_2xl, a.font_bold]}> 99 <Trans>Export My Data</Trans> 100 </Text> 101 <Text 102 nativeID="dialog-description" 103 style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_high]}> 104 <Trans> 105 Your account repository, containing all public data records, can 106 be downloaded as a "CAR" file. This file does not include media 107 embeds, such as images, or your private data, which must be 108 fetched separately. 109 </Trans> 110 </Text> 111 112 <Button 113 color="primary" 114 size="large" 115 label={_(msg`Download CAR file`)} 116 disabled={!!loading} 117 onPress={download}> 118 <ButtonIcon icon={DownloadIcon} /> 119 <ButtonText> 120 <Trans>Download CAR file</Trans> 121 </ButtonText> 122 {loading === 'repo' && <ButtonIcon icon={Loader} />} 123 </Button> 124 125 <Text style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_high]}> 126 <Trans> 127 You can also download your chat data as a "JSONL" file. This file 128 only includes chat messages that you have sent and does not 129 include chat messages that you have received. 130 </Trans> 131 </Text> 132 133 <Button 134 color="secondary" 135 size="large" 136 label={_(msg`Download chat data`)} 137 disabled={!!loading} 138 onPress={downloadChatData}> 139 <ButtonIcon icon={DownloadIcon} /> 140 <ButtonText> 141 <Trans>Download chat data</Trans> 142 </ButtonText> 143 {loading === 'chat' && <ButtonIcon icon={Loader} />} 144 </Button> 145 146 <Text 147 style={[ 148 t.atoms.text_contrast_medium, 149 a.text_sm, 150 a.leading_snug, 151 a.flex_1, 152 ]}> 153 <Trans> 154 This feature is in beta. You can read more about repository 155 exports in{' '} 156 <InlineLinkText 157 label={_(msg`View blogpost for more details`)} 158 to="https://docs.bsky.app/blog/repo-export" 159 style={[a.text_sm]}> 160 this blogpost 161 </InlineLinkText> 162 . 163 </Trans> 164 </Text> 165 </View> 166 <Dialog.Close /> 167 </Dialog.ScrollableInner> 168 </Dialog.Outer> 169 ) 170}