@vali98/react-native-fs 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,7 +18,7 @@ This was not tested on lower Android SDK API's, might not working on Android 9 a
18
18
  ## Installation
19
19
 
20
20
  ```sh
21
- npm install @chatterui/react-native-local-download
21
+ npm install @vali98/react-native-fs
22
22
  ```
23
23
 
24
24
  ## Usage
@@ -35,4 +35,16 @@ public abstract class NativeReactNativeLocalDownloadSpec extends ReactContextBas
35
35
  @ReactMethod
36
36
  @DoNotStrip
37
37
  public abstract void localDownload(String uri, Promise promise);
38
+
39
+ @ReactMethod
40
+ @DoNotStrip
41
+ public abstract void getContentFd(String uri, Promise promise);
42
+
43
+ @ReactMethod
44
+ @DoNotStrip
45
+ public abstract void closeFd(String fd, Promise promise);
46
+
47
+ @ReactMethod
48
+ @DoNotStrip
49
+ public abstract void persistContentPermission(String uri, Promise promise);
38
50
  }
@@ -17,9 +17,27 @@ static facebook::jsi::Value __hostFunction_NativeReactNativeLocalDownloadSpecJSI
17
17
  return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "localDownload", "(Ljava/lang/String;Lcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
18
18
  }
19
19
 
20
+ static facebook::jsi::Value __hostFunction_NativeReactNativeLocalDownloadSpecJSI_getContentFd(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
21
+ static jmethodID cachedMethodId = nullptr;
22
+ return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "getContentFd", "(Ljava/lang/String;Lcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
23
+ }
24
+
25
+ static facebook::jsi::Value __hostFunction_NativeReactNativeLocalDownloadSpecJSI_closeFd(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
26
+ static jmethodID cachedMethodId = nullptr;
27
+ return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "closeFd", "(Ljava/lang/String;Lcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
28
+ }
29
+
30
+ static facebook::jsi::Value __hostFunction_NativeReactNativeLocalDownloadSpecJSI_persistContentPermission(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
31
+ static jmethodID cachedMethodId = nullptr;
32
+ return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "persistContentPermission", "(Ljava/lang/String;Lcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
33
+ }
34
+
20
35
  NativeReactNativeLocalDownloadSpecJSI::NativeReactNativeLocalDownloadSpecJSI(const JavaTurboModule::InitParams &params)
21
36
  : JavaTurboModule(params) {
22
37
  methodMap_["localDownload"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadSpecJSI_localDownload};
38
+ methodMap_["getContentFd"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadSpecJSI_getContentFd};
39
+ methodMap_["closeFd"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadSpecJSI_closeFd};
40
+ methodMap_["persistContentPermission"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadSpecJSI_persistContentPermission};
23
41
  }
24
42
 
25
43
  std::shared_ptr<TurboModule> RNReactNativeLocalDownloadSpec_ModuleProvider(const std::string &moduleName, const JavaTurboModule::InitParams &params) {
@@ -17,10 +17,31 @@ static jsi::Value __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_localD
17
17
  count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
18
18
  );
19
19
  }
20
+ static jsi::Value __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_getContentFd(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
21
+ return static_cast<NativeReactNativeLocalDownloadCxxSpecJSI *>(&turboModule)->getContentFd(
22
+ rt,
23
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
24
+ );
25
+ }
26
+ static jsi::Value __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_closeFd(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
27
+ return static_cast<NativeReactNativeLocalDownloadCxxSpecJSI *>(&turboModule)->closeFd(
28
+ rt,
29
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
30
+ );
31
+ }
32
+ static jsi::Value __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_persistContentPermission(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
33
+ return static_cast<NativeReactNativeLocalDownloadCxxSpecJSI *>(&turboModule)->persistContentPermission(
34
+ rt,
35
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
36
+ );
37
+ }
20
38
 
21
39
  NativeReactNativeLocalDownloadCxxSpecJSI::NativeReactNativeLocalDownloadCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker)
22
40
  : TurboModule("ReactNativeLocalDownload", jsInvoker) {
23
41
  methodMap_["localDownload"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_localDownload};
42
+ methodMap_["getContentFd"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_getContentFd};
43
+ methodMap_["closeFd"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_closeFd};
44
+ methodMap_["persistContentPermission"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_persistContentPermission};
24
45
  }
25
46
 
26
47
 
@@ -21,6 +21,9 @@ protected:
21
21
 
22
22
  public:
23
23
  virtual jsi::Value localDownload(jsi::Runtime &rt, jsi::String uri) = 0;
24
+ virtual jsi::Value getContentFd(jsi::Runtime &rt, jsi::String uri) = 0;
25
+ virtual jsi::Value closeFd(jsi::Runtime &rt, jsi::String fd) = 0;
26
+ virtual jsi::Value persistContentPermission(jsi::Runtime &rt, jsi::String uri) = 0;
24
27
 
25
28
  };
26
29
 
@@ -59,6 +62,30 @@ private:
59
62
  return bridging::callFromJs<jsi::Value>(
60
63
  rt, &T::localDownload, jsInvoker_, instance_, std::move(uri));
61
64
  }
65
+ jsi::Value getContentFd(jsi::Runtime &rt, jsi::String uri) override {
66
+ static_assert(
67
+ bridging::getParameterCount(&T::getContentFd) == 2,
68
+ "Expected getContentFd(...) to have 2 parameters");
69
+
70
+ return bridging::callFromJs<jsi::Value>(
71
+ rt, &T::getContentFd, jsInvoker_, instance_, std::move(uri));
72
+ }
73
+ jsi::Value closeFd(jsi::Runtime &rt, jsi::String fd) override {
74
+ static_assert(
75
+ bridging::getParameterCount(&T::closeFd) == 2,
76
+ "Expected closeFd(...) to have 2 parameters");
77
+
78
+ return bridging::callFromJs<jsi::Value>(
79
+ rt, &T::closeFd, jsInvoker_, instance_, std::move(fd));
80
+ }
81
+ jsi::Value persistContentPermission(jsi::Runtime &rt, jsi::String uri) override {
82
+ static_assert(
83
+ bridging::getParameterCount(&T::persistContentPermission) == 2,
84
+ "Expected persistContentPermission(...) to have 2 parameters");
85
+
86
+ return bridging::callFromJs<jsi::Value>(
87
+ rt, &T::persistContentPermission, jsInvoker_, instance_, std::move(uri));
88
+ }
62
89
 
63
90
  private:
64
91
  friend class NativeReactNativeLocalDownloadCxxSpec;
@@ -7,7 +7,10 @@ import com.facebook.react.module.annotations.ReactModule
7
7
  import android.content.ContentValues
8
8
  import android.content.ContentResolver
9
9
  import android.net.Uri
10
+ import android.system.Os
11
+ import android.content.Intent
10
12
  import android.provider.MediaStore
13
+ import android.os.ParcelFileDescriptor
11
14
  import java.io.File
12
15
  import java.io.FileInputStream
13
16
  import java.io.FileOutputStream
@@ -21,6 +24,77 @@ class ReactNativeLocalDownloadModule(private val reactContext: ReactApplicationC
21
24
  return NAME
22
25
  }
23
26
 
27
+ override fun getContentFd(contentUri: String, promise: Promise) {
28
+ try {
29
+ val uri = Uri.parse(contentUri)
30
+
31
+ val pfd = reactContext.contentResolver
32
+ .openFileDescriptor(uri, "r")
33
+ ?: run {
34
+ promise.reject("FD_OPEN_FAILED", "Unable to open content URI")
35
+ return
36
+ }
37
+
38
+ val fd = pfd.detachFd()
39
+ promise.resolve("/proc/self/fd/$fd")
40
+
41
+ } catch (e: Exception) {
42
+ promise.reject(
43
+ "GET_FD_ERROR",
44
+ "Failed to get detached FD: ${e.message}",
45
+ e
46
+ )
47
+ }
48
+ }
49
+
50
+ override fun closeFd(fdOrPath: String, promise: Promise) {
51
+ try {
52
+ val fdInt = when {
53
+ fdOrPath.startsWith("/proc/") ->
54
+ fdOrPath.substringAfterLast("/").toInt()
55
+ else ->
56
+ fdOrPath.toInt()
57
+ }
58
+ ParcelFileDescriptor.adoptFd(fdInt).close()
59
+ promise.resolve(true)
60
+ } catch (e: Exception) {
61
+ promise.reject(
62
+ "FD_CLOSE_ERROR",
63
+ "Failed to close FD: ${e.message}",
64
+ e
65
+ )
66
+ }
67
+ }
68
+
69
+ override fun persistContentPermission(uriString: String, promise: Promise) {
70
+ try {
71
+ val uri = Uri.parse(uriString)
72
+
73
+ val flags =
74
+ Intent.FLAG_GRANT_READ_URI_PERMISSION or
75
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION
76
+
77
+ reactContext.contentResolver
78
+ .takePersistableUriPermission(uri, flags)
79
+
80
+ promise.resolve(true)
81
+
82
+ } catch (e: SecurityException) {
83
+ promise.reject(
84
+ "PERSIST_PERMISSION_DENIED",
85
+ "Persistable permission not granted for this URI",
86
+ e
87
+ )
88
+ } catch (e: Exception) {
89
+ promise.reject(
90
+ "PERSIST_PERMISSION_ERROR",
91
+ e.message,
92
+ e
93
+ )
94
+ }
95
+ }
96
+
97
+
24
98
  override fun localDownload(uri: String, promise: Promise) {
25
99
  try {
26
100
  val inputFile = File(uri)
@@ -26,10 +26,39 @@ RCT_EXPORT_MODULE()
26
26
  });
27
27
  }
28
28
 
29
+ #pragma mark - Android-only FD stubs (iOS)
30
+
31
+ - (void)getContentFd:(NSString *)uri
32
+ resolve:(RCTPromiseResolveBlock)resolve
33
+ reject:(RCTPromiseRejectBlock)reject
34
+ {
35
+ reject(
36
+ @"UNSUPPORTED_PLATFORM",
37
+ @"getContentFd is not supported on iOS",
38
+ nil
39
+ );
40
+ }
41
+
42
+ - (void)closeFd:(NSString *)fdOrPath
43
+ resolve:(RCTPromiseResolveBlock)resolve
44
+ reject:(RCTPromiseRejectBlock)reject
45
+ {
46
+ // No-op stub for iOS
47
+ resolve(@(YES));
48
+ }
49
+
29
50
  - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
30
51
  (const facebook::react::ObjCTurboModule::InitParams &)params
31
52
  {
32
53
  return std::make_shared<facebook::react::NativeReactNativeLocalDownloadSpecJSI>(params);
33
54
  }
34
55
 
56
+ - (void)persistContentPermission:(NSString *)uri
57
+ resolve:(RCTPromiseResolveBlock)resolve
58
+ reject:(RCTPromiseRejectBlock)reject
59
+ {
60
+ // iOS sandbox does not support persistable URI permissions
61
+ resolve(@(YES));
62
+ }
63
+
35
64
  @end
@@ -30,10 +30,31 @@ namespace facebook::react {
30
30
  return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "localDownload", @selector(localDownload:resolve:reject:), args, count);
31
31
  }
32
32
 
33
+ static facebook::jsi::Value __hostFunction_NativeReactNativeLocalDownloadSpecJSI_getContentFd(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
34
+ return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "getContentFd", @selector(getContentFd:resolve:reject:), args, count);
35
+ }
36
+
37
+ static facebook::jsi::Value __hostFunction_NativeReactNativeLocalDownloadSpecJSI_closeFd(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
38
+ return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "closeFd", @selector(closeFd:resolve:reject:), args, count);
39
+ }
40
+
41
+ static facebook::jsi::Value __hostFunction_NativeReactNativeLocalDownloadSpecJSI_persistContentPermission(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
42
+ return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "persistContentPermission", @selector(persistContentPermission:resolve:reject:), args, count);
43
+ }
44
+
33
45
  NativeReactNativeLocalDownloadSpecJSI::NativeReactNativeLocalDownloadSpecJSI(const ObjCTurboModule::InitParams &params)
34
46
  : ObjCTurboModule(params) {
35
47
 
36
48
  methodMap_["localDownload"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadSpecJSI_localDownload};
37
49
 
50
+
51
+ methodMap_["getContentFd"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadSpecJSI_getContentFd};
52
+
53
+
54
+ methodMap_["closeFd"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadSpecJSI_closeFd};
55
+
56
+
57
+ methodMap_["persistContentPermission"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadSpecJSI_persistContentPermission};
58
+
38
59
  }
39
60
  } // namespace facebook::react
@@ -38,6 +38,15 @@ NS_ASSUME_NONNULL_BEGIN
38
38
  - (void)localDownload:(NSString *)uri
39
39
  resolve:(RCTPromiseResolveBlock)resolve
40
40
  reject:(RCTPromiseRejectBlock)reject;
41
+ - (void)getContentFd:(NSString *)uri
42
+ resolve:(RCTPromiseResolveBlock)resolve
43
+ reject:(RCTPromiseRejectBlock)reject;
44
+ - (void)closeFd:(NSString *)fd
45
+ resolve:(RCTPromiseResolveBlock)resolve
46
+ reject:(RCTPromiseRejectBlock)reject;
47
+ - (void)persistContentPermission:(NSString *)uri
48
+ resolve:(RCTPromiseResolveBlock)resolve
49
+ reject:(RCTPromiseRejectBlock)reject;
41
50
 
42
51
  @end
43
52
 
@@ -17,10 +17,31 @@ static jsi::Value __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_localD
17
17
  count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
18
18
  );
19
19
  }
20
+ static jsi::Value __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_getContentFd(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
21
+ return static_cast<NativeReactNativeLocalDownloadCxxSpecJSI *>(&turboModule)->getContentFd(
22
+ rt,
23
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
24
+ );
25
+ }
26
+ static jsi::Value __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_closeFd(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
27
+ return static_cast<NativeReactNativeLocalDownloadCxxSpecJSI *>(&turboModule)->closeFd(
28
+ rt,
29
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
30
+ );
31
+ }
32
+ static jsi::Value __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_persistContentPermission(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
33
+ return static_cast<NativeReactNativeLocalDownloadCxxSpecJSI *>(&turboModule)->persistContentPermission(
34
+ rt,
35
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
36
+ );
37
+ }
20
38
 
21
39
  NativeReactNativeLocalDownloadCxxSpecJSI::NativeReactNativeLocalDownloadCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker)
22
40
  : TurboModule("ReactNativeLocalDownload", jsInvoker) {
23
41
  methodMap_["localDownload"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_localDownload};
42
+ methodMap_["getContentFd"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_getContentFd};
43
+ methodMap_["closeFd"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_closeFd};
44
+ methodMap_["persistContentPermission"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_persistContentPermission};
24
45
  }
25
46
 
26
47
 
@@ -21,6 +21,9 @@ protected:
21
21
 
22
22
  public:
23
23
  virtual jsi::Value localDownload(jsi::Runtime &rt, jsi::String uri) = 0;
24
+ virtual jsi::Value getContentFd(jsi::Runtime &rt, jsi::String uri) = 0;
25
+ virtual jsi::Value closeFd(jsi::Runtime &rt, jsi::String fd) = 0;
26
+ virtual jsi::Value persistContentPermission(jsi::Runtime &rt, jsi::String uri) = 0;
24
27
 
25
28
  };
26
29
 
@@ -59,6 +62,30 @@ private:
59
62
  return bridging::callFromJs<jsi::Value>(
60
63
  rt, &T::localDownload, jsInvoker_, instance_, std::move(uri));
61
64
  }
65
+ jsi::Value getContentFd(jsi::Runtime &rt, jsi::String uri) override {
66
+ static_assert(
67
+ bridging::getParameterCount(&T::getContentFd) == 2,
68
+ "Expected getContentFd(...) to have 2 parameters");
69
+
70
+ return bridging::callFromJs<jsi::Value>(
71
+ rt, &T::getContentFd, jsInvoker_, instance_, std::move(uri));
72
+ }
73
+ jsi::Value closeFd(jsi::Runtime &rt, jsi::String fd) override {
74
+ static_assert(
75
+ bridging::getParameterCount(&T::closeFd) == 2,
76
+ "Expected closeFd(...) to have 2 parameters");
77
+
78
+ return bridging::callFromJs<jsi::Value>(
79
+ rt, &T::closeFd, jsInvoker_, instance_, std::move(fd));
80
+ }
81
+ jsi::Value persistContentPermission(jsi::Runtime &rt, jsi::String uri) override {
82
+ static_assert(
83
+ bridging::getParameterCount(&T::persistContentPermission) == 2,
84
+ "Expected persistContentPermission(...) to have 2 parameters");
85
+
86
+ return bridging::callFromJs<jsi::Value>(
87
+ rt, &T::persistContentPermission, jsInvoker_, instance_, std::move(uri));
88
+ }
62
89
 
63
90
  private:
64
91
  friend class NativeReactNativeLocalDownloadCxxSpec;
@@ -2,7 +2,12 @@ import type { TurboModule } from 'react-native';
2
2
  import { TurboModuleRegistry } from 'react-native';
3
3
 
4
4
  export interface Spec extends TurboModule {
5
- localDownload(uri: string): Promise<void>
5
+ localDownload(uri: string): Promise<void>;
6
+ getContentFd(uri: string): Promise<string>;
7
+ closeFd(fd: string): Promise<void>;
8
+ persistContentPermission(uri: string): Promise<void>;
6
9
  }
7
10
 
8
- export default TurboModuleRegistry.getEnforcing<Spec>('ReactNativeLocalDownload');
11
+ export default TurboModuleRegistry.getEnforcing<Spec>(
12
+ 'ReactNativeLocalDownload'
13
+ );
@@ -2,6 +2,21 @@
2
2
 
3
3
  import ReactNativeLocalDownload from './NativeReactNativeLocalDownload';
4
4
  import { PermissionsAndroid, Platform, Linking, Alert } from 'react-native';
5
+ export async function getContentFd(uri) {
6
+ if (Platform.OS !== 'android') return;
7
+ if (!uri.includes('content://')) {
8
+ throw new TypeError('URI provided is not a content URI');
9
+ }
10
+ return ReactNativeLocalDownload.getContentFd(uri);
11
+ }
12
+ export async function closeFd(uri) {
13
+ if (Platform.OS !== 'android') return;
14
+ return ReactNativeLocalDownload.closeFd(uri);
15
+ }
16
+ export async function persistContentPermission(uri) {
17
+ if (Platform.OS !== 'android') return;
18
+ return ReactNativeLocalDownload.persistContentPermission(uri);
19
+ }
5
20
  /**
6
21
  *
7
22
  * @param uri path to content to be sent to downloads
@@ -1 +1 @@
1
- {"version":3,"names":["ReactNativeLocalDownload","PermissionsAndroid","Platform","Linking","Alert","localDownload","uri","never_ask_function","granted","requestStoragePermission","RESULTS","NEVER_ASK_AGAIN","alert","text","style","onPress","openSettings","Error","OS","GRANTED","Number","Version","hasPermission","check","PERMISSIONS","WRITE_EXTERNAL_STORAGE","request","title","message","buttonPositive","err","console","warn","DENIED"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,OAAOA,wBAAwB,MAAM,kCAAkC;AAEvE,SAASC,kBAAkB,EAAEC,QAAQ,EAAEC,OAAO,EAAEC,KAAK,QAAQ,cAAc;AAC3E;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,eAAeC,aAAaA,CAC/BC,GAAW,EACXC,kBAA2C,EAC9B;EACb,MAAMC,OAAO,GAAG,MAAMC,wBAAwB,CAAC,CAAC;EAChD,IAAID,OAAO,KAAK,SAAS,EAAE;IACvB,IAAIA,OAAO,KAAKP,kBAAkB,CAACS,OAAO,CAACC,eAAe,EAAE;MACxD,IAAI,CAACJ,kBAAkB,EACnBH,KAAK,CAACQ,KAAK,CACP,qBAAqB,EACrB,6EAA6E,EAC7E,CACI;QAAEC,IAAI,EAAE,QAAQ;QAAEC,KAAK,EAAE;MAAS,CAAC,EACnC;QAAED,IAAI,EAAE,eAAe;QAAEE,OAAO,EAAEA,CAAA,KAAMZ,OAAO,CAACa,YAAY,CAAC;MAAE,CAAC,CAExE,CAAC,MACAT,kBAAkB,CAAC,CAAC;IAC7B;IACA,MAAM,IAAIU,KAAK,CAAC,yBAAyB,CAAC;EAC9C;EACA,OAAOjB,wBAAwB,CAACK,aAAa,CAACC,GAAG,CAAC;AACtD;AAEA,OAAO,MAAMG,wBAAwB,GAAG,MAAAA,CAAA,KAEnC;EACD,IAAIP,QAAQ,CAACgB,EAAE,KAAK,SAAS,EAAE,OAAOjB,kBAAkB,CAACS,OAAO,CAACS,OAAO;EAExE,IAAI;IACA,IAAIC,MAAM,CAAClB,QAAQ,CAACmB,OAAO,CAAC,IAAI,EAAE,EAAE;MAChC,OAAO,SAAS;IACpB;IAEA,MAAMC,aAAa,GAAG,MAAMrB,kBAAkB,CAACsB,KAAK,CAChDtB,kBAAkB,CAACuB,WAAW,CAACC,sBACnC,CAAC;IACD,IAAIH,aAAa,EAAE;MACf,OAAO,SAAS;IACpB;IAEA,MAAMd,OAAO,GAAG,MAAMP,kBAAkB,CAACyB,OAAO,CAC5CzB,kBAAkB,CAACuB,WAAW,CAACC,sBAAsB,EACrD;MACIE,KAAK,EAAE,oBAAoB;MAC3BC,OAAO,EAAE,0DAA0D;MACnEC,cAAc,EAAE;IACpB,CACJ,CAAC;IACD,OAAOrB,OAAO;EAClB,CAAC,CAAC,OAAOsB,GAAG,EAAE;IACVC,OAAO,CAACC,IAAI,CAACF,GAAG,CAAC;IACjB,OAAO7B,kBAAkB,CAACS,OAAO,CAACuB,MAAM;EAC5C;AACJ,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["ReactNativeLocalDownload","PermissionsAndroid","Platform","Linking","Alert","getContentFd","uri","OS","includes","TypeError","closeFd","persistContentPermission","localDownload","never_ask_function","granted","requestStoragePermission","RESULTS","NEVER_ASK_AGAIN","alert","text","style","onPress","openSettings","Error","GRANTED","Number","Version","hasPermission","check","PERMISSIONS","WRITE_EXTERNAL_STORAGE","request","title","message","buttonPositive","err","console","warn","DENIED"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,OAAOA,wBAAwB,MAAM,kCAAkC;AAEvE,SAASC,kBAAkB,EAAEC,QAAQ,EAAEC,OAAO,EAAEC,KAAK,QAAQ,cAAc;AAE3E,OAAO,eAAeC,YAAYA,CAACC,GAAW,EAAE;EAC9C,IAAIJ,QAAQ,CAACK,EAAE,KAAK,SAAS,EAAE;EAC/B,IAAI,CAACD,GAAG,CAACE,QAAQ,CAAC,YAAY,CAAC,EAAE;IAC/B,MAAM,IAAIC,SAAS,CAAC,mCAAmC,CAAC;EAC1D;EACA,OAAOT,wBAAwB,CAACK,YAAY,CAACC,GAAG,CAAC;AACnD;AAEA,OAAO,eAAeI,OAAOA,CAACJ,GAAW,EAAE;EACzC,IAAIJ,QAAQ,CAACK,EAAE,KAAK,SAAS,EAAE;EAC/B,OAAOP,wBAAwB,CAACU,OAAO,CAACJ,GAAG,CAAC;AAC9C;AAEA,OAAO,eAAeK,wBAAwBA,CAACL,GAAW,EAAE;EAC1D,IAAIJ,QAAQ,CAACK,EAAE,KAAK,SAAS,EAAE;EAC/B,OAAOP,wBAAwB,CAACW,wBAAwB,CAACL,GAAG,CAAC;AAC/D;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,eAAeM,aAAaA,CACjCN,GAAW,EACXO,kBAA2C,EAC5B;EACf,MAAMC,OAAO,GAAG,MAAMC,wBAAwB,CAAC,CAAC;EAChD,IAAID,OAAO,KAAK,SAAS,EAAE;IACzB,IAAIA,OAAO,KAAKb,kBAAkB,CAACe,OAAO,CAACC,eAAe,EAAE;MAC1D,IAAI,CAACJ,kBAAkB,EACrBT,KAAK,CAACc,KAAK,CACT,qBAAqB,EACrB,6EAA6E,EAC7E,CACE;QAAEC,IAAI,EAAE,QAAQ;QAAEC,KAAK,EAAE;MAAS,CAAC,EACnC;QAAED,IAAI,EAAE,eAAe;QAAEE,OAAO,EAAEA,CAAA,KAAMlB,OAAO,CAACmB,YAAY,CAAC;MAAE,CAAC,CAEpE,CAAC,CAAC,KACCT,kBAAkB,CAAC,CAAC;IAC3B;IACA,MAAM,IAAIU,KAAK,CAAC,yBAAyB,CAAC;EAC5C;EACA,OAAOvB,wBAAwB,CAACY,aAAa,CAACN,GAAG,CAAC;AACpD;AAEA,OAAO,MAAMS,wBAAwB,GAAG,MAAAA,CAAA,KAEnC;EACH,IAAIb,QAAQ,CAACK,EAAE,KAAK,SAAS,EAAE,OAAON,kBAAkB,CAACe,OAAO,CAACQ,OAAO;EAExE,IAAI;IACF,IAAIC,MAAM,CAACvB,QAAQ,CAACwB,OAAO,CAAC,IAAI,EAAE,EAAE;MAClC,OAAO,SAAS;IAClB;IAEA,MAAMC,aAAa,GAAG,MAAM1B,kBAAkB,CAAC2B,KAAK,CAClD3B,kBAAkB,CAAC4B,WAAW,CAACC,sBACjC,CAAC;IACD,IAAIH,aAAa,EAAE;MACjB,OAAO,SAAS;IAClB;IAEA,MAAMb,OAAO,GAAG,MAAMb,kBAAkB,CAAC8B,OAAO,CAC9C9B,kBAAkB,CAAC4B,WAAW,CAACC,sBAAsB,EACrD;MACEE,KAAK,EAAE,oBAAoB;MAC3BC,OAAO,EAAE,0DAA0D;MACnEC,cAAc,EAAE;IAClB,CACF,CAAC;IACD,OAAOpB,OAAO;EAChB,CAAC,CAAC,OAAOqB,GAAG,EAAE;IACZC,OAAO,CAACC,IAAI,CAACF,GAAG,CAAC;IACjB,OAAOlC,kBAAkB,CAACe,OAAO,CAACsB,MAAM;EAC1C;AACF,CAAC","ignoreList":[]}
@@ -1,6 +1,9 @@
1
1
  import type { TurboModule } from 'react-native';
2
2
  export interface Spec extends TurboModule {
3
3
  localDownload(uri: string): Promise<void>;
4
+ getContentFd(uri: string): Promise<string>;
5
+ closeFd(fd: string): Promise<void>;
6
+ persistContentPermission(uri: string): Promise<void>;
4
7
  }
5
8
  declare const _default: Spec;
6
9
  export default _default;
@@ -1 +1 @@
1
- {"version":3,"file":"NativeReactNativeLocalDownload.d.ts","sourceRoot":"","sources":["../../../src/NativeReactNativeLocalDownload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1C;;AAED,wBAAkF"}
1
+ {"version":3,"file":"NativeReactNativeLocalDownload.d.ts","sourceRoot":"","sources":["../../../src/NativeReactNativeLocalDownload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3C,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD;;AAED,wBAEE"}
@@ -1,4 +1,7 @@
1
1
  import { PermissionsAndroid } from 'react-native';
2
+ export declare function getContentFd(uri: string): Promise<string | undefined>;
3
+ export declare function closeFd(uri: string): Promise<void>;
4
+ export declare function persistContentPermission(uri: string): Promise<void>;
2
5
  /**
3
6
  *
4
7
  * @param uri path to content to be sent to downloads
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAA4B,MAAM,cAAc,CAAA;AAC3E;;;;;GAKG;AACH,wBAAsB,aAAa,CAC/B,GAAG,EAAE,MAAM,EACX,kBAAkB,CAAC,EAAE,MAAM,IAAI,GAAG,SAAS,GAC5C,OAAO,CAAC,IAAI,CAAC,CAkBf;AAED,eAAO,MAAM,wBAAwB,QAAa,OAAO,CACrD,OAAO,kBAAkB,CAAC,OAAO,CAAC,MAAM,CA6B3C,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAA4B,MAAM,cAAc,CAAC;AAE5E,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,+BAM7C;AAED,wBAAsB,OAAO,CAAC,GAAG,EAAE,MAAM,iBAGxC;AAED,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,MAAM,iBAGzD;AACD;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,kBAAkB,CAAC,EAAE,MAAM,IAAI,GAAG,SAAS,GAC1C,OAAO,CAAC,IAAI,CAAC,CAkBf;AAED,eAAO,MAAM,wBAAwB,QAAa,OAAO,CACvD,OAAO,kBAAkB,CAAC,OAAO,CAAC,MAAM,CA6BzC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vali98/react-native-fs",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Download folder access for react-native",
5
5
  "source": "./src/index.tsx",
6
6
  "main": "./lib/module/index.js",
@@ -2,7 +2,12 @@ import type { TurboModule } from 'react-native';
2
2
  import { TurboModuleRegistry } from 'react-native';
3
3
 
4
4
  export interface Spec extends TurboModule {
5
- localDownload(uri: string): Promise<void>
5
+ localDownload(uri: string): Promise<void>;
6
+ getContentFd(uri: string): Promise<string>;
7
+ closeFd(fd: string): Promise<void>;
8
+ persistContentPermission(uri: string): Promise<void>;
6
9
  }
7
10
 
8
- export default TurboModuleRegistry.getEnforcing<Spec>('ReactNativeLocalDownload');
11
+ export default TurboModuleRegistry.getEnforcing<Spec>(
12
+ 'ReactNativeLocalDownload'
13
+ );
package/src/index.tsx CHANGED
@@ -1,6 +1,24 @@
1
1
  import ReactNativeLocalDownload from './NativeReactNativeLocalDownload';
2
2
 
3
- import { PermissionsAndroid, Platform, Linking, Alert } from 'react-native'
3
+ import { PermissionsAndroid, Platform, Linking, Alert } from 'react-native';
4
+
5
+ export async function getContentFd(uri: string) {
6
+ if (Platform.OS !== 'android') return;
7
+ if (!uri.includes('content://')) {
8
+ throw new TypeError('URI provided is not a content URI');
9
+ }
10
+ return ReactNativeLocalDownload.getContentFd(uri);
11
+ }
12
+
13
+ export async function closeFd(uri: string) {
14
+ if (Platform.OS !== 'android') return;
15
+ return ReactNativeLocalDownload.closeFd(uri);
16
+ }
17
+
18
+ export async function persistContentPermission(uri: string) {
19
+ if (Platform.OS !== 'android') return;
20
+ return ReactNativeLocalDownload.persistContentPermission(uri);
21
+ }
4
22
  /**
5
23
  *
6
24
  * @param uri path to content to be sent to downloads
@@ -8,57 +26,56 @@ import { PermissionsAndroid, Platform, Linking, Alert } from 'react-native'
8
26
  * @returns
9
27
  */
10
28
  export async function localDownload(
11
- uri: string,
12
- never_ask_function?: () => void | undefined
29
+ uri: string,
30
+ never_ask_function?: () => void | undefined
13
31
  ): Promise<void> {
14
- const granted = await requestStoragePermission()
15
- if (granted !== 'granted') {
16
- if (granted === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN) {
17
- if (!never_ask_function)
18
- Alert.alert(
19
- 'Permission Required',
20
- 'You have permanently denied storage access. Please enable it from settings.',
21
- [
22
- { text: 'Cancel', style: 'cancel' },
23
- { text: 'Open Settings', onPress: () => Linking.openSettings() },
24
- ]
25
- )
26
- else never_ask_function()
27
- }
28
- throw new Error('Permissions not granted')
32
+ const granted = await requestStoragePermission();
33
+ if (granted !== 'granted') {
34
+ if (granted === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN) {
35
+ if (!never_ask_function)
36
+ Alert.alert(
37
+ 'Permission Required',
38
+ 'You have permanently denied storage access. Please enable it from settings.',
39
+ [
40
+ { text: 'Cancel', style: 'cancel' },
41
+ { text: 'Open Settings', onPress: () => Linking.openSettings() },
42
+ ]
43
+ );
44
+ else never_ask_function();
29
45
  }
30
- return ReactNativeLocalDownload.localDownload(uri)
46
+ throw new Error('Permissions not granted');
47
+ }
48
+ return ReactNativeLocalDownload.localDownload(uri);
31
49
  }
32
50
 
33
51
  export const requestStoragePermission = async (): Promise<
34
- typeof PermissionsAndroid.RESULTS.DENIED
52
+ typeof PermissionsAndroid.RESULTS.DENIED
35
53
  > => {
36
- if (Platform.OS !== 'android') return PermissionsAndroid.RESULTS.GRANTED
37
-
38
- try {
39
- if (Number(Platform.Version) >= 33) {
40
- return 'granted'
41
- }
54
+ if (Platform.OS !== 'android') return PermissionsAndroid.RESULTS.GRANTED;
42
55
 
43
- const hasPermission = await PermissionsAndroid.check(
44
- PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE
45
- )
46
- if (hasPermission) {
47
- return 'granted'
48
- }
56
+ try {
57
+ if (Number(Platform.Version) >= 33) {
58
+ return 'granted';
59
+ }
49
60
 
50
- const granted = await PermissionsAndroid.request(
51
- PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
52
- {
53
- title: 'Storage Permission',
54
- message: 'This app needs access to your storage to download files.',
55
- buttonPositive: 'OK',
56
- }
57
- )
58
- return granted
59
- } catch (err) {
60
- console.warn(err)
61
- return PermissionsAndroid.RESULTS.DENIED
61
+ const hasPermission = await PermissionsAndroid.check(
62
+ PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE
63
+ );
64
+ if (hasPermission) {
65
+ return 'granted';
62
66
  }
63
- }
64
67
 
68
+ const granted = await PermissionsAndroid.request(
69
+ PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
70
+ {
71
+ title: 'Storage Permission',
72
+ message: 'This app needs access to your storage to download files.',
73
+ buttonPositive: 'OK',
74
+ }
75
+ );
76
+ return granted;
77
+ } catch (err) {
78
+ console.warn(err);
79
+ return PermissionsAndroid.RESULTS.DENIED;
80
+ }
81
+ };