expo-document-picker 11.2.1 → 11.3.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/CHANGELOG.md +13 -0
- package/README.md +1 -1
- package/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/documentpicker/DocumentPickerModule.kt +57 -19
- package/android/src/main/java/expo/modules/documentpicker/DocumentPickerOptions.kt +4 -1
- package/android/src/main/java/expo/modules/documentpicker/DocumentPickerResults.kt +6 -6
- package/build/ExpoDocumentPicker.web.d.ts +2 -2
- package/build/ExpoDocumentPicker.web.d.ts.map +1 -1
- package/build/ExpoDocumentPicker.web.js +4 -2
- package/build/ExpoDocumentPicker.web.js.map +1 -1
- package/build/index.d.ts +3 -3
- package/build/index.d.ts.map +1 -1
- package/build/index.js +34 -1
- package/build/index.js.map +1 -1
- package/build/types.d.ts +57 -14
- package/build/types.d.ts.map +1 -1
- package/build/types.js.map +1 -1
- package/expo-module.config.json +3 -0
- package/ios/DocumentPickerModule.swift +172 -0
- package/ios/DocumentPickerOptions.swift +68 -0
- package/ios/DocumentPickerResponse.swift +13 -0
- package/ios/DocumentPickingDelegate.swift +26 -0
- package/ios/Exceptions.swift +19 -0
- package/ios/{EXDocumentPicker.podspec → ExpoDocumentPicker.podspec} +10 -3
- package/package.json +2 -2
- package/src/ExpoDocumentPicker.web.ts +6 -4
- package/src/index.ts +43 -4
- package/src/types.ts +91 -47
- package/ios/EXDocumentPicker/EXDocumentPickerModule.h +0 -7
- package/ios/EXDocumentPicker/EXDocumentPickerModule.m +0 -257
- package/unimodule.json +0 -4
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,19 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 11.3.0 — 2023-04-14
|
|
14
|
+
|
|
15
|
+
### 🎉 New features
|
|
16
|
+
|
|
17
|
+
- Migrated to Expo Modules API. ([#20336](https://github.com/expo/expo/pull/20336) by [@alanhughes](https://github.com/alanjhughes))
|
|
18
|
+
- Added support for picking multiple documents. ([#20365](https://github.com/expo/expo/pull/20365) by [@alanhughes](https://github.com/alanjhughes))
|
|
19
|
+
|
|
20
|
+
## 11.2.2 - 2023-03-28
|
|
21
|
+
|
|
22
|
+
### 🐛 Bug fixes
|
|
23
|
+
|
|
24
|
+
- Fixed cancellation type not being marked as a `Record` on Android. ([#21588](https://github.com/expo/expo/pull/21588) by [@alanjhughes](https://github.com/alanjhughes))
|
|
25
|
+
|
|
13
26
|
## 11.2.1 — 2023-02-09
|
|
14
27
|
|
|
15
28
|
_This version does not introduce any user-facing changes._
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Provides access to the system's UI for selecting documents from the available pr
|
|
|
9
9
|
|
|
10
10
|
# Installation in managed Expo projects
|
|
11
11
|
|
|
12
|
-
For [managed](https://docs.expo.dev/
|
|
12
|
+
For [managed](https://docs.expo.dev/archive/managed-vs-bare/) Expo projects, please follow the installation instructions in the [API documentation for the latest stable release](https://docs.expo.dev/versions/latest/sdk/document-picker/).
|
|
13
13
|
|
|
14
14
|
# Installation in bare React Native projects
|
|
15
15
|
|
package/android/build.gradle
CHANGED
|
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
|
|
|
3
3
|
apply plugin: 'maven-publish'
|
|
4
4
|
|
|
5
5
|
group = 'host.exp.exponent'
|
|
6
|
-
version = '11.
|
|
6
|
+
version = '11.3.0'
|
|
7
7
|
|
|
8
8
|
buildscript {
|
|
9
9
|
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
@@ -74,7 +74,7 @@ android {
|
|
|
74
74
|
minSdkVersion safeExtGet("minSdkVersion", 21)
|
|
75
75
|
targetSdkVersion safeExtGet("targetSdkVersion", 33)
|
|
76
76
|
versionCode 17
|
|
77
|
-
versionName '11.
|
|
77
|
+
versionName '11.3.0'
|
|
78
78
|
}
|
|
79
79
|
lintOptions {
|
|
80
80
|
abortOnError false
|
|
@@ -6,6 +6,7 @@ import android.content.Intent
|
|
|
6
6
|
import android.net.Uri
|
|
7
7
|
import expo.modules.core.utilities.FileUtilities
|
|
8
8
|
import expo.modules.kotlin.Promise
|
|
9
|
+
import expo.modules.kotlin.exception.CodedException
|
|
9
10
|
import expo.modules.kotlin.exception.Exceptions
|
|
10
11
|
import expo.modules.kotlin.modules.Module
|
|
11
12
|
import expo.modules.kotlin.modules.ModuleDefinition
|
|
@@ -36,6 +37,7 @@ class DocumentPickerModule : Module() {
|
|
|
36
37
|
copyToCacheDirectory = options.copyToCacheDirectory
|
|
37
38
|
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
|
38
39
|
addCategory(Intent.CATEGORY_OPENABLE)
|
|
40
|
+
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, options.multiple)
|
|
39
41
|
type = if (options.type.size > 1) {
|
|
40
42
|
putExtra(Intent.EXTRA_MIME_TYPES, options.type.toTypedArray())
|
|
41
43
|
"*/*"
|
|
@@ -54,31 +56,21 @@ class DocumentPickerModule : Module() {
|
|
|
54
56
|
val promise = pendingPromise!!
|
|
55
57
|
|
|
56
58
|
if (resultCode == Activity.RESULT_OK) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
originalDocumentDetails
|
|
59
|
+
try {
|
|
60
|
+
if (intent?.clipData != null) {
|
|
61
|
+
handleMultipleSelection(intent)
|
|
61
62
|
} else {
|
|
62
|
-
|
|
63
|
-
copyPath?.let {
|
|
64
|
-
originalDocumentDetails.copy(uri = it)
|
|
65
|
-
} ?: throw FailedToCopyToCacheException()
|
|
63
|
+
handleSingleSelection(intent)
|
|
66
64
|
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
uri = details.uri,
|
|
71
|
-
name = details.name,
|
|
72
|
-
mimeType = details.mimeType,
|
|
73
|
-
size = details.size
|
|
74
|
-
)
|
|
75
|
-
promise.resolve(result)
|
|
76
|
-
} ?: throw FailedToReadDocumentException()
|
|
65
|
+
} catch (e: CodedException) {
|
|
66
|
+
promise.resolve(e)
|
|
67
|
+
}
|
|
77
68
|
} else {
|
|
78
69
|
promise.resolve(
|
|
79
|
-
|
|
70
|
+
DocumentPickerResult(canceled = true)
|
|
80
71
|
)
|
|
81
72
|
}
|
|
73
|
+
|
|
82
74
|
pendingPromise = null
|
|
83
75
|
}
|
|
84
76
|
}
|
|
@@ -102,4 +94,50 @@ class DocumentPickerModule : Module() {
|
|
|
102
94
|
}
|
|
103
95
|
return Uri.fromFile(outputFile).toString()
|
|
104
96
|
}
|
|
97
|
+
|
|
98
|
+
private fun handleSingleSelection(intent: Intent?) {
|
|
99
|
+
intent?.data?.let { uri ->
|
|
100
|
+
val details = readDocumentDetails(uri)
|
|
101
|
+
val result = DocumentPickerResult(
|
|
102
|
+
assets = listOf(details),
|
|
103
|
+
)
|
|
104
|
+
pendingPromise?.resolve(result)
|
|
105
|
+
} ?: throw FailedToReadDocumentException()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private fun handleMultipleSelection(intent: Intent?) {
|
|
109
|
+
val count = intent?.clipData?.itemCount ?: 0
|
|
110
|
+
val assets = mutableListOf<DocumentInfo>()
|
|
111
|
+
|
|
112
|
+
for (i in 0 until count) {
|
|
113
|
+
val uri = intent?.clipData?.getItemAt(i)?.uri
|
|
114
|
+
?: throw FailedToReadDocumentException()
|
|
115
|
+
val document = readDocumentDetails(uri)
|
|
116
|
+
assets.add(document)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
pendingPromise?.resolve(DocumentPickerResult(assets = assets))
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private fun readDocumentDetails(uri: Uri): DocumentInfo {
|
|
123
|
+
val originalDocumentDetails = DocumentDetailsReader(context).read(uri)
|
|
124
|
+
|
|
125
|
+
val details = if (!copyToCacheDirectory || originalDocumentDetails == null) {
|
|
126
|
+
originalDocumentDetails
|
|
127
|
+
} else {
|
|
128
|
+
val copyPath = copyDocumentToCacheDirectory(uri, originalDocumentDetails.name)
|
|
129
|
+
copyPath?.let {
|
|
130
|
+
originalDocumentDetails.copy(uri = it)
|
|
131
|
+
} ?: throw FailedToCopyToCacheException()
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return details?.let { it ->
|
|
135
|
+
DocumentInfo(
|
|
136
|
+
uri = it.uri,
|
|
137
|
+
name = it.name,
|
|
138
|
+
mimeType = it.mimeType,
|
|
139
|
+
size = it.size
|
|
140
|
+
)
|
|
141
|
+
} ?: throw FailedToReadDocumentException()
|
|
142
|
+
}
|
|
105
143
|
}
|
|
@@ -5,8 +5,13 @@ import expo.modules.kotlin.records.Record
|
|
|
5
5
|
|
|
6
6
|
data class DocumentPickerResult(
|
|
7
7
|
@Field
|
|
8
|
-
val
|
|
8
|
+
val canceled: Boolean = false,
|
|
9
9
|
|
|
10
|
+
@Field
|
|
11
|
+
val assets: List<DocumentInfo>? = null
|
|
12
|
+
) : Record
|
|
13
|
+
|
|
14
|
+
data class DocumentInfo(
|
|
10
15
|
@Field
|
|
11
16
|
val uri: String,
|
|
12
17
|
|
|
@@ -19,8 +24,3 @@ data class DocumentPickerResult(
|
|
|
19
24
|
@Field
|
|
20
25
|
val size: Int?
|
|
21
26
|
) : Record
|
|
22
|
-
|
|
23
|
-
data class DocumentPickerCancelled(
|
|
24
|
-
@Field
|
|
25
|
-
val type: String
|
|
26
|
-
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { DocumentPickerOptions,
|
|
1
|
+
import { DocumentPickerOptions, DocumentPickerResult } from './types';
|
|
2
2
|
declare const _default: {
|
|
3
3
|
readonly name: string;
|
|
4
|
-
getDocumentAsync({ type, multiple, }: DocumentPickerOptions): Promise<
|
|
4
|
+
getDocumentAsync({ type, multiple, }: DocumentPickerOptions): Promise<DocumentPickerResult>;
|
|
5
5
|
};
|
|
6
6
|
export default _default;
|
|
7
7
|
//# sourceMappingURL=ExpoDocumentPicker.web.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoDocumentPicker.web.d.ts","sourceRoot":"","sources":["../src/ExpoDocumentPicker.web.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,
|
|
1
|
+
{"version":3,"file":"ExpoDocumentPicker.web.d.ts","sourceRoot":"","sources":["../src/ExpoDocumentPicker.web.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;;;0CAUjE,qBAAqB,GAAG,QAAQ,oBAAoB,CAAC;;AAR1D,wBA8DE"}
|
|
@@ -6,7 +6,7 @@ export default {
|
|
|
6
6
|
async getDocumentAsync({ type = '*/*', multiple = false, }) {
|
|
7
7
|
// SSR guard
|
|
8
8
|
if (!Platform.isDOMAvailable) {
|
|
9
|
-
return {
|
|
9
|
+
return { canceled: true, assets: null };
|
|
10
10
|
}
|
|
11
11
|
const input = document.createElement('input');
|
|
12
12
|
input.style.display = 'none';
|
|
@@ -29,9 +29,11 @@ export default {
|
|
|
29
29
|
reader.onload = ({ target }) => {
|
|
30
30
|
const uri = target.result;
|
|
31
31
|
resolve({
|
|
32
|
+
canceled: false,
|
|
32
33
|
type: 'success',
|
|
33
34
|
uri,
|
|
34
35
|
mimeType,
|
|
36
|
+
assets: [],
|
|
35
37
|
name: targetFile.name,
|
|
36
38
|
file: targetFile,
|
|
37
39
|
lastModified: targetFile.lastModified,
|
|
@@ -43,7 +45,7 @@ export default {
|
|
|
43
45
|
reader.readAsDataURL(targetFile);
|
|
44
46
|
}
|
|
45
47
|
else {
|
|
46
|
-
resolve({
|
|
48
|
+
resolve({ canceled: true, assets: null });
|
|
47
49
|
}
|
|
48
50
|
document.body.removeChild(input);
|
|
49
51
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoDocumentPicker.web.js","sourceRoot":"","sources":["../src/ExpoDocumentPicker.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAI7C,eAAe;IACb,IAAI,IAAI;QACN,OAAO,oBAAoB,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,EACrB,IAAI,GAAG,KAAK,EACZ,QAAQ,GAAG,KAAK,GACM;QACtB,YAAY;QACZ,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE;YAC5B,OAAO,EAAE,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"ExpoDocumentPicker.web.js","sourceRoot":"","sources":["../src/ExpoDocumentPicker.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAI7C,eAAe;IACb,IAAI,IAAI;QACN,OAAO,oBAAoB,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,EACrB,IAAI,GAAG,KAAK,EACZ,QAAQ,GAAG,KAAK,GACM;QACtB,YAAY;QACZ,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE;YAC5B,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;SACzC;QAED,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC9C,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC;QAC7B,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACnC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC1E,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAChD,IAAI,QAAQ,EAAE;YACZ,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;SAC5C;QAED,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAEjC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,KAAK,CAAC,gBAAgB,CAAC,QAAQ,EAAE,GAAG,EAAE;gBACpC,IAAI,KAAK,CAAC,KAAK,EAAE;oBACf,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;oBAClC,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC;oBACjC,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;oBAChC,MAAM,CAAC,OAAO,GAAG,GAAG,EAAE;wBACpB,MAAM,CAAC,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC,CAAC;oBACvF,CAAC,CAAC;oBACF,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE;wBAC7B,MAAM,GAAG,GAAI,MAAc,CAAC,MAAM,CAAC;wBACnC,OAAO,CAAC;4BACN,QAAQ,EAAE,KAAK;4BACf,IAAI,EAAE,SAAS;4BACf,GAAG;4BACH,QAAQ;4BACR,MAAM,EAAE,EAAE;4BACV,IAAI,EAAE,UAAU,CAAC,IAAI;4BACrB,IAAI,EAAE,UAAU;4BAChB,YAAY,EAAE,UAAU,CAAC,YAAY;4BACrC,IAAI,EAAE,UAAU,CAAC,IAAI;4BACrB,MAAM,EAAE,KAAK,CAAC,KAAK;yBACpB,CAAC,CAAC;oBACL,CAAC,CAAC;oBACF,6CAA6C;oBAC7C,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;iBAClC;qBAAM;oBACL,OAAO,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;iBAC3C;gBAED,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YACnC,CAAC,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC;YACtC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAC","sourcesContent":["import { Platform } from 'expo-modules-core';\n\nimport { DocumentPickerOptions, DocumentPickerResult } from './types';\n\nexport default {\n get name(): string {\n return 'ExpoDocumentPicker';\n },\n\n async getDocumentAsync({\n type = '*/*',\n multiple = false,\n }: DocumentPickerOptions): Promise<DocumentPickerResult> {\n // SSR guard\n if (!Platform.isDOMAvailable) {\n return { canceled: true, assets: null };\n }\n\n const input = document.createElement('input');\n input.style.display = 'none';\n input.setAttribute('type', 'file');\n input.setAttribute('accept', Array.isArray(type) ? type.join(',') : type);\n input.setAttribute('id', String(Math.random()));\n if (multiple) {\n input.setAttribute('multiple', 'multiple');\n }\n\n document.body.appendChild(input);\n\n return new Promise((resolve, reject) => {\n input.addEventListener('change', () => {\n if (input.files) {\n const targetFile = input.files[0];\n const mimeType = targetFile.type;\n const reader = new FileReader();\n reader.onerror = () => {\n reject(new Error(`Failed to read the selected media because the operation failed.`));\n };\n reader.onload = ({ target }) => {\n const uri = (target as any).result;\n resolve({\n canceled: false,\n type: 'success',\n uri,\n mimeType,\n assets: [],\n name: targetFile.name,\n file: targetFile,\n lastModified: targetFile.lastModified,\n size: targetFile.size,\n output: input.files,\n });\n };\n // Read in the image file as a binary string.\n reader.readAsDataURL(targetFile);\n } else {\n resolve({ canceled: true, assets: null });\n }\n\n document.body.removeChild(input);\n });\n\n const event = new MouseEvent('click');\n input.dispatchEvent(event);\n });\n },\n};\n"]}
|
package/build/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DocumentPickerOptions,
|
|
2
|
-
export { DocumentPickerOptions, DocumentResult };
|
|
1
|
+
import { DocumentPickerOptions, DocumentPickerResult } from './types';
|
|
2
|
+
export { DocumentPickerOptions, DocumentPickerResult as DocumentResult };
|
|
3
3
|
/**
|
|
4
4
|
* Display the system UI for choosing a document. By default, the chosen file is copied to [the app's internal cache directory](filesystem.md#filesystemcachedirectory).
|
|
5
5
|
* > **Notes for Web:** The system UI can only be shown after user activation (e.g. a `Button` press).
|
|
@@ -11,5 +11,5 @@ export { DocumentPickerOptions, DocumentResult };
|
|
|
11
11
|
*
|
|
12
12
|
* If the user cancelled the document picking, the promise resolves to `{ type: 'cancel' }`.
|
|
13
13
|
*/
|
|
14
|
-
export declare function getDocumentAsync({ type, copyToCacheDirectory, multiple, }?: DocumentPickerOptions): Promise<
|
|
14
|
+
export declare function getDocumentAsync({ type, copyToCacheDirectory, multiple, }?: DocumentPickerOptions): Promise<DocumentPickerResult>;
|
|
15
15
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,IAAI,cAAc,EAAE,CAAC;AAqCzE;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CAAC,EACrC,IAAY,EACZ,oBAA2B,EAC3B,QAAgB,GACjB,GAAE,qBAA0B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAU5D"}
|
package/build/index.js
CHANGED
|
@@ -1,4 +1,32 @@
|
|
|
1
1
|
import ExpoDocumentPicker from './ExpoDocumentPicker';
|
|
2
|
+
const DEPRECATED_RESULT_KEYS = [
|
|
3
|
+
'name',
|
|
4
|
+
'size',
|
|
5
|
+
'uri',
|
|
6
|
+
'mimeType',
|
|
7
|
+
'lastModified',
|
|
8
|
+
'file',
|
|
9
|
+
'output',
|
|
10
|
+
];
|
|
11
|
+
function mergeDeprecatedResult(result) {
|
|
12
|
+
const firstAsset = result.assets?.[0];
|
|
13
|
+
const deprecatedResult = {
|
|
14
|
+
...result,
|
|
15
|
+
get type() {
|
|
16
|
+
console.warn('Key "type" in the document picker result is deprecated and will be removed in SDK 49, use "canceled" instead');
|
|
17
|
+
return this.canceled ? 'cancel' : 'success';
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
for (const key of DEPRECATED_RESULT_KEYS) {
|
|
21
|
+
Object.defineProperty(deprecatedResult, key, {
|
|
22
|
+
get() {
|
|
23
|
+
console.warn(`Key "${key}" in the document picker result is deprecated and will be removed in SDK 49, you can access selected assets through the "assets" array instead`);
|
|
24
|
+
return firstAsset?.[key];
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return deprecatedResult;
|
|
29
|
+
}
|
|
2
30
|
// @needsAudit
|
|
3
31
|
/**
|
|
4
32
|
* Display the system UI for choosing a document. By default, the chosen file is copied to [the app's internal cache directory](filesystem.md#filesystemcachedirectory).
|
|
@@ -15,6 +43,11 @@ export async function getDocumentAsync({ type = '*/*', copyToCacheDirectory = tr
|
|
|
15
43
|
if (typeof type === 'string') {
|
|
16
44
|
type = [type];
|
|
17
45
|
}
|
|
18
|
-
|
|
46
|
+
const result = await ExpoDocumentPicker.getDocumentAsync({
|
|
47
|
+
type,
|
|
48
|
+
copyToCacheDirectory,
|
|
49
|
+
multiple,
|
|
50
|
+
});
|
|
51
|
+
return mergeDeprecatedResult(result);
|
|
19
52
|
}
|
|
20
53
|
//# sourceMappingURL=index.js.map
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,MAAM,sBAAsB,CAAC;AAItD,cAAc;AACd;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,EACrC,IAAI,GAAG,KAAK,EACZ,oBAAoB,GAAG,IAAI,EAC3B,QAAQ,GAAG,KAAK,MACS,EAAE;IAC3B,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;QAC5B,IAAI,GAAG,CAAC,IAAI,CAAa,CAAC;KAC3B;IACD,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,MAAM,sBAAsB,CAAC;AAItD,MAAM,sBAAsB,GAAG;IAC7B,MAAM;IACN,MAAM;IACN,KAAK;IACL,UAAU;IACV,cAAc;IACd,MAAM;IACN,QAAQ;CACT,CAAC;AAEF,SAAS,qBAAqB,CAAC,MAA4B;IACzD,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACtC,MAAM,gBAAgB,GAAG;QACvB,GAAG,MAAM;QACT,IAAI,IAAI;YACN,OAAO,CAAC,IAAI,CACV,8GAA8G,CAC/G,CAAC;YACF,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9C,CAAC;KACF,CAAC;IACF,KAAK,MAAM,GAAG,IAAI,sBAAsB,EAAE;QACxC,MAAM,CAAC,cAAc,CAAC,gBAAgB,EAAE,GAAG,EAAE;YAC3C,GAAG;gBACD,OAAO,CAAC,IAAI,CACV,QAAQ,GAAG,gJAAgJ,CAC5J,CAAC;gBACF,OAAO,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC;YAC3B,CAAC;SACF,CAAC,CAAC;KACJ;IACD,OAAO,gBAAgB,CAAC;AAC1B,CAAC;AAED,cAAc;AACd;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,EACrC,IAAI,GAAG,KAAK,EACZ,oBAAoB,GAAG,IAAI,EAC3B,QAAQ,GAAG,KAAK,MACS,EAAE;IAC3B,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;QAC5B,IAAI,GAAG,CAAC,IAAI,CAAa,CAAC;KAC3B;IACD,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,gBAAgB,CAAC;QACvD,IAAI;QACJ,oBAAoB;QACpB,QAAQ;KACT,CAAC,CAAC;IACH,OAAO,qBAAqB,CAAC,MAAM,CAAC,CAAC;AACvC,CAAC","sourcesContent":["import ExpoDocumentPicker from './ExpoDocumentPicker';\nimport { DocumentPickerOptions, DocumentPickerResult } from './types';\nexport { DocumentPickerOptions, DocumentPickerResult as DocumentResult };\n\nconst DEPRECATED_RESULT_KEYS = [\n 'name',\n 'size',\n 'uri',\n 'mimeType',\n 'lastModified',\n 'file',\n 'output',\n];\n\nfunction mergeDeprecatedResult(result: DocumentPickerResult): DocumentPickerResult {\n const firstAsset = result.assets?.[0];\n const deprecatedResult = {\n ...result,\n get type() {\n console.warn(\n 'Key \"type\" in the document picker result is deprecated and will be removed in SDK 49, use \"canceled\" instead'\n );\n return this.canceled ? 'cancel' : 'success';\n },\n };\n for (const key of DEPRECATED_RESULT_KEYS) {\n Object.defineProperty(deprecatedResult, key, {\n get() {\n console.warn(\n `Key \"${key}\" in the document picker result is deprecated and will be removed in SDK 49, you can access selected assets through the \"assets\" array instead`\n );\n return firstAsset?.[key];\n },\n });\n }\n return deprecatedResult;\n}\n\n// @needsAudit\n/**\n * Display the system UI for choosing a document. By default, the chosen file is copied to [the app's internal cache directory](filesystem.md#filesystemcachedirectory).\n * > **Notes for Web:** The system UI can only be shown after user activation (e.g. a `Button` press).\n * > Therefore, calling `getDocumentAsync` in `componentDidMount`, for example, will **not** work as\n * > intended. The `cancel` event will not be returned in the browser due to platform restrictions and\n * > inconsistencies across browsers.\n *\n * @return On success returns a promise that fulfils with [`DocumentResult`](#documentresult) object.\n *\n * If the user cancelled the document picking, the promise resolves to `{ type: 'cancel' }`.\n */\nexport async function getDocumentAsync({\n type = '*/*',\n copyToCacheDirectory = true,\n multiple = false,\n}: DocumentPickerOptions = {}): Promise<DocumentPickerResult> {\n if (typeof type === 'string') {\n type = [type] as string[];\n }\n const result = await ExpoDocumentPicker.getDocumentAsync({\n type,\n copyToCacheDirectory,\n multiple,\n });\n return mergeDeprecatedResult(result);\n}\n"]}
|
package/build/types.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type DocumentPickerOptions = {
|
|
2
2
|
/**
|
|
3
3
|
* The [MIME type(s)](https://en.wikipedia.org/wiki/Media_type) of the documents that are available
|
|
4
|
-
* to be picked.
|
|
4
|
+
* to be picked. It also supports wildcards like `'image/*'` to choose any image. To allow any type
|
|
5
5
|
* of document you can use `'*/*'`.
|
|
6
6
|
* @default '*/*'
|
|
7
7
|
*/
|
|
@@ -17,36 +17,65 @@ export type DocumentPickerOptions = {
|
|
|
17
17
|
/**
|
|
18
18
|
* Allows multiple files to be selected from the system UI.
|
|
19
19
|
* @default false
|
|
20
|
-
*
|
|
20
|
+
*
|
|
21
21
|
*/
|
|
22
22
|
multiple?: boolean;
|
|
23
23
|
};
|
|
24
|
-
|
|
25
|
-
* First object represents the result when the document pick has been cancelled.
|
|
26
|
-
* The second one represents the successful document pick result.
|
|
27
|
-
*/
|
|
28
|
-
export type DocumentResult = {
|
|
24
|
+
export type DocumentPickerAsset = {
|
|
29
25
|
/**
|
|
30
|
-
*
|
|
26
|
+
* Document original name.
|
|
31
27
|
*/
|
|
32
|
-
|
|
33
|
-
} | {
|
|
28
|
+
name: string;
|
|
34
29
|
/**
|
|
35
|
-
*
|
|
30
|
+
* Document size in bytes.
|
|
36
31
|
*/
|
|
37
|
-
|
|
32
|
+
size?: number;
|
|
33
|
+
/**
|
|
34
|
+
* An URI to the local document file.
|
|
35
|
+
*/
|
|
36
|
+
uri: string;
|
|
37
|
+
/**
|
|
38
|
+
* Document MIME type.
|
|
39
|
+
*/
|
|
40
|
+
mimeType?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Timestamp of last document modification.
|
|
43
|
+
*/
|
|
44
|
+
lastModified?: number;
|
|
45
|
+
/**
|
|
46
|
+
* `File` object for the parity with web File API.
|
|
47
|
+
* @platform web
|
|
48
|
+
*/
|
|
49
|
+
file?: File;
|
|
50
|
+
/**
|
|
51
|
+
* `FileList` object for the parity with web File API.
|
|
52
|
+
* @platform web
|
|
53
|
+
*/
|
|
54
|
+
output?: FileList | null;
|
|
55
|
+
};
|
|
56
|
+
export type DocumentPickerResult = {
|
|
57
|
+
/**
|
|
58
|
+
* Boolean flag which shows if request was canceled. If asset data have been returned this should
|
|
59
|
+
* always be `false`.
|
|
60
|
+
*/
|
|
61
|
+
canceled: boolean;
|
|
62
|
+
type?: string;
|
|
38
63
|
/**
|
|
39
64
|
* Document original name.
|
|
40
65
|
*/
|
|
41
|
-
name
|
|
66
|
+
name?: string;
|
|
42
67
|
/**
|
|
43
68
|
* Document size in bytes.
|
|
44
69
|
*/
|
|
45
70
|
size?: number;
|
|
71
|
+
/**
|
|
72
|
+
* An array of picked assets or `null` when the request was canceled.
|
|
73
|
+
*/
|
|
74
|
+
assets: DocumentPickerAsset[] | null;
|
|
46
75
|
/**
|
|
47
76
|
* An URI to the local document file.
|
|
48
77
|
*/
|
|
49
|
-
uri
|
|
78
|
+
uri?: string;
|
|
50
79
|
/**
|
|
51
80
|
* Document MIME type.
|
|
52
81
|
*/
|
|
@@ -65,5 +94,19 @@ export type DocumentResult = {
|
|
|
65
94
|
* @platform web
|
|
66
95
|
*/
|
|
67
96
|
output?: FileList | null;
|
|
97
|
+
} & (DocumentPickerSuccessResult | DocumentPickerCanceledResult);
|
|
98
|
+
/**
|
|
99
|
+
* @hidden
|
|
100
|
+
*/
|
|
101
|
+
export type DocumentPickerSuccessResult = {
|
|
102
|
+
canceled: false;
|
|
103
|
+
assets: DocumentPickerAsset[];
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* @hidden
|
|
107
|
+
*/
|
|
108
|
+
export type DocumentPickerCanceledResult = {
|
|
109
|
+
canceled: true;
|
|
110
|
+
assets: null;
|
|
68
111
|
};
|
|
69
112
|
//# sourceMappingURL=types.d.ts.map
|
package/build/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,qBAAqB,GAAG;IAClC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB;;;;;;OAMG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,qBAAqB,GAAG;IAClC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB;;;;;;OAMG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ;;;OAGG;IACH,MAAM,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC1B,CAAC;AAGF,MAAM,MAAM,oBAAoB,GAAG;IACjC;;;OAGG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,MAAM,EAAE,mBAAmB,EAAE,GAAG,IAAI,CAAC;IACrC;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ;;;OAGG;IACH,MAAM,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC1B,GAAG,CAAC,2BAA2B,GAAG,4BAA4B,CAAC,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,EAAE,KAAK,CAAC;IAChB,MAAM,EAAE,mBAAmB,EAAE,CAAC;CAC/B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,4BAA4B,GAAG;IACzC,QAAQ,EAAE,IAAI,CAAC;IACf,MAAM,EAAE,IAAI,CAAC;CACd,CAAC"}
|
package/build/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["// @needsAudit\nexport type DocumentPickerOptions = {\n /**\n * The [MIME type(s)](https://en.wikipedia.org/wiki/Media_type) of the documents that are available\n * to be picked.
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["// @needsAudit\nexport type DocumentPickerOptions = {\n /**\n * The [MIME type(s)](https://en.wikipedia.org/wiki/Media_type) of the documents that are available\n * to be picked. It also supports wildcards like `'image/*'` to choose any image. To allow any type\n * of document you can use `'*/*'`.\n * @default '*/*'\n */\n type?: string | string[];\n /**\n * If `true`, the picked file is copied to [`FileSystem.CacheDirectory`](./filesystem#filesystemcachedirectory),\n * which allows other Expo APIs to read the file immediately. This may impact performance for\n * large files, so you should consider setting this to `false` if you expect users to pick\n * particularly large files and your app does not need immediate read access.\n * @default true\n */\n copyToCacheDirectory?: boolean;\n /**\n * Allows multiple files to be selected from the system UI.\n * @default false\n *\n */\n multiple?: boolean;\n};\n\nexport type DocumentPickerAsset = {\n /**\n * Document original name.\n */\n name: string;\n /**\n * Document size in bytes.\n */\n size?: number;\n /**\n * An URI to the local document file.\n */\n uri: string;\n /**\n * Document MIME type.\n */\n mimeType?: string;\n /**\n * Timestamp of last document modification.\n */\n lastModified?: number;\n /**\n * `File` object for the parity with web File API.\n * @platform web\n */\n file?: File;\n /**\n * `FileList` object for the parity with web File API.\n * @platform web\n */\n output?: FileList | null;\n};\n\n// @needsAudit @docsMissing\nexport type DocumentPickerResult = {\n /**\n * Boolean flag which shows if request was canceled. If asset data have been returned this should\n * always be `false`.\n */\n canceled: boolean;\n type?: string;\n /**\n * Document original name.\n */\n name?: string;\n /**\n * Document size in bytes.\n */\n size?: number;\n /**\n * An array of picked assets or `null` when the request was canceled.\n */\n assets: DocumentPickerAsset[] | null;\n /**\n * An URI to the local document file.\n */\n uri?: string;\n /**\n * Document MIME type.\n */\n mimeType?: string;\n /**\n * Timestamp of last document modification.\n */\n lastModified?: number;\n /**\n * `File` object for the parity with web File API.\n * @platform web\n */\n file?: File;\n /**\n * `FileList` object for the parity with web File API.\n * @platform web\n */\n output?: FileList | null;\n} & (DocumentPickerSuccessResult | DocumentPickerCanceledResult);\n\n/**\n * @hidden\n */\nexport type DocumentPickerSuccessResult = {\n canceled: false;\n assets: DocumentPickerAsset[];\n};\n\n/**\n * @hidden\n */\nexport type DocumentPickerCanceledResult = {\n canceled: true;\n assets: null;\n};\n"]}
|
package/expo-module.config.json
CHANGED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
import UIKit
|
|
3
|
+
import MobileCoreServices
|
|
4
|
+
|
|
5
|
+
struct PickingContext {
|
|
6
|
+
let promise: Promise
|
|
7
|
+
let options: DocumentPickerOptions
|
|
8
|
+
let delegate: DocumentPickingDelegate
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
public class DocumentPickerModule: Module, PickingResultHandler {
|
|
12
|
+
private var pickingContext: PickingContext?
|
|
13
|
+
|
|
14
|
+
public func definition() -> ModuleDefinition {
|
|
15
|
+
Name("ExpoDocumentPicker")
|
|
16
|
+
|
|
17
|
+
AsyncFunction("getDocumentAsync") { (options: DocumentPickerOptions, promise: Promise) in
|
|
18
|
+
if pickingContext != nil {
|
|
19
|
+
throw PickingInProgressException()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
guard let currentVc = appContext?.utilities?.currentViewController() else {
|
|
23
|
+
throw MissingViewControllerException()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let documentPickerVC = createDocumentPicker(with: options)
|
|
27
|
+
let pickerDelegate = DocumentPickingDelegate(resultHandler: self)
|
|
28
|
+
|
|
29
|
+
pickingContext = PickingContext(promise: promise, options: options, delegate: pickerDelegate)
|
|
30
|
+
|
|
31
|
+
documentPickerVC.delegate = pickerDelegate
|
|
32
|
+
documentPickerVC.presentationController?.delegate = pickerDelegate
|
|
33
|
+
documentPickerVC.allowsMultipleSelection = options.multiple
|
|
34
|
+
|
|
35
|
+
if UIDevice.current.userInterfaceIdiom == .pad {
|
|
36
|
+
let viewFrame = currentVc.view.frame
|
|
37
|
+
documentPickerVC.popoverPresentationController?.sourceRect = CGRect(
|
|
38
|
+
x: viewFrame.midX,
|
|
39
|
+
y: viewFrame.maxY,
|
|
40
|
+
width: 0,
|
|
41
|
+
height: 0
|
|
42
|
+
)
|
|
43
|
+
documentPickerVC.popoverPresentationController?.sourceView = currentVc.view
|
|
44
|
+
documentPickerVC.modalPresentationStyle = .pageSheet
|
|
45
|
+
}
|
|
46
|
+
currentVc.present(documentPickerVC, animated: true)
|
|
47
|
+
}.runOnQueue(.main)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
func didPickDocumentsAt(urls: [URL]) {
|
|
51
|
+
guard let options = self.pickingContext?.options,
|
|
52
|
+
let promise = self.pickingContext?.promise else {
|
|
53
|
+
log.error("Picking context has been lost.")
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
pickingContext = nil
|
|
57
|
+
|
|
58
|
+
do {
|
|
59
|
+
if options.multiple {
|
|
60
|
+
let assets = try urls.map {
|
|
61
|
+
try readDocumentDetails(
|
|
62
|
+
documentUrl: $0,
|
|
63
|
+
copy: options.copyToCacheDirectory
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
promise.resolve(DocumentPickerResponse(assets: assets))
|
|
67
|
+
} else {
|
|
68
|
+
let asset = try readDocumentDetails(
|
|
69
|
+
documentUrl: urls[0],
|
|
70
|
+
copy: options.copyToCacheDirectory
|
|
71
|
+
)
|
|
72
|
+
promise.resolve(DocumentPickerResponse(assets: [asset]))
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
promise.reject(error)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
func didCancelPicking() {
|
|
80
|
+
guard let context = pickingContext else {
|
|
81
|
+
log.error("Picking context lost")
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
pickingContext = nil
|
|
86
|
+
context.promise.resolve(DocumentPickerResponse(canceled: true))
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private func getFileSize(path: URL) -> Int? {
|
|
90
|
+
guard let resource = try? path.resourceValues(forKeys: [.fileSizeKey, .isDirectoryKey]) else {
|
|
91
|
+
return 0
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if let isDirectory = resource.isDirectory {
|
|
95
|
+
if !isDirectory {
|
|
96
|
+
return resource.fileSize
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
guard let contents = try? FileManager.default.contentsOfDirectory(atPath: path.absoluteString) else {
|
|
101
|
+
return 0
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let folderSize = contents.reduce(0) { currentSize, file in
|
|
105
|
+
let fileSize = getFileSize(path: path.appendingPathComponent(file)) ?? 0
|
|
106
|
+
return currentSize + fileSize
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return folderSize
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private func readDocumentDetails(documentUrl: URL, copy: Bool) throws -> DocumentInfo {
|
|
113
|
+
let pathExtension = documentUrl.pathExtension
|
|
114
|
+
var newUrl = documentUrl
|
|
115
|
+
|
|
116
|
+
guard let fileSystem = self.appContext?.fileSystem else {
|
|
117
|
+
throw Exceptions.FileSystemModuleNotFound()
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
guard let fileSize = try? getFileSize(path: documentUrl) else {
|
|
121
|
+
throw InvalidFileException()
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if copy {
|
|
125
|
+
let directory = fileSystem.cachesDirectory.appending("DocumentPicker")
|
|
126
|
+
let path = fileSystem.generatePath(inDirectory: directory, withExtension: pathExtension)
|
|
127
|
+
newUrl = URL(fileURLWithPath: path)
|
|
128
|
+
try FileManager.default.copyItem(at: documentUrl, to: newUrl)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let mimeType = self.getMimeType(from: pathExtension)
|
|
132
|
+
|
|
133
|
+
return DocumentInfo(
|
|
134
|
+
uri: newUrl.absoluteString,
|
|
135
|
+
name: documentUrl.lastPathComponent,
|
|
136
|
+
size: fileSize,
|
|
137
|
+
mimeType: mimeType
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private func getMimeType(from pathExtension: String) -> String? {
|
|
142
|
+
if #available(iOS 14, *) {
|
|
143
|
+
return UTType(filenameExtension: pathExtension)?.preferredMIMEType
|
|
144
|
+
} else {
|
|
145
|
+
if let uti = UTTypeCreatePreferredIdentifierForTag(
|
|
146
|
+
kUTTagClassFilenameExtension,
|
|
147
|
+
pathExtension as NSString, nil
|
|
148
|
+
)?.takeRetainedValue() {
|
|
149
|
+
if let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() {
|
|
150
|
+
return mimetype as String
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return nil
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private func createDocumentPicker(with options: DocumentPickerOptions) -> UIDocumentPickerViewController {
|
|
158
|
+
if #available(iOS 14.0, *) {
|
|
159
|
+
let utTypes = options.type.compactMap { $0.toUTType() }
|
|
160
|
+
return UIDocumentPickerViewController(
|
|
161
|
+
forOpeningContentTypes: utTypes,
|
|
162
|
+
asCopy: true
|
|
163
|
+
)
|
|
164
|
+
} else {
|
|
165
|
+
let utiTypes = options.type.map { $0.toUTI() }
|
|
166
|
+
return UIDocumentPickerViewController(
|
|
167
|
+
documentTypes: utiTypes,
|
|
168
|
+
in: .import
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|