@react-native-documents/picker 11.0.4 → 12.0.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.
Files changed (37) hide show
  1. package/android/src/main/java/com/reactnativedocumentpicker/CopyDestination.kt +1 -2
  2. package/android/src/main/java/com/reactnativedocumentpicker/DocumentMetadataBuilder.kt +4 -3
  3. package/android/src/main/java/com/reactnativedocumentpicker/FileOperations.kt +2 -2
  4. package/android/src/main/java/com/reactnativedocumentpicker/IsKnownTypeImpl.kt +26 -28
  5. package/android/src/main/java/com/reactnativedocumentpicker/MetadataGetter.kt +69 -45
  6. package/android/src/main/java/com/reactnativedocumentpicker/PickOptions.kt +43 -23
  7. package/android/src/main/java/com/reactnativedocumentpicker/PromiseWrapper.kt +83 -0
  8. package/android/src/main/java/com/reactnativedocumentpicker/RNDocumentPickerModule.kt +5 -5
  9. package/ios/RNDocumentPicker.mm +19 -41
  10. package/ios/swift/DocPicker.swift +46 -28
  11. package/ios/swift/DocSaver.swift +18 -23
  12. package/ios/swift/DocumentMetadataBuilder.swift +1 -1
  13. package/ios/swift/FileOperations.swift +21 -19
  14. package/ios/swift/IsKnownTypeImpl.swift +16 -10
  15. package/ios/swift/LocalCopyResponse.swift +3 -5
  16. package/ios/swift/PickerBase.swift +31 -49
  17. package/ios/swift/PickerOptions.swift +53 -23
  18. package/ios/swift/PromiseWrapper.swift +71 -58
  19. package/ios/swift/SaverOptions.swift +39 -17
  20. package/jest/build/src/errors.js +2 -0
  21. package/lib/module/errors.js +3 -2
  22. package/lib/module/errors.js.map +1 -1
  23. package/lib/module/release.js +1 -1
  24. package/lib/typescript/src/errors.d.ts +6 -3
  25. package/lib/typescript/src/errors.d.ts.map +1 -1
  26. package/lib/typescript/src/release.d.ts +1 -1
  27. package/lib/typescript/src/saveDocuments.d.ts +1 -1
  28. package/lib/typescript/src/types.d.ts +3 -3
  29. package/package.json +3 -2
  30. package/react-native-document-picker.podspec +1 -0
  31. package/src/errors.ts +8 -4
  32. package/src/release.ts +1 -1
  33. package/src/saveDocuments.ts +1 -1
  34. package/src/types.ts +3 -3
  35. package/android/src/main/java/com/reactnativedocumentpicker/IntentFactory.kt +0 -36
  36. package/android/src/main/java/com/reactnativedocumentpicker/PromiseWrapper.java +0 -105
  37. package/ios/swift/PromiseSupport.swift +0 -2
@@ -6,7 +6,6 @@ enum class CopyDestination(val preset: String) {
6
6
  DOCUMENT_DIRECTORY("documentDirectory");
7
7
 
8
8
  companion object {
9
- // keep values() for RN 73 compatibility
10
- fun fromPath(path: String): CopyDestination = values().find { it.preset == path } ?: CACHES_DIRECTORY
9
+ fun fromPath(path: String): CopyDestination = entries.find { it.preset == path } ?: CACHES_DIRECTORY
11
10
  }
12
11
  }
@@ -52,10 +52,11 @@ class DocumentMetadataBuilder(forUri: Uri) {
52
52
  openableMimeTypes?.let {
53
53
  val arrayOfExtensionsAndMime = Arguments.createArray()
54
54
  it.forEach { mimeType ->
55
- val virtualFileDetails = Arguments.createMap()
56
55
  val maybeExtension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType)
57
- virtualFileDetails.putString("mimeType", mimeType)
58
- virtualFileDetails.putString("extension", maybeExtension)
56
+ val virtualFileDetails = Arguments.createMap().apply {
57
+ putString("mimeType", mimeType)
58
+ putString("extension", maybeExtension)
59
+ }
59
60
  arrayOfExtensionsAndMime.pushMap(virtualFileDetails)
60
61
  }
61
62
  map.putArray("convertibleToMimeTypes", arrayOfExtensionsAndMime)
@@ -189,8 +189,8 @@ class FileOperations(private val uriMap: MutableMap<String, Uri>) {
189
189
  }
190
190
 
191
191
  val copyStreamToAnother: (InputStream, OutputStream) -> Long = { inputStream, outputStream ->
192
- inputStream.use { input ->
193
- outputStream.use { output ->
192
+ inputStream.use { _ ->
193
+ outputStream.use { _ ->
194
194
  val bytesCopied = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
195
195
  FileUtils.copy(inputStream, outputStream)
196
196
  } else {
@@ -4,37 +4,35 @@ import android.webkit.MimeTypeMap
4
4
  import com.facebook.react.bridge.Arguments
5
5
  import com.facebook.react.bridge.WritableMap
6
6
 
7
- class IsKnownTypeImpl {
8
- companion object {
9
- fun isKnownType(kind: String, value: String): WritableMap {
10
- return when (kind) {
11
- "mimeType" -> {
12
- val extensionForMime = MimeTypeMap.getSingleton().getExtensionFromMimeType(value)
13
- createMap(
14
- isKnown = extensionForMime != null,
15
- preferredFilenameExtension = extensionForMime,
16
- mimeType = if (extensionForMime != null) value else null
17
- )
18
- }
19
- "extension" -> {
20
- val mimeForExtension = MimeTypeMap.getSingleton().getMimeTypeFromExtension(value)
21
- createMap(
22
- isKnown = mimeForExtension != null,
23
- preferredFilenameExtension = if (mimeForExtension != null) value else null,
24
- mimeType = mimeForExtension
25
- )
26
- }
27
- else -> createMap(isKnown = false, preferredFilenameExtension = null, mimeType = null)
7
+ object IsKnownTypeImpl {
8
+ fun isKnownType(kind: String, value: String): WritableMap {
9
+ return when (kind) {
10
+ "mimeType" -> {
11
+ val extensionForMime = MimeTypeMap.getSingleton().getExtensionFromMimeType(value)
12
+ createMap(
13
+ isKnown = extensionForMime != null,
14
+ preferredFilenameExtension = extensionForMime,
15
+ mimeType = if (extensionForMime != null) value else null
16
+ )
28
17
  }
18
+ "extension" -> {
19
+ val mimeForExtension = MimeTypeMap.getSingleton().getMimeTypeFromExtension(value)
20
+ createMap(
21
+ isKnown = mimeForExtension != null,
22
+ preferredFilenameExtension = if (mimeForExtension != null) value else null,
23
+ mimeType = mimeForExtension
24
+ )
25
+ }
26
+ else -> createMap(isKnown = false, preferredFilenameExtension = null, mimeType = null)
29
27
  }
28
+ }
30
29
 
31
- private fun createMap(isKnown: Boolean, preferredFilenameExtension: String?, mimeType: String?): WritableMap {
32
- return Arguments.createMap().apply {
33
- putNull("UTType")
34
- putBoolean("isKnown", isKnown)
35
- putString("preferredFilenameExtension", preferredFilenameExtension)
36
- putString("mimeType", mimeType)
37
- }
30
+ private fun createMap(isKnown: Boolean, preferredFilenameExtension: String?, mimeType: String?): WritableMap {
31
+ return Arguments.createMap().apply {
32
+ putNull("UTType")
33
+ putBoolean("isKnown", isKnown)
34
+ putString("preferredFilenameExtension", preferredFilenameExtension)
35
+ putString("mimeType", mimeType)
38
36
  }
39
37
  }
40
38
  }
@@ -75,17 +75,39 @@ class MetadataGetter(private val uriMap: MutableMap<String, Uri>) {
75
75
  contentResolver: ContentResolver,
76
76
  metadataBuilder: DocumentMetadataBuilder,
77
77
  couldBeVirtualFile: Boolean
78
+ ) {
79
+ try {
80
+ queryContentResolverMetadataInternal(contentResolver, metadataBuilder, couldBeVirtualFile)
81
+ } catch (e: Exception) {
82
+ val suppressedSummary =
83
+ e.suppressed.joinToString(separator = "; ") { suppressed ->
84
+ "${suppressed.javaClass.simpleName}: ${suppressed.message ?: "no message"}"
85
+ }
86
+ metadataBuilder.metadataReadingError(
87
+ "Could not read file metadata: ${e.javaClass.simpleName}: ${e.message ?: "no message"}" +
88
+ (" (suppressed summary: [$suppressedSummary])")
89
+ )
90
+ }
91
+ }
92
+
93
+ private fun queryContentResolverMetadataInternal(
94
+ contentResolver: ContentResolver,
95
+ metadataBuilder: DocumentMetadataBuilder,
96
+ couldBeVirtualFile: Boolean
78
97
  ) {
79
98
  val forUri = metadataBuilder.getUri()
99
+ val hasNoMime = !metadataBuilder.hasMime()
80
100
 
81
101
  val projection = mutableListOf(
82
- DocumentsContract.Document.COLUMN_MIME_TYPE,
83
102
  OpenableColumns.DISPLAY_NAME,
84
103
  OpenableColumns.SIZE,
85
104
  ).apply {
86
105
  if (couldBeVirtualFile) {
87
106
  add(DocumentsContract.Document.COLUMN_FLAGS)
88
107
  }
108
+ if (hasNoMime) {
109
+ add(DocumentsContract.Document.COLUMN_MIME_TYPE)
110
+ }
89
111
  }.toTypedArray()
90
112
 
91
113
  contentResolver
@@ -97,39 +119,41 @@ class MetadataGetter(private val uriMap: MutableMap<String, Uri>) {
97
119
  null
98
120
  )
99
121
  .use { cursor ->
100
- if (cursor != null && cursor.moveToFirst()) {
101
- metadataBuilder.name(
102
- getCursorValue(cursor, OpenableColumns.DISPLAY_NAME, String::class.java)
103
- )
104
-
105
- if (!metadataBuilder.hasMime()) {
106
- metadataBuilder.mimeType(
107
- getCursorValue(
108
- cursor, DocumentsContract.Document.COLUMN_MIME_TYPE, String::class.java
109
- )
122
+ if (cursor == null) {
123
+ metadataBuilder.metadataReadingError("Could not read file metadata because cursor was null. This is likely an issue with the underlying ContentProvider.")
124
+ return
125
+ }
126
+ if (!cursor.moveToFirst()) {
127
+ metadataBuilder.metadataReadingError("Could not read file metadata because cursor could not move to the first result row. This is likely an issue with the underlying ContentProvider. Row count: ${cursor.count}, columns: ${cursor.columnNames.joinToString(",")}")
128
+ return
129
+ }
130
+ metadataBuilder.name(
131
+ getCursorValue(cursor, OpenableColumns.DISPLAY_NAME, String::class.java)
132
+ )
133
+ metadataBuilder.size(getCursorValue(cursor, OpenableColumns.SIZE, Long::class.java))
134
+
135
+ if (hasNoMime) {
136
+ metadataBuilder.mimeType(
137
+ getCursorValue(
138
+ cursor, DocumentsContract.Document.COLUMN_MIME_TYPE, String::class.java
110
139
  )
111
- }
140
+ )
141
+ }
112
142
 
113
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
114
- // https://developer.android.com/training/data-storage/shared/documents-files#open-virtual-file
115
- val isVirtual =
116
- if (couldBeVirtualFile) {
117
- val cursorValue: Int =
118
- getCursorValue(
119
- cursor, DocumentsContract.Document.COLUMN_FLAGS, Int::class.java
120
- )
121
- ?: 0
122
- cursorValue and DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT != 0
123
- } else {
124
- false
125
- }
126
- metadataBuilder.virtual(isVirtual)
127
- }
128
- metadataBuilder.size(getCursorValue(cursor, OpenableColumns.SIZE, Long::class.java))
129
- } else {
130
- // metadataBuilder only contains the uri, type and error in this unlikely case
131
- // there's nothing more we can do
132
- metadataBuilder.metadataReadingError("Could not read file metadata")
143
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
144
+ // https://developer.android.com/training/data-storage/shared/documents-files#open-virtual-file
145
+ val isVirtual =
146
+ if (couldBeVirtualFile) {
147
+ val cursorValue: Int =
148
+ getCursorValue(
149
+ cursor, DocumentsContract.Document.COLUMN_FLAGS, Int::class.java
150
+ )
151
+ ?: 0
152
+ cursorValue and DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT != 0
153
+ } else {
154
+ false
155
+ }
156
+ metadataBuilder.virtual(isVirtual)
133
157
  }
134
158
  }
135
159
  }
@@ -137,19 +161,19 @@ class MetadataGetter(private val uriMap: MutableMap<String, Uri>) {
137
161
  @Suppress("UNCHECKED_CAST")
138
162
  private fun <T> getCursorValue(cursor: Cursor, columnName: String, valueType: Class<T>): T? {
139
163
  val columnIndex = cursor.getColumnIndex(columnName)
140
- if (columnIndex != -1 && !cursor.isNull(columnIndex)) {
141
- return runCatching {
142
- when (valueType) {
143
- String::class.java -> cursor.getString(columnIndex) as T
144
- Int::class.java -> cursor.getInt(columnIndex) as T
145
- Long::class.java -> cursor.getLong(columnIndex) as T
146
- Double::class.java -> cursor.getDouble(columnIndex) as T
147
- Float::class.java -> cursor.getFloat(columnIndex) as T
148
- else -> null
149
- }
150
- // throw should not happen but if it does, we return null
151
- }.getOrNull()
164
+ if (columnIndex == -1 || cursor.isNull(columnIndex)) {
165
+ return null
152
166
  }
153
- return null
167
+ return runCatching {
168
+ when (valueType) {
169
+ String::class.java -> cursor.getString(columnIndex) as T
170
+ Int::class.java -> cursor.getInt(columnIndex) as T
171
+ Long::class.java -> cursor.getLong(columnIndex) as T
172
+ Double::class.java -> cursor.getDouble(columnIndex) as T
173
+ Float::class.java -> cursor.getFloat(columnIndex) as T
174
+ else -> null
175
+ }
176
+ // throw should not happen but if it does, we return null
177
+ }.getOrNull()
154
178
  }
155
179
  }
@@ -2,6 +2,8 @@
2
2
  package com.reactnativedocumentpicker
3
3
 
4
4
  import android.content.Intent
5
+ import android.os.Build
6
+ import android.provider.DocumentsContract
5
7
  import com.facebook.react.bridge.ReadableArray
6
8
  import com.facebook.react.bridge.ReadableMap
7
9
 
@@ -14,6 +16,20 @@ data class PickOptions(
14
16
  val requestLongTermAccess: Boolean,
15
17
  val allowVirtualFiles: Boolean,
16
18
  ) {
19
+ constructor(readableMap: ReadableMap) : this(
20
+ mode = readableMap.getString("mode"),
21
+ mimeTypes = if (readableMap.hasKey("type") && !readableMap.isNull("type")) {
22
+ readableMap.getArray("type")?.let { readableArrayToStringArray(it) } ?: arrayOf("*/*")
23
+ } else {
24
+ arrayOf("*/*")
25
+ },
26
+ initialDirectoryUrl = if (readableMap.hasKey("initialDirectoryUrl")) readableMap.getString("initialDirectoryUrl") else null,
27
+ localOnly = readableMap.hasKey("localOnly") && readableMap.getBoolean("localOnly"),
28
+ multiple = readableMap.hasKey("allowMultiSelection") && readableMap.getBoolean("allowMultiSelection"),
29
+ requestLongTermAccess = readableMap.hasKey("requestLongTermAccess") && readableMap.getBoolean("requestLongTermAccess"),
30
+ allowVirtualFiles = readableMap.hasKey("allowVirtualFiles") && readableMap.getBoolean("allowVirtualFiles")
31
+ )
32
+
17
33
  val action: String
18
34
  get() = if ("open" == mode) Intent.ACTION_OPEN_DOCUMENT else Intent.ACTION_GET_CONTENT
19
35
 
@@ -26,33 +42,37 @@ data class PickOptions(
26
42
  mimeTypes.joinToString("|")
27
43
  }
28
44
  }
29
- }
30
45
 
31
- fun parsePickOptions(readableMap: ReadableMap): PickOptions {
32
- val mode = readableMap.getString("mode")
46
+ fun getPickIntent(): Intent {
47
+ // TODO option for extra task on stack?
48
+ // reminder - flags are for granting rights to others
33
49
 
34
- val mimeTypes = if (readableMap.hasKey("type") && !readableMap.isNull("type")) {
35
- readableMap.getArray("type")?.let { readableArrayToStringArray(it) } ?: arrayOf("*/*")
36
- } else {
37
- arrayOf("*/*")
38
- }
50
+ return Intent(action).apply {
51
+ val types = mimeTypes
39
52
 
40
- val initialDirectoryUrl = if (readableMap.hasKey("initialDirectoryUrl")) readableMap.getString("initialDirectoryUrl") else null
41
- val localOnly = readableMap.hasKey("localOnly") && readableMap.getBoolean("localOnly")
42
- val multiple = readableMap.hasKey("allowMultiSelection") && readableMap.getBoolean("allowMultiSelection")
43
- val requestLongTermAccess = readableMap.hasKey("requestLongTermAccess") && readableMap.getBoolean("requestLongTermAccess")
44
- val allowVirtualFiles = readableMap.hasKey("allowVirtualFiles") && readableMap.getBoolean("allowVirtualFiles")
45
-
46
- return PickOptions(
47
- mode = mode,
48
- mimeTypes = mimeTypes,
49
- initialDirectoryUrl = initialDirectoryUrl,
50
- localOnly = localOnly,
51
- multiple = multiple,
52
- requestLongTermAccess = requestLongTermAccess,
53
- allowVirtualFiles = allowVirtualFiles,
54
- )
53
+ type =
54
+ if (types.size > 1) {
55
+ putExtra(Intent.EXTRA_MIME_TYPES, types)
56
+ intentFilterTypes
57
+ } else {
58
+ types[0]
59
+ }
60
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
61
+ initialDirectoryUrl != null
62
+ ) {
63
+ // only works for ACTION_OPEN_DOCUMENT
64
+ // TODO must be URI
65
+ putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialDirectoryUrl)
66
+ }
67
+ if (!allowVirtualFiles) {
68
+ addCategory(Intent.CATEGORY_OPENABLE)
69
+ }
70
+ putExtra(Intent.EXTRA_LOCAL_ONLY, localOnly)
71
+ putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiple)
72
+ }
73
+ }
55
74
  }
75
+
56
76
  fun readableArrayToStringArray(readableArray: ReadableArray): Array<String> {
57
77
  /**
58
78
  * MIME type and Uri scheme matching in the
@@ -0,0 +1,83 @@
1
+ // LICENSE: see License.md in the package root
2
+ package com.reactnativedocumentpicker
3
+
4
+ import android.util.Log
5
+ import com.facebook.react.bridge.Promise
6
+
7
+ class PromiseWrapper(private val MODULE_NAME: String) {
8
+ private var promise: Promise? = null
9
+ private var nameOfCallInProgress: String? = null
10
+
11
+ fun trySetPromiseRejectingIncoming(promise: Promise, fromCallsite: String): Boolean {
12
+ val previousPromise = this.promise
13
+ if (previousPromise != null) {
14
+ rejectNewPromiseBecauseOldOneIsInProgress(promise, fromCallsite)
15
+ return false
16
+ }
17
+ this.promise = promise
18
+ nameOfCallInProgress = fromCallsite
19
+ return true
20
+ }
21
+
22
+ fun resolve(value: Any?) {
23
+ val resolver = promise
24
+ if (resolver == null) {
25
+ Log.e(MODULE_NAME, "cannot resolve promise because it's null")
26
+ return
27
+ }
28
+
29
+ resetMembers()
30
+ resolver.resolve(value)
31
+ }
32
+
33
+ fun reject(code: String, e: Exception) {
34
+ val message =
35
+ e.localizedMessage ?: e.message ?: "unknown error"
36
+
37
+ this.reject(code, message, e)
38
+ }
39
+
40
+ fun reject(e: Exception) {
41
+ val message =
42
+ e.localizedMessage ?: e.message ?: "unknown error"
43
+
44
+ this.reject(nameOfCallInProgress ?: "unknown call in progress", message, e)
45
+ }
46
+
47
+ fun rejectAsUserCancelledOperation() {
48
+ this.reject(E_DOCUMENT_PICKER_CANCELED, "user canceled the document picker")
49
+ }
50
+
51
+ fun reject(code: String, message: String, e: Exception? = null) {
52
+ val rejecter = promise
53
+ if (rejecter == null) {
54
+ Log.e(MODULE_NAME, "cannot reject promise because it's null")
55
+ return
56
+ }
57
+
58
+ resetMembers()
59
+ rejecter.reject(code, message, e)
60
+ }
61
+
62
+ private fun resetMembers() {
63
+ nameOfCallInProgress = null
64
+ promise = null
65
+ }
66
+
67
+ private fun rejectNewPromiseBecauseOldOneIsInProgress(
68
+ promise: Promise,
69
+ requestedOperation: String?
70
+ ) {
71
+ // TODO better message
72
+ promise.reject(
73
+ ASYNC_OP_IN_PROGRESS,
74
+ "Warning: previous promise did not settle and you attempted to overwrite it. " +
75
+ "You've called \"" + requestedOperation + "\" while \"" + this.nameOfCallInProgress + "\" was already in progress and has not completed yet."
76
+ )
77
+ }
78
+
79
+ companion object {
80
+ const val ASYNC_OP_IN_PROGRESS: String = "ASYNC_OP_IN_PROGRESS"
81
+ const val E_DOCUMENT_PICKER_CANCELED: String = "OPERATION_CANCELED"
82
+ }
83
+ }
@@ -86,11 +86,11 @@ class RNDocumentPickerModule(reactContext: ReactApplicationContext) :
86
86
  if (!promiseWrapper.trySetPromiseRejectingIncoming(promise, "pick")) {
87
87
  return
88
88
  }
89
- val options = parsePickOptions(opts)
89
+ val options = PickOptions(opts)
90
90
  currentPickOptions = options
91
91
 
92
92
  try {
93
- val intent = IntentFactory.getPickIntent(options)
93
+ val intent = options.getPickIntent()
94
94
  currentActivity.startActivityForResult(intent, PICK_FILES_REQUEST_CODE)
95
95
  } catch (e: ActivityNotFoundException) {
96
96
  promiseWrapper.reject(UNABLE_TO_OPEN_FILE_TYPE, e)
@@ -140,7 +140,7 @@ class RNDocumentPickerModule(reactContext: ReactApplicationContext) :
140
140
  if (!promiseWrapper.trySetPromiseRejectingIncoming(promise, "pickDirectory")) {
141
141
  return
142
142
  }
143
- val options = parsePickOptions(opts)
143
+ val options = PickOptions(opts)
144
144
  currentPickOptions = options
145
145
  try {
146
146
  val intent =
@@ -316,13 +316,13 @@ class RNDocumentPickerModule(reactContext: ReactApplicationContext) :
316
316
 
317
317
  companion object {
318
318
  fun rejectWithNullActivity(promise: Promise) {
319
- promise.reject(PRESENTER_IS_NULL, PRESENTER_IS_NULL)
319
+ promise.reject(NULL_PRESENTER, "Current activity is null. Cannot present sign-in UI. Make sure there are no modal windows being presented or dismissed.")
320
320
  }
321
321
 
322
322
  private const val PICK_FILES_REQUEST_CODE = 41
323
323
  private const val PICK_DIR_REQUEST_CODE = 42
324
324
  private const val SAVE_DOC_REQUEST_CODE = 43
325
- private const val PRESENTER_IS_NULL = "NULL_PRESENTER"
325
+ private const val NULL_PRESENTER = "NULL_PRESENTER"
326
326
  private const val UNABLE_TO_OPEN_FILE_TYPE = "UNABLE_TO_OPEN_FILE_TYPE"
327
327
  private const val E_OTHER_PRESENTING_ERROR = "OTHER_PRESENTING_ERROR"
328
328
  private const val E_INVALID_DATA_RETURNED = "INVALID_DATA_RETURNED"
@@ -18,20 +18,23 @@
18
18
  @end
19
19
 
20
20
  @implementation RNDocumentPicker {
21
- DocPicker *docPicker;
22
- DocSaver *docSaver;
21
+ DocPicker *_docPicker;
22
+ DocSaver *_docSaver;
23
23
  }
24
24
 
25
- - (instancetype)init {
26
- if ((self = [super init])) {
27
- docPicker = [DocPicker new];
28
- docSaver = [DocSaver new];
25
+ // initialization happens on serial queue so there are no races
26
+ - (DocPicker *)docPicker {
27
+ if (!_docPicker) {
28
+ _docPicker = [DocPicker new];
29
29
  }
30
- return self;
30
+ return _docPicker;
31
31
  }
32
32
 
33
- + (BOOL)requiresMainQueueSetup {
34
- return NO;
33
+ - (DocSaver *)docSaver {
34
+ if (!_docSaver) {
35
+ _docSaver = [DocSaver new];
36
+ }
37
+ return _docSaver;
35
38
  }
36
39
 
37
40
  RCT_EXPORT_MODULE()
@@ -43,26 +46,9 @@ RCT_EXPORT_METHOD(pick:
43
46
  reject:
44
47
  (RCTPromiseRejectBlock) reject)
45
48
  {
46
- // https://stackoverflow.com/questions/5270519/what-is-difference-between-uimodaltransitionstyle-and-uimodalpresentationstyle
47
- UIModalPresentationStyle presentationStyle = [RCTConvert UIModalPresentationStyle:options[@"presentationStyle"]];
48
- UIModalTransitionStyle transitionStyle = [RCTConvert UIModalTransitionStyle:options[@"transitionStyle"]];
49
- NSArray *allowedUTIs = [RCTConvert NSArray:options[@"type"]];
50
- BOOL allowMultiple = [RCTConvert BOOL:options[@"allowMultiSelection"]];
51
- BOOL showExtensions = [RCTConvert BOOL:options[@"showFileExtensions"]];
52
- NSString *mode = options[@"mode"];
53
- NSString *initialDir = options[@"initialDirectoryUrl"];
54
- BOOL requestLongTermAccess = [RCTConvert BOOL:options[@"requestLongTermAccess"]];
55
-
56
- PickerOptions *pickerOptions = [[PickerOptions alloc] initWithTypes:allowedUTIs
57
- mode:mode
58
- initialDirectoryUrl:initialDir
59
- allowMultiSelection:allowMultiple
60
- shouldShowFileExtensions:showExtensions
61
- transitionStyle:transitionStyle
62
- presentationStyle:presentationStyle
63
- requestLongTermAccess:requestLongTermAccess];
64
-
65
- [docPicker presentWithOptions:pickerOptions resolve:resolve reject:reject];
49
+ dispatch_async(dispatch_get_main_queue(), ^{
50
+ [self.docPicker presentWithOptionsDict:options resolve:resolve reject:reject];
51
+ });
66
52
  }
67
53
 
68
54
  RCT_EXPORT_METHOD(pickDirectory:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
@@ -92,21 +78,13 @@ RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD(NSDictionary *, isKnownType:(NSString *)kind
92
78
  }
93
79
 
94
80
  RCT_EXPORT_METHOD(writeDocuments:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
95
- UIModalPresentationStyle presentationStyle = [RCTConvert UIModalPresentationStyle:options[@"presentationStyle"]];
96
- UIModalTransitionStyle transitionStyle = [RCTConvert UIModalTransitionStyle:options[@"transitionStyle"]];
97
- BOOL showExtensions = [RCTConvert BOOL:options[@"showFileExtensions"]];
98
- BOOL asCopy = [RCTConvert BOOL:options[@"copy"]];
99
-
100
- NSString *initialDir = options[@"initialDirectoryUri"];
101
- NSArray<NSString*> *documentUrl = options[@"sourceUris"];
102
-
103
- SaverOptions* saverOptions = [[SaverOptions alloc] initWithSourceUrlStrings:documentUrl asCopy:asCopy initialDirectoryUrl:initialDir shouldShowFileExtensions:showExtensions transitionStyle:transitionStyle presentationStyle:presentationStyle];
104
-
105
- [docSaver presentWithOptions:saverOptions resolve:resolve reject:reject];
81
+ dispatch_async(dispatch_get_main_queue(), ^{
82
+ [self.docSaver presentWithOptionsDict:options resolve:resolve reject:reject];
83
+ });
106
84
  }
107
85
 
108
86
  RCT_EXPORT_METHOD(releaseSecureAccess:(NSArray *)uris resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
109
- [docPicker stopAccessingOpenedUrls:uris];
87
+ [self.docPicker stopAccessingOpenedUrls:uris];
110
88
  resolve([NSNull null]);
111
89
  }
112
90