// // UpdatedHeader.swift // IceGlass-iOS // // Copyright 2026 Rouslan Zenetl. All Rights Reserved. // import SwiftUI /// "Updated 2 min ago" + "as of Apr 24, 4:32 PM ET" header. /// Color escalates as data ages: secondary → orange (>5min) → red (>30min or never). /// Self-refreshes every 30s via TimelineView so the relative label stays current /// without a network call. struct UpdatedHeader: View { let lastUpdated: Date? let isRefreshing: Bool var body: some View { TimelineView(.periodic(from: .now, by: 30)) { context in HStack(alignment: .firstTextBaseline, spacing: 8) { VStack(alignment: .leading, spacing: 2) { Text(relativeLabel(now: context.date)) .font(.subheadline) .fontWeight(.medium) if let absolute = absoluteLabel() { Text(absolute) .font(.caption) .foregroundStyle(.tertiary) } } .foregroundStyle(staleness(now: context.date).color) if isRefreshing { ProgressView() .controlSize(.small) } Spacer() } } } private enum Staleness { case fresh case warm // >5 min case stale // >30 min or no data var color: Color { switch self { case .fresh: return .secondary case .warm: return .orange case .stale: return .red } } } private func staleness(now: Date) -> Staleness { guard let lastUpdated else { return .stale } let age = now.timeIntervalSince(lastUpdated) if age > 30 * 60 { return .stale } if age > 5 * 60 { return .warm } return .fresh } private func relativeLabel(now: Date) -> String { guard let lastUpdated else { return "Never updated" } let age = now.timeIntervalSince(lastUpdated) if age < 60 { return "Updated just now" } let formatter = RelativeDateTimeFormatter() formatter.unitsStyle = .abbreviated return "Updated \(formatter.localizedString(for: lastUpdated, relativeTo: now))" } private func absoluteLabel() -> String? { guard let lastUpdated else { return nil } let formatter = DateFormatter() formatter.dateFormat = "MMM d, h:mm a" formatter.timeZone = TimeZone(identifier: "America/New_York") let stamp = formatter.string(from: lastUpdated) return "as of \(stamp) ET" } }