this repo has no description
at main 6.3 kB view raw
1// 2// DownloadSection.swift 3// AtProtoBackup 4// 5// Created by Corey Alexander on 8/25/25. 6// 7 8import SwiftUI 9 10struct DownloadSection: View { 11 let account: Account 12 @ObservedObject var downloadManager: DownloadManager 13 @State private var lastBackupMetadata: BackupMetadata? 14 15 var body: some View { 16 VStack(spacing: 12) { 17 Button(action: { 18 downloadManager.startDownload(for: account) 19 }) { 20 HStack { 21 Image(systemName: "arrow.down.circle.fill") 22 Text("Download Backup") 23 } 24 .padding(.horizontal, 16) 25 .padding(.vertical, 8) 26 .background(Color.blue) 27 .foregroundColor(.white) 28 .cornerRadius(8) 29 } 30 .buttonStyle(PlainButtonStyle()) 31 32 // Display last backup time 33 if let metadata = lastBackupMetadata { 34 LastBackupView(metadata: metadata) 35 } 36 37 if let download = downloadManager.getDownload(for: account) { 38 DownloadStatusView(download: download, account: account, downloadManager: downloadManager) 39 } 40 } 41 .onAppear { 42 loadLastBackupMetadata() 43 } 44 .onChange(of: downloadManager.getDownload(for: account)?.progress) { oldValue, newValue in 45 // Reload metadata when download completes 46 if newValue == 1.0 { 47 DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { 48 loadLastBackupMetadata() 49 } 50 } 51 } 52 } 53 54 private func loadLastBackupMetadata() { 55 guard let saveLocation = try? BackupDiscovery.accountBackupDirectory(for: account.did) else { 56 return 57 } 58 59 do { 60 lastBackupMetadata = try BackupMetadata.load(from: saveLocation) 61 } catch { 62 // No metadata file exists yet or failed to load 63 lastBackupMetadata = nil 64 } 65 } 66} 67 68struct LastBackupView: View { 69 let metadata: BackupMetadata 70 71 var body: some View { 72 HStack(spacing: 6) { 73 Image(systemName: "clock.fill") 74 .font(.caption2) 75 .foregroundColor(.secondary) 76 Text("Last backup: \(formatDate(metadata.completedAt))") 77 .font(.caption) 78 .foregroundColor(.secondary) 79 } 80 } 81 82 private func formatDate(_ date: Date) -> String { 83 let now = Date() 84 let calendar = Calendar.current 85 let components = calendar.dateComponents([.minute, .hour, .day], from: date, to: now) 86 87 if let days = components.day, days > 0 { 88 if days == 1 { 89 return "yesterday" 90 } else if days < 7 { 91 return "\(days) days ago" 92 } else { 93 let formatter = DateFormatter() 94 formatter.dateStyle = .medium 95 formatter.timeStyle = .none 96 return formatter.string(from: date) 97 } 98 } else if let hours = components.hour, hours > 0 { 99 return hours == 1 ? "1 hour ago" : "\(hours) hours ago" 100 } else if let minutes = components.minute, minutes > 0 { 101 return minutes == 1 ? "1 minute ago" : "\(minutes) minutes ago" 102 } else { 103 return "just now" 104 } 105 } 106} 107 108struct DownloadStatusView: View { 109 let download: DownloadInfo 110 let account: Account 111 @ObservedObject var downloadManager: DownloadManager 112 113 var body: some View { 114 VStack(spacing: 8) { 115 if download.isDownloading { 116 VStack(spacing: 4) { 117 if let totalBlobs = download.totalBlobs { 118 ProgressView(value: download.progress) { 119 Text("Downloading... \(Int(download.progress * 100))%") 120 .font(.caption) 121 } 122 .progressViewStyle(LinearProgressViewStyle()) 123 124 Text("\(Int(download.progress * Double(totalBlobs))) of \(totalBlobs) blobs") 125 .font(.caption2) 126 .foregroundColor(.secondary) 127 } else { 128 HStack { 129 ProgressView() 130 .scaleEffect(0.8) 131 Text("Fetching repository data...") 132 .font(.caption) 133 .foregroundColor(.secondary) 134 } 135 } 136 } 137 } else if download.progress > 0 && download.progress < 1.0 { 138 VStack(spacing: 6) { 139 HStack { 140 Text("Download paused at \(Int(download.progress * 100))%") 141 .font(.caption) 142 .foregroundColor(.secondary) 143 144 Button("Resume") { 145 downloadManager.startDownload(for: account) 146 } 147 .buttonStyle(BorderedButtonStyle()) 148 } 149 150 if let totalBlobs = download.totalBlobs { 151 Text("\(Int(download.progress * Double(totalBlobs))) of \(totalBlobs) blobs") 152 .font(.caption2) 153 .foregroundColor(.secondary) 154 } 155 } 156 } else if download.progress >= 1.0 { 157 HStack { 158 Image(systemName: "checkmark.circle.fill") 159 .foregroundColor(.green) 160 VStack(alignment: .leading, spacing: 2) { 161 Text("Download complete") 162 .font(.caption) 163 .foregroundColor(.secondary) 164 if let totalBlobs = download.totalBlobs { 165 Text("\(totalBlobs) blobs downloaded") 166 .font(.caption2) 167 .foregroundColor(.secondary) 168 } 169 } 170 } 171 } 172 } 173 } 174}