198 lines
6.5 KiB
Swift
198 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: HomeLocationProvider
|
||
|
||
init(
|
||
locationStateStore: HomeLocationStateStore = HomeLocationStateStore(),
|
||
locationProvider: HomeLocationProvider = HomeLocationProvider()
|
||
) {
|
||
self.locationStateStore = locationStateStore
|
||
self.locationProvider = locationProvider
|
||
self.locationReportService = LocationReportService(
|
||
locationStateStore: locationStateStore,
|
||
locationProvider: locationProvider
|
||
)
|
||
self.locationStateStore.onStateChange = { [weak self] in
|
||
self?.notifyStateChange()
|
||
}
|
||
}
|
||
|
||
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?()
|
||
}
|
||
}
|