Sync OTG transfer photo list UI

This commit is contained in:
2026-07-08 11:00:44 +08:00
parent d642234b38
commit 87fba3d5d6
5 changed files with 839 additions and 91 deletions

View File

@ -142,6 +142,68 @@ final class WiredCameraTransferViewModelTests: XCTestCase {
XCTAssertTrue(viewModel.filteredPhotos().isEmpty)
}
func testTransferPhotoSectionsAndSidebarUseDescendingHalfHourSlots() {
let photos = [
makeOTGPhotoItem(id: "a", capturedAt: "2026-07-08 12:05:00"),
makeOTGPhotoItem(id: "b", capturedAt: "2026-07-08 12:29:59"),
makeOTGPhotoItem(id: "c", capturedAt: "2026-07-08 12:30:00"),
makeOTGPhotoItem(id: "d", capturedAt: "2026-07-07 18:10:00"),
]
let sections = photos.buildPhotoSections()
let groups = photos.buildSidebarGroups()
XCTAssertEqual(sections.map(\.slotId), [
"2026-07-08T12:30",
"2026-07-08T12:00",
"2026-07-07T18:00",
])
XCTAssertEqual(sections[0].headerTitle, "07月08日 12:30 - 13:00")
XCTAssertEqual(sections[1].photos.map(\.id), ["b", "a"])
XCTAssertEqual(groups.map(\.dateLabel), ["07月08日", "07月07日"])
XCTAssertEqual(groups.first?.slots.map(\.slotStartLabel), ["12:30", "12:00"])
XCTAssertEqual(groups.first?.slots.last?.photoCount, 2)
}
func testSelectedTimeSlotFallsBackWhenTabChanges() throws {
let context = makeOTGTestContext()
context.store.save([
try makePersistedOTGRecord(context: context, id: "pending", capturedAt: "2026-07-08 12:05:00", status: .pending),
try makePersistedOTGRecord(context: context, id: "failed", capturedAt: "2026-07-08 12:30:00", status: .failed),
], albumId: 9)
let viewModel = makeWiredViewModel(
context: context,
manager: MockWiredCameraConnectionManager(driver: MockCameraDriver(objects: []))
)
viewModel.reloadLocalPhotos()
viewModel.selectTimeSlot(id: "2026-07-08T12:00")
viewModel.selectTab(.failed)
XCTAssertEqual(viewModel.selectedTimeSlotId, "2026-07-08T12:30")
}
func testSelectionModeAllowsPendingAndFailedOnly() throws {
let context = makeOTGTestContext()
context.store.save([
try makePersistedOTGRecord(context: context, id: "pending", capturedAt: "2026-07-08 12:05:00", status: .pending),
try makePersistedOTGRecord(context: context, id: "failed", capturedAt: "2026-07-08 12:10:00", status: .failed),
try makePersistedOTGRecord(context: context, id: "uploaded", capturedAt: "2026-07-08 12:15:00", status: .uploaded),
], albumId: 9)
let viewModel = makeWiredViewModel(
context: context,
manager: MockWiredCameraConnectionManager(driver: MockCameraDriver(objects: []))
)
viewModel.reloadLocalPhotos()
viewModel.onBatchUploadButtonClick()
viewModel.toggleTransferPhotoSelection(photoId: "pending")
viewModel.toggleTransferPhotoSelection(photoId: "failed")
viewModel.toggleTransferPhotoSelection(photoId: "uploaded")
XCTAssertEqual(viewModel.selectedPhotoIds, ["pending", "failed"])
}
func testEnteringPageDoesNotListCameraObjects() async {
let context = makeOTGTestContext()
let object = CameraObject(id: "A|10", filename: "A.JPG", fileSize: 10, capturedAt: Date())
@ -422,6 +484,66 @@ private func makeWiredViewModel(
)
}
private func makeOTGPhotoItem(
id: String,
capturedAt: String,
status: TravelAlbumOTGUploadStatus = .pending
) -> TravelAlbumOTGPhotoItem {
TravelAlbumOTGPhotoItem(
id: id,
sourceId: id,
fileName: "\(id).JPG",
thumbnailURL: nil,
capturedAt: capturedAt,
fileSizeText: "1 KB",
fileSizeBytes: 1024,
status: status,
progress: 0,
errorMessage: nil,
localPath: "originals/\(id).JPG",
remoteUrl: ""
)
}
private func makeOTGRecord(
id: String,
capturedAt: String,
status: TravelAlbumOTGUploadStatus
) -> TravelAlbumOTGPhotoRecord {
TravelAlbumOTGPhotoRecord(
id: id,
fileName: "\(id).JPG",
localPath: "originals/\(id).JPG",
capturedAt: capturedAt,
fileSizeBytes: 1024,
status: status,
albumId: 9,
userId: "u1"
)
}
private func makePersistedOTGRecord(
context: OTGTestContext,
id: String,
capturedAt: String,
status: TravelAlbumOTGUploadStatus
) throws -> TravelAlbumOTGPhotoRecord {
let relativePath = "originals/\(id).JPG"
let directory = try context.store.originalsDirectory(albumId: 9)
let url = directory.appendingPathComponent("\(id).JPG")
try Data([0xFF, 0xD8, 0xFF]).write(to: url)
return TravelAlbumOTGPhotoRecord(
id: id,
fileName: "\(id).JPG",
localPath: relativePath,
capturedAt: capturedAt,
fileSizeBytes: 3,
status: status,
albumId: 9,
userId: "u1"
)
}
@MainActor
private func makeOTGTestContext() -> OTGTestContext {
let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)