Files
suixinkan_uikit/suixinkan/Features/Home/Services/HomeLocationStateStore.swift

153 lines
4.9 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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<Void, Never>?
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?()
}
}