this repo has no description
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