125 lines
3.5 KiB
Swift
125 lines
3.5 KiB
Swift
// ── Watch Timer Store ─────────────────────────────────────────
|
|
// Reads timer data from App Group shared by the iOS app
|
|
// Lightweight store for watchOS — read-only from shared data + local quick timers
|
|
|
|
import Foundation
|
|
import Combine
|
|
import WatchKit
|
|
|
|
@MainActor
|
|
final class WatchTimerStore: ObservableObject {
|
|
// MARK: - Published State
|
|
|
|
@Published var timers: [TimerSnapshot] = []
|
|
@Published var now: Date = Date()
|
|
|
|
// MARK: - Private
|
|
|
|
private var tickTimer: Timer?
|
|
private let sharedData = SharedTimerDataManager.shared
|
|
|
|
// MARK: - Init
|
|
|
|
init() {
|
|
loadFromSharedData()
|
|
startTicking()
|
|
}
|
|
|
|
deinit {
|
|
tickTimer?.invalidate()
|
|
}
|
|
|
|
// MARK: - Data Loading
|
|
|
|
func loadFromSharedData() {
|
|
timers = sharedData.readActiveSnapshots()
|
|
}
|
|
|
|
// MARK: - Queries
|
|
|
|
var nextFiringTimer: TimerSnapshot? {
|
|
timers
|
|
.filter { [.active, .warning].contains($0.state) }
|
|
.sorted { $0.targetTime < $1.targetTime }
|
|
.first
|
|
}
|
|
|
|
var activeTimers: [TimerSnapshot] {
|
|
timers.filter { [.active, .warning, .snoozed, .firing].contains($0.state) }
|
|
}
|
|
|
|
// MARK: - Quick Timer Creation (writes to App Group)
|
|
|
|
func createQuickTimer(minutes: Int, label: String) {
|
|
let now = Date()
|
|
let targetTime = now.addingTimeInterval(TimeInterval(minutes * 60))
|
|
let intervals = CascadePreset.minimal.defaultIntervals
|
|
|
|
let snapshot = TimerSnapshot(
|
|
id: UUID().uuidString,
|
|
label: label,
|
|
type: .countdown,
|
|
urgency: .standard,
|
|
state: .active,
|
|
targetTime: targetTime,
|
|
duration: TimeInterval(minutes * 60),
|
|
startedAt: now,
|
|
elapsedBeforePause: 0,
|
|
snoozeCount: 0,
|
|
category: nil,
|
|
pomodoroCurrentRound: nil,
|
|
pomodoroTotalRounds: nil,
|
|
pomodoroIsBreak: nil,
|
|
nextWarningTime: intervals.first.map { targetTime.addingTimeInterval(-Double($0) * 60) },
|
|
totalWarnings: intervals.count,
|
|
firedWarnings: 0
|
|
)
|
|
|
|
// Add to local list and persist
|
|
timers.append(snapshot)
|
|
timers.sort { $0.targetTime < $1.targetTime }
|
|
|
|
// Write back to shared data so iOS app picks it up
|
|
sharedData.writeSnapshots(timers)
|
|
|
|
// Haptic confirmation
|
|
WKInterfaceDevice.current().play(.success)
|
|
}
|
|
|
|
// MARK: - Tick
|
|
|
|
private func startTicking() {
|
|
tickTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
|
|
Task { @MainActor in
|
|
self?.tick()
|
|
}
|
|
}
|
|
}
|
|
|
|
private func tick() {
|
|
now = Date()
|
|
|
|
// Refresh from shared data every 30 seconds
|
|
let interval = Int(now.timeIntervalSince1970) % 30
|
|
if interval == 0 {
|
|
loadFromSharedData()
|
|
}
|
|
|
|
// Check for fired timers
|
|
var changed = false
|
|
for i in timers.indices {
|
|
if timers[i].state == .active || timers[i].state == .warning {
|
|
if now >= timers[i].targetTime {
|
|
// Timer has fired — haptic alert
|
|
WKInterfaceDevice.current().play(.notification)
|
|
changed = true
|
|
}
|
|
}
|
|
}
|
|
|
|
if changed {
|
|
loadFromSharedData() // Refresh to get updated states
|
|
}
|
|
}
|
|
}
|