react-native-kookit 0.2.4 → 0.2.7
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/ANDROID_FTP_UPDATE.md +161 -0
- package/ANDROID_PARAMETER_FIX.md +0 -0
- package/API_UNIFICATION.md +180 -0
- package/FTP_CLIENT_API.md +301 -0
- package/FTP_EXAMPLE_IMPLEMENTATION.md +0 -0
- package/FTP_FILE_LIST_ENHANCEMENT.md +186 -0
- package/FTP_FILE_OPERATIONS.md +0 -0
- package/android/src/main/java/expo/modules/kookit/FtpClient.kt +2 -0
- package/android/src/main/java/expo/modules/kookit/ReactNativeKookitModule.kt +252 -0
- package/build/FtpClient.d.ts +97 -0
- package/build/FtpClient.d.ts.map +1 -0
- package/build/FtpClient.js +199 -0
- package/build/FtpClient.js.map +1 -0
- package/build/ReactNativeKookit.types.d.ts +13 -5
- package/build/ReactNativeKookit.types.d.ts.map +1 -1
- package/build/ReactNativeKookit.types.js.map +1 -1
- package/build/ReactNativeKookitModule.d.ts +54 -12
- package/build/ReactNativeKookitModule.d.ts.map +1 -1
- package/build/ReactNativeKookitModule.js.map +1 -1
- package/build/index.d.ts +4 -3
- package/build/index.d.ts.map +1 -1
- package/build/index.js +4 -3
- package/build/index.js.map +1 -1
- package/ios/ReactNativeKookitModule.swift +199 -40
- package/package.json +1 -1
|
@@ -292,10 +292,17 @@ class FtpClient: @unchecked Sendable {
|
|
|
292
292
|
}
|
|
293
293
|
dataConnection.cancel()
|
|
294
294
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
295
|
+
// Check if the response already contained the completion message (226)
|
|
296
|
+
if response.contains("226") {
|
|
297
|
+
print("FTP DEBUG: Transfer completion already received in LIST response - skipping final response read")
|
|
298
|
+
} else {
|
|
299
|
+
print("FTP DEBUG: Reading final response...")
|
|
300
|
+
let finalResponse = try await readResponse()
|
|
301
|
+
let finalFirstLine = finalResponse.components(separatedBy: .newlines).first ?? finalResponse
|
|
302
|
+
print("FTP DEBUG: Final response: \(finalFirstLine)")
|
|
303
|
+
guard finalFirstLine.hasPrefix("226") else {
|
|
304
|
+
throw FtpError.commandFailed("LIST completion failed: \(finalFirstLine)")
|
|
305
|
+
}
|
|
299
306
|
}
|
|
300
307
|
|
|
301
308
|
let files = parseListingData(data)
|
|
@@ -803,7 +810,7 @@ public class ReactNativeKookitModule: Module {
|
|
|
803
810
|
private var volumeObserver: NSKeyValueObservation?
|
|
804
811
|
private var isVolumeKeyInterceptionEnabled = false
|
|
805
812
|
private var previousVolume: Float = 0.0
|
|
806
|
-
private var
|
|
813
|
+
private var ftpClients: [String: FtpClient] = [:] // Store multiple FTP clients by ID
|
|
807
814
|
|
|
808
815
|
// Each module class must implement the definition function. The definition consists of components
|
|
809
816
|
// that describes the module's functionality and behavior.
|
|
@@ -846,8 +853,31 @@ public class ReactNativeKookitModule: Module {
|
|
|
846
853
|
self.disableVolumeKeyInterception()
|
|
847
854
|
}
|
|
848
855
|
|
|
849
|
-
// FTP
|
|
850
|
-
AsyncFunction("
|
|
856
|
+
// New FTP Client API - Create new FTP client instance
|
|
857
|
+
AsyncFunction("createFtpClient") { (clientId: String, promise: Promise) in
|
|
858
|
+
if self.ftpClients[clientId] != nil {
|
|
859
|
+
promise.reject("FTP_CLIENT_EXISTS", "FTP client with ID '\(clientId)' already exists")
|
|
860
|
+
return
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
let ftpClient = FtpClient()
|
|
864
|
+
let progressDelegate = FtpProgressDelegateImpl(module: self, clientId: clientId)
|
|
865
|
+
ftpClient.setProgressDelegate(progressDelegate)
|
|
866
|
+
self.ftpClients[clientId] = ftpClient
|
|
867
|
+
|
|
868
|
+
promise.resolve([
|
|
869
|
+
"clientId": clientId,
|
|
870
|
+
"created": true
|
|
871
|
+
])
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
// Connect FTP client
|
|
875
|
+
AsyncFunction("ftpClientConnect") { (clientId: String, config: [String: Any], promise: Promise) in
|
|
876
|
+
guard let ftpClient = self.ftpClients[clientId] else {
|
|
877
|
+
promise.reject("FTP_CLIENT_NOT_FOUND", "FTP client with ID '\(clientId)' not found")
|
|
878
|
+
return
|
|
879
|
+
}
|
|
880
|
+
|
|
851
881
|
Task {
|
|
852
882
|
do {
|
|
853
883
|
let ftpConfig = FtpConnectionConfig(
|
|
@@ -859,30 +889,60 @@ public class ReactNativeKookitModule: Module {
|
|
|
859
889
|
timeout: config["timeout"] as? TimeInterval ?? 30.0
|
|
860
890
|
)
|
|
861
891
|
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
promise.resolve()
|
|
892
|
+
try await ftpClient.connect(config: ftpConfig)
|
|
893
|
+
promise.resolve([
|
|
894
|
+
"clientId": clientId,
|
|
895
|
+
"connected": true
|
|
896
|
+
])
|
|
868
897
|
} catch {
|
|
869
898
|
promise.reject("FTP_CONNECT_ERROR", error.localizedDescription)
|
|
870
899
|
}
|
|
871
900
|
}
|
|
872
901
|
}
|
|
873
902
|
|
|
874
|
-
|
|
903
|
+
// Disconnect FTP client
|
|
904
|
+
AsyncFunction("ftpClientDisconnect") { (clientId: String, promise: Promise) in
|
|
905
|
+
guard let ftpClient = self.ftpClients[clientId] else {
|
|
906
|
+
promise.reject("FTP_CLIENT_NOT_FOUND", "FTP client with ID '\(clientId)' not found")
|
|
907
|
+
return
|
|
908
|
+
}
|
|
909
|
+
|
|
875
910
|
Task {
|
|
876
|
-
await
|
|
877
|
-
|
|
878
|
-
|
|
911
|
+
await ftpClient.disconnect()
|
|
912
|
+
promise.resolve([
|
|
913
|
+
"clientId": clientId,
|
|
914
|
+
"disconnected": true
|
|
915
|
+
])
|
|
879
916
|
}
|
|
880
917
|
}
|
|
881
918
|
|
|
882
|
-
|
|
919
|
+
// Dispose FTP client
|
|
920
|
+
AsyncFunction("disposeFtpClient") { (clientId: String, promise: Promise) in
|
|
921
|
+
guard let ftpClient = self.ftpClients[clientId] else {
|
|
922
|
+
promise.reject("FTP_CLIENT_NOT_FOUND", "FTP client with ID '\(clientId)' not found")
|
|
923
|
+
return
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
Task {
|
|
927
|
+
await ftpClient.disconnect()
|
|
928
|
+
self.ftpClients.removeValue(forKey: clientId)
|
|
929
|
+
promise.resolve([
|
|
930
|
+
"clientId": clientId,
|
|
931
|
+
"disposed": true
|
|
932
|
+
])
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
// List files
|
|
937
|
+
AsyncFunction("ftpClientList") { (clientId: String, path: String?, promise: Promise) in
|
|
938
|
+
guard let ftpClient = self.ftpClients[clientId] else {
|
|
939
|
+
promise.reject("FTP_CLIENT_NOT_FOUND", "FTP client with ID '\(clientId)' not found")
|
|
940
|
+
return
|
|
941
|
+
}
|
|
942
|
+
|
|
883
943
|
Task {
|
|
884
944
|
do {
|
|
885
|
-
let files = try await
|
|
945
|
+
let files = try await ftpClient.listFiles(path: path)
|
|
886
946
|
let result = files.map { file in
|
|
887
947
|
[
|
|
888
948
|
"name": file.name,
|
|
@@ -899,72 +959,163 @@ public class ReactNativeKookitModule: Module {
|
|
|
899
959
|
}
|
|
900
960
|
}
|
|
901
961
|
|
|
902
|
-
|
|
962
|
+
// Download file
|
|
963
|
+
AsyncFunction("ftpClientDownload") { (clientId: String, remotePath: String, localPath: String, promise: Promise) in
|
|
964
|
+
guard let ftpClient = self.ftpClients[clientId] else {
|
|
965
|
+
promise.reject("FTP_CLIENT_NOT_FOUND", "FTP client with ID '\(clientId)' not found")
|
|
966
|
+
return
|
|
967
|
+
}
|
|
968
|
+
|
|
903
969
|
Task {
|
|
904
970
|
do {
|
|
905
|
-
try await
|
|
906
|
-
promise.resolve(
|
|
971
|
+
try await ftpClient.downloadFile(remotePath: remotePath, localPath: localPath)
|
|
972
|
+
promise.resolve([
|
|
973
|
+
"clientId": clientId,
|
|
974
|
+
"remotePath": remotePath,
|
|
975
|
+
"localPath": localPath,
|
|
976
|
+
"downloaded": true
|
|
977
|
+
])
|
|
907
978
|
} catch {
|
|
908
979
|
promise.reject("FTP_DOWNLOAD_ERROR", error.localizedDescription)
|
|
909
980
|
}
|
|
910
981
|
}
|
|
911
982
|
}
|
|
912
983
|
|
|
913
|
-
|
|
984
|
+
// Upload file
|
|
985
|
+
AsyncFunction("ftpClientUpload") { (clientId: String, localPath: String, remotePath: String, promise: Promise) in
|
|
986
|
+
guard let ftpClient = self.ftpClients[clientId] else {
|
|
987
|
+
promise.reject("FTP_CLIENT_NOT_FOUND", "FTP client with ID '\(clientId)' not found")
|
|
988
|
+
return
|
|
989
|
+
}
|
|
990
|
+
|
|
914
991
|
Task {
|
|
915
992
|
do {
|
|
916
|
-
try await
|
|
917
|
-
promise.resolve(
|
|
993
|
+
try await ftpClient.uploadFile(localPath: localPath, remotePath: remotePath)
|
|
994
|
+
promise.resolve([
|
|
995
|
+
"clientId": clientId,
|
|
996
|
+
"localPath": localPath,
|
|
997
|
+
"remotePath": remotePath,
|
|
998
|
+
"uploaded": true
|
|
999
|
+
])
|
|
918
1000
|
} catch {
|
|
919
1001
|
promise.reject("FTP_UPLOAD_ERROR", error.localizedDescription)
|
|
920
1002
|
}
|
|
921
1003
|
}
|
|
922
1004
|
}
|
|
923
1005
|
|
|
924
|
-
|
|
1006
|
+
// Delete file
|
|
1007
|
+
AsyncFunction("ftpClientDelete") { (clientId: String, remotePath: String, isDirectory: Bool?, promise: Promise) in
|
|
1008
|
+
guard let ftpClient = self.ftpClients[clientId] else {
|
|
1009
|
+
promise.reject("FTP_CLIENT_NOT_FOUND", "FTP client with ID '\(clientId)' not found")
|
|
1010
|
+
return
|
|
1011
|
+
}
|
|
1012
|
+
|
|
925
1013
|
Task {
|
|
926
1014
|
do {
|
|
927
|
-
try await
|
|
928
|
-
promise.resolve(
|
|
1015
|
+
try await ftpClient.deleteFile(remotePath: remotePath, isDirectory: isDirectory ?? false)
|
|
1016
|
+
promise.resolve([
|
|
1017
|
+
"clientId": clientId,
|
|
1018
|
+
"remotePath": remotePath,
|
|
1019
|
+
"deleted": true
|
|
1020
|
+
])
|
|
929
1021
|
} catch {
|
|
930
1022
|
promise.reject("FTP_DELETE_ERROR", error.localizedDescription)
|
|
931
1023
|
}
|
|
932
1024
|
}
|
|
933
1025
|
}
|
|
934
1026
|
|
|
935
|
-
|
|
1027
|
+
// Create directory
|
|
1028
|
+
AsyncFunction("ftpClientCreateDirectory") { (clientId: String, remotePath: String, promise: Promise) in
|
|
1029
|
+
guard let ftpClient = self.ftpClients[clientId] else {
|
|
1030
|
+
promise.reject("FTP_CLIENT_NOT_FOUND", "FTP client with ID '\(clientId)' not found")
|
|
1031
|
+
return
|
|
1032
|
+
}
|
|
1033
|
+
|
|
936
1034
|
Task {
|
|
937
1035
|
do {
|
|
938
|
-
try await
|
|
939
|
-
promise.resolve(
|
|
1036
|
+
try await ftpClient.createDirectory(remotePath: remotePath)
|
|
1037
|
+
promise.resolve([
|
|
1038
|
+
"clientId": clientId,
|
|
1039
|
+
"remotePath": remotePath,
|
|
1040
|
+
"created": true
|
|
1041
|
+
])
|
|
940
1042
|
} catch {
|
|
941
1043
|
promise.reject("FTP_CREATE_DIR_ERROR", error.localizedDescription)
|
|
942
1044
|
}
|
|
943
1045
|
}
|
|
944
1046
|
}
|
|
945
1047
|
|
|
946
|
-
|
|
1048
|
+
// Change directory
|
|
1049
|
+
AsyncFunction("ftpClientChangeDirectory") { (clientId: String, remotePath: String, promise: Promise) in
|
|
1050
|
+
guard let ftpClient = self.ftpClients[clientId] else {
|
|
1051
|
+
promise.reject("FTP_CLIENT_NOT_FOUND", "FTP client with ID '\(clientId)' not found")
|
|
1052
|
+
return
|
|
1053
|
+
}
|
|
1054
|
+
|
|
947
1055
|
Task {
|
|
948
1056
|
do {
|
|
949
|
-
try await
|
|
950
|
-
promise.resolve(
|
|
1057
|
+
try await ftpClient.changeDirectory(remotePath: remotePath)
|
|
1058
|
+
promise.resolve([
|
|
1059
|
+
"clientId": clientId,
|
|
1060
|
+
"currentDirectory": remotePath,
|
|
1061
|
+
"changed": true
|
|
1062
|
+
])
|
|
951
1063
|
} catch {
|
|
952
1064
|
promise.reject("FTP_CHANGE_DIR_ERROR", error.localizedDescription)
|
|
953
1065
|
}
|
|
954
1066
|
}
|
|
955
1067
|
}
|
|
956
1068
|
|
|
957
|
-
|
|
1069
|
+
// Get current directory
|
|
1070
|
+
AsyncFunction("ftpClientGetCurrentDirectory") { (clientId: String, promise: Promise) in
|
|
1071
|
+
guard let ftpClient = self.ftpClients[clientId] else {
|
|
1072
|
+
promise.reject("FTP_CLIENT_NOT_FOUND", "FTP client with ID '\(clientId)' not found")
|
|
1073
|
+
return
|
|
1074
|
+
}
|
|
1075
|
+
|
|
958
1076
|
Task {
|
|
959
1077
|
do {
|
|
960
|
-
let currentDir = try await
|
|
961
|
-
promise.resolve(
|
|
1078
|
+
let currentDir = try await ftpClient.getCurrentDirectory()
|
|
1079
|
+
promise.resolve([
|
|
1080
|
+
"clientId": clientId,
|
|
1081
|
+
"currentDirectory": currentDir
|
|
1082
|
+
])
|
|
962
1083
|
} catch {
|
|
963
1084
|
promise.reject("FTP_PWD_ERROR", error.localizedDescription)
|
|
964
1085
|
}
|
|
965
1086
|
}
|
|
966
1087
|
}
|
|
967
1088
|
|
|
1089
|
+
// Get FTP client status
|
|
1090
|
+
AsyncFunction("getFtpClientStatus") { (clientId: String, promise: Promise) in
|
|
1091
|
+
guard let ftpClient = self.ftpClients[clientId] else {
|
|
1092
|
+
promise.resolve([
|
|
1093
|
+
"exists": false,
|
|
1094
|
+
"connected": false
|
|
1095
|
+
])
|
|
1096
|
+
return
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
promise.resolve([
|
|
1100
|
+
"exists": true,
|
|
1101
|
+
"connected": ftpClient.isConnected
|
|
1102
|
+
])
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
// List all FTP clients
|
|
1106
|
+
AsyncFunction("listFtpClients") { (promise: Promise) in
|
|
1107
|
+
let clientsInfo = self.ftpClients.mapValues { client in
|
|
1108
|
+
[
|
|
1109
|
+
"connected": client.isConnected
|
|
1110
|
+
]
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
promise.resolve([
|
|
1114
|
+
"clients": clientsInfo,
|
|
1115
|
+
"count": self.ftpClients.count
|
|
1116
|
+
])
|
|
1117
|
+
}
|
|
1118
|
+
|
|
968
1119
|
// Enables the module to be used as a native view. Definition components that are accepted as part of
|
|
969
1120
|
// the view definition: Prop, Events.
|
|
970
1121
|
View(ReactNativeKookitView.self) {
|
|
@@ -1063,14 +1214,17 @@ public class ReactNativeKookitModule: Module {
|
|
|
1063
1214
|
// Helper class to handle FTP progress callbacks
|
|
1064
1215
|
private class FtpProgressDelegateImpl: FtpProgressDelegate {
|
|
1065
1216
|
weak var module: ReactNativeKookitModule?
|
|
1217
|
+
let clientId: String
|
|
1066
1218
|
|
|
1067
|
-
init(module: ReactNativeKookitModule) {
|
|
1219
|
+
init(module: ReactNativeKookitModule, clientId: String) {
|
|
1068
1220
|
self.module = module
|
|
1221
|
+
self.clientId = clientId
|
|
1069
1222
|
}
|
|
1070
1223
|
|
|
1071
1224
|
func onProgress(transferred: Int64, total: Int64) {
|
|
1072
1225
|
let percentage = total > 0 ? Int((transferred * 100) / total) : 0
|
|
1073
1226
|
module?.sendEvent("onFtpProgress", [
|
|
1227
|
+
"clientId": clientId,
|
|
1074
1228
|
"transferred": transferred,
|
|
1075
1229
|
"total": total,
|
|
1076
1230
|
"percentage": percentage
|
|
@@ -1078,10 +1232,15 @@ private class FtpProgressDelegateImpl: FtpProgressDelegate {
|
|
|
1078
1232
|
}
|
|
1079
1233
|
|
|
1080
1234
|
func onComplete() {
|
|
1081
|
-
module?.sendEvent("onFtpComplete"
|
|
1235
|
+
module?.sendEvent("onFtpComplete", [
|
|
1236
|
+
"clientId": clientId
|
|
1237
|
+
])
|
|
1082
1238
|
}
|
|
1083
1239
|
|
|
1084
1240
|
func onError(error: String) {
|
|
1085
|
-
module?.sendEvent("onFtpError", [
|
|
1241
|
+
module?.sendEvent("onFtpError", [
|
|
1242
|
+
"clientId": clientId,
|
|
1243
|
+
"error": error
|
|
1244
|
+
])
|
|
1086
1245
|
}
|
|
1087
1246
|
}
|
package/package.json
CHANGED