feat: add AI retouch and album preview actions

This commit is contained in:
2026-08-10 16:00:32 +08:00
parent 24fa66281c
commit 439edf827c
14 changed files with 2110 additions and 118 deletions
@@ -173,6 +173,39 @@ final class TravelAlbumDetailViewModelTests: XCTestCase {
XCTAssertEqual(viewModel.selectedMaterialIds, [1])
}
func testCompletingAIRetouchClearsSelectionModeAndSelectedMaterials() {
let viewModel = TravelAlbumDetailViewModel(albumId: 2)
viewModel.toggleSelectionMode()
viewModel.toggleMaterialSelection(TravelAlbumMaterial(id: 1, status: 1))
viewModel.completeAIRetouchSubmission()
XCTAssertFalse(viewModel.isSelectionMode)
XCTAssertTrue(viewModel.selectedMaterialIds.isEmpty)
}
func testPreviewDeletionRemovesMaterialAndSynchronizesCounts() async {
let api = TravelAlbumMockAPI()
api.materialListResponses = [
TravelAlbumListResponse(
total: 3,
list: [
TravelAlbumMaterial(id: 1),
TravelAlbumMaterial(id: 2, isPurchased: true),
TravelAlbumMaterial(id: 3),
]
),
]
let viewModel = TravelAlbumDetailViewModel(albumId: 2)
await viewModel.loadMaterials(reset: true, api: api)
viewModel.removeMaterialAfterPreviewDeletion(id: 2)
XCTAssertEqual(viewModel.materials.map(\.id), [1, 3])
XCTAssertEqual(viewModel.allPhotoCount, 2)
XCTAssertEqual(viewModel.purchasedPhotoCount, 0)
}
func testDeleteAlbumCallsCallback() async {
let api = TravelAlbumMockAPI()
let viewModel = TravelAlbumDetailViewModel(albumId: 5)
@@ -1015,7 +1048,13 @@ final class TravelAlbumMockAPI: TravelAlbumServing {
var uploadMaterialResponse = TravelAlbumMaterial()
var materialClientPhotoIdsResponse = TravelAlbumMaterialClientPhotoIDsResponse(clientPhotoIds: [])
var mpCodeResponse = TravelAlbumMpCodeResponse(mpCodeOssUrl: "")
var aiRetouchTemplatesResponse = TravelAlbumAIRetouchTemplatesResponse()
var createError: Error?
var aiRetouchTemplatesError: Error?
var submitAIRetouchError: Error?
var submitAIRetouchDelayNanoseconds: UInt64 = 0
var deleteMaterialError: Error?
var deleteMaterialDelayNanoseconds: UInt64 = 0
private(set) var availableOrdersCallCount = 0
private(set) var createRequests: [TravelAlbumCreateRequest] = []
@@ -1024,6 +1063,8 @@ final class TravelAlbumMockAPI: TravelAlbumServing {
private(set) var materialClientPhotoIdsCallCount = 0
private(set) var deletedAlbumIds: [Int] = []
private(set) var deletedMaterialIds: [Int] = []
private(set) var aiRetouchTemplateScenicIds: [Int] = []
private(set) var aiRetouchRequests: [TravelAlbumAIRetouchRequest] = []
func availableOrders() async throws -> [TravelAlbumAvailableOrder] {
availableOrdersCallCount += 1
@@ -1084,9 +1125,27 @@ final class TravelAlbumMockAPI: TravelAlbumServing {
func deleteMaterial(id: Int) async throws {
deletedMaterialIds.append(id)
if deleteMaterialDelayNanoseconds > 0 {
try await Task.sleep(nanoseconds: deleteMaterialDelayNanoseconds)
}
if let deleteMaterialError { throw deleteMaterialError }
}
func mpCode(id: Int) async throws -> TravelAlbumMpCodeResponse {
mpCodeResponse
}
func aiRetouchTemplates(scenicId: Int) async throws -> TravelAlbumAIRetouchTemplatesResponse {
aiRetouchTemplateScenicIds.append(scenicId)
if let aiRetouchTemplatesError { throw aiRetouchTemplatesError }
return aiRetouchTemplatesResponse
}
func submitAIRetouch(_ request: TravelAlbumAIRetouchRequest) async throws {
aiRetouchRequests.append(request)
if submitAIRetouchDelayNanoseconds > 0 {
try await Task.sleep(nanoseconds: submitAIRetouchDelayNanoseconds)
}
if let submitAIRetouchError { throw submitAIRetouchError }
}
}