krz/moonrise

Night-sky companion for iPhone, Apple Watch and Vision Pro: what's up tonight, the Moon, planets, launches and missions, and a Library of the solar system. SwiftUI app with a Cloudflare Workers backend. astronomy cloudflare-workers ios night-sky space swiftui visionos watchos

App/Widgets/LaunchLiveActivity.swift

main
moonrise/App/Widgets/LaunchLiveActivity.swift history · blame · raw

56 lines · 2646 bytes

2 symbols in this file
 1import ActivityKit
 2import SwiftUI
 3import WidgetKit
 4
 5struct LaunchLiveActivity: Widget {
 6    private let ink = Color(red: 0.95, green: 0.95, blue: 0.93)
 7    private let accent = Color(red: 0.88, green: 0.24, blue: 0.19)
 8
 9    var body: some WidgetConfiguration {
10        ActivityConfiguration(for: LaunchActivityAttributes.self) { context in
11            HStack(alignment: .center, spacing: 12) {
12                VStack(alignment: .leading, spacing: 2) {
13                    Text(context.attributes.rocket).font(.system(size: 17, weight: .medium))
14                    Text(context.attributes.mission).font(.system(size: 13)).lineLimit(1).opacity(0.8)
15                    Text(context.attributes.location).font(.system(size: 11)).lineLimit(1).opacity(0.6)
16                }
17                Spacer()
18                VStack(alignment: .trailing, spacing: 2) {
19                    Text(context.state.status.uppercased()).font(.system(size: 10, weight: .medium)).tracking(0.6)
20                        .foregroundStyle(context.state.status == "Hold" ? accent : ink)
21                    HStack(spacing: 2) {
22                        Text(context.state.net > .now ? "T−" : "T+")
23                        countdown(context.state.net)
24                    }
25                    .font(.system(size: 22, weight: .medium).monospacedDigit())
26                }
27            }
28            .padding(16)
29            .foregroundStyle(ink)
30            .activityBackgroundTint(Color(red: 0.043, green: 0.078, blue: 0.141))
31        } dynamicIsland: { context in
32            DynamicIsland {
33                DynamicIslandExpandedRegion(.leading) {
34                    Text(context.attributes.rocket).font(.system(size: 14, weight: .medium))
35                }
36                DynamicIslandExpandedRegion(.trailing) {
37                    countdown(context.state.net).font(.system(size: 14, weight: .medium).monospacedDigit())
38                }
39                DynamicIslandExpandedRegion(.bottom) {
40                    Text(context.attributes.mission).font(.system(size: 12)).lineLimit(1).opacity(0.8)
41                }
42            } compactLeading: {
43                Text("T−").font(.system(size: 12, weight: .medium))
44            } compactTrailing: {
45                countdown(context.state.net).font(.system(size: 12).monospacedDigit()).frame(maxWidth: 64)
46            } minimal: {
47                Image(systemName: "arrow.up")
48            }
49        }
50    }
51
52    /// Counts down to T-0, then shows elapsed time.
53    private func countdown(_ net: Date) -> Text {
54        net > .now ? Text(timerInterval: Date.now...net, countsDown: true) : Text(net, style: .timer)
55    }
56}