同步相册云盘界面并完善相关交互
This commit is contained in:
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user