Sources/OrgstarMobile/LiveClock.swift
47 lines · 2112 bytes
1#if os(iOS)
2import ActivityKit
3import AppIntents
4import OrgApp
5
6/// The running clock's Live Activity: one while a clock runs, started when it starts and
7/// ended when it stops, however that happens.
8@MainActor
9enum LiveClock {
10 nonisolated private static func isLive(_ activity: Activity<ClockActivityAttributes>) -> Bool {
11 activity.activityState == .active || activity.activityState == .stale
12 }
13
14 nonisolated private static func shows(_ activity: Activity<ClockActivityAttributes>, _ running: ClockModel.Running) -> Bool {
15 activity.attributes.path == running.path && activity.attributes.start == running.start
16 }
17
18 /// Ends activities of other clocks; starts one for `running` unless it has one.
19 nonisolated static func sync(_ running: ClockModel.Running?) async {
20 let activities = Activity<ClockActivityAttributes>.activities.filter(isLive)
21 for activity in activities where running.map({ !shows(activity, $0) }) ?? true {
22 await activity.end(nil, dismissalPolicy: .immediate)
23 }
24 guard let running, !activities.contains(where: { shows($0, running) }), ActivityAuthorizationInfo().areActivitiesEnabled else { return }
25 let state = ClockActivityAttributes.ContentState(heading: running.heading, startedAt: running.startedAt)
26 _ = try? Activity.request(
27 attributes: ClockActivityAttributes(path: running.path, start: running.start), content: ActivityContent(state: state, staleDate: nil)
28 )
29 }
30
31 /// The Clock Out button's work, in the app's process.
32 static func install() {
33 ClockOutIntent.handler = {
34 let model = MobileModel.shared
35 let path = model.clock.running?.path
36 _ = model.clock.clockOut(model.session)
37 if let path, model.session.dirtyPaths.contains(path) { _ = model.session.save(path) }
38 await sync(model.clock.running)
39 }
40 }
41}
42
43/// The App Intents OrgApp declares.
44struct OrgstarIntents: AppIntentsPackage {
45 static var includedPackages: [any AppIntentsPackage.Type] { [OrgAppIntents.self] }
46}
47#endif