import SwiftUI struct HistoryView: View { @EnvironmentObject var store: ConnectionStore var body: some View { ZStack { Color.white.ignoresSafeArea() if store.historyEntries.isEmpty { VStack(spacing: 12) { Image(systemName: "clock.arrow.circlepath") .font(.system(size: 36, weight: .light)) .foregroundStyle(AppTheme.inkQuaternary) Text("No backup history yet") .font(AppTheme.body()) .foregroundStyle(AppTheme.inkTertiary) } } else { List { ForEach(store.historyEntries) { entry in HistoryRowView(entry: entry) .listRowSeparator(.hidden) .listRowInsets(EdgeInsets()) } .onDelete { offsets in store.removeHistoryEntries(at: offsets) } } .listStyle(.plain) } } .navigationTitle("History") .toolbar { if !store.historyEntries.isEmpty { ToolbarItem(placement: .navigationBarTrailing) { Button("Clear") { store.clearHistory() } .font(AppTheme.caption()) .foregroundStyle(AppTheme.destructive) } } } } } struct HistoryRowView: View { let entry: BackupHistoryEntry private var hasErrors: Bool { entry.failedCount > 0 } var body: some View { VStack(spacing: 0) { HStack(alignment: .top, spacing: 14) { Circle() .fill(hasErrors ? AppTheme.destructive : AppTheme.positive) .frame(width: 7, height: 7) .padding(.top, 5) VStack(alignment: .leading, spacing: 5) { HStack(alignment: .firstTextBaseline, spacing: 8) { Text(entry.date, style: .date) .font(.system(size: 15, weight: .medium)) .foregroundStyle(AppTheme.ink) if entry.triggeredByLAN { Text("AUTO") .font(.system(size: 9, weight: .semibold)) .foregroundStyle(AppTheme.inkSecondary) .padding(.horizontal, 5) .padding(.vertical, 2) .overlay( RoundedRectangle(cornerRadius: 3, style: .continuous) .stroke(AppTheme.inkQuaternary, lineWidth: 0.75) ) } Spacer() Text(entry.date, style: .time) .font(AppTheme.micro(11)) .foregroundStyle(AppTheme.inkTertiary) } HStack(spacing: 8) { Text("\(entry.uploadedCount) uploaded") .font(AppTheme.caption()) .foregroundStyle(AppTheme.inkSecondary) if entry.skippedCount > 0 { dot Text("\(entry.skippedCount) skipped") .font(AppTheme.caption()) .foregroundStyle(AppTheme.inkTertiary) } if entry.failedCount > 0 { dot Text("\(entry.failedCount) errors") .font(AppTheme.caption()) .foregroundStyle(AppTheme.destructive) } } Text(entry.nasHost) .font(AppTheme.micro(11)) .foregroundStyle(AppTheme.inkQuaternary) } } .padding(.horizontal, 16) .padding(.vertical, 14) Rectangle() .fill(AppTheme.separator) .frame(height: 0.5) .padding(.leading, 37) } } private var dot: some View { Circle() .fill(AppTheme.inkQuaternary) .frame(width: 3, height: 3) } }