修复边拍边传自动上传范围,并避免上传状态被旧快照回退。

拍后传输下载的历史照片切换模式后不再自动补传;刷新相机文件仅同步本地。重试上传后刷新单张状态,并忽略乱序 pipeline 通知导致的已上传回退。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-30 14:50:45 +08:00
parent ceac8ba67e
commit 5ebcec1cfc
5 changed files with 98 additions and 11 deletions

View File

@ -62,6 +62,8 @@ SpecifyUploadBottomSheet # 指定上传选项弹层
4. **时间侧栏**:按 30 分钟时间段分组,点击侧栏滚动到对应 section
5. **格式 Chip**:当前固定展示 JPG与「不修图」一样不可点击RAW 等格式后续扩展
6. **传输模式**:「边拍边传 / 拍后传输」按账号缓存,下次进入有线传图页自动恢复
- 「边拍边传」只自动上传该模式下由相机新照片事件发现的照片
- 「拍后传输」下载到 App 的旧照片切换模式后不会自动补传,需通过批量上传、指定上传、勾选上传或单张重试显式上传
## 业务流程

View File

@ -678,6 +678,7 @@ final class WiredCameraTransferViewModel: ObservableObject {
if pipeline.task(forAssetID: id) != nil {
await pipeline.uploadAssets(withIDs: [id])
await refreshPhotoFromPipelineTask(id: id)
return
}
@ -695,6 +696,7 @@ final class WiredCameraTransferViewModel: ObservableObject {
filename: record.fileName,
localPath: record.localPath
)
await refreshPhotoFromPipelineTask(id: id)
}
///
@ -764,6 +766,11 @@ final class WiredCameraTransferViewModel: ObservableObject {
replacePhoto(updated, rebuildGroups: photo.status != status)
}
private func refreshPhotoFromPipelineTask(id: String) async {
guard let task = pipeline.task(forAssetID: id) else { return }
await applyPipelineTasks([task], enforceAlbumBinding: false)
}
private func persistPhotoStatus(
id: String,
status: WiredTransferUploadStatus,
@ -884,7 +891,7 @@ final class WiredCameraTransferViewModel: ObservableObject {
setPhotos(records.map { $0.toPhotoItem() }.sortedByCaptureTimeDescending(), rebuildGroups: true)
}
private func applyPipelineTasks(_ tasks: [CameraTransferTask]) async {
private func applyPipelineTasks(_ tasks: [CameraTransferTask], enforceAlbumBinding: Bool = true) async {
guard let userIDProvider else { return }
let userID = userIDProvider()
@ -896,14 +903,17 @@ final class WiredCameraTransferViewModel: ObservableObject {
)
for task in tasks {
guard belongsToCurrentAlbum(task.assetID) else { continue }
guard !enforceAlbumBinding || belongsToCurrentAlbum(task.assetID) else { continue }
let status = mapPipelineStatus(task.status)
let existing = existingByID[task.assetID] ?? existingByFileName[task.filename]
if shouldIgnoreStalePipelineStatus(existing: existing?.status, incoming: status) {
continue
}
let storedLocalPath = task.localPath ?? ""
let localURL = task.localURL
let fileSizeBytes = await CameraDownloadStorage.fileSizeBytes(for: localURL)
let existing = existingByID[task.assetID] ?? existingByFileName[task.filename]
let existingRecord = persistedRecordsByID[task.assetID]
let capturedAt = await resolveCapturedAt(
existing: existing,
@ -1027,6 +1037,21 @@ final class WiredCameraTransferViewModel: ObservableObject {
}
}
/// 退
private func shouldIgnoreStalePipelineStatus(
existing: WiredTransferUploadStatus?,
incoming: WiredTransferUploadStatus
) -> Bool {
switch (existing, incoming) {
case (.uploaded?, .pending), (.uploaded?, .transferring), (.uploaded?, .uploading), (.uploaded?, .failed):
return true
case (.uploading?, .pending), (.uploading?, .transferring):
return true
default:
return false
}
}
/// URL IO
private func shouldPersistPipelineUpdate(
photoID: String,