// ── Urgency System ───────────────────────────────────────────── // 5 levels mapping to notification style, sound, vibration, visual intensity, snooze behavior // Ported from web/src/lib/urgency.ts import SwiftUI // MARK: - Urgency Level enum UrgencyLevel: String, Codable, CaseIterable, Identifiable { case critical case important case standard case gentle case passive var id: String { rawValue } } // MARK: - Notification Style enum NotificationStyle: String, Codable { case persistent // Critical: won't auto-dismiss case prominent // Important: sound + banner case `default` // Standard: normal notification case subtle // Gentle: silent banner case badge // Passive: badge only } // MARK: - Urgency Configuration struct UrgencyConfig { let level: UrgencyLevel let label: String let color: Color let colorHex: String let notificationStyle: NotificationStyle let soundEnabled: Bool let vibrationPattern: [Int] // ms on/off pattern let visualIntensity: Double // 0-1 let autoSnoozeMinutes: Int? // nil = no auto-snooze let requireConfirmToDismiss: Bool let fullScreenOverlay: Bool } // MARK: - Urgency Configs let urgencyConfigs: [UrgencyLevel: UrgencyConfig] = [ .critical: UrgencyConfig( level: .critical, label: "Critical", color: Color(hex: 0xFF4757), colorHex: "#FF4757", notificationStyle: .persistent, soundEnabled: true, vibrationPattern: [200, 100, 200, 100, 400], visualIntensity: 1.0, autoSnoozeMinutes: nil, requireConfirmToDismiss: true, fullScreenOverlay: true ), .important: UrgencyConfig( level: .important, label: "Important", color: Color(hex: 0xFF9F43), colorHex: "#FF9F43", notificationStyle: .prominent, soundEnabled: true, vibrationPattern: [200, 100, 200], visualIntensity: 0.8, autoSnoozeMinutes: 10, requireConfirmToDismiss: false, fullScreenOverlay: false ), .standard: UrgencyConfig( level: .standard, label: "Standard", color: Color(hex: 0xFECA57), colorHex: "#FECA57", notificationStyle: .default, soundEnabled: true, vibrationPattern: [200], visualIntensity: 0.6, autoSnoozeMinutes: 5, requireConfirmToDismiss: false, fullScreenOverlay: false ), .gentle: UrgencyConfig( level: .gentle, label: "Gentle", color: Color(hex: 0x2ED573), colorHex: "#2ED573", notificationStyle: .subtle, soundEnabled: true, vibrationPattern: [100], visualIntensity: 0.3, autoSnoozeMinutes: 5, requireConfirmToDismiss: false, fullScreenOverlay: false ), .passive: UrgencyConfig( level: .passive, label: "Passive", color: Color(hex: 0xA5B1C7), colorHex: "#A5B1C7", notificationStyle: .badge, soundEnabled: false, vibrationPattern: [], visualIntensity: 0.1, autoSnoozeMinutes: nil, requireConfirmToDismiss: false, fullScreenOverlay: false ), ] let urgencyOrder: [UrgencyLevel] = [.critical, .important, .standard, .gentle, .passive] func getUrgencyConfig(_ level: UrgencyLevel) -> UrgencyConfig { urgencyConfigs[level]! } func getUrgencyIndex(_ level: UrgencyLevel) -> Int { urgencyOrder.firstIndex(of: level) ?? 0 } // MARK: - Color Extension extension Color { init(hex: UInt, alpha: Double = 1.0) { self.init( .sRGB, red: Double((hex >> 16) & 0xFF) / 255.0, green: Double((hex >> 8) & 0xFF) / 255.0, blue: Double(hex & 0xFF) / 255.0, opacity: alpha ) } }