同步相册云盘界面并完善相关交互

This commit is contained in:
2026-07-10 17:15:15 +08:00
parent ab5220e460
commit ceca780ab3
13 changed files with 2269 additions and 532 deletions

View File

@ -31,6 +31,7 @@ final class CloudTransferManager {
private let photoSaver: any CloudPhotoSaving
private let scenicIdProvider: () -> Int
private var runningTasks: [String: Task<Void, Never>] = [:]
private var operationGenerations: [String: Int] = [:]
///
init(
@ -95,6 +96,7 @@ final class CloudTransferManager {
func pauseUpload(id: String) {
runningTasks[id]?.cancel()
runningTasks[id] = nil
invalidateOperation(id: id)
updateUploadTask(id: id) { task in
task.status = .paused
task.updatedAt = Date().timeIntervalSince1970
@ -110,6 +112,7 @@ final class CloudTransferManager {
func cancelUpload(id: String) {
runningTasks[id]?.cancel()
runningTasks[id] = nil
operationGenerations[id] = nil
if let task = uploadTasks.first(where: { $0.id == id }), !task.localPath.isEmpty {
try? FileManager.default.removeItem(atPath: task.localPath)
}
@ -121,6 +124,7 @@ final class CloudTransferManager {
func pauseDownload(id: String) {
runningTasks[id]?.cancel()
runningTasks[id] = nil
invalidateOperation(id: id)
updateDownloadTask(id: id) { task in
task.status = .paused
task.updatedAt = Date().timeIntervalSince1970
@ -136,6 +140,7 @@ final class CloudTransferManager {
func cancelDownload(id: String) {
runningTasks[id]?.cancel()
runningTasks[id] = nil
operationGenerations[id] = nil
downloadTasks.removeAll { $0.id == id }
persistAndNotify()
}
@ -149,6 +154,7 @@ final class CloudTransferManager {
uploadTasks[index].progress = max(uploadTasks[index].progress, 1)
uploadTasks[index].updatedAt = Date().timeIntervalSince1970
let seed = uploadTasks[index]
let generation = nextOperationGeneration(id: id)
persistAndNotify()
runningTasks[id] = Task { [weak self] in
@ -166,24 +172,30 @@ final class CloudTransferManager {
scenicId: scenicIdProvider()
) { [weak self] progress in
Task { @MainActor in
self?.updateUploadProgress(id: id, progress: progress)
guard let self, self.isCurrentOperation(id: id, generation: generation) else { return }
self.updateUploadProgress(id: id, progress: progress)
}
}
try Task.checkCancellation()
guard isCurrentOperation(id: id, generation: generation) else { return }
try await api.uploadCloudFile(
parentFolderId: seed.parentFolderId,
fileUrl: uploadedURL,
fileName: seed.fileName
)
guard isCurrentOperation(id: id, generation: generation) else { return }
try? FileManager.default.removeItem(atPath: seed.localPath)
runningTasks[id] = nil
operationGenerations[id] = nil
uploadTasks.removeAll { $0.id == id }
persistAndNotify()
onUploadCompleted?(seed.parentFolderId)
} catch is CancellationError {
guard isCurrentOperation(id: id, generation: generation) else { return }
runningTasks[id] = nil
markUploadPaused(id: id)
} catch {
guard isCurrentOperation(id: id, generation: generation) else { return }
runningTasks[id] = nil
markUploadFailed(id: id, message: error.localizedDescription)
}
@ -199,6 +211,7 @@ final class CloudTransferManager {
downloadTasks[index].progress = max(downloadTasks[index].progress, 1)
downloadTasks[index].updatedAt = Date().timeIntervalSince1970
let seed = downloadTasks[index]
let generation = nextOperationGeneration(id: id)
persistAndNotify()
runningTasks[id] = Task { [weak self] in
@ -209,22 +222,28 @@ final class CloudTransferManager {
fileName: seed.fileName
) { [weak self] progress in
Task { @MainActor in
self?.updateDownloadProgress(id: id, progress: progress)
guard let self, self.isCurrentOperation(id: id, generation: generation) else { return }
self.updateDownloadProgress(id: id, progress: progress)
}
}
try Task.checkCancellation()
guard isCurrentOperation(id: id, generation: generation) else { return }
try await photoSaver.saveToPhotoLibrary(fileURL: localURL, fileType: seed.fileType)
guard isCurrentOperation(id: id, generation: generation) else { return }
try? FileManager.default.removeItem(at: localURL)
runningTasks[id] = nil
operationGenerations[id] = nil
updateDownloadTask(id: id) { task in
task.status = .completed
task.progress = 100
task.updatedAt = Date().timeIntervalSince1970
}
} catch is CancellationError {
guard isCurrentOperation(id: id, generation: generation) else { return }
runningTasks[id] = nil
markDownloadPaused(id: id)
} catch {
guard isCurrentOperation(id: id, generation: generation) else { return }
runningTasks[id] = nil
markDownloadFailed(id: id, message: error.localizedDescription)
}
@ -291,6 +310,20 @@ final class CloudTransferManager {
store.saveTasks(uploadTasks + downloadTasks)
onTasksChange?()
}
private func nextOperationGeneration(id: String) -> Int {
let generation = (operationGenerations[id] ?? 0) + 1
operationGenerations[id] = generation
return generation
}
private func invalidateOperation(id: String) {
operationGenerations[id] = (operationGenerations[id] ?? 0) + 1
}
private func isCurrentOperation(id: String, generation: Int) -> Bool {
operationGenerations[id] == generation
}
}
private extension Comparable {

View File

@ -34,11 +34,14 @@ enum CloudDriveFilterType: Int, CaseIterable, Sendable {
case image = 2
case folder = 99
/// Android
var title: String {
/// Android
static let androidMenuOrder: [CloudDriveFilterType] = [.all, .image, .video, .folder]
/// Android
var menuTitle: String {
switch self {
case .all:
"全部类型"
"全部"
case .video:
"视频"
case .image:
@ -47,6 +50,20 @@ enum CloudDriveFilterType: Int, CaseIterable, Sendable {
"文件夹"
}
}
/// Android
var displayTitle: String {
switch self {
case .all:
"全部"
case .video:
"视频"
case .image:
"文件"
case .folder:
"文件夹"
}
}
}
private struct CloudDriveCacheKey: Hashable {