mycode/myApp/DeepWorkTimer/TimeWidget/TimeWidgetLiveActivity.swift
2026-06-19 19:53:54 +09:00

105 lines
3.8 KiB
Swift

//
// TimeWidgetLiveActivity.swift
// TimeWidget
//
// Created by on 6/13/26.
//
import ActivityKit
import WidgetKit
import SwiftUI
struct TimeWidgetLiveActivity: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: TimerAttributes.self) { context in
// Lock Screen / Notification Banner
// Text(timerInterval:) is a system primitive that counts down in real time
// without waking the app no Timer or polling needed.
HStack(spacing: 14) {
Image(systemName: "timer")
.font(.title2.weight(.semibold))
.foregroundStyle(.white)
VStack(alignment: .leading, spacing: 2) {
Text(context.attributes.sessionName)
.font(.caption)
.foregroundStyle(.white.opacity(0.75))
let safeEnd = max(Date.now, context.state.endDate)
Text(timerInterval: Date.now...safeEnd, countsDown: true)
.font(.title.monospacedDigit().bold())
.foregroundStyle(.white)
}
Spacer()
}
.padding(.horizontal, 20)
.padding(.vertical, 14)
.activityBackgroundTint(Color(red: 0.07, green: 0.07, blue: 0.18))
.activitySystemActionForegroundColor(.white)
} dynamicIsland: { context in
DynamicIsland {
// Expanded shown when the user long-presses the Dynamic Island.
DynamicIslandExpandedRegion(.leading) {
Label(context.attributes.sessionName, systemImage: "timer")
.font(.caption)
.foregroundStyle(.secondary)
.lineLimit(1)
}
DynamicIslandExpandedRegion(.trailing) {
EmptyView()
}
DynamicIslandExpandedRegion(.center) {
let safeEnd = max(Date.now, context.state.endDate)
Text(timerInterval: Date.now...safeEnd, countsDown: true)
.font(.title2.monospacedDigit().bold())
.foregroundStyle(.primary)
}
DynamicIslandExpandedRegion(.bottom) {
Text("Stay focused")
.font(.caption2)
.foregroundStyle(.tertiary)
}
} compactLeading: {
Image(systemName: "timer")
.foregroundStyle(.indigo)
} compactTrailing: {
let safeEnd = max(Date.now, context.state.endDate)
Text(timerInterval: Date.now...safeEnd, countsDown: true)
.font(.caption.monospacedDigit().bold())
.foregroundStyle(.primary)
.frame(width: 52)
} minimal: {
Image(systemName: "timer")
.foregroundStyle(.indigo)
}
.keylineTint(.indigo)
}
}
}
// MARK: - Previews
extension TimerAttributes {
fileprivate static var preview: TimerAttributes {
TimerAttributes(sessionName: "Deep Work")
}
}
extension TimerAttributes.ContentState {
fileprivate static var active: TimerAttributes.ContentState {
TimerAttributes.ContentState(endDate: Date.now.addingTimeInterval(22 * 60))
}
fileprivate static var nearEnd: TimerAttributes.ContentState {
TimerAttributes.ContentState(endDate: Date.now.addingTimeInterval(2 * 60))
}
}
#Preview("Notification", as: .content, using: TimerAttributes.preview) {
TimeWidgetLiveActivity()
} contentStates: {
TimerAttributes.ContentState.active
TimerAttributes.ContentState.nearEnd
}