1- import * as FileSystem from 'expo-file-system' ;
21import { decode as decodeBase64 , encode as encodeBase64 } from 'base64-arraybuffer' ;
32import { AttachmentData , EncodingType , LocalStorageAdapter } from '@powersync/common' ;
43
5- /**
6- * ExpoFileSystemAdapter implements LocalStorageAdapter using Expo's FileSystem.
7- * Suitable for React Native applications using Expo or Expo modules.
8- */
9- export class ExpoFileSystemAdapter implements LocalStorageAdapter {
4+ try {
5+ var rnfs = require ( '@dr.pogodin/react-native-fs' ) ;
6+ } catch ( e ) {
7+ throw new Error ( `Could not resolve @dr.pogodin/react-native-fs.
8+ To use the React Native File System attachment adapter please install @dr.pogodin/react-native-fs.` ) ;
9+ }
10+
11+ export class ReactNativeFileSystemStorageAdapter implements LocalStorageAdapter {
1012 private storageDirectory : string ;
1113
1214 constructor ( storageDirectory ?: string ) {
1315 // Default to a subdirectory in the document directory
14- this . storageDirectory = storageDirectory ?? `${ FileSystem . documentDirectory } attachments/` ;
16+ this . storageDirectory = storageDirectory ?? `${ rnfs . DocumentDirectoryPath } / attachments/` ;
1517 }
1618
1719 async initialize ( ) : Promise < void > {
18- const dirInfo = await FileSystem . getInfoAsync ( this . storageDirectory ) ;
19- if ( ! dirInfo . exists ) {
20- await FileSystem . makeDirectoryAsync ( this . storageDirectory , { intermediates : true } ) ;
20+ const dirExists = await rnfs . exists ( this . storageDirectory ) ;
21+ if ( ! dirExists ) {
22+ await rnfs . mkdir ( this . storageDirectory ) ;
2123 }
2224 }
2325
2426 async clear ( ) : Promise < void > {
25- const dirInfo = await FileSystem . getInfoAsync ( this . storageDirectory ) ;
26- if ( dirInfo . exists ) {
27- await FileSystem . deleteAsync ( this . storageDirectory ) ;
27+ const dirExists = await rnfs . exists ( this . storageDirectory ) ;
28+ if ( dirExists ) {
29+ await rnfs . unlink ( this . storageDirectory ) ;
2830 }
2931 }
3032
@@ -40,27 +42,19 @@ export class ExpoFileSystemAdapter implements LocalStorageAdapter {
4042 let size : number ;
4143
4244 if ( typeof data === 'string' ) {
43- // Handle string data (typically base64 or UTF8)
4445 const encoding = options ?. encoding ?? EncodingType . Base64 ;
45- await FileSystem . writeAsStringAsync ( filePath , data , {
46- encoding : encoding === EncodingType . Base64 ? FileSystem . EncodingType . Base64 : FileSystem . EncodingType . UTF8
47- } ) ;
48-
46+ await rnfs . writeFile ( filePath , data , encoding === EncodingType . Base64 ? 'base64' : 'utf8' ) ;
47+
4948 // Calculate size based on encoding
5049 if ( encoding === EncodingType . Base64 ) {
51- // Base64 string length / 4 * 3 gives approximate byte size
5250 size = Math . ceil ( ( data . length / 4 ) * 3 ) ;
5351 } else {
54- // UTF8: Use TextEncoder to get accurate byte count
5552 const encoder = new TextEncoder ( ) ;
5653 size = encoder . encode ( data ) . byteLength ;
5754 }
5855 } else {
59- // Handle ArrayBuffer data
6056 const base64 = encodeBase64 ( data ) ;
61- await FileSystem . writeAsStringAsync ( filePath , base64 , {
62- encoding : FileSystem . EncodingType . Base64
63- } ) ;
57+ await rnfs . WriteFile ( filePath , base64 , 'base64' ) ;
6458 size = data . byteLength ;
6559 }
6660
@@ -69,25 +63,19 @@ export class ExpoFileSystemAdapter implements LocalStorageAdapter {
6963
7064 async readFile ( filePath : string , options ?: { encoding ?: EncodingType ; mediaType ?: string } ) : Promise < ArrayBuffer > {
7165 const encoding = options ?. encoding ?? EncodingType . Base64 ;
72-
73- // Let the native function throw if file doesn't exist
74- const content = await FileSystem . readAsStringAsync ( filePath , {
75- encoding : encoding === EncodingType . Base64 ? FileSystem . EncodingType . Base64 : FileSystem . EncodingType . UTF8
76- } ) ;
66+
67+ const content = await rnfs . readFile ( filePath , encoding === EncodingType . Base64 ? 'base64' : 'utf8' ) ;
7768
7869 if ( encoding === EncodingType . UTF8 ) {
79- // Convert UTF8 string to ArrayBuffer
8070 const encoder = new TextEncoder ( ) ;
8171 return encoder . encode ( content ) . buffer ;
8272 } else {
83- // Convert base64 string to ArrayBuffer
8473 return decodeBase64 ( content ) ;
8574 }
8675 }
8776
8877 async deleteFile ( filePath : string , options ?: { filename ?: string } ) : Promise < void > {
89- await FileSystem . deleteAsync ( filePath ) . catch ( ( error : any ) => {
90- // Gracefully ignore file not found errors, throw others
78+ await rnfs . unlink ( filePath ) . catch ( ( error : any ) => {
9179 if ( error ?. message ?. includes ( 'not exist' ) || error ?. message ?. includes ( 'ENOENT' ) ) {
9280 return ;
9381 }
@@ -97,19 +85,17 @@ export class ExpoFileSystemAdapter implements LocalStorageAdapter {
9785
9886 async fileExists ( filePath : string ) : Promise < boolean > {
9987 try {
100- const info = await FileSystem . getInfoAsync ( filePath ) ;
101- return info . exists ;
88+ return await rnfs . exists ( filePath ) ;
10289 } catch {
10390 return false ;
10491 }
10592 }
10693
10794 async makeDir ( path : string ) : Promise < void > {
108- await FileSystem . makeDirectoryAsync ( path , { intermediates : true } ) ;
95+ await rnfs . mkdir ( path ) ;
10996 }
11097
11198 async rmDir ( path : string ) : Promise < void > {
112- await FileSystem . deleteAsync ( path ) ;
99+ await rnfs . unlink ( path ) ;
113100 }
114101}
115-
0 commit comments