// // HomeLocationStateStore.swift // suixinkan // import Foundation /// 首页在线状态与 2 小时倒计时存储,对齐 Android `LocationStateRepository`。 final class HomeLocationStateStore { static let onlineDurationMillis: Int64 = 2 * 60 * 60 * 1000 private(set) var isOnline = false private(set) var elapsedSeconds: Int64 = 0 private(set) var nextReportCountdownSeconds: Int64 = 0 private(set) var reminderMinutes = 0 var onStateChange: (() -> Void)? private let store: AppStore private var countdownTask: Task? private var anchorTimestamp: Int64? init(store: AppStore = .shared) { self.store = store reminderMinutes = store.locationReminderMinutes isOnline = store.onlineStatus } /// 启动时恢复在线倒计时。 func restoreStateIfNeeded() { guard store.onlineStatus else { return } let lastTime = store.lastLocationReportTime guard lastTime > 0 else { return } let now = Int64(Date().timeIntervalSince1970 * 1000) let elapsed = now - lastTime if elapsed >= Self.onlineDurationMillis { updateOnlineStatus(false) store.clearLastLocationReportTime() notifyChange() return } anchorTimestamp = lastTime let remaining = Self.onlineDurationMillis - elapsed elapsedSeconds = max(0, elapsed / 1000) nextReportCountdownSeconds = max(0, remaining / 1000) startCountdown(anchorTime: lastTime) notifyChange() } /// 更新在线状态并持久化。 func updateOnlineStatus(_ online: Bool) { isOnline = online store.onlineStatus = online if !online { stopCountdown() store.clearLastLocationReportTime() } notifyChange() } /// 更新提醒分钟数。 func updateReminderMinutes(_ minutes: Int) { reminderMinutes = minutes store.locationReminderMinutes = minutes notifyChange() } /// 开始新的位置上报会话。 func startLocationReport(at timestamp: Int64 = Int64(Date().timeIntervalSince1970 * 1000)) { anchorTimestamp = timestamp store.lastLocationReportTime = timestamp isOnline = true store.onlineStatus = true elapsedSeconds = 0 nextReportCountdownSeconds = Self.onlineDurationMillis / 1000 startCountdown(anchorTime: timestamp) notifyChange() } var countdownDisplayText: String { formattedCountdown(from: nextReportCountdownSeconds) } /// Android 独立页倒计时格式 `H:MM:SS`。 var countdownDisplayTextSingleHour: String { formattedCountdownSingleHour(from: nextReportCountdownSeconds) } /// 格式化倒计时文本 `HH:MM:SS`。 func formattedCountdown(from seconds: Int64) -> String { let hours = seconds / 3600 let minutes = (seconds % 3600) / 60 let secs = seconds % 60 return String(format: "%02d:%02d:%02d", hours, minutes, secs) } /// 格式化倒计时文本 `H:MM:SS`。 func formattedCountdownSingleHour(from seconds: Int64) -> String { let hours = seconds / 3600 let minutes = (seconds % 3600) / 60 let secs = seconds % 60 return String(format: "%d:%02d:%02d", hours, minutes, secs) } /// 是否到达提前提醒窗口。 func shouldTriggerTimeoutReminder() -> Bool { guard reminderMinutes > 0, isOnline else { return false } let reminderSeconds = Int64(reminderMinutes * 60) return nextReportCountdownSeconds > 0 && nextReportCountdownSeconds <= reminderSeconds } private func startCountdown(anchorTime: Int64) { countdownTask?.cancel() countdownTask = Task { [weak self] in while !Task.isCancelled { guard let self else { return } guard self.isOnline else { return } let now = Int64(Date().timeIntervalSince1970 * 1000) let elapsed = now - anchorTime let remaining = Self.onlineDurationMillis - elapsed let elapsedSeconds = max(0, elapsed / 1000) self.elapsedSeconds = elapsedSeconds if remaining <= 0 { self.nextReportCountdownSeconds = 0 self.updateOnlineStatus(false) self.store.clearLastLocationReportTime() return } self.nextReportCountdownSeconds = max(0, remaining / 1000) self.notifyChange() try? await Task.sleep(nanoseconds: 1_000_000_000) } } } private func stopCountdown() { countdownTask?.cancel() countdownTask = nil anchorTimestamp = nil elapsedSeconds = 0 nextReportCountdownSeconds = 0 } private func notifyChange() { onStateChange?() } }