import ActivityKit import WidgetKit import SwiftUI @available(iOS 16.1, *) struct TaskLiveActivity: Widget { private let accent = WidgetTheme.accent // brand orange var body: some WidgetConfiguration { ActivityConfiguration(for: TaskActivityAttributes.self) { context in // ── Lock screen / banner ── HStack(spacing: 12) { Image(systemName: context.state.isComplete ? "checkmark.circle.fill" : "hourglass") .font(.system(size: 20)) .foregroundColor(context.state.isComplete ? .green : accent) .frame(width: 24) VStack(alignment: .leading, spacing: 2) { Text(context.attributes.title) .font(.system(.body, design: .monospaced)) .foregroundColor(.white) .lineLimit(1) .minimumScaleFactor(0.8) .strikethrough(context.state.isComplete) if let due = context.state.dueDate { Text(due, style: context.state.isComplete ? .date : .relative) .font(.system(.caption, design: .monospaced)) .foregroundColor(.white.opacity(0.6)) .lineLimit(1) } } .layoutPriority(1) if let due = context.state.dueDate, !context.state.isComplete { Text(due, style: .timer) .font(.system(.callout, design: .monospaced)) .monospacedDigit() .foregroundColor(accent) .lineLimit(1) .minimumScaleFactor(0.72) .frame(width: 92, alignment: .trailing) .fixedSize(horizontal: false, vertical: true) } // Explicit stop control — a Live Activity can't be prevented // from being swiped away by the system, so this gives an // intentional, in-card way to end tracking instead. if #available(iOS 17.0, *), !context.state.isComplete { Button(intent: StopCountdownIntent(taskID: context.attributes.taskID)) { Image(systemName: "xmark.circle.fill") .font(.system(size: 18)) .foregroundColor(.white.opacity(0.5)) } .buttonStyle(.plain) } } .padding(.leading, 14) .padding(.trailing, 10) .padding(.vertical, 12) .activityBackgroundTint(WidgetTheme.background.opacity(0.9)) .activitySystemActionForegroundColor(.white) } dynamicIsland: { context in DynamicIsland { DynamicIslandExpandedRegion(.leading) { Image(systemName: "checklist") .foregroundColor(accent) } DynamicIslandExpandedRegion(.trailing) { if let due = context.state.dueDate, !context.state.isComplete { Text(due, style: .timer) .font(.system(.callout, design: .monospaced)) .monospacedDigit() .foregroundColor(accent) .lineLimit(1) .minimumScaleFactor(0.75) .frame(width: 86, alignment: .trailing) } } DynamicIslandExpandedRegion(.center) { Text(context.attributes.title) .font(.system(.callout, design: .monospaced)) .lineLimit(1) } DynamicIslandExpandedRegion(.bottom) { if #available(iOS 17.0, *), !context.state.isComplete { Button(intent: StopCountdownIntent(taskID: context.attributes.taskID)) { Label("Stop Countdown", systemImage: "xmark.circle") .font(.system(.caption, design: .monospaced)) } .buttonStyle(.plain) .tint(accent) } } } compactLeading: { Image(systemName: "checklist").foregroundColor(accent) } compactTrailing: { if let due = context.state.dueDate, !context.state.isComplete { Text(due, style: .timer) .font(.system(.caption2, design: .monospaced)) .monospacedDigit() .foregroundColor(accent) .lineLimit(1) .minimumScaleFactor(0.7) .frame(width: 48, alignment: .trailing) } } minimal: { Image(systemName: "checklist").foregroundColor(accent) } } } }