Bluesky app fork with some witchin' additions 💫

Add chat data export to Export My Data dialog (#9900)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

authored by samuel.fm

Claude Opus 4.6 and committed by
GitHub
775a041b 98d17fd9

+60 -4
+60 -4
src/screens/Settings/components/ExportCarDialog.tsx
··· 3 import {msg, Trans} from '@lingui/macro' 4 import {useLingui} from '@lingui/react' 5 6 import {saveBytesToDisk} from '#/lib/media/manip' 7 import {logger} from '#/logger' 8 import {useAgent} from '#/state/session' ··· 23 const {_} = useLingui() 24 const t = useTheme() 25 const agent = useAgent() 26 - const [loading, setLoading] = useState(false) 27 28 const download = useCallback(async () => { 29 if (!agent.session) { 30 return // shouldnt ever happen 31 } 32 try { 33 - setLoading(true) 34 const did = agent.session.did 35 const downloadRes = await agent.com.atproto.sync.getRepo({did}) 36 const saveRes = await saveBytesToDisk( ··· 51 } 52 }, [_, control, agent]) 53 54 return ( 55 <Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}> 56 <Dialog.Handle /> ··· 77 color="primary" 78 size="large" 79 label={_(msg`Download CAR file`)} 80 - disabled={loading} 81 onPress={download}> 82 <ButtonIcon icon={DownloadIcon} /> 83 <ButtonText> 84 <Trans>Download CAR file</Trans> 85 </ButtonText> 86 - {loading && <ButtonIcon icon={Loader} />} 87 </Button> 88 89 <Text
··· 3 import {msg, Trans} from '@lingui/macro' 4 import {useLingui} from '@lingui/react' 5 6 + import {DM_SERVICE_HEADERS} from '#/lib/constants' 7 import {saveBytesToDisk} from '#/lib/media/manip' 8 import {logger} from '#/logger' 9 import {useAgent} from '#/state/session' ··· 24 const {_} = useLingui() 25 const t = useTheme() 26 const agent = useAgent() 27 + const [loading, setLoading] = useState<'repo' | 'chat' | false>(false) 28 29 const download = useCallback(async () => { 30 if (!agent.session) { 31 return // shouldnt ever happen 32 } 33 try { 34 + setLoading('repo') 35 const did = agent.session.did 36 const downloadRes = await agent.com.atproto.sync.getRepo({did}) 37 const saveRes = await saveBytesToDisk( ··· 52 } 53 }, [_, control, agent]) 54 55 + const downloadChatData = useCallback(async () => { 56 + if (!agent.session) { 57 + return 58 + } 59 + try { 60 + setLoading('chat') 61 + // Using raw fetch because the XRPC client incorrectly tries to JSON-parse 62 + // application/jsonl responses (substring match on application/json). 63 + const res = await agent.sessionManager.fetchHandler( 64 + '/xrpc/chat.bsky.actor.exportAccountData', 65 + {headers: DM_SERVICE_HEADERS}, 66 + ) 67 + if (!res.ok) { 68 + throw new Error(`HTTP ${res.status}`) 69 + } 70 + const data = new Uint8Array(await res.arrayBuffer()) 71 + const saveRes = await saveBytesToDisk( 72 + 'chat.jsonl', 73 + data, 74 + res.headers.get('content-type') || 'application/jsonl', 75 + ) 76 + 77 + if (saveRes) { 78 + Toast.show(_(msg`File saved successfully!`)) 79 + } 80 + } catch (e) { 81 + logger.error('Error occurred while downloading chat data', {message: e}) 82 + Toast.show(_(msg`Error occurred while saving file`), {type: 'error'}) 83 + } finally { 84 + setLoading(false) 85 + control.close() 86 + } 87 + }, [_, control, agent]) 88 + 89 return ( 90 <Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}> 91 <Dialog.Handle /> ··· 112 color="primary" 113 size="large" 114 label={_(msg`Download CAR file`)} 115 + disabled={!!loading} 116 onPress={download}> 117 <ButtonIcon icon={DownloadIcon} /> 118 <ButtonText> 119 <Trans>Download CAR file</Trans> 120 </ButtonText> 121 + {loading === 'repo' && <ButtonIcon icon={Loader} />} 122 + </Button> 123 + 124 + <Text style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_high]}> 125 + <Trans> 126 + You can also download your chat data as a "JSONL" file. This file 127 + only includes chat messages that you have sent and does not 128 + include chat messages that you have received. 129 + </Trans> 130 + </Text> 131 + 132 + <Button 133 + color="secondary" 134 + size="large" 135 + label={_(msg`Download chat data`)} 136 + disabled={!!loading} 137 + onPress={downloadChatData}> 138 + <ButtonIcon icon={DownloadIcon} /> 139 + <ButtonText> 140 + <Trans>Download chat data</Trans> 141 + </ButtonText> 142 + {loading === 'chat' && <ButtonIcon icon={Loader} />} 143 </Button> 144 145 <Text