react-native-kookit 0.2.6 → 0.2.8
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_BUILD_FIX.md +117 -0
- package/SMB_COMPLETE_GUIDE.md +308 -0
- package/SMB_IMPLEMENTATION_SUMMARY.md +229 -0
- package/SMB_USAGE.md +275 -0
- package/android/build.gradle +7 -0
- package/android/src/main/java/expo/modules/kookit/ReactNativeKookitModule.kt +224 -1
- package/android/src/main/java/expo/modules/kookit/SmbClient.kt +173 -0
- package/build/ReactNativeKookit.types.d.ts +39 -0
- package/build/ReactNativeKookit.types.d.ts.map +1 -1
- package/build/ReactNativeKookit.types.js.map +1 -1
- package/build/ReactNativeKookitModule.d.ts +23 -1
- package/build/ReactNativeKookitModule.d.ts.map +1 -1
- package/build/ReactNativeKookitModule.js.map +1 -1
- package/build/SmbClient.d.ts +45 -0
- package/build/SmbClient.d.ts.map +1 -0
- package/build/SmbClient.js +122 -0
- package/build/SmbClient.js.map +1 -0
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/ios/ReactNativeKookit.podspec +1 -0
- package/ios/ReactNativeKookitModule.swift +418 -6
- package/ios/SMBClient.podspec +18 -0
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@ import MediaPlayer
|
|
|
4
4
|
import AVFoundation
|
|
5
5
|
import Foundation
|
|
6
6
|
import Network
|
|
7
|
+
import SMBClient
|
|
7
8
|
|
|
8
9
|
// MARK: - FTP Related Types and Classes
|
|
9
10
|
|
|
@@ -292,10 +293,17 @@ class FtpClient: @unchecked Sendable {
|
|
|
292
293
|
}
|
|
293
294
|
dataConnection.cancel()
|
|
294
295
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
296
|
+
// Check if the response already contained the completion message (226)
|
|
297
|
+
if response.contains("226") {
|
|
298
|
+
print("FTP DEBUG: Transfer completion already received in LIST response - skipping final response read")
|
|
299
|
+
} else {
|
|
300
|
+
print("FTP DEBUG: Reading final response...")
|
|
301
|
+
let finalResponse = try await readResponse()
|
|
302
|
+
let finalFirstLine = finalResponse.components(separatedBy: .newlines).first ?? finalResponse
|
|
303
|
+
print("FTP DEBUG: Final response: \(finalFirstLine)")
|
|
304
|
+
guard finalFirstLine.hasPrefix("226") else {
|
|
305
|
+
throw FtpError.commandFailed("LIST completion failed: \(finalFirstLine)")
|
|
306
|
+
}
|
|
299
307
|
}
|
|
300
308
|
|
|
301
309
|
let files = parseListingData(data)
|
|
@@ -796,7 +804,171 @@ class FtpClient: @unchecked Sendable {
|
|
|
796
804
|
}
|
|
797
805
|
}
|
|
798
806
|
|
|
799
|
-
// MARK: -
|
|
807
|
+
// MARK: - SMB Client with SMBClient library
|
|
808
|
+
|
|
809
|
+
struct SmbFileInfo {
|
|
810
|
+
let name: String
|
|
811
|
+
let isDirectory: Bool
|
|
812
|
+
let size: Int64
|
|
813
|
+
let lastModified: String
|
|
814
|
+
let attributes: String?
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
class SmbClient: @unchecked Sendable {
|
|
818
|
+
private var client: SMBClient?
|
|
819
|
+
private(set) var isConnected: Bool = false
|
|
820
|
+
private var currentShare: String?
|
|
821
|
+
private weak var progressDelegate: SmbProgressDelegate?
|
|
822
|
+
|
|
823
|
+
func connect(host: String, username: String, password: String, domain: String? = nil, share: String? = nil, timeout: TimeInterval = 10.0) async throws {
|
|
824
|
+
client = SMBClient(host: host)
|
|
825
|
+
|
|
826
|
+
try await client!.login(username: username, password: password)
|
|
827
|
+
isConnected = true
|
|
828
|
+
|
|
829
|
+
if let share = share {
|
|
830
|
+
try await client!.connectShare(share)
|
|
831
|
+
currentShare = share
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
func connectShare(_ shareName: String) async throws {
|
|
836
|
+
guard let client = client, isConnected else {
|
|
837
|
+
throw FtpError.notConnected
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
if currentShare != nil {
|
|
841
|
+
try await client.disconnectShare()
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
try await client.connectShare(shareName)
|
|
845
|
+
currentShare = shareName
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
func listFiles(path: String? = nil) async throws -> [SmbFileInfo] {
|
|
849
|
+
guard let client = client, isConnected else {
|
|
850
|
+
throw FtpError.notConnected
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
guard currentShare != nil else {
|
|
854
|
+
throw FtpError.commandFailed("No share connected. Connect to a share first.")
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
let directoryPath = path?.isEmpty == false ? path! : ""
|
|
858
|
+
let files = try await client.listDirectory(path: directoryPath)
|
|
859
|
+
|
|
860
|
+
return files.map { file in
|
|
861
|
+
SmbFileInfo(
|
|
862
|
+
name: file.name,
|
|
863
|
+
isDirectory: file.isDirectory,
|
|
864
|
+
size: Int64(file.size),
|
|
865
|
+
lastModified: formatDate(file.lastWriteTime),
|
|
866
|
+
attributes: nil
|
|
867
|
+
)
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
func downloadFile(remotePath: String, localPath: String) async throws {
|
|
872
|
+
guard let client = client, isConnected else {
|
|
873
|
+
throw FtpError.notConnected
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
guard currentShare != nil else {
|
|
877
|
+
throw FtpError.commandFailed("No share connected. Connect to a share first.")
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
let localURL = URL(fileURLWithPath: localPath)
|
|
881
|
+
try FileManager.default.createDirectory(at: localURL.deletingLastPathComponent(),
|
|
882
|
+
withIntermediateDirectories: true,
|
|
883
|
+
attributes: nil)
|
|
884
|
+
|
|
885
|
+
let data = try await client.download(path: remotePath)
|
|
886
|
+
try data.write(to: localURL)
|
|
887
|
+
|
|
888
|
+
progressDelegate?.onComplete()
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
func uploadFile(localPath: String, remotePath: String) async throws {
|
|
892
|
+
guard let client = client, isConnected else {
|
|
893
|
+
throw FtpError.notConnected
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
guard currentShare != nil else {
|
|
897
|
+
throw FtpError.commandFailed("No share connected. Connect to a share first.")
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
let localURL = URL(fileURLWithPath: localPath)
|
|
901
|
+
guard FileManager.default.fileExists(atPath: localPath) else {
|
|
902
|
+
throw FtpError.fileNotFound("Local file not found: \(localPath)")
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
let data = try Data(contentsOf: localURL)
|
|
906
|
+
try await client.upload(content: data, path: remotePath)
|
|
907
|
+
|
|
908
|
+
progressDelegate?.onComplete()
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
func deleteFile(remotePath: String, isDirectory: Bool = false) async throws {
|
|
912
|
+
guard let client = client, isConnected else {
|
|
913
|
+
throw FtpError.notConnected
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
guard currentShare != nil else {
|
|
917
|
+
throw FtpError.commandFailed("No share connected. Connect to a share first.")
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
if isDirectory {
|
|
921
|
+
try await client.deleteDirectory(path: remotePath)
|
|
922
|
+
} else {
|
|
923
|
+
try await client.deleteFile(path: remotePath)
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
func createDirectory(remotePath: String) async throws {
|
|
928
|
+
guard let client = client, isConnected else {
|
|
929
|
+
throw FtpError.notConnected
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
guard currentShare != nil else {
|
|
933
|
+
throw FtpError.commandFailed("No share connected. Connect to a share first.")
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
try await client.createDirectory(path: remotePath)
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
func disconnect() async {
|
|
940
|
+
guard let client = client else { return }
|
|
941
|
+
|
|
942
|
+
do {
|
|
943
|
+
if currentShare != nil {
|
|
944
|
+
try await client.disconnectShare()
|
|
945
|
+
}
|
|
946
|
+
try await client.logoff()
|
|
947
|
+
} catch {
|
|
948
|
+
// Ignore errors during disconnect
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
self.client = nil
|
|
952
|
+
isConnected = false
|
|
953
|
+
currentShare = nil
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
func setProgressDelegate(_ delegate: SmbProgressDelegate?) {
|
|
957
|
+
self.progressDelegate = delegate
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
private func formatDate(_ date: Date) -> String {
|
|
961
|
+
let formatter = DateFormatter()
|
|
962
|
+
formatter.dateFormat = "MMM dd HH:mm"
|
|
963
|
+
return formatter.string(from: date)
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
protocol SmbProgressDelegate: AnyObject {
|
|
968
|
+
func onProgress(transferred: Int64, total: Int64)
|
|
969
|
+
func onComplete()
|
|
970
|
+
func onError(error: String)
|
|
971
|
+
}// MARK: - Main Module
|
|
800
972
|
|
|
801
973
|
public class ReactNativeKookitModule: Module {
|
|
802
974
|
private var volumeView: MPVolumeView?
|
|
@@ -804,6 +976,7 @@ public class ReactNativeKookitModule: Module {
|
|
|
804
976
|
private var isVolumeKeyInterceptionEnabled = false
|
|
805
977
|
private var previousVolume: Float = 0.0
|
|
806
978
|
private var ftpClients: [String: FtpClient] = [:] // Store multiple FTP clients by ID
|
|
979
|
+
private var smbClients: [String: SmbClient] = [:] // Store multiple SMB clients by ID
|
|
807
980
|
|
|
808
981
|
// Each module class must implement the definition function. The definition consists of components
|
|
809
982
|
// that describes the module's functionality and behavior.
|
|
@@ -820,7 +993,9 @@ public class ReactNativeKookitModule: Module {
|
|
|
820
993
|
])
|
|
821
994
|
|
|
822
995
|
// Defines event names that the module can send to JavaScript.
|
|
823
|
-
|
|
996
|
+
Events("onChange", "onVolumeButtonPressed",
|
|
997
|
+
"onFtpProgress", "onFtpComplete", "onFtpError",
|
|
998
|
+
"onSmbProgress", "onSmbComplete", "onSmbError")
|
|
824
999
|
|
|
825
1000
|
// Defines a JavaScript synchronous function that runs the native code on the JavaScript thread.
|
|
826
1001
|
Function("hello") {
|
|
@@ -1109,6 +1284,209 @@ public class ReactNativeKookitModule: Module {
|
|
|
1109
1284
|
])
|
|
1110
1285
|
}
|
|
1111
1286
|
|
|
1287
|
+
// MARK: - SMB Client API
|
|
1288
|
+
|
|
1289
|
+
AsyncFunction("createSmbClient") { (clientId: String, promise: Promise) in
|
|
1290
|
+
if self.smbClients[clientId] != nil {
|
|
1291
|
+
promise.reject("SMB_CLIENT_EXISTS", "SMB client with ID '\(clientId)' already exists")
|
|
1292
|
+
return
|
|
1293
|
+
}
|
|
1294
|
+
self.smbClients[clientId] = SmbClient()
|
|
1295
|
+
promise.resolve(["clientId": clientId])
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
AsyncFunction("disposeSmbClient") { (clientId: String, promise: Promise) in
|
|
1299
|
+
if let client = self.smbClients[clientId] {
|
|
1300
|
+
Task {
|
|
1301
|
+
await client.disconnect()
|
|
1302
|
+
self.smbClients.removeValue(forKey: clientId)
|
|
1303
|
+
promise.resolve([:])
|
|
1304
|
+
}
|
|
1305
|
+
} else {
|
|
1306
|
+
promise.resolve([:])
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
AsyncFunction("getSmbClientStatus") { (clientId: String, promise: Promise) in
|
|
1311
|
+
guard let client = self.smbClients[clientId] else {
|
|
1312
|
+
promise.resolve(["exists": false, "connected": false])
|
|
1313
|
+
return
|
|
1314
|
+
}
|
|
1315
|
+
promise.resolve(["exists": true, "connected": client.isConnected])
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
AsyncFunction("listSmbClients") { (promise: Promise) in
|
|
1319
|
+
let clientsInfo = self.smbClients.mapValues { client in
|
|
1320
|
+
["connected": client.isConnected]
|
|
1321
|
+
}
|
|
1322
|
+
promise.resolve(["clients": clientsInfo, "count": self.smbClients.count])
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
AsyncFunction("smbClientConnect") { (clientId: String, config: [String: Any], promise: Promise) in
|
|
1326
|
+
guard let client = self.smbClients[clientId] else {
|
|
1327
|
+
promise.reject("SMB_CLIENT_NOT_FOUND", "SMB client with ID '\(clientId)' not found")
|
|
1328
|
+
return
|
|
1329
|
+
}
|
|
1330
|
+
let host = config["host"] as? String ?? ""
|
|
1331
|
+
let username = config["username"] as? String ?? ""
|
|
1332
|
+
let password = config["password"] as? String ?? ""
|
|
1333
|
+
let domain = config["domain"] as? String
|
|
1334
|
+
let share = config["share"] as? String
|
|
1335
|
+
let timeout = (config["timeout"] as? TimeInterval) ?? 10.0
|
|
1336
|
+
if host.isEmpty || username.isEmpty {
|
|
1337
|
+
promise.reject("SMB_INVALID_CONFIG", "Missing host or username")
|
|
1338
|
+
return
|
|
1339
|
+
}
|
|
1340
|
+
Task {
|
|
1341
|
+
do {
|
|
1342
|
+
try await client.connect(host: host, username: username, password: password, domain: domain, share: share, timeout: timeout)
|
|
1343
|
+
promise.resolve(["clientId": clientId, "connected": true])
|
|
1344
|
+
} catch {
|
|
1345
|
+
promise.reject("SMB_CONNECT_ERROR", error.localizedDescription)
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
AsyncFunction("smbClientDisconnect") { (clientId: String, promise: Promise) in
|
|
1351
|
+
guard let client = self.smbClients[clientId] else {
|
|
1352
|
+
promise.reject("SMB_CLIENT_NOT_FOUND", "SMB client with ID '\(clientId)' not found")
|
|
1353
|
+
return
|
|
1354
|
+
}
|
|
1355
|
+
Task {
|
|
1356
|
+
await client.disconnect()
|
|
1357
|
+
promise.resolve(["clientId": clientId, "disconnected": true])
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
AsyncFunction("smbClientConnectShare") { (clientId: String, shareName: String, promise: Promise) in
|
|
1362
|
+
guard let client = self.smbClients[clientId] else {
|
|
1363
|
+
promise.reject("SMB_CLIENT_NOT_FOUND", "SMB client with ID '\(clientId)' not found")
|
|
1364
|
+
return
|
|
1365
|
+
}
|
|
1366
|
+
Task {
|
|
1367
|
+
do {
|
|
1368
|
+
try await client.connectShare(shareName)
|
|
1369
|
+
promise.resolve(["clientId": clientId, "share": shareName, "connected": true])
|
|
1370
|
+
} catch {
|
|
1371
|
+
promise.reject("SMB_CONNECT_SHARE_ERROR", error.localizedDescription)
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
AsyncFunction("smbClientList") { (clientId: String, path: String?, promise: Promise) in
|
|
1377
|
+
guard let client = self.smbClients[clientId] else {
|
|
1378
|
+
promise.reject("SMB_CLIENT_NOT_FOUND", "SMB client with ID '\(clientId)' not found")
|
|
1379
|
+
return
|
|
1380
|
+
}
|
|
1381
|
+
Task {
|
|
1382
|
+
do {
|
|
1383
|
+
let files = try await client.listFiles(path: path)
|
|
1384
|
+
let result = files.map { file in
|
|
1385
|
+
[
|
|
1386
|
+
"name": file.name,
|
|
1387
|
+
"isDirectory": file.isDirectory,
|
|
1388
|
+
"size": file.size,
|
|
1389
|
+
"lastModified": file.lastModified,
|
|
1390
|
+
"attributes": file.attributes as Any
|
|
1391
|
+
]
|
|
1392
|
+
}
|
|
1393
|
+
promise.resolve(result)
|
|
1394
|
+
} catch {
|
|
1395
|
+
promise.reject("SMB_LIST_ERROR", error.localizedDescription)
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
AsyncFunction("smbClientDownload") { (clientId: String, remotePath: String, localPath: String, promise: Promise) in
|
|
1401
|
+
guard let client = self.smbClients[clientId] else {
|
|
1402
|
+
promise.reject("SMB_CLIENT_NOT_FOUND", "SMB client with ID '\(clientId)' not found")
|
|
1403
|
+
return
|
|
1404
|
+
}
|
|
1405
|
+
Task {
|
|
1406
|
+
do {
|
|
1407
|
+
let progressDelegate = SmbProgressDelegateImpl(module: self, clientId: clientId)
|
|
1408
|
+
client.setProgressDelegate(progressDelegate)
|
|
1409
|
+
try await client.downloadFile(remotePath: remotePath, localPath: localPath)
|
|
1410
|
+
promise.resolve([
|
|
1411
|
+
"clientId": clientId,
|
|
1412
|
+
"remotePath": remotePath,
|
|
1413
|
+
"localPath": localPath,
|
|
1414
|
+
"downloaded": true
|
|
1415
|
+
])
|
|
1416
|
+
} catch {
|
|
1417
|
+
self.sendEvent("onSmbError", [
|
|
1418
|
+
"clientId": clientId,
|
|
1419
|
+
"error": error.localizedDescription
|
|
1420
|
+
])
|
|
1421
|
+
promise.reject("SMB_DOWNLOAD_ERROR", error.localizedDescription)
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
AsyncFunction("smbClientUpload") { (clientId: String, localPath: String, remotePath: String, promise: Promise) in
|
|
1427
|
+
guard let client = self.smbClients[clientId] else {
|
|
1428
|
+
promise.reject("SMB_CLIENT_NOT_FOUND", "SMB client with ID '\(clientId)' not found")
|
|
1429
|
+
return
|
|
1430
|
+
}
|
|
1431
|
+
Task {
|
|
1432
|
+
do {
|
|
1433
|
+
let progressDelegate = SmbProgressDelegateImpl(module: self, clientId: clientId)
|
|
1434
|
+
client.setProgressDelegate(progressDelegate)
|
|
1435
|
+
try await client.uploadFile(localPath: localPath, remotePath: remotePath)
|
|
1436
|
+
promise.resolve([
|
|
1437
|
+
"clientId": clientId,
|
|
1438
|
+
"localPath": localPath,
|
|
1439
|
+
"remotePath": remotePath,
|
|
1440
|
+
"uploaded": true
|
|
1441
|
+
])
|
|
1442
|
+
} catch {
|
|
1443
|
+
self.sendEvent("onSmbError", [
|
|
1444
|
+
"clientId": clientId,
|
|
1445
|
+
"error": error.localizedDescription
|
|
1446
|
+
])
|
|
1447
|
+
promise.reject("SMB_UPLOAD_ERROR", error.localizedDescription)
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
AsyncFunction("smbClientDelete") { (clientId: String, remotePath: String, isDirectory: Bool?, promise: Promise) in
|
|
1453
|
+
guard let client = self.smbClients[clientId] else {
|
|
1454
|
+
promise.reject("SMB_CLIENT_NOT_FOUND", "SMB client with ID '\(clientId)' not found")
|
|
1455
|
+
return
|
|
1456
|
+
}
|
|
1457
|
+
Task {
|
|
1458
|
+
do {
|
|
1459
|
+
try await client.deleteFile(remotePath: remotePath, isDirectory: isDirectory ?? false)
|
|
1460
|
+
promise.resolve([
|
|
1461
|
+
"clientId": clientId,
|
|
1462
|
+
"remotePath": remotePath,
|
|
1463
|
+
"deleted": true
|
|
1464
|
+
])
|
|
1465
|
+
} catch {
|
|
1466
|
+
promise.reject("SMB_DELETE_ERROR", error.localizedDescription)
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
AsyncFunction("smbClientCreateDirectory") { (clientId: String, remotePath: String, promise: Promise) in
|
|
1472
|
+
guard let client = self.smbClients[clientId] else {
|
|
1473
|
+
promise.reject("SMB_CLIENT_NOT_FOUND", "SMB client with ID '\(clientId)' not found")
|
|
1474
|
+
return
|
|
1475
|
+
}
|
|
1476
|
+
Task {
|
|
1477
|
+
do {
|
|
1478
|
+
try await client.createDirectory(remotePath: remotePath)
|
|
1479
|
+
promise.resolve([
|
|
1480
|
+
"clientId": clientId,
|
|
1481
|
+
"remotePath": remotePath,
|
|
1482
|
+
"created": true
|
|
1483
|
+
])
|
|
1484
|
+
} catch {
|
|
1485
|
+
promise.reject("SMB_CREATE_DIR_ERROR", error.localizedDescription)
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1112
1490
|
// Enables the module to be used as a native view. Definition components that are accepted as part of
|
|
1113
1491
|
// the view definition: Prop, Events.
|
|
1114
1492
|
View(ReactNativeKookitView.self) {
|
|
@@ -1236,4 +1614,38 @@ private class FtpProgressDelegateImpl: FtpProgressDelegate {
|
|
|
1236
1614
|
"error": error
|
|
1237
1615
|
])
|
|
1238
1616
|
}
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
// Helper class to handle SMB progress callbacks
|
|
1620
|
+
private class SmbProgressDelegateImpl: SmbProgressDelegate {
|
|
1621
|
+
weak var module: ReactNativeKookitModule?
|
|
1622
|
+
let clientId: String
|
|
1623
|
+
|
|
1624
|
+
init(module: ReactNativeKookitModule, clientId: String) {
|
|
1625
|
+
self.module = module
|
|
1626
|
+
self.clientId = clientId
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
func onProgress(transferred: Int64, total: Int64) {
|
|
1630
|
+
let percentage = total > 0 ? Int((transferred * 100) / total) : 0
|
|
1631
|
+
module?.sendEvent("onSmbProgress", [
|
|
1632
|
+
"clientId": clientId,
|
|
1633
|
+
"transferred": transferred,
|
|
1634
|
+
"total": total,
|
|
1635
|
+
"percentage": percentage
|
|
1636
|
+
])
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
func onComplete() {
|
|
1640
|
+
module?.sendEvent("onSmbComplete", [
|
|
1641
|
+
"clientId": clientId
|
|
1642
|
+
])
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
func onError(error: String) {
|
|
1646
|
+
module?.sendEvent("onSmbError", [
|
|
1647
|
+
"clientId": clientId,
|
|
1648
|
+
"error": error
|
|
1649
|
+
])
|
|
1650
|
+
}
|
|
1239
1651
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Pod::Spec.new do |s|
|
|
2
|
+
s.name = 'SMBClient'
|
|
3
|
+
s.version = '0.3.1'
|
|
4
|
+
s.summary = 'SMB2/3 client implementation for iOS/macOS'
|
|
5
|
+
s.description = 'A Swift implementation of SMB2/3 client protocol for iOS and macOS'
|
|
6
|
+
s.homepage = 'https://github.com/kishikawakatsumi/SMBClient'
|
|
7
|
+
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
|
8
|
+
s.author = { 'Kishikawa Katsumi' => 'kishikawakatsumi@mac.com' }
|
|
9
|
+
s.source = { :git => 'https://github.com/kishikawakatsumi/SMBClient.git', :tag => s.version.to_s }
|
|
10
|
+
|
|
11
|
+
s.ios.deployment_target = '13.0'
|
|
12
|
+
s.osx.deployment_target = '10.15'
|
|
13
|
+
s.swift_version = '5.9'
|
|
14
|
+
|
|
15
|
+
s.source_files = 'Sources/SMBClient/**/*'
|
|
16
|
+
|
|
17
|
+
s.frameworks = 'Foundation', 'Network'
|
|
18
|
+
end
|
package/package.json
CHANGED