Files
suixinkan_uikit/suixinkan/Features/LocationReport/ViewModels/LocationReportViewModel.swift
T

202 lines
6.5 KiB
Swift

//
// LocationReportViewModel.swift
// suixinkan
//
import Foundation
/// 独立位置上报页 ViewModel,对齐 Android `LocationReportViewModel` + 共享上报逻辑。
final class LocationReportViewModel {
private(set) var currentLatitude: Double?
private(set) var currentLongitude: Double?
private(set) var currentAddress: String?
private(set) var isLocating = false
private(set) var isMarking = false
private(set) var markerLatitude: Double?
private(set) var markerLongitude: Double?
private(set) var markerAddress: String?
private(set) var showOnlineStatusDialog = false
private(set) var showLocationReportSuccessDialog = false
private(set) var reportTimeText = ""
let locationStateStore: HomeLocationStateStore
let locationReportService: LocationReportService
var onStateChange: (() -> Void)?
var onShowMessage: ((String) -> Void)?
private let locationProvider: any LocationProviding
init(
locationStateStore: HomeLocationStateStore = .shared,
locationProvider: any LocationProviding = LocationProvider.shared
) {
self.locationStateStore = locationStateStore
self.locationProvider = locationProvider
self.locationReportService = LocationReportService(
locationStateStore: locationStateStore,
locationProvider: locationProvider
)
self.locationStateStore.observe(self) { [weak self] in
self?.notifyStateChange()
}
}
deinit {
locationStateStore.removeObserver(self)
}
var isOnline: Bool { locationStateStore.isOnline }
var countdownDisplayText: String { locationStateStore.countdownDisplayTextSingleHour }
var reminderMinutes: Int { locationStateStore.reminderMinutes }
var isReporting: Bool { locationReportService.isReporting }
/// 进入页面时恢复在线倒计时。
func restoreStateIfNeeded() {
locationStateStore.restoreStateIfNeeded()
notifyStateChange()
}
/// 开始定位并更新地图中心。
func startLocation() {
guard !isLocating else { return }
isLocating = true
notifyStateChange()
Task {
defer {
isLocating = false
notifyStateChange()
}
do {
let snapshot = try await locationProvider.requestSnapshot()
currentLatitude = snapshot.latitude
currentLongitude = snapshot.longitude
currentAddress = snapshot.address.isEmpty
? String(format: "%.5f, %.5f", snapshot.latitude, snapshot.longitude)
: snapshot.address
notifyStateChange()
} catch {
onShowMessage?((error as? LocalizedError)?.errorDescription ?? error.localizedDescription)
}
}
}
func toggleMarking() {
isMarking.toggle()
if !isMarking {
markerLatitude = nil
markerLongitude = nil
markerAddress = nil
}
notifyStateChange()
}
/// 地图点击设置标记点并逆地理。
func setMarkerLocation(latitude: Double, longitude: Double) {
guard isMarking else { return }
markerLatitude = latitude
markerLongitude = longitude
markerAddress = nil
notifyStateChange()
Task {
let address = await locationProvider.reverseGeocode(latitude: latitude, longitude: longitude)
markerAddress = address.isEmpty
? String(format: "%.5f, %.5f", latitude, longitude)
: address
notifyStateChange()
}
}
func showOnlineStatusSwitchDialog() {
showOnlineStatusDialog = true
notifyStateChange()
}
func hideOnlineStatusSwitchDialog() {
showOnlineStatusDialog = false
notifyStateChange()
}
func hideLocationReportSuccessDialog() {
showLocationReportSuccessDialog = false
notifyStateChange()
}
func updateReminderMinutes(_ minutes: Int) {
locationStateStore.updateReminderMinutes(minutes)
notifyStateChange()
}
func switchOnlineStatus(api: HomeAPI) async {
hideOnlineStatusSwitchDialog()
do {
let goingOnline = !isOnline
if let result = try await locationReportService.switchOnlineStatus(api: api) {
applyReportSuccess(result)
onShowMessage?(goingOnline ? "已切换到在线状态,正在上报位置" : "已切换到离线状态")
} else {
onShowMessage?("已切换到离线状态")
}
} catch let error as LocationReportServiceError {
onShowMessage?(error.localizedDescription)
} catch {
onShowMessage?(error.localizedDescription)
}
}
/// 立即上报:标记模式 type=2,否则 type=1。
func reportLocation(api: HomeAPI) async {
if isMarking {
guard let lat = markerLatitude, let lng = markerLongitude else {
onShowMessage?("请先在地图上选择标记点")
return
}
await reportCoordinates(api: api, latitude: lat, longitude: lng, address: markerAddress, type: 2)
return
}
guard let lat = currentLatitude, let lng = currentLongitude else {
onShowMessage?("正在定位中,请稍候")
return
}
await reportCoordinates(api: api, latitude: lat, longitude: lng, address: currentAddress, type: 1)
}
private func reportCoordinates(
api: HomeAPI,
latitude: Double,
longitude: Double,
address: String?,
type: Int
) async {
do {
let result = try await locationReportService.reportWithCoordinates(
api: api,
latitude: latitude,
longitude: longitude,
address: address,
type: type
)
applyReportSuccess(result)
onShowMessage?("位置上报成功")
} catch let error as LocationReportServiceError {
onShowMessage?(error.localizedDescription)
} catch {
onShowMessage?(error.localizedDescription)
}
}
private func applyReportSuccess(_ result: LocationReportSuccessResult) {
if result.shouldShowSuccessDialog {
reportTimeText = result.reportTimeText
showLocationReportSuccessDialog = true
}
notifyStateChange()
}
private func notifyStateChange() {
onStateChange?()
}
}