Add TravelAlbum, ProfileSpace, and wired camera transfer modules.

Introduce travel album entry with Sony PTP tethering pipeline, profile space settings page, home routing updates, Launch Screen storyboard, and related tests/docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-30 09:41:05 +08:00
parent 5692134efc
commit d2fe5d71e4
48 changed files with 6477 additions and 11 deletions

View File

@ -0,0 +1,130 @@
//
// TravelAlbumAPI.swift
// suixinkan
//
// Created by Codex on 2026/6/29.
//
import Foundation
private let travelAlbumBase = "/api/yf-handset-app/photog/travel-album"
/// 便 ViewModel
@MainActor
protocol TravelAlbumServing {
func availableOrders() async throws -> [TravelAlbumAvailableOrder]
func createAlbum(_ request: TravelAlbumCreateRequest) async throws -> TravelAlbumCreateResponse
func albumList(page: Int, pageSize: Int) async throws -> ListPayload<TravelAlbumItem>
func albumInfo(id: Int) async throws -> TravelAlbumItem
func editAlbum(_ request: TravelAlbumEditRequest) async throws
func deleteAlbum(_ request: TravelAlbumDeleteRequest) async throws
func materialList(userEquityTravelId: Int, page: Int, pageSize: Int) async throws -> ListPayload<TravelAlbumMaterial>
func uploadMaterial(_ request: TravelAlbumUploadMaterialRequest) async throws -> TravelAlbumMaterial
func deleteMaterial(_ request: TravelAlbumDeleteMaterialRequest) async throws
func mpCode(id: Int) async throws -> TravelAlbumMpCodeResponse
}
/// API travel-album
@MainActor
final class TravelAlbumAPI: TravelAlbumServing {
private let client: APIClient
/// API
init(client: APIClient) {
self.client = client
}
///
func availableOrders() async throws -> [TravelAlbumAvailableOrder] {
try await client.send(
APIRequest(method: .get, path: "\(travelAlbumBase)/available-order")
)
}
///
func createAlbum(_ request: TravelAlbumCreateRequest) async throws -> TravelAlbumCreateResponse {
try await client.send(
APIRequest(method: .post, path: "\(travelAlbumBase)/create", body: request)
)
}
///
func albumList(page: Int, pageSize: Int) async throws -> ListPayload<TravelAlbumItem> {
try await client.send(
APIRequest(
method: .get,
path: "\(travelAlbumBase)/list",
queryItems: [
URLQueryItem(name: "page", value: "\(max(page, 1))"),
URLQueryItem(name: "page_size", value: "\(max(pageSize, 1))")
]
)
)
}
///
func albumInfo(id: Int) async throws -> TravelAlbumItem {
try await client.send(
APIRequest(
method: .get,
path: "\(travelAlbumBase)/info",
queryItems: [URLQueryItem(name: "id", value: "\(id)")]
)
)
}
///
func editAlbum(_ request: TravelAlbumEditRequest) async throws {
_ = try await client.send(
APIRequest(method: .post, path: "\(travelAlbumBase)/edit", body: request)
) as EmptyPayload
}
///
func deleteAlbum(_ request: TravelAlbumDeleteRequest) async throws {
_ = try await client.send(
APIRequest(method: .post, path: "\(travelAlbumBase)/delete", body: request)
) as EmptyPayload
}
///
func materialList(userEquityTravelId: Int, page: Int, pageSize: Int) async throws -> ListPayload<TravelAlbumMaterial> {
try await client.send(
APIRequest(
method: .get,
path: "\(travelAlbumBase)/material-list",
queryItems: [
URLQueryItem(name: "user_equity_travel_id", value: "\(userEquityTravelId)"),
URLQueryItem(name: "page", value: "\(max(page, 1))"),
URLQueryItem(name: "page_size", value: "\(max(pageSize, 1))"),
URLQueryItem(name: "order_by", value: "2")
]
)
)
}
/// OSS
func uploadMaterial(_ request: TravelAlbumUploadMaterialRequest) async throws -> TravelAlbumMaterial {
try await client.send(
APIRequest(method: .post, path: "\(travelAlbumBase)/upload-material", body: request)
)
}
///
func deleteMaterial(_ request: TravelAlbumDeleteMaterialRequest) async throws {
_ = try await client.send(
APIRequest(method: .post, path: "\(travelAlbumBase)/delete-material", body: request)
) as EmptyPayload
}
///
func mpCode(id: Int) async throws -> TravelAlbumMpCodeResponse {
try await client.send(
APIRequest(
method: .get,
path: "\(travelAlbumBase)/mp-code",
queryItems: [URLQueryItem(name: "id", value: "\(id)")]
)
)
}
}