learning_ai_clock/ios/ChronoMind/Views/Components/AlarmOverlay.swift
2026-02-27 21:15:30 -08:00

148 lines
5.9 KiB
Swift

// Alarm Overlay
// Full-screen overlay for CRITICAL urgency firing timers
// Requires confirm-to-dismiss
import SwiftUI
struct AlarmOverlay: View {
let timer: CMTimer
@EnvironmentObject var store: TimerStore
@State private var showConfirmDismiss = false
@State private var pulseScale: CGFloat = 1.0
var body: some View {
ZStack {
// Background
CMColors.bg.opacity(0.95)
.ignoresSafeArea()
VStack(spacing: CMSpacing.xxl) {
Spacer()
// Pulsing urgency ring
ZStack {
Circle()
.stroke(CMColors.critical.opacity(0.2), lineWidth: 4)
.frame(width: 200, height: 200)
.scaleEffect(pulseScale)
.animation(
.easeInOut(duration: 1.0).repeatForever(autoreverses: true),
value: pulseScale
)
Circle()
.stroke(CMColors.critical, lineWidth: 3)
.frame(width: 160, height: 160)
VStack(spacing: CMSpacing.sm) {
Image(systemName: "bell.fill")
.font(.system(size: 36))
.foregroundStyle(CMColors.critical)
Text("NOW")
.font(CMFonts.mono(size: 24, weight: .bold))
.foregroundStyle(CMColors.critical)
}
}
// Timer info
VStack(spacing: CMSpacing.sm) {
UrgencyBadge(urgency: .critical)
Text(timer.label)
.font(CMFonts.display(size: 28))
.foregroundStyle(CMColors.text)
.multilineTextAlignment(.center)
if let desc = timer.description, !desc.isEmpty {
Text(desc)
.font(CMFonts.body(size: 16))
.foregroundStyle(CMColors.textSecondary)
.multilineTextAlignment(.center)
}
if timer.snoozeCount > 0 {
Text("Snoozed \(timer.snoozeCount) time\(timer.snoozeCount == 1 ? "" : "s")")
.font(CMFonts.body(size: 14))
.foregroundStyle(CMColors.textMuted)
}
}
Spacer()
// Snooze buttons
HStack(spacing: CMSpacing.lg) {
Button {
HapticEngine.tap()
store.snooze(timer.id, minutes: 5)
} label: {
VStack(spacing: CMSpacing.xs) {
Image(systemName: "moon.zzz")
.font(.title3)
Text("5 min")
.font(CMFonts.body(size: 13, weight: .medium))
}
.foregroundStyle(CMColors.text)
.frame(maxWidth: .infinity)
.padding(.vertical, CMSpacing.lg)
.background(CMColors.surface)
.clipShape(RoundedRectangle(cornerRadius: CMRadius.md))
.overlay(
RoundedRectangle(cornerRadius: CMRadius.md)
.stroke(CMColors.border, lineWidth: 1)
)
}
Button {
HapticEngine.tap()
store.snooze(timer.id, minutes: 15)
} label: {
VStack(spacing: CMSpacing.xs) {
Image(systemName: "moon.zzz.fill")
.font(.title3)
Text("15 min")
.font(CMFonts.body(size: 13, weight: .medium))
}
.foregroundStyle(CMColors.text)
.frame(maxWidth: .infinity)
.padding(.vertical, CMSpacing.lg)
.background(CMColors.surface)
.clipShape(RoundedRectangle(cornerRadius: CMRadius.md))
.overlay(
RoundedRectangle(cornerRadius: CMRadius.md)
.stroke(CMColors.border, lineWidth: 1)
)
}
}
.padding(.horizontal, CMSpacing.xl)
// Dismiss button (requires confirmation for Critical)
Button {
HapticEngine.tap()
showConfirmDismiss = true
} label: {
Text("Dismiss")
.font(CMFonts.body(size: 18, weight: .bold))
.foregroundStyle(.white)
.frame(maxWidth: .infinity)
.padding(.vertical, CMSpacing.lg)
.background(CMColors.critical)
.clipShape(RoundedRectangle(cornerRadius: CMRadius.md))
}
.padding(.horizontal, CMSpacing.xl)
.padding(.bottom, CMSpacing.xxl)
}
}
.onAppear { pulseScale = 1.15 }
.alert("Dismiss Critical Timer?", isPresented: $showConfirmDismiss) {
Button("Cancel", role: .cancel) {}
Button("Dismiss", role: .destructive) {
store.dismiss(timer.id)
}
} message: {
Text("Are you sure you want to dismiss \"\(timer.label)\"? This is a critical timer.")
}
}
}