react-native-my-uploader-android 1.0.21
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/LICENSE +20 -0
- package/MyUploader.podspec +21 -0
- package/README.md +205 -0
- package/android/build.gradle +40 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/myuploader/MyUploaderModule.kt +173 -0
- package/android/src/main/java/com/myuploader/MyUploaderPackage.kt +16 -0
- package/ios/MyUploader.h +8 -0
- package/ios/MyUploader.mm +11 -0
- package/ios/myUploader.swift +98 -0
- package/lib/commonjs/NativeMyUploader.js +17 -0
- package/lib/commonjs/NativeMyUploader.js.map +1 -0
- package/lib/commonjs/index.d.js +2 -0
- package/lib/commonjs/index.d.js.map +1 -0
- package/lib/commonjs/index.js +55 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/types.js +2 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/module/NativeMyUploader.js +12 -0
- package/lib/module/NativeMyUploader.js.map +1 -0
- package/lib/module/index.d.js +2 -0
- package/lib/module/index.d.js.map +1 -0
- package/lib/module/index.js +50 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/package.json +161 -0
- package/src/NativeMyUploader.ts +27 -0
- package/src/index.d.ts +60 -0
- package/src/index.tsx +58 -0
- package/src/types.ts +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Yaren Coskun
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = "MyUploader"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.authors = package["author"]
|
|
12
|
+
|
|
13
|
+
s.platforms = { :ios => min_ios_version_supported }
|
|
14
|
+
s.source = { :git => "https://www.npmjs.com/~yrncskn.git", :tag => "#{s.version}" }
|
|
15
|
+
|
|
16
|
+
s.source_files = "ios/**/*.{h,m,mm,cpp,swift}"
|
|
17
|
+
s.private_header_files = "ios/**/*.h"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
install_modules_dependencies(s)
|
|
21
|
+
end
|
package/README.md
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# react-native-my-uploader
|
|
2
|
+
|
|
3
|
+
file uploader
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
npm install react-native-my-uploader
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
import React, {useState} from 'react';
|
|
18
|
+
import {
|
|
19
|
+
StyleSheet,
|
|
20
|
+
View,
|
|
21
|
+
Button,
|
|
22
|
+
Text,
|
|
23
|
+
Alert,
|
|
24
|
+
ScrollView,
|
|
25
|
+
StatusBar,
|
|
26
|
+
} from 'react-native';
|
|
27
|
+
|
|
28
|
+
import {
|
|
29
|
+
pickFile,
|
|
30
|
+
FileInfo,
|
|
31
|
+
DocumentPickerOptions,
|
|
32
|
+
} from 'react-native-my-uploader';
|
|
33
|
+
|
|
34
|
+
const App = () => {
|
|
35
|
+
const [selectedFiles, setSelectedFiles] = useState<FileInfo[]>([]);
|
|
36
|
+
|
|
37
|
+
const handlePickFile = async (options: DocumentPickerOptions) => {
|
|
38
|
+
try {
|
|
39
|
+
console.log('Dosya seçici şu seçeneklerle başlatılıyor:', options);
|
|
40
|
+
const files = await pickFile(options);
|
|
41
|
+
console.log('Başarıyla seçilen dosyalar:', files);
|
|
42
|
+
|
|
43
|
+
if (options.multipleFiles) {
|
|
44
|
+
setSelectedFiles(prevFiles => [...prevFiles, ...files]);
|
|
45
|
+
Alert.alert(
|
|
46
|
+
'Başarılı!',
|
|
47
|
+
`${files.length} adet yeni dosya listeye eklendi.`,
|
|
48
|
+
);
|
|
49
|
+
} else {
|
|
50
|
+
setSelectedFiles(files);
|
|
51
|
+
Alert.alert('Başarılı!', `1 adet dosya başarıyla seçildi.`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
} catch (error: any) {
|
|
55
|
+
if (error.code === 'E_PICKER_CANCELLED') {
|
|
56
|
+
console.log('Kullanıcı seçimi iptal etti.');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
console.error('Dosya seçme hatası:', error);
|
|
60
|
+
Alert.alert('Bir Hata Oluştu', error.message || 'Bilinmeyen bir hata.');
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<View style={styles.container}>
|
|
66
|
+
<StatusBar barStyle="dark-content" />
|
|
67
|
+
<View style={styles.header}>
|
|
68
|
+
<Text style={styles.title}>Dosya Yükleyici Paket Testi</Text>
|
|
69
|
+
</View>
|
|
70
|
+
|
|
71
|
+
<View style={styles.buttonGrid}>
|
|
72
|
+
<Button
|
|
73
|
+
title="Tek Bir Görüntü Seç"
|
|
74
|
+
onPress={() =>
|
|
75
|
+
handlePickFile({
|
|
76
|
+
multipleFiles: false,
|
|
77
|
+
fileTypes: ['image/*'],
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
/>
|
|
81
|
+
<Button
|
|
82
|
+
title="birden Fazla Dosya Seç (Maks 2MB)"
|
|
83
|
+
onPress={() =>
|
|
84
|
+
handlePickFile({
|
|
85
|
+
multipleFiles: true,
|
|
86
|
+
fileTypes: ['application/*'],
|
|
87
|
+
maxSize: 2,
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
/>
|
|
91
|
+
<Button
|
|
92
|
+
title="Listeyi Temizle"
|
|
93
|
+
color="#c0392b"
|
|
94
|
+
onPress={() => setSelectedFiles([])}
|
|
95
|
+
/>
|
|
96
|
+
</View>
|
|
97
|
+
|
|
98
|
+
<View style={styles.resultsArea}>
|
|
99
|
+
<Text style={styles.resultsTitle}>
|
|
100
|
+
Seçilen Dosyalar ({selectedFiles.length} adet)
|
|
101
|
+
</Text>
|
|
102
|
+
<ScrollView contentContainerStyle={styles.scrollViewContent}>
|
|
103
|
+
{selectedFiles.length > 0 ? (
|
|
104
|
+
selectedFiles.map((file, index) => (
|
|
105
|
+
<View key={`${file.fileUri}-${index}`} style={styles.fileCard}>
|
|
106
|
+
<Text style={styles.fileName} numberOfLines={1}>
|
|
107
|
+
{file.fileName}
|
|
108
|
+
</Text>
|
|
109
|
+
<Text style={styles.fileDetail}>
|
|
110
|
+
Boyut: {(file.fileSize / 1024).toFixed(2)} KB
|
|
111
|
+
</Text>
|
|
112
|
+
</View>
|
|
113
|
+
))
|
|
114
|
+
) : (
|
|
115
|
+
<Text style={styles.noFilesText}>
|
|
116
|
+
Seçilen dosya yok.
|
|
117
|
+
</Text>
|
|
118
|
+
)}
|
|
119
|
+
</ScrollView>
|
|
120
|
+
</View>
|
|
121
|
+
</View>
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const styles = StyleSheet.create({
|
|
126
|
+
container: {
|
|
127
|
+
flex: 1,
|
|
128
|
+
backgroundColor: '#f0f2f5',
|
|
129
|
+
},
|
|
130
|
+
header: {
|
|
131
|
+
padding: 20,
|
|
132
|
+
alignItems: 'center',
|
|
133
|
+
},
|
|
134
|
+
title: {
|
|
135
|
+
fontSize: 22,
|
|
136
|
+
fontWeight: 'bold',
|
|
137
|
+
color: '#1c1e21',
|
|
138
|
+
},
|
|
139
|
+
buttonGrid: {
|
|
140
|
+
paddingHorizontal: 20,
|
|
141
|
+
gap: 10,
|
|
142
|
+
},
|
|
143
|
+
resultsArea: {
|
|
144
|
+
flex: 1,
|
|
145
|
+
marginTop: 20,
|
|
146
|
+
borderTopWidth: 1,
|
|
147
|
+
borderTopColor: '#dddfe2',
|
|
148
|
+
paddingHorizontal: 20,
|
|
149
|
+
},
|
|
150
|
+
resultsTitle: {
|
|
151
|
+
fontSize: 18,
|
|
152
|
+
fontWeight: '600',
|
|
153
|
+
paddingVertical: 15,
|
|
154
|
+
color: '#1c1e21',
|
|
155
|
+
},
|
|
156
|
+
scrollViewContent: {
|
|
157
|
+
paddingBottom: 20,
|
|
158
|
+
},
|
|
159
|
+
fileCard: {
|
|
160
|
+
backgroundColor: '#ffffff',
|
|
161
|
+
padding: 15,
|
|
162
|
+
borderRadius: 8,
|
|
163
|
+
marginBottom: 10,
|
|
164
|
+
shadowColor: '#000',
|
|
165
|
+
shadowOffset: {width: 0, height: 1},
|
|
166
|
+
shadowOpacity: 0.2,
|
|
167
|
+
shadowRadius: 1.41,
|
|
168
|
+
elevation: 2,
|
|
169
|
+
},
|
|
170
|
+
fileName: {
|
|
171
|
+
fontSize: 16,
|
|
172
|
+
fontWeight: 'bold',
|
|
173
|
+
color: '#000',
|
|
174
|
+
},
|
|
175
|
+
fileDetail: {
|
|
176
|
+
fontSize: 13,
|
|
177
|
+
color: '#555',
|
|
178
|
+
marginTop: 4,
|
|
179
|
+
},
|
|
180
|
+
noFilesText: {
|
|
181
|
+
textAlign: 'center',
|
|
182
|
+
color: '#888',
|
|
183
|
+
marginTop: 30,
|
|
184
|
+
fontStyle: 'italic',
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
export default App;
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
## Contributing
|
|
194
|
+
|
|
195
|
+
- [Development workflow](CONTRIBUTING.md#development-workflow)
|
|
196
|
+
- [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
|
|
197
|
+
- [Code of conduct](CODE_OF_CONDUCT.md)
|
|
198
|
+
|
|
199
|
+
## License
|
|
200
|
+
|
|
201
|
+
MIT
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
apply plugin: "com.android.library"
|
|
2
|
+
apply plugin: "kotlin-android"
|
|
3
|
+
|
|
4
|
+
def getExtOrDefault(name) {
|
|
5
|
+
return rootProject.hasProperty(name) ? rootProject.ext[name] : project.properties[name]
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
android {
|
|
9
|
+
namespace "com.myuploader"
|
|
10
|
+
|
|
11
|
+
compileSdkVersion getExtOrDefault('compileSdkVersion')
|
|
12
|
+
|
|
13
|
+
defaultConfig {
|
|
14
|
+
minSdkVersion getExtOrDefault('minSdkVersion')
|
|
15
|
+
targetSdkVersion getExtOrDefault('targetSdkVersion')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
compileOptions {
|
|
19
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
20
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
sourceSets {
|
|
24
|
+
main {
|
|
25
|
+
java.srcDirs = ['src/main/java']
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
repositories {
|
|
31
|
+
mavenCentral()
|
|
32
|
+
google()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
36
|
+
|
|
37
|
+
dependencies {
|
|
38
|
+
implementation "com.facebook.react:react-native:+"
|
|
39
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
40
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
package com.myuploader
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.content.Intent
|
|
5
|
+
import android.net.Uri
|
|
6
|
+
import android.provider.OpenableColumns
|
|
7
|
+
import android.util.Base64
|
|
8
|
+
import com.facebook.react.bridge.*
|
|
9
|
+
import java.io.ByteArrayOutputStream
|
|
10
|
+
import java.net.URLConnection
|
|
11
|
+
|
|
12
|
+
class MyUploaderModule(private val reactContext: ReactApplicationContext) :
|
|
13
|
+
ReactContextBaseJavaModule(reactContext), ActivityEventListener {
|
|
14
|
+
|
|
15
|
+
private var pickerPromise: Promise? = null
|
|
16
|
+
private var maxSize: Double = 0.0
|
|
17
|
+
private var maxFiles: Int = 0
|
|
18
|
+
private var excludedUris: List<String> = emptyList()
|
|
19
|
+
|
|
20
|
+
init {
|
|
21
|
+
reactContext.addActivityEventListener(this)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
override fun getName(): String = "DocumentPicker"
|
|
25
|
+
|
|
26
|
+
companion object {
|
|
27
|
+
private const val REQUEST_CODE = 9900
|
|
28
|
+
private const val E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST"
|
|
29
|
+
private const val E_PICKER_CANCELLED = "E_PICKER_CANCELLED"
|
|
30
|
+
private const val E_FAILED_TO_OPEN_DOCUMENT = "E_FAILED_TO_OPEN_DOCUMENT"
|
|
31
|
+
private const val E_FILE_TOO_LARGE = "E_FILE_TOO_LARGE"
|
|
32
|
+
private const val E_MAX_FILES_EXCEEDED = "E_MAX_FILES_EXCEEDED"
|
|
33
|
+
private const val E_INVALID_OPTIONS = "E_INVALID_OPTIONS"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@ReactMethod
|
|
37
|
+
fun openDocument(options: ReadableMap, promise: Promise) {
|
|
38
|
+
if (pickerPromise != null) {
|
|
39
|
+
promise.reject(E_FAILED_TO_OPEN_DOCUMENT, "Başka bir dosya seçme işlemi zaten devam ediyor.")
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
this.pickerPromise = promise
|
|
44
|
+
|
|
45
|
+
val multipleFiles = options.takeIf { it.hasKey("multipleFiles") }?.getBoolean("multipleFiles") ?: false
|
|
46
|
+
val jsMaxFiles = options.takeIf { it.hasKey("maxFiles") }?.getInt("maxFiles") ?: 0
|
|
47
|
+
|
|
48
|
+
// KURAL KONTROLÜ (NATIVE): JS tarafındaki kontrolü burada da yaparak güvenliği artır.
|
|
49
|
+
if (!multipleFiles && jsMaxFiles > 1) {
|
|
50
|
+
promise.reject(E_INVALID_OPTIONS, "`maxFiles` değeri, `multipleFiles` false iken 1'den büyük olamaz.")
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// KURAL UYGULAMA (NATIVE): `maxFiles` için nihai değeri hesapla.
|
|
55
|
+
val effectiveMaxFiles = when {
|
|
56
|
+
multipleFiles && jsMaxFiles == 0 -> 3 // Varsayılan: 3
|
|
57
|
+
!multipleFiles -> 1 // Tekli seçimde her zaman 1
|
|
58
|
+
else -> jsMaxFiles // Belirtilen değeri kullan
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Değerleri sınıf değişkenlerine ata
|
|
62
|
+
this.maxFiles = effectiveMaxFiles
|
|
63
|
+
this.maxSize = options.takeIf { it.hasKey("maxSize") }?.getDouble("maxSize") ?: 0.0
|
|
64
|
+
this.excludedUris = options.takeIf { it.hasKey("excludedUris") }
|
|
65
|
+
?.getArray("excludedUris")?.toArrayList()?.mapNotNull { it.toString() } ?: emptyList()
|
|
66
|
+
|
|
67
|
+
val currentActivity = reactContext.currentActivity
|
|
68
|
+
if (currentActivity == null) {
|
|
69
|
+
pickerPromise?.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity mevcut değil.")
|
|
70
|
+
pickerPromise = null
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
val fileTypes = options.takeIf { it.hasKey("fileTypes") }
|
|
75
|
+
?.getArray("fileTypes")?.toArrayList()?.mapNotNull { it.toString() } ?: listOf("*/*")
|
|
76
|
+
val mimeTypes = if (fileTypes.isEmpty()) arrayOf("*/*") else fileTypes.toTypedArray()
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
// ANAHTAR: ACTION_GET_CONTENT, güvenilir çoklu seçimi etkinleştirir.
|
|
80
|
+
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
|
81
|
+
type = "*/*"
|
|
82
|
+
addCategory(Intent.CATEGORY_OPENABLE)
|
|
83
|
+
putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
|
|
84
|
+
|
|
85
|
+
if (multipleFiles) {
|
|
86
|
+
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
currentActivity.startActivityForResult(Intent.createChooser(intent, "Dosya Seçin"), REQUEST_CODE)
|
|
90
|
+
} catch (e: Exception) {
|
|
91
|
+
pickerPromise?.reject(E_FAILED_TO_OPEN_DOCUMENT, e)
|
|
92
|
+
pickerPromise = null
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) {
|
|
97
|
+
if (requestCode != REQUEST_CODE || pickerPromise == null) { return }
|
|
98
|
+
|
|
99
|
+
if (resultCode == Activity.RESULT_OK && data != null) {
|
|
100
|
+
val allSelectedUris = mutableListOf<Uri>()
|
|
101
|
+
data.clipData?.let { clip ->
|
|
102
|
+
for (i in 0 until clip.itemCount) {
|
|
103
|
+
allSelectedUris.add(clip.getItemAt(i).uri)
|
|
104
|
+
}
|
|
105
|
+
} ?: data.data?.let { uri ->
|
|
106
|
+
allSelectedUris.add(uri)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
val newSelectedUris = allSelectedUris.filter { uri -> !excludedUris.contains(uri.toString()) }
|
|
110
|
+
|
|
111
|
+
if (this.maxFiles > 0 && newSelectedUris.size > this.maxFiles) {
|
|
112
|
+
pickerPromise?.reject(E_MAX_FILES_EXCEEDED, "En fazla ${this.maxFiles} adet yeni dosya seçebilirsiniz.")
|
|
113
|
+
pickerPromise = null
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
val fileInfoArray = processSelectedFiles(newSelectedUris)
|
|
119
|
+
pickerPromise?.resolve(fileInfoArray)
|
|
120
|
+
} catch (e: Exception) {
|
|
121
|
+
pickerPromise?.reject(E_FILE_TOO_LARGE, e.message)
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
pickerPromise?.reject(E_PICKER_CANCELLED, "Dosya seçimi iptal edildi.")
|
|
125
|
+
}
|
|
126
|
+
pickerPromise = null
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
override fun onNewIntent(intent: Intent) {}
|
|
130
|
+
|
|
131
|
+
private fun processSelectedFiles(uris: List<Uri>): WritableArray {
|
|
132
|
+
val fileObjects = WritableNativeArray()
|
|
133
|
+
val maxSizeBytes = (maxSize * 1024 * 1024).toLong()
|
|
134
|
+
|
|
135
|
+
for (uri in uris) {
|
|
136
|
+
reactContext.contentResolver.query(uri, null, null, null, null)?.use { cursor ->
|
|
137
|
+
if (cursor.moveToFirst()) {
|
|
138
|
+
val fileNameIndex = cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)
|
|
139
|
+
val fileSizeIndex = cursor.getColumnIndexOrThrow(OpenableColumns.SIZE)
|
|
140
|
+
val fileName = cursor.getString(fileNameIndex)
|
|
141
|
+
val fileSize = cursor.getLong(fileSizeIndex)
|
|
142
|
+
|
|
143
|
+
if (maxSizeBytes > 0 && fileSize > maxSizeBytes) {
|
|
144
|
+
throw Exception("Seçilen dosya ($fileName) belirtilen ${maxSize}MB boyutundan büyük.")
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
val mimeType = getMimeType(fileName)
|
|
148
|
+
val base64 = reactContext.contentResolver.openInputStream(uri)?.use { inputStream ->
|
|
149
|
+
val byteBuffer = ByteArrayOutputStream()
|
|
150
|
+
val buffer = ByteArray(1024)
|
|
151
|
+
var len: Int
|
|
152
|
+
while (inputStream.read(buffer).also { len = it } != -1) {
|
|
153
|
+
byteBuffer.write(buffer, 0, len)
|
|
154
|
+
}
|
|
155
|
+
Base64.encodeToString(byteBuffer.toByteArray(), Base64.NO_WRAP)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
val fileMap = WritableNativeMap().apply {
|
|
159
|
+
putString("fileName", fileName)
|
|
160
|
+
putDouble("fileSize", fileSize.toDouble())
|
|
161
|
+
putString("fileType", mimeType)
|
|
162
|
+
putString("fileUri", uri.toString())
|
|
163
|
+
putString("base64", base64)
|
|
164
|
+
}
|
|
165
|
+
fileObjects.pushMap(fileMap)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return fileObjects
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private fun getMimeType(fileName: String): String? = URLConnection.guessContentTypeFromName(fileName)
|
|
173
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
package com.myuploader
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.ReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.uimanager.ViewManager
|
|
7
|
+
|
|
8
|
+
class MyUploaderPackage : ReactPackage {
|
|
9
|
+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
10
|
+
return listOf(MyUploaderModule(reactContext))
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
|
14
|
+
return emptyList()
|
|
15
|
+
}
|
|
16
|
+
}
|
package/ios/MyUploader.h
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
|
|
2
|
+
#import <Foundation/Foundation.h>
|
|
3
|
+
#import <React/RCTBridgeModule.h>
|
|
4
|
+
|
|
5
|
+
@interface RCT_EXTERN_MODULE(MyUploader, NSObject)
|
|
6
|
+
|
|
7
|
+
RCT_EXTERN_METHOD(openDocument:(NSDictionary)options
|
|
8
|
+
successCallback:(RCTResponseSenderBlock)successCallback
|
|
9
|
+
failureCallback:(RCTResponseSenderBlock)failureCallback)
|
|
10
|
+
|
|
11
|
+
@end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import MobileCoreServices
|
|
3
|
+
import UIKit
|
|
4
|
+
import UniformTypeIdentifiers
|
|
5
|
+
import React
|
|
6
|
+
|
|
7
|
+
@objc(MyUploader)
|
|
8
|
+
class MyUploader: NSObject, UIDocumentPickerDelegate {
|
|
9
|
+
|
|
10
|
+
private var resolveCallback: RCTResponseSenderBlock?
|
|
11
|
+
private var rejectCallback: RCTResponseSenderBlock?
|
|
12
|
+
private var maxFileSize: Int64 = 3 * 1024 * 1024 // 3 MB
|
|
13
|
+
private var currentPicker: UIDocumentPickerViewController?
|
|
14
|
+
|
|
15
|
+
@objc static func requiresMainQueueSetup() -> Bool {
|
|
16
|
+
return true
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@objc(openDocument:successCallback:failureCallback:)
|
|
20
|
+
func openDocument(_ options: NSDictionary,
|
|
21
|
+
successCallback: @escaping RCTResponseSenderBlock,
|
|
22
|
+
failureCallback: @escaping RCTResponseSenderBlock) {
|
|
23
|
+
|
|
24
|
+
self.resolveCallback = successCallback
|
|
25
|
+
self.rejectCallback = failureCallback
|
|
26
|
+
|
|
27
|
+
let multipleFiles = options["multipleFiles"] as? Bool ?? false
|
|
28
|
+
let fileTypes = options["fileTypes"] as? [String] ?? ["public.data"]
|
|
29
|
+
|
|
30
|
+
let supportedTypes: [UTType] = fileTypes.compactMap { mime in
|
|
31
|
+
switch mime {
|
|
32
|
+
case "application/pdf": return .pdf
|
|
33
|
+
case "image/*": return .image
|
|
34
|
+
case "application/msword": return UTType(importedAs: "com.microsoft.word.doc")
|
|
35
|
+
case "application/vnd.openxmlformats-officedocument.wordprocessingml.document": return UTType(importedAs: "org.openxmlformats.wordprocessingml.document")
|
|
36
|
+
case "application/vnd.ms-excel": return UTType(importedAs: "com.microsoft.excel.xls")
|
|
37
|
+
case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": return UTType(importedAs: "org.openxmlformats.spreadsheetml.sheet")
|
|
38
|
+
case "application/vnd.ms-powerpoint": return UTType(importedAs: "com.microsoft.powerpoint.ppt")
|
|
39
|
+
case "application/vnd.openxmlformats-officedocument.presentationml.presentation": return UTType(importedAs: "org.openxmlformats.presentationml.presentation")
|
|
40
|
+
default: return nil
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let picker = UIDocumentPickerViewController(forOpeningContentTypes: supportedTypes, asCopy: true)
|
|
45
|
+
picker.allowsMultipleSelection = multipleFiles
|
|
46
|
+
picker.delegate = self
|
|
47
|
+
|
|
48
|
+
DispatchQueue.main.async {
|
|
49
|
+
if let rootVC = UIApplication.shared.windows.first?.rootViewController {
|
|
50
|
+
var topVC = rootVC
|
|
51
|
+
while let presented = topVC.presentedViewController {
|
|
52
|
+
topVC = presented
|
|
53
|
+
}
|
|
54
|
+
topVC.present(picker, animated: true, completion: nil)
|
|
55
|
+
} else {
|
|
56
|
+
failureCallback(["{\"code\":\"NO_ROOT\", \"message\":\"Root view controller bulunamadı.\"}"])
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
|
|
62
|
+
var results: [[String: Any]] = []
|
|
63
|
+
|
|
64
|
+
for url in urls {
|
|
65
|
+
do {
|
|
66
|
+
let fileData = try Data(contentsOf: url)
|
|
67
|
+
let fileSize = Int64(fileData.count)
|
|
68
|
+
|
|
69
|
+
if fileSize > maxFileSize {
|
|
70
|
+
rejectCallback?(["{\"code\":\"TOO_LARGE\", \"message\":\"Seçilen dosya 3 MB'dan büyük olamaz.\"}"])
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let base64String = fileData.base64EncodedString()
|
|
75
|
+
let fileName = url.lastPathComponent
|
|
76
|
+
let fileType = url.pathExtension
|
|
77
|
+
|
|
78
|
+
let dict: [String: Any] = [
|
|
79
|
+
"fileName": fileName,
|
|
80
|
+
"fileSize": fileSize,
|
|
81
|
+
"fileType": fileType,
|
|
82
|
+
"fileUri": url.absoluteString,
|
|
83
|
+
"base64": base64String
|
|
84
|
+
]
|
|
85
|
+
results.append(dict)
|
|
86
|
+
} catch {
|
|
87
|
+
rejectCallback?(["{\"code\":\"READ_ERROR\", \"message\":\"Dosya okunamadı: \(error.localizedDescription)\"}"])
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
resolveCallback?(results)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
|
|
96
|
+
rejectCallback?(["{\"code\":\"CANCELLED\", \"message\":\"Kullanıcı iptal etti\"}"])
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _reactNative = require("react-native");
|
|
8
|
+
// Native modülün tip tanımı
|
|
9
|
+
|
|
10
|
+
const LINKING_ERROR = `The package 'react--native-my-uploader' doesn't seem to be linked. Make sure: \n\n` + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n';
|
|
11
|
+
const DocumentPicker = _reactNative.NativeModules.DocumentPicker ? _reactNative.NativeModules.DocumentPicker : new Proxy({}, {
|
|
12
|
+
get() {
|
|
13
|
+
throw new Error(LINKING_ERROR);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
var _default = exports.default = DocumentPicker;
|
|
17
|
+
//# sourceMappingURL=NativeMyUploader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_reactNative","require","LINKING_ERROR","DocumentPicker","NativeModules","Proxy","get","Error","_default","exports","default"],"sources":["NativeMyUploader.ts"],"sourcesContent":["import { NativeModules } from 'react-native';\nimport type { FileInfo, DocumentPickerOptions } from './types';\n\n\n// Native modülün tip tanımı\ninterface MyUploaderType {\n openDocument(options: DocumentPickerOptions): Promise<FileInfo[]>;\n}\n\nconst LINKING_ERROR =\n `The package 'react--native-my-uploader' doesn't seem to be linked. Make sure: \\n\\n` +\n '- You rebuilt the app after installing the package\\n' +\n '- You are not using Expo Go\\n';\n \n\nconst DocumentPicker = NativeModules.DocumentPicker\n ? (NativeModules.DocumentPicker as MyUploaderType)\n : new Proxy(\n {},\n {\n get() {\n throw new Error(LINKING_ERROR);\n },\n }\n );\n\nexport default DocumentPicker;"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAIA;;AAKA,MAAMC,aAAa,GACjB,oFAAoF,GACpF,sDAAsD,GACtD,+BAA+B;AAGjC,MAAMC,cAAc,GAAGC,0BAAa,CAACD,cAAc,GAC9CC,0BAAa,CAACD,cAAc,GAC7B,IAAIE,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACL,aAAa,CAAC;EAChC;AACF,CACF,CAAC;AAAC,IAAAM,QAAA,GAAAC,OAAA,CAAAC,OAAA,GAESP,cAAc","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":["index.d.ts"],"sourcesContent":["// Dosya: index.d.ts\n\n// ADIM 1: Tipleri DIŞA AKTARARAK (export) doğrudan burada tanımla.\n// (Artık 'import' kullanmıyoruz)\n\n/**\n * Native modülden dönen tek bir dosyanın yapısını tanımlar.\n * Bu tip, paket kullanıcıları tarafından import edilebilir.\n */\nexport interface FileInfo {\n fileName: string;\n fileSize: number; // in bytes\n fileType: string | null;\n fileUri: string;\n base64: string;\n}\n\n/**\n * `pickFile` fonksiyonuna gönderilebilecek tüm opsiyonları tanımlar.\n * Bu tip, paket kullanıcıları tarafından import edilebilir.\n */\nexport interface DocumentPickerOptions {\n /**\n * Birden fazla dosya seçimine izin ver.\n * @default false\n */\n multipleFiles?: boolean;\n\n /**\n * Filtrelenecek dosya tipleri (MIME types).\n * @default ['* / *']\n */\n fileTypes?: string[];\n\n /**\n * MB cinsinden maksimum dosya boyutu.\n * @default 0 (limitsiz)\n */\n maxSize?: number;\n\n /**\n * Seçilebilecek maksimum dosya sayısı. `multipleFiles: true` olmalıdır.\n * Belirtilmezse ve `multipleFiles: true` ise varsayılan olarak 3'tür.\n */\n maxFiles?: number;\n\n /**\n * Zaten seçilmiş olduğu için tekrar seçilmesi engellenecek dosyaların URI listesi.\n */\n excludedUris?: string[];\n}\n\n// ADIM 2: Fonksiyonu da DIŞA AKTARARAK (export) tanımla.\n\n/**\n * Dosya seçiciyi açar ve seçilen dosyaların bilgilerini döndürür.\n * @param options Dosya seçici için yapılandırma ayarları.\n * @returns Seçilen dosya bilgilerini içeren bir Promise<FileInfo[]> döner.\n */\nexport function pickFile(options?: DocumentPickerOptions): Promise<FileInfo[]>;"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.pickFile = pickFile;
|
|
7
|
+
var _reactNative = require("react-native");
|
|
8
|
+
// Gerekli tipleri ve arayüzleri import et
|
|
9
|
+
|
|
10
|
+
// Native modülün düzgün bir şekilde bağlanıp bağlanmadığını kontrol eden güvenlik mekanizması
|
|
11
|
+
const LINKING_ERROR = `The package 'react-native-my-uploader' doesn't seem to be linked.`;
|
|
12
|
+
const DocumentPicker = _reactNative.NativeModules.DocumentPicker ? _reactNative.NativeModules.DocumentPicker : new Proxy({}, {
|
|
13
|
+
get() {
|
|
14
|
+
throw new Error(LINKING_ERROR);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Dosya seçiciyi açar ve seçilen dosyaların bilgilerini döndürür.
|
|
20
|
+
* @param options Dosya seçici için yapılandırma ayarları.
|
|
21
|
+
* @returns Seçilen dosya bilgilerini içeren bir Promise<FileInfo[]> döner.
|
|
22
|
+
*/
|
|
23
|
+
async function pickFile(options = {}) {
|
|
24
|
+
var _options$excludedUris, _options$fileTypes, _options$maxSize;
|
|
25
|
+
// Gelen opsiyonları varsayılan değerlerle birleştir
|
|
26
|
+
const {
|
|
27
|
+
multipleFiles = false,
|
|
28
|
+
maxFiles = 0,
|
|
29
|
+
...rest
|
|
30
|
+
} = options;
|
|
31
|
+
if (!multipleFiles && maxFiles > 1) {
|
|
32
|
+
throw new Error('`maxFiles` cannot be greater than 1 when `multipleFiles` is false.');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// KURAL: `multipleFiles: true` iken `maxFiles` belirtilmemişse, varsayılan olarak 3 ata.
|
|
36
|
+
let effectiveMaxFiles = maxFiles;
|
|
37
|
+
if (multipleFiles && maxFiles === 0) {
|
|
38
|
+
effectiveMaxFiles = 3;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Native koda gönderilecek nihai, temizlenmiş ve varsayılanları atanmış opsiyonları oluştur.
|
|
42
|
+
const finalOptions = {
|
|
43
|
+
...rest,
|
|
44
|
+
multipleFiles,
|
|
45
|
+
maxFiles: effectiveMaxFiles,
|
|
46
|
+
// Diğer opsiyonlar için de null/undefined kontrolü yap
|
|
47
|
+
excludedUris: (_options$excludedUris = options.excludedUris) !== null && _options$excludedUris !== void 0 ? _options$excludedUris : [],
|
|
48
|
+
fileTypes: (_options$fileTypes = options.fileTypes) !== null && _options$fileTypes !== void 0 ? _options$fileTypes : ['*/*'],
|
|
49
|
+
maxSize: (_options$maxSize = options.maxSize) !== null && _options$maxSize !== void 0 ? _options$maxSize : 0
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Nihai opsiyonlarla native modülü çağır
|
|
53
|
+
return DocumentPicker.openDocument(finalOptions);
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_reactNative","require","LINKING_ERROR","DocumentPicker","NativeModules","Proxy","get","Error","pickFile","options","_options$excludedUris","_options$fileTypes","_options$maxSize","multipleFiles","maxFiles","rest","effectiveMaxFiles","finalOptions","excludedUris","fileTypes","maxSize","openDocument"],"sources":["index.tsx"],"sourcesContent":["import { NativeModules } from 'react-native';\n\n// Gerekli tipleri ve arayüzleri import et\nimport type { FileInfo, DocumentPickerOptions } from './types';\n\n// Native modülün düzgün bir şekilde bağlanıp bağlanmadığını kontrol eden güvenlik mekanizması\nconst LINKING_ERROR = `The package 'react-native-my-uploader' doesn't seem to be linked.`;\n\nconst DocumentPicker = NativeModules.DocumentPicker\n ? NativeModules.DocumentPicker\n : new Proxy(\n {},\n {\n get() {\n throw new Error(LINKING_ERROR);\n },\n }\n );\n\n/**\n * Dosya seçiciyi açar ve seçilen dosyaların bilgilerini döndürür.\n * @param options Dosya seçici için yapılandırma ayarları.\n * @returns Seçilen dosya bilgilerini içeren bir Promise<FileInfo[]> döner.\n */\nexport async function pickFile(\n options: DocumentPickerOptions = {}\n): Promise<FileInfo[]> {\n // Gelen opsiyonları varsayılan değerlerle birleştir\n const { multipleFiles = false, maxFiles = 0, ...rest } = options;\n\n if (!multipleFiles && maxFiles > 1) {\n throw new Error(\n '`maxFiles` cannot be greater than 1 when `multipleFiles` is false.'\n );\n }\n\n // KURAL: `multipleFiles: true` iken `maxFiles` belirtilmemişse, varsayılan olarak 3 ata.\n let effectiveMaxFiles = maxFiles;\n if (multipleFiles && maxFiles === 0) {\n effectiveMaxFiles = 3;\n }\n\n // Native koda gönderilecek nihai, temizlenmiş ve varsayılanları atanmış opsiyonları oluştur.\n const finalOptions: DocumentPickerOptions = {\n ...rest,\n multipleFiles,\n maxFiles: effectiveMaxFiles,\n // Diğer opsiyonlar için de null/undefined kontrolü yap\n excludedUris: options.excludedUris ?? [],\n fileTypes: options.fileTypes ?? ['*/*'],\n maxSize: options.maxSize ?? 0,\n };\n\n // Nihai opsiyonlarla native modülü çağır\n return DocumentPicker.openDocument(finalOptions);\n}\n\nexport type { FileInfo, DocumentPickerOptions };"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAEA;;AAGA;AACA,MAAMC,aAAa,GAAG,mEAAmE;AAEzF,MAAMC,cAAc,GAAGC,0BAAa,CAACD,cAAc,GAC/CC,0BAAa,CAACD,cAAc,GAC5B,IAAIE,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACL,aAAa,CAAC;EAChC;AACF,CACF,CAAC;;AAEL;AACA;AACA;AACA;AACA;AACO,eAAeM,QAAQA,CAC5BC,OAA8B,GAAG,CAAC,CAAC,EACd;EAAA,IAAAC,qBAAA,EAAAC,kBAAA,EAAAC,gBAAA;EACrB;EACA,MAAM;IAAEC,aAAa,GAAG,KAAK;IAAEC,QAAQ,GAAG,CAAC;IAAE,GAAGC;EAAK,CAAC,GAAGN,OAAO;EAEhE,IAAI,CAACI,aAAa,IAAIC,QAAQ,GAAG,CAAC,EAAE;IAClC,MAAM,IAAIP,KAAK,CACb,oEACF,CAAC;EACH;;EAEA;EACA,IAAIS,iBAAiB,GAAGF,QAAQ;EAChC,IAAID,aAAa,IAAIC,QAAQ,KAAK,CAAC,EAAE;IACnCE,iBAAiB,GAAG,CAAC;EACvB;;EAEA;EACA,MAAMC,YAAmC,GAAG;IAC1C,GAAGF,IAAI;IACPF,aAAa;IACbC,QAAQ,EAAEE,iBAAiB;IAC3B;IACAE,YAAY,GAAAR,qBAAA,GAAED,OAAO,CAACS,YAAY,cAAAR,qBAAA,cAAAA,qBAAA,GAAI,EAAE;IACxCS,SAAS,GAAAR,kBAAA,GAAEF,OAAO,CAACU,SAAS,cAAAR,kBAAA,cAAAA,kBAAA,GAAI,CAAC,KAAK,CAAC;IACvCS,OAAO,GAAAR,gBAAA,GAAEH,OAAO,CAACW,OAAO,cAAAR,gBAAA,cAAAA,gBAAA,GAAI;EAC9B,CAAC;;EAED;EACA,OAAOT,cAAc,CAACkB,YAAY,CAACJ,YAAY,CAAC;AAClD","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":["types.ts"],"sourcesContent":["// Dosya: src/types.ts\n\n/**\n * Native modülden dönen tek bir dosyanın yapısını tanımlar.\n */\nexport interface FileInfo {\n fileName: string;\n fileSize: number; // in bytes\n fileType: string | null;\n fileUri: string;\n base64: string;\n}\n\n/**\n * `pickFile` fonksiyonuna gönderilebilecek tüm opsiyonları tanımlar.\n */\nexport interface DocumentPickerOptions {\n /**\n * Birden fazla dosya seçimine izin ver.\n * @default false\n */\n multipleFiles?: boolean;\n\n /**\n * Filtrelenecek dosya tipleri (MIME types).\n * @default ['* / *'] (tüm dosyalar)\n * @example ['image/jpeg', 'application/pdf']\n */\n fileTypes?: string[];\n\n /**\n * MB cinsinden maksimum dosya boyutu.\n * @default 0 (limitsiz)\n */\n maxSize?: number;\n\n /**\n * Seçilebilecek maksimum dosya sayısı. `multipleFiles: true` olmalıdır.\n * Belirtilmezse ve `multipleFiles: true` ise varsayılan olarak 3'tür.\n * @default 0 (limitsiz, ama varsayılan mantığı uygulanır)\n */\n maxFiles?: number;\n\n /**\n * Zaten seçilmiş olduğu için tekrar seçilmesi engellenecek dosyaların URI listesi.\n */\n excludedUris?: string[];\n}"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { NativeModules } from 'react-native';
|
|
2
|
+
|
|
3
|
+
// Native modülün tip tanımı
|
|
4
|
+
|
|
5
|
+
const LINKING_ERROR = `The package 'react--native-my-uploader' doesn't seem to be linked. Make sure: \n\n` + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n';
|
|
6
|
+
const DocumentPicker = NativeModules.DocumentPicker ? NativeModules.DocumentPicker : new Proxy({}, {
|
|
7
|
+
get() {
|
|
8
|
+
throw new Error(LINKING_ERROR);
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
export default DocumentPicker;
|
|
12
|
+
//# sourceMappingURL=NativeMyUploader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["NativeModules","LINKING_ERROR","DocumentPicker","Proxy","get","Error"],"sources":["NativeMyUploader.ts"],"sourcesContent":["import { NativeModules } from 'react-native';\nimport type { FileInfo, DocumentPickerOptions } from './types';\n\n\n// Native modülün tip tanımı\ninterface MyUploaderType {\n openDocument(options: DocumentPickerOptions): Promise<FileInfo[]>;\n}\n\nconst LINKING_ERROR =\n `The package 'react--native-my-uploader' doesn't seem to be linked. Make sure: \\n\\n` +\n '- You rebuilt the app after installing the package\\n' +\n '- You are not using Expo Go\\n';\n \n\nconst DocumentPicker = NativeModules.DocumentPicker\n ? (NativeModules.DocumentPicker as MyUploaderType)\n : new Proxy(\n {},\n {\n get() {\n throw new Error(LINKING_ERROR);\n },\n }\n );\n\nexport default DocumentPicker;"],"mappings":"AAAA,SAASA,aAAa,QAAQ,cAAc;;AAI5C;;AAKA,MAAMC,aAAa,GACjB,oFAAoF,GACpF,sDAAsD,GACtD,+BAA+B;AAGjC,MAAMC,cAAc,GAAGF,aAAa,CAACE,cAAc,GAC9CF,aAAa,CAACE,cAAc,GAC7B,IAAIC,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACJ,aAAa,CAAC;EAChC;AACF,CACF,CAAC;AAEL,eAAeC,cAAc","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":["index.d.ts"],"sourcesContent":["// Dosya: index.d.ts\n\n// ADIM 1: Tipleri DIŞA AKTARARAK (export) doğrudan burada tanımla.\n// (Artık 'import' kullanmıyoruz)\n\n/**\n * Native modülden dönen tek bir dosyanın yapısını tanımlar.\n * Bu tip, paket kullanıcıları tarafından import edilebilir.\n */\nexport interface FileInfo {\n fileName: string;\n fileSize: number; // in bytes\n fileType: string | null;\n fileUri: string;\n base64: string;\n}\n\n/**\n * `pickFile` fonksiyonuna gönderilebilecek tüm opsiyonları tanımlar.\n * Bu tip, paket kullanıcıları tarafından import edilebilir.\n */\nexport interface DocumentPickerOptions {\n /**\n * Birden fazla dosya seçimine izin ver.\n * @default false\n */\n multipleFiles?: boolean;\n\n /**\n * Filtrelenecek dosya tipleri (MIME types).\n * @default ['* / *']\n */\n fileTypes?: string[];\n\n /**\n * MB cinsinden maksimum dosya boyutu.\n * @default 0 (limitsiz)\n */\n maxSize?: number;\n\n /**\n * Seçilebilecek maksimum dosya sayısı. `multipleFiles: true` olmalıdır.\n * Belirtilmezse ve `multipleFiles: true` ise varsayılan olarak 3'tür.\n */\n maxFiles?: number;\n\n /**\n * Zaten seçilmiş olduğu için tekrar seçilmesi engellenecek dosyaların URI listesi.\n */\n excludedUris?: string[];\n}\n\n// ADIM 2: Fonksiyonu da DIŞA AKTARARAK (export) tanımla.\n\n/**\n * Dosya seçiciyi açar ve seçilen dosyaların bilgilerini döndürür.\n * @param options Dosya seçici için yapılandırma ayarları.\n * @returns Seçilen dosya bilgilerini içeren bir Promise<FileInfo[]> döner.\n */\nexport function pickFile(options?: DocumentPickerOptions): Promise<FileInfo[]>;"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { NativeModules } from 'react-native';
|
|
2
|
+
|
|
3
|
+
// Gerekli tipleri ve arayüzleri import et
|
|
4
|
+
|
|
5
|
+
// Native modülün düzgün bir şekilde bağlanıp bağlanmadığını kontrol eden güvenlik mekanizması
|
|
6
|
+
const LINKING_ERROR = `The package 'react-native-my-uploader' doesn't seem to be linked.`;
|
|
7
|
+
const DocumentPicker = NativeModules.DocumentPicker ? NativeModules.DocumentPicker : new Proxy({}, {
|
|
8
|
+
get() {
|
|
9
|
+
throw new Error(LINKING_ERROR);
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Dosya seçiciyi açar ve seçilen dosyaların bilgilerini döndürür.
|
|
15
|
+
* @param options Dosya seçici için yapılandırma ayarları.
|
|
16
|
+
* @returns Seçilen dosya bilgilerini içeren bir Promise<FileInfo[]> döner.
|
|
17
|
+
*/
|
|
18
|
+
export async function pickFile(options = {}) {
|
|
19
|
+
var _options$excludedUris, _options$fileTypes, _options$maxSize;
|
|
20
|
+
// Gelen opsiyonları varsayılan değerlerle birleştir
|
|
21
|
+
const {
|
|
22
|
+
multipleFiles = false,
|
|
23
|
+
maxFiles = 0,
|
|
24
|
+
...rest
|
|
25
|
+
} = options;
|
|
26
|
+
if (!multipleFiles && maxFiles > 1) {
|
|
27
|
+
throw new Error('`maxFiles` cannot be greater than 1 when `multipleFiles` is false.');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// KURAL: `multipleFiles: true` iken `maxFiles` belirtilmemişse, varsayılan olarak 3 ata.
|
|
31
|
+
let effectiveMaxFiles = maxFiles;
|
|
32
|
+
if (multipleFiles && maxFiles === 0) {
|
|
33
|
+
effectiveMaxFiles = 3;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Native koda gönderilecek nihai, temizlenmiş ve varsayılanları atanmış opsiyonları oluştur.
|
|
37
|
+
const finalOptions = {
|
|
38
|
+
...rest,
|
|
39
|
+
multipleFiles,
|
|
40
|
+
maxFiles: effectiveMaxFiles,
|
|
41
|
+
// Diğer opsiyonlar için de null/undefined kontrolü yap
|
|
42
|
+
excludedUris: (_options$excludedUris = options.excludedUris) !== null && _options$excludedUris !== void 0 ? _options$excludedUris : [],
|
|
43
|
+
fileTypes: (_options$fileTypes = options.fileTypes) !== null && _options$fileTypes !== void 0 ? _options$fileTypes : ['*/*'],
|
|
44
|
+
maxSize: (_options$maxSize = options.maxSize) !== null && _options$maxSize !== void 0 ? _options$maxSize : 0
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Nihai opsiyonlarla native modülü çağır
|
|
48
|
+
return DocumentPicker.openDocument(finalOptions);
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["NativeModules","LINKING_ERROR","DocumentPicker","Proxy","get","Error","pickFile","options","_options$excludedUris","_options$fileTypes","_options$maxSize","multipleFiles","maxFiles","rest","effectiveMaxFiles","finalOptions","excludedUris","fileTypes","maxSize","openDocument"],"sources":["index.tsx"],"sourcesContent":["import { NativeModules } from 'react-native';\n\n// Gerekli tipleri ve arayüzleri import et\nimport type { FileInfo, DocumentPickerOptions } from './types';\n\n// Native modülün düzgün bir şekilde bağlanıp bağlanmadığını kontrol eden güvenlik mekanizması\nconst LINKING_ERROR = `The package 'react-native-my-uploader' doesn't seem to be linked.`;\n\nconst DocumentPicker = NativeModules.DocumentPicker\n ? NativeModules.DocumentPicker\n : new Proxy(\n {},\n {\n get() {\n throw new Error(LINKING_ERROR);\n },\n }\n );\n\n/**\n * Dosya seçiciyi açar ve seçilen dosyaların bilgilerini döndürür.\n * @param options Dosya seçici için yapılandırma ayarları.\n * @returns Seçilen dosya bilgilerini içeren bir Promise<FileInfo[]> döner.\n */\nexport async function pickFile(\n options: DocumentPickerOptions = {}\n): Promise<FileInfo[]> {\n // Gelen opsiyonları varsayılan değerlerle birleştir\n const { multipleFiles = false, maxFiles = 0, ...rest } = options;\n\n if (!multipleFiles && maxFiles > 1) {\n throw new Error(\n '`maxFiles` cannot be greater than 1 when `multipleFiles` is false.'\n );\n }\n\n // KURAL: `multipleFiles: true` iken `maxFiles` belirtilmemişse, varsayılan olarak 3 ata.\n let effectiveMaxFiles = maxFiles;\n if (multipleFiles && maxFiles === 0) {\n effectiveMaxFiles = 3;\n }\n\n // Native koda gönderilecek nihai, temizlenmiş ve varsayılanları atanmış opsiyonları oluştur.\n const finalOptions: DocumentPickerOptions = {\n ...rest,\n multipleFiles,\n maxFiles: effectiveMaxFiles,\n // Diğer opsiyonlar için de null/undefined kontrolü yap\n excludedUris: options.excludedUris ?? [],\n fileTypes: options.fileTypes ?? ['*/*'],\n maxSize: options.maxSize ?? 0,\n };\n\n // Nihai opsiyonlarla native modülü çağır\n return DocumentPicker.openDocument(finalOptions);\n}\n\nexport type { FileInfo, DocumentPickerOptions };"],"mappings":"AAAA,SAASA,aAAa,QAAQ,cAAc;;AAE5C;;AAGA;AACA,MAAMC,aAAa,GAAG,mEAAmE;AAEzF,MAAMC,cAAc,GAAGF,aAAa,CAACE,cAAc,GAC/CF,aAAa,CAACE,cAAc,GAC5B,IAAIC,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACJ,aAAa,CAAC;EAChC;AACF,CACF,CAAC;;AAEL;AACA;AACA;AACA;AACA;AACA,OAAO,eAAeK,QAAQA,CAC5BC,OAA8B,GAAG,CAAC,CAAC,EACd;EAAA,IAAAC,qBAAA,EAAAC,kBAAA,EAAAC,gBAAA;EACrB;EACA,MAAM;IAAEC,aAAa,GAAG,KAAK;IAAEC,QAAQ,GAAG,CAAC;IAAE,GAAGC;EAAK,CAAC,GAAGN,OAAO;EAEhE,IAAI,CAACI,aAAa,IAAIC,QAAQ,GAAG,CAAC,EAAE;IAClC,MAAM,IAAIP,KAAK,CACb,oEACF,CAAC;EACH;;EAEA;EACA,IAAIS,iBAAiB,GAAGF,QAAQ;EAChC,IAAID,aAAa,IAAIC,QAAQ,KAAK,CAAC,EAAE;IACnCE,iBAAiB,GAAG,CAAC;EACvB;;EAEA;EACA,MAAMC,YAAmC,GAAG;IAC1C,GAAGF,IAAI;IACPF,aAAa;IACbC,QAAQ,EAAEE,iBAAiB;IAC3B;IACAE,YAAY,GAAAR,qBAAA,GAAED,OAAO,CAACS,YAAY,cAAAR,qBAAA,cAAAA,qBAAA,GAAI,EAAE;IACxCS,SAAS,GAAAR,kBAAA,GAAEF,OAAO,CAACU,SAAS,cAAAR,kBAAA,cAAAA,kBAAA,GAAI,CAAC,KAAK,CAAC;IACvCS,OAAO,GAAAR,gBAAA,GAAEH,OAAO,CAACW,OAAO,cAAAR,gBAAA,cAAAA,gBAAA,GAAI;EAC9B,CAAC;;EAED;EACA,OAAOR,cAAc,CAACiB,YAAY,CAACJ,YAAY,CAAC;AAClD","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":["types.ts"],"sourcesContent":["// Dosya: src/types.ts\n\n/**\n * Native modülden dönen tek bir dosyanın yapısını tanımlar.\n */\nexport interface FileInfo {\n fileName: string;\n fileSize: number; // in bytes\n fileType: string | null;\n fileUri: string;\n base64: string;\n}\n\n/**\n * `pickFile` fonksiyonuna gönderilebilecek tüm opsiyonları tanımlar.\n */\nexport interface DocumentPickerOptions {\n /**\n * Birden fazla dosya seçimine izin ver.\n * @default false\n */\n multipleFiles?: boolean;\n\n /**\n * Filtrelenecek dosya tipleri (MIME types).\n * @default ['* / *'] (tüm dosyalar)\n * @example ['image/jpeg', 'application/pdf']\n */\n fileTypes?: string[];\n\n /**\n * MB cinsinden maksimum dosya boyutu.\n * @default 0 (limitsiz)\n */\n maxSize?: number;\n\n /**\n * Seçilebilecek maksimum dosya sayısı. `multipleFiles: true` olmalıdır.\n * Belirtilmezse ve `multipleFiles: true` ise varsayılan olarak 3'tür.\n * @default 0 (limitsiz, ama varsayılan mantığı uygulanır)\n */\n maxFiles?: number;\n\n /**\n * Zaten seçilmiş olduğu için tekrar seçilmesi engellenecek dosyaların URI listesi.\n */\n excludedUris?: string[];\n}"],"mappings":"","ignoreList":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-native-my-uploader-android",
|
|
3
|
+
"version": "1.0.21",
|
|
4
|
+
"description": "file uploader",
|
|
5
|
+
"main": "./lib/module/index.js",
|
|
6
|
+
"types": "./src/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"source": "./src/index.tsx",
|
|
10
|
+
"types": "./src/index.d.ts",
|
|
11
|
+
"default": "./lib/module/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"src",
|
|
16
|
+
"lib",
|
|
17
|
+
"android",
|
|
18
|
+
"ios",
|
|
19
|
+
"cpp",
|
|
20
|
+
"*.podspec",
|
|
21
|
+
"react-native.config.js",
|
|
22
|
+
"!ios/build",
|
|
23
|
+
"!android/build",
|
|
24
|
+
"!android/gradle",
|
|
25
|
+
"!android/gradlew",
|
|
26
|
+
"!android/gradlew.bat",
|
|
27
|
+
"!android/local.properties",
|
|
28
|
+
"!**/__tests__",
|
|
29
|
+
"!**/__fixtures__",
|
|
30
|
+
"!**/__mocks__",
|
|
31
|
+
"!**/.*"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"example": "yarn workspace react-native-my-uploader-example",
|
|
35
|
+
"test": "jest",
|
|
36
|
+
"typecheck": "tsc",
|
|
37
|
+
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
38
|
+
"clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
|
|
39
|
+
"prepare": "bob build",
|
|
40
|
+
"release": "release-it --only-version"
|
|
41
|
+
},
|
|
42
|
+
"keywords": [
|
|
43
|
+
"react-native",
|
|
44
|
+
"ios",
|
|
45
|
+
"android"
|
|
46
|
+
],
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "git+https://www.npmjs.com/~yrncskn.git"
|
|
50
|
+
},
|
|
51
|
+
"author": "Yaren Coskun <yarencoskunceng@gmail.com> (https://www.npmjs.com/~yrncskn)",
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"bugs": {
|
|
54
|
+
"url": "https://www.npmjs.com/~yrncskn/issues"
|
|
55
|
+
},
|
|
56
|
+
"homepage": "https://www.npmjs.com/~yrncskn#readme",
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"registry": "https://registry.npmjs.org/"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@commitlint/config-conventional": "^19.8.1",
|
|
62
|
+
"@eslint/compat": "^1.3.2",
|
|
63
|
+
"@eslint/eslintrc": "^3.3.1",
|
|
64
|
+
"@eslint/js": "^9.35.0",
|
|
65
|
+
"@evilmartians/lefthook": "^1.12.3",
|
|
66
|
+
"@react-native-community/bob": "^0.17.1",
|
|
67
|
+
"@react-native-community/cli": "20.0.1",
|
|
68
|
+
"@react-native/babel-preset": "0.81.1",
|
|
69
|
+
"@react-native/eslint-config": "^0.81.1",
|
|
70
|
+
"@release-it/conventional-changelog": "^10.0.1",
|
|
71
|
+
"@types/jest": "^29.5.14",
|
|
72
|
+
"@types/react": "^19.1.0",
|
|
73
|
+
"@typescript-eslint/eslint-plugin": "^8.46.4",
|
|
74
|
+
"@typescript-eslint/parser": "^8.46.4",
|
|
75
|
+
"commitlint": "^19.8.1",
|
|
76
|
+
"del-cli": "^6.0.0",
|
|
77
|
+
"eslint": "^9.35.0",
|
|
78
|
+
"eslint-config-prettier": "^10.1.8",
|
|
79
|
+
"eslint-plugin-ft-flow": "^3.0.11",
|
|
80
|
+
"eslint-plugin-jest": "^29.0.1",
|
|
81
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
82
|
+
"eslint-plugin-react-native": "^5.0.0",
|
|
83
|
+
"jest": "^29.7.0",
|
|
84
|
+
"prettier": "^3.6.2",
|
|
85
|
+
"react": "19.1.0",
|
|
86
|
+
"react-native": "0.81.1",
|
|
87
|
+
"react-native-builder-bob": "^0.40.14",
|
|
88
|
+
"release-it": "^19.0.4",
|
|
89
|
+
"turbo": "^2.5.6",
|
|
90
|
+
"typescript": "^5.9.2"
|
|
91
|
+
},
|
|
92
|
+
"peerDependencies": {
|
|
93
|
+
"react": "*",
|
|
94
|
+
"react-native": "*"
|
|
95
|
+
},
|
|
96
|
+
"workspaces": [
|
|
97
|
+
"example"
|
|
98
|
+
],
|
|
99
|
+
"packageManager": "yarn@3.6.1",
|
|
100
|
+
"jest": {
|
|
101
|
+
"preset": "react-native",
|
|
102
|
+
"modulePathIgnorePatterns": [
|
|
103
|
+
"<rootDir>/example/node_modules",
|
|
104
|
+
"<rootDir>/lib/"
|
|
105
|
+
]
|
|
106
|
+
},
|
|
107
|
+
"commitlint": {
|
|
108
|
+
"extends": [
|
|
109
|
+
"@commitlint/config-conventional"
|
|
110
|
+
]
|
|
111
|
+
},
|
|
112
|
+
"release-it": {
|
|
113
|
+
"git": {
|
|
114
|
+
"commitMessage": "chore: release ${version}",
|
|
115
|
+
"tagName": "v${version}"
|
|
116
|
+
},
|
|
117
|
+
"npm": {
|
|
118
|
+
"publish": true
|
|
119
|
+
},
|
|
120
|
+
"github": {
|
|
121
|
+
"release": true
|
|
122
|
+
},
|
|
123
|
+
"plugins": {
|
|
124
|
+
"@release-it/conventional-changelog": {
|
|
125
|
+
"preset": {
|
|
126
|
+
"name": "angular"
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"prettier": {
|
|
132
|
+
"quoteProps": "consistent",
|
|
133
|
+
"singleQuote": true,
|
|
134
|
+
"tabWidth": 2,
|
|
135
|
+
"trailingComma": "es5",
|
|
136
|
+
"useTabs": false
|
|
137
|
+
},
|
|
138
|
+
"react-native-builder-bob": {
|
|
139
|
+
"source": "src",
|
|
140
|
+
"output": "lib",
|
|
141
|
+
"targets": [
|
|
142
|
+
[
|
|
143
|
+
"module",
|
|
144
|
+
{
|
|
145
|
+
"esm": true
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
[
|
|
149
|
+
"typescript",
|
|
150
|
+
{
|
|
151
|
+
"project": "tsconfig.build.json"
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
]
|
|
155
|
+
},
|
|
156
|
+
"create-react-native-library": {
|
|
157
|
+
"languages": "kotlin-objc",
|
|
158
|
+
"type": "turbo-module",
|
|
159
|
+
"version": "0.54.8"
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { NativeModules } from 'react-native';
|
|
2
|
+
import type { FileInfo, DocumentPickerOptions } from './types';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
// Native modülün tip tanımı
|
|
6
|
+
interface MyUploaderType {
|
|
7
|
+
openDocument(options: DocumentPickerOptions): Promise<FileInfo[]>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const LINKING_ERROR =
|
|
11
|
+
`The package 'react--native-my-uploader' doesn't seem to be linked. Make sure: \n\n` +
|
|
12
|
+
'- You rebuilt the app after installing the package\n' +
|
|
13
|
+
'- You are not using Expo Go\n';
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
const DocumentPicker = NativeModules.DocumentPicker
|
|
17
|
+
? (NativeModules.DocumentPicker as MyUploaderType)
|
|
18
|
+
: new Proxy(
|
|
19
|
+
{},
|
|
20
|
+
{
|
|
21
|
+
get() {
|
|
22
|
+
throw new Error(LINKING_ERROR);
|
|
23
|
+
},
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
export default DocumentPicker;
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Dosya: index.d.ts
|
|
2
|
+
|
|
3
|
+
// ADIM 1: Tipleri DIŞA AKTARARAK (export) doğrudan burada tanımla.
|
|
4
|
+
// (Artık 'import' kullanmıyoruz)
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Native modülden dönen tek bir dosyanın yapısını tanımlar.
|
|
8
|
+
* Bu tip, paket kullanıcıları tarafından import edilebilir.
|
|
9
|
+
*/
|
|
10
|
+
export interface FileInfo {
|
|
11
|
+
fileName: string;
|
|
12
|
+
fileSize: number; // in bytes
|
|
13
|
+
fileType: string | null;
|
|
14
|
+
fileUri: string;
|
|
15
|
+
base64: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* `pickFile` fonksiyonuna gönderilebilecek tüm opsiyonları tanımlar.
|
|
20
|
+
* Bu tip, paket kullanıcıları tarafından import edilebilir.
|
|
21
|
+
*/
|
|
22
|
+
export interface DocumentPickerOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Birden fazla dosya seçimine izin ver.
|
|
25
|
+
* @default false
|
|
26
|
+
*/
|
|
27
|
+
multipleFiles?: boolean;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Filtrelenecek dosya tipleri (MIME types).
|
|
31
|
+
* @default ['* / *']
|
|
32
|
+
*/
|
|
33
|
+
fileTypes?: string[];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* MB cinsinden maksimum dosya boyutu.
|
|
37
|
+
* @default 0 (limitsiz)
|
|
38
|
+
*/
|
|
39
|
+
maxSize?: number;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Seçilebilecek maksimum dosya sayısı. `multipleFiles: true` olmalıdır.
|
|
43
|
+
* Belirtilmezse ve `multipleFiles: true` ise varsayılan olarak 3'tür.
|
|
44
|
+
*/
|
|
45
|
+
maxFiles?: number;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Zaten seçilmiş olduğu için tekrar seçilmesi engellenecek dosyaların URI listesi.
|
|
49
|
+
*/
|
|
50
|
+
excludedUris?: string[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ADIM 2: Fonksiyonu da DIŞA AKTARARAK (export) tanımla.
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Dosya seçiciyi açar ve seçilen dosyaların bilgilerini döndürür.
|
|
57
|
+
* @param options Dosya seçici için yapılandırma ayarları.
|
|
58
|
+
* @returns Seçilen dosya bilgilerini içeren bir Promise<FileInfo[]> döner.
|
|
59
|
+
*/
|
|
60
|
+
export function pickFile(options?: DocumentPickerOptions): Promise<FileInfo[]>;
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { NativeModules } from 'react-native';
|
|
2
|
+
|
|
3
|
+
// Gerekli tipleri ve arayüzleri import et
|
|
4
|
+
import type { FileInfo, DocumentPickerOptions } from './types';
|
|
5
|
+
|
|
6
|
+
// Native modülün düzgün bir şekilde bağlanıp bağlanmadığını kontrol eden güvenlik mekanizması
|
|
7
|
+
const LINKING_ERROR = `The package 'react-native-my-uploader' doesn't seem to be linked.`;
|
|
8
|
+
|
|
9
|
+
const DocumentPicker = NativeModules.DocumentPicker
|
|
10
|
+
? NativeModules.DocumentPicker
|
|
11
|
+
: new Proxy(
|
|
12
|
+
{},
|
|
13
|
+
{
|
|
14
|
+
get() {
|
|
15
|
+
throw new Error(LINKING_ERROR);
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Dosya seçiciyi açar ve seçilen dosyaların bilgilerini döndürür.
|
|
22
|
+
* @param options Dosya seçici için yapılandırma ayarları.
|
|
23
|
+
* @returns Seçilen dosya bilgilerini içeren bir Promise<FileInfo[]> döner.
|
|
24
|
+
*/
|
|
25
|
+
export async function pickFile(
|
|
26
|
+
options: DocumentPickerOptions = {}
|
|
27
|
+
): Promise<FileInfo[]> {
|
|
28
|
+
// Gelen opsiyonları varsayılan değerlerle birleştir
|
|
29
|
+
const { multipleFiles = false, maxFiles = 0, ...rest } = options;
|
|
30
|
+
|
|
31
|
+
if (!multipleFiles && maxFiles > 1) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
'`maxFiles` cannot be greater than 1 when `multipleFiles` is false.'
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// KURAL: `multipleFiles: true` iken `maxFiles` belirtilmemişse, varsayılan olarak 3 ata.
|
|
38
|
+
let effectiveMaxFiles = maxFiles;
|
|
39
|
+
if (multipleFiles && maxFiles === 0) {
|
|
40
|
+
effectiveMaxFiles = 3;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Native koda gönderilecek nihai, temizlenmiş ve varsayılanları atanmış opsiyonları oluştur.
|
|
44
|
+
const finalOptions: DocumentPickerOptions = {
|
|
45
|
+
...rest,
|
|
46
|
+
multipleFiles,
|
|
47
|
+
maxFiles: effectiveMaxFiles,
|
|
48
|
+
// Diğer opsiyonlar için de null/undefined kontrolü yap
|
|
49
|
+
excludedUris: options.excludedUris ?? [],
|
|
50
|
+
fileTypes: options.fileTypes ?? ['*/*'],
|
|
51
|
+
maxSize: options.maxSize ?? 0,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Nihai opsiyonlarla native modülü çağır
|
|
55
|
+
return DocumentPicker.openDocument(finalOptions);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type { FileInfo, DocumentPickerOptions };
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Dosya: src/types.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Native modülden dönen tek bir dosyanın yapısını tanımlar.
|
|
5
|
+
*/
|
|
6
|
+
export interface FileInfo {
|
|
7
|
+
fileName: string;
|
|
8
|
+
fileSize: number; // in bytes
|
|
9
|
+
fileType: string | null;
|
|
10
|
+
fileUri: string;
|
|
11
|
+
base64: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* `pickFile` fonksiyonuna gönderilebilecek tüm opsiyonları tanımlar.
|
|
16
|
+
*/
|
|
17
|
+
export interface DocumentPickerOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Birden fazla dosya seçimine izin ver.
|
|
20
|
+
* @default false
|
|
21
|
+
*/
|
|
22
|
+
multipleFiles?: boolean;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Filtrelenecek dosya tipleri (MIME types).
|
|
26
|
+
* @default ['* / *'] (tüm dosyalar)
|
|
27
|
+
* @example ['image/jpeg', 'application/pdf']
|
|
28
|
+
*/
|
|
29
|
+
fileTypes?: string[];
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* MB cinsinden maksimum dosya boyutu.
|
|
33
|
+
* @default 0 (limitsiz)
|
|
34
|
+
*/
|
|
35
|
+
maxSize?: number;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Seçilebilecek maksimum dosya sayısı. `multipleFiles: true` olmalıdır.
|
|
39
|
+
* Belirtilmezse ve `multipleFiles: true` ise varsayılan olarak 3'tür.
|
|
40
|
+
* @default 0 (limitsiz, ama varsayılan mantığı uygulanır)
|
|
41
|
+
*/
|
|
42
|
+
maxFiles?: number;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Zaten seçilmiş olduğu için tekrar seçilmesi engellenecek dosyaların URI listesi.
|
|
46
|
+
*/
|
|
47
|
+
excludedUris?: string[];
|
|
48
|
+
}
|