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 3 import {msg, Trans} from '@lingui/macro' 4 4 import {useLingui} from '@lingui/react' 5 5 6 + import {DM_SERVICE_HEADERS} from '#/lib/constants' 6 7 import {saveBytesToDisk} from '#/lib/media/manip' 7 8 import {logger} from '#/logger' 8 9 import {useAgent} from '#/state/session' ··· 23 24 const {_} = useLingui() 24 25 const t = useTheme() 25 26 const agent = useAgent() 26 - const [loading, setLoading] = useState(false) 27 + const [loading, setLoading] = useState<'repo' | 'chat' | false>(false) 27 28 28 29 const download = useCallback(async () => { 29 30 if (!agent.session) { 30 31 return // shouldnt ever happen 31 32 } 32 33 try { 33 - setLoading(true) 34 + setLoading('repo') 34 35 const did = agent.session.did 35 36 const downloadRes = await agent.com.atproto.sync.getRepo({did}) 36 37 const saveRes = await saveBytesToDisk( ··· 51 52 } 52 53 }, [_, control, agent]) 53 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 + 54 89 return ( 55 90 <Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}> 56 91 <Dialog.Handle /> ··· 77 112 color="primary" 78 113 size="large" 79 114 label={_(msg`Download CAR file`)} 80 - disabled={loading} 115 + disabled={!!loading} 81 116 onPress={download}> 82 117 <ButtonIcon icon={DownloadIcon} /> 83 118 <ButtonText> 84 119 <Trans>Download CAR file</Trans> 85 120 </ButtonText> 86 - {loading && <ButtonIcon icon={Loader} />} 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} />} 87 143 </Button> 88 144 89 145 <Text