将最低部署版本降至 iOS 16,以 ObservableObject 替换 @Observable,新增导航与 UI 兼容层,并补充登录冒烟 UI 测试。 Co-authored-by: Cursor <cursoragent@cursor.com>
60 lines
1.6 KiB
Swift
60 lines
1.6 KiB
Swift
//
|
||
// ScenicSpotContext.swift
|
||
// suixinkan
|
||
//
|
||
// Created by Codex on 2026/6/22.
|
||
//
|
||
|
||
import Foundation
|
||
import Combine
|
||
|
||
/// 景点加载阶段实体,表示当前景区景点列表的加载状态。
|
||
enum ScenicSpotLoadState: Equatable {
|
||
case idle
|
||
case loading
|
||
case loaded
|
||
case failed(String)
|
||
}
|
||
|
||
@MainActor
|
||
/// 景点上下文状态中心,保存当前景区下的景点或打卡点列表。
|
||
final class ScenicSpotContext: ObservableObject {
|
||
@Published private(set) var scenicId: Int?
|
||
@Published private(set) var spots: [ScenicSpotItem] = []
|
||
@Published private(set) var loadState: ScenicSpotLoadState = .idle
|
||
|
||
/// 按景区 ID 重新加载景点列表,失败只影响景点模块本身。
|
||
func reload(scenicId: Int?, api: AccountContextServing) async {
|
||
guard let scenicId else {
|
||
reset()
|
||
return
|
||
}
|
||
|
||
if self.scenicId != scenicId {
|
||
spots = []
|
||
}
|
||
self.scenicId = scenicId
|
||
loadState = .loading
|
||
|
||
do {
|
||
let response = try await api.scenicSpotListAll(scenicId: scenicId)
|
||
guard !Task.isCancelled else { return }
|
||
spots = response.list
|
||
loadState = .loaded
|
||
} catch is CancellationError {
|
||
loadState = .idle
|
||
} catch {
|
||
guard !Task.isCancelled else { return }
|
||
spots = []
|
||
loadState = .failed(error.localizedDescription)
|
||
}
|
||
}
|
||
|
||
/// 清空景点上下文,通常在退出登录或没有当前景区时调用。
|
||
func reset() {
|
||
scenicId = nil
|
||
spots = []
|
||
loadState = .idle
|
||
}
|
||
}
|