Skip to content

Commit fd7e297

Browse files
committed
Memory & stability optimisation
1 parent e7ff994 commit fd7e297

11 files changed

Lines changed: 219 additions & 256 deletions

File tree

.DS_Store

0 Bytes
Binary file not shown.
Binary file not shown.

Throttle 2/DetailsView/TorrentDetailsView.swift

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ struct DetailsView: View {
2222
@State var fileStat: TorrentResponse?
2323
@State private var sheetUnwantedFiles: Set<Int> = []
2424
@State private var sheetFileTree: [FileNode] = []
25+
@State private var isBuildingFileTree: Bool = false
2526

2627
private let toastOptions = SimpleToastOptions(
2728
hideAfter: 5
@@ -404,18 +405,34 @@ Spacer()
404405
let wanted = stat["wanted"] as? Bool ?? true
405406
return wanted ? nil : i
406407
})
407-
// Convert TorrentFile array to FileNode tree
408-
let fileTreeCopy = torrentFiles.enumerated().map { (i, file) in (i, file) }.toFileTree()
409408
let selectedCount = torrentFiles.count - unwantedFiles.count
410409
let totalCount = torrentFiles.count
411410
HStack {
412-
Button("\(selectedCount) of \(totalCount) files selected") {
411+
Image(systemName:"checklist")
412+
Button("\(selectedCount) of \(totalCount) files Active") {
413+
isBuildingFileTree = true
413414
sheetUnwantedFiles = unwantedFiles
414-
sheetFileTree = fileTreeCopy
415-
showFiles = true
415+
DispatchQueue.global(qos: .userInitiated).async {
416+
let fileTreeCopy = torrentFiles.enumerated().map { (i, file) in (i, file) }.toFileTree()
417+
DispatchQueue.main.async {
418+
sheetFileTree = fileTreeCopy
419+
isBuildingFileTree = false
420+
showFiles = true
421+
}
422+
}
416423
}
424+
417425
Spacer()
418426
}
427+
.overlay(
428+
Group {
429+
if isBuildingFileTree {
430+
ProgressView("Building file list...")
431+
.frame(maxWidth: .infinity, maxHeight: .infinity)
432+
.background(Color.black.opacity(0.2))
433+
}
434+
}
435+
)
419436
.sheet(isPresented: $showFiles) {
420437
FileSelectionView(
421438
fileTree: sheetFileTree,
@@ -605,13 +622,23 @@ Spacer()
605622
if store.selectedTorrentId != nil{
606623
try await detailedTorrent = manager.fetchTorrentDetails(id: store.selectedTorrentId!)
607624
magnet = detailedTorrent?.dynamicFields["magnetLink"]?.value as? String ?? ""
625+
if let torrentFiles = detailedTorrent?.files {
626+
// fileTree = torrentFiles.enumerated().map { (i, file) in (i, file) }.toFileTree()
627+
} else {
628+
// fileTree = []
629+
}
608630
}
609631
}
610632
}
611633
.onChange(of: store.selectedTorrentId, {
612634
Task {
613635
try await detailedTorrent = manager.fetchTorrentDetails(id: store.selectedTorrentId ?? 0)
614636
magnet = detailedTorrent?.dynamicFields["magnetLink"]?.value as? String ?? ""
637+
if let torrentFiles = detailedTorrent?.files {
638+
// fileTree = torrentFiles.enumerated().map { (i, file) in (i, file) }.toFileTree()
639+
} else {
640+
// fileTree = []
641+
}
615642
}
616643
})
617644
#if os(iOS)

Throttle 2/SSH/Browser/SFTPFolderPicker.swift

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,14 @@ class FileBrowserViewModel: ObservableObject {
2828
}
2929

3030
private func connectToServer() {
31-
Task {
31+
Task { [weak self] in
32+
guard let self = self else { return }
3233
do {
33-
// Create and connect a new SSH connection
34-
if let server = server {
34+
if let server = self.server {
3535
let connection = SSHConnection(server: server)
3636
try await connection.connect()
37-
38-
// Store the connection for future use
3937
self.sshConnection = connection
40-
41-
// Fetch initial directory listing
42-
await fetchItems()
38+
await self.fetchItems()
4339
} else {
4440
throw NSError(domain: "FileBrowser", code: -1,
4541
userInfo: [NSLocalizedDescriptionKey: "Server configuration missing"])
@@ -55,17 +51,12 @@ class FileBrowserViewModel: ObservableObject {
5551

5652
func createFolder(name: String) {
5753
let newFolderPath = "\(currentPath)/\(name)".replacingOccurrences(of: "//", with: "/")
58-
59-
Task {
54+
Task { [weak self] in
55+
guard let self = self else { return }
6056
do {
61-
// Use the existing connection or create a new one if needed
62-
let connection = try await getOrCreateConnection()
63-
64-
// Create the directory using SSH
57+
let connection = try await self.getOrCreateConnection()
6558
try await connection.createDirectory(path: newFolderPath)
66-
67-
// Refresh directory listing
68-
await fetchItems()
59+
await self.fetchItems()
6960
} catch {
7061
await MainActor.run {
7162
print("❌ Failed to create folder: \(error)")
@@ -171,10 +162,12 @@ class FileBrowserViewModel: ObservableObject {
171162
/// ✅ Navigate into a folder and refresh UI
172163
func navigateToFolder(_ folderName: String) {
173164
let newPath = "\(currentPath)/\(folderName)".replacingOccurrences(of: "//", with: "/")
174-
175-
Task { @MainActor in
176-
self.currentPath = newPath
177-
await fetchItems()
165+
Task { [weak self] in
166+
guard let self = self else { return }
167+
await MainActor.run {
168+
self.currentPath = newPath
169+
}
170+
await self.fetchItems()
178171
}
179172
}
180173

@@ -190,9 +183,12 @@ class FileBrowserViewModel: ObservableObject {
190183
.joined(separator: "/")
191184
let newPath = trimmedPath.isEmpty ? basePath : "/" + trimmedPath
192185

193-
Task { @MainActor in
194-
self.currentPath = newPath
195-
await fetchItems()
186+
Task { [weak self] in
187+
guard let self = self else { return }
188+
await MainActor.run {
189+
self.currentPath = newPath
190+
}
191+
await self.fetchItems()
196192
}
197193
}
198194

Throttle 2/SSH/Browser/SFTPUploadHandler.swift

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,30 +29,34 @@ class SFTPUploadManager: ObservableObject {
2929
self.uploadHandler = uploadHandler
3030
}
3131

32+
deinit {
33+
cancelUpload()
34+
}
35+
3236
func uploadFile(from localURL: URL) {
3337
guard localURL.startAccessingSecurityScopedResource() else {
3438
error = "Failed to access file"
3539
return
3640
}
37-
3841
isUploading = true
3942
currentUploadFileName = localURL.lastPathComponent
4043
uploadProgress = 0
41-
42-
// Create a new SSH connection using the server from the handler
4344
if let server = uploadHandler.getServer() {
4445
uploadWithSSH(localURL: localURL, server: server)
4546
} else {
46-
Task { @MainActor in
47-
self.error = "Server configuration not found"
48-
self.isUploading = false
47+
Task { [weak self] in
48+
await MainActor.run {
49+
self?.error = "Server configuration not found"
50+
self?.isUploading = false
51+
}
4952
localURL.stopAccessingSecurityScopedResource()
5053
}
5154
}
5255
}
5356

5457
private func uploadWithSSH(localURL: URL, server: ServerEntity) {
55-
uploadTask = Task {
58+
uploadTask = Task { [weak self] in
59+
guard let self = self else { return }
5660
do {
5761
defer { localURL.stopAccessingSecurityScopedResource() }
5862
let destinationPath = "\(self.uploadHandler.currentPath)/\(localURL.lastPathComponent)"
@@ -126,17 +130,20 @@ class SFTPUploadManager: ObservableObject {
126130
if let server = uploadHandler.getServer() {
127131
uploadFolderWithSSH(localURL: localURL, server: server, enumeratedURLs: enumeratedURLs)
128132
} else {
129-
Task { @MainActor in
130-
self.error = "Server configuration not found"
131-
self.isUploading = false
133+
Task { [weak self] in
134+
await MainActor.run {
135+
self?.error = "Server configuration not found"
136+
self?.isUploading = false
137+
}
132138
localURL.stopAccessingSecurityScopedResource()
133139
}
134140
}
135141
}
136142

137143
// Add enumeratedURLs as a parameter
138144
private func uploadFolderWithSSH(localURL: URL, server: ServerEntity, enumeratedURLs: [URL]) {
139-
uploadTask = Task {
145+
uploadTask = Task { [weak self] in
146+
guard let self = self else { return }
140147
do {
141148
defer {
142149
localURL.stopAccessingSecurityScopedResource()
@@ -207,16 +214,18 @@ class SFTPUploadManager: ObservableObject {
207214

208215
// Also disconnect the SSH connection if one exists
209216
if let connection = sshConnection {
210-
Task {
217+
Task { [weak self] in
211218
await connection.disconnect()
212-
self.sshConnection = nil
219+
self?.sshConnection = nil
213220
}
214221
}
215222

216223
// Update the UI
217-
Task { @MainActor in
218-
self.isUploading = false
219-
self.error = "Upload cancelled"
224+
Task { [weak self] in
225+
await MainActor.run {
226+
self?.isUploading = false
227+
self?.error = "Upload cancelled"
228+
}
220229
}
221230
}
222231
}

0 commit comments

Comments
 (0)