krz/orgstar

A native macOS editor for org-mode files. editor org-mode swift

Sources/OrgstarMobile/LiveClock.swift

47 lines · 2112 bytes

6 symbols in this file
 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