this repo has no description
at main 9.6 kB view raw
1#if os(iOS) 2import ActivityKit 3import SwiftUI 4import WidgetKit 5 6struct DownloadLiveActivityView: View { 7 let context: ActivityViewContext<DownloadActivityAttributes> 8 9 var body: some View { 10 HStack(spacing: 16) { 11 VStack(alignment: .leading, spacing: 4) { 12 Text(context.attributes.accountHandle) 13 .font(.caption2) 14 .foregroundColor(.secondary) 15 16 switch context.state.status { 17 case .fetchingData: 18 Text("Fetching data...") 19 .font(.caption) 20 .foregroundColor(.primary) 21 case .downloading: 22 Text("Downloading") 23 .font(.caption) 24 .foregroundColor(.primary) 25 case .paused: 26 Text("Paused") 27 .font(.caption) 28 .foregroundColor(.orange) 29 case .completed: 30 Text("Complete") 31 .font(.caption) 32 .foregroundColor(.green) 33 } 34 } 35 36 Spacer() 37 38 if context.state.status == .downloading || context.state.status == .paused { 39 VStack(alignment: .trailing, spacing: 4) { 40 Text("\(Int(context.state.progress * 100))%") 41 .font(.system(.title3, design: .rounded)) 42 .fontWeight(.semibold) 43 .foregroundColor(.primary) 44 45 if let totalBlobs = context.state.totalBlobs { 46 Text("\(context.state.downloadedBlobs)/\(totalBlobs)") 47 .font(.caption2) 48 .foregroundColor(.secondary) 49 } 50 } 51 } else if context.state.status == .completed { 52 Image(systemName: "checkmark.circle.fill") 53 .foregroundColor(.green) 54 .font(.title2) 55 } 56 } 57 .padding(.horizontal, 20) 58 .padding(.vertical, 12) 59 .activityBackgroundTint(Color.clear) 60 .activitySystemActionForegroundColor(Color.primary) 61 } 62} 63 64struct DownloadLiveActivityExpandedView: View { 65 let context: ActivityViewContext<DownloadActivityAttributes> 66 67 var body: some View { 68 VStack(spacing: 12) { 69 HStack { 70 VStack(alignment: .leading, spacing: 4) { 71 Text("Downloading Backup") 72 .font(.headline) 73 Text(context.attributes.accountHandle) 74 .font(.subheadline) 75 .foregroundColor(.secondary) 76 } 77 Spacer() 78 } 79 80 if context.state.status == .downloading || context.state.status == .paused { 81 VStack(spacing: 8) { 82 ProgressView(value: context.state.progress) 83 .progressViewStyle(.linear) 84 .tint(context.state.status == .paused ? .orange : .accentColor) 85 86 HStack { 87 Text(statusText) 88 .font(.caption) 89 .foregroundColor(.secondary) 90 Spacer() 91 Text("\(Int(context.state.progress * 100))%") 92 .font(.caption) 93 .fontWeight(.medium) 94 } 95 96 if let totalBlobs = context.state.totalBlobs { 97 Text("\(context.state.downloadedBlobs) of \(totalBlobs) blobs") 98 .font(.caption2) 99 .foregroundColor(.secondary) 100 } 101 } 102 } else if context.state.status == .fetchingData { 103 HStack { 104 ProgressView() 105 .progressViewStyle(.circular) 106 .scaleEffect(0.8) 107 Text("Fetching repository data...") 108 .font(.caption) 109 .foregroundColor(.secondary) 110 } 111 } else if context.state.status == .completed { 112 HStack(spacing: 8) { 113 Image(systemName: "checkmark.circle.fill") 114 .foregroundColor(.green) 115 .font(.title3) 116 Text("Download complete") 117 .font(.subheadline) 118 .foregroundColor(.green) 119 } 120 } 121 } 122 .padding() 123 .activityBackgroundTint(Color.clear) 124 } 125 126 private var statusText: String { 127 switch context.state.status { 128 case .downloading: 129 return "Downloading..." 130 case .paused: 131 return "Download paused" 132 default: 133 return "" 134 } 135 } 136} 137 138struct DownloadActivityWidget: Widget { 139 var body: some WidgetConfiguration { 140 ActivityConfiguration(for: DownloadActivityAttributes.self) { context in 141 DownloadLiveActivityExpandedView(context: context) 142 .background(Color(UIColor.systemBackground)) 143 } dynamicIsland: { context in 144 DynamicIsland { 145 DynamicIslandExpandedRegion(.leading) { 146 VStack(alignment: .leading, spacing: 2) { 147 Text(context.attributes.accountHandle) 148 .font(.caption2) 149 .foregroundColor(.secondary) 150 statusLabel(for: context.state.status) 151 } 152 } 153 154 DynamicIslandExpandedRegion(.trailing) { 155 if context.state.status == .downloading || context.state.status == .paused { 156 VStack(alignment: .trailing, spacing: 2) { 157 Text("\(Int(context.state.progress * 100))%") 158 .font(.system(.title3, design: .rounded)) 159 .fontWeight(.semibold) 160 if let totalBlobs = context.state.totalBlobs { 161 Text("\(context.state.downloadedBlobs)/\(totalBlobs)") 162 .font(.caption2) 163 .foregroundColor(.secondary) 164 } 165 } 166 } else if context.state.status == .completed { 167 Image(systemName: "checkmark.circle.fill") 168 .foregroundColor(.green) 169 .font(.title2) 170 } 171 } 172 173 DynamicIslandExpandedRegion(.bottom) { 174 if context.state.status == .downloading || context.state.status == .paused { 175 ProgressView(value: context.state.progress) 176 .progressViewStyle(.linear) 177 .tint(context.state.status == .paused ? .orange : .accentColor) 178 } 179 } 180 } compactLeading: { 181 Image(systemName: iconName(for: context.state.status)) 182 .foregroundColor(iconColor(for: context.state.status)) 183 } compactTrailing: { 184 if context.state.status == .downloading || context.state.status == .paused { 185 Text("\(Int(context.state.progress * 100))%") 186 .font(.caption) 187 .fontWeight(.semibold) 188 } else if context.state.status == .fetchingData { 189 ProgressView() 190 .progressViewStyle(.circular) 191 .scaleEffect(0.6) 192 } 193 } minimal: { 194 Image(systemName: iconName(for: context.state.status)) 195 .foregroundColor(iconColor(for: context.state.status)) 196 } 197 .widgetURL(URL(string: "atprotobackup://download/\(context.attributes.accountDid)")) 198 .keylineTint(iconColor(for: context.state.status)) 199 } 200 } 201 202 private func iconName(for status: DownloadActivityAttributes.ContentState.DownloadStatus) -> String { 203 switch status { 204 case .fetchingData, .downloading: 205 return "arrow.down.circle.fill" 206 case .paused: 207 return "pause.circle.fill" 208 case .completed: 209 return "checkmark.circle.fill" 210 } 211 } 212 213 private func iconColor(for status: DownloadActivityAttributes.ContentState.DownloadStatus) -> Color { 214 switch status { 215 case .fetchingData, .downloading: 216 return .accentColor 217 case .paused: 218 return .orange 219 case .completed: 220 return .green 221 } 222 } 223 224 @ViewBuilder 225 private func statusLabel(for status: DownloadActivityAttributes.ContentState.DownloadStatus) -> some View { 226 switch status { 227 case .fetchingData: 228 Text("Fetching...") 229 .font(.caption) 230 case .downloading: 231 Text("Downloading") 232 .font(.caption) 233 case .paused: 234 Text("Paused") 235 .font(.caption) 236 .foregroundColor(.orange) 237 case .completed: 238 Text("Complete") 239 .font(.caption) 240 .foregroundColor(.green) 241 } 242 } 243} 244#endif