@@ -74,6 +74,7 @@ struct OfficialCertificatesView: View {
7474 @State private var selectedRelease : Release ? = nil
7575 @State private var statusMessage = " "
7676 @State private var isChecking = false
77+ @State private var isLoadingReleases = true
7778 @State private var p12Data : Data ? = nil
7879 @State private var provData : Data ? = nil
7980 @State private var password : String ? = nil
@@ -84,6 +85,16 @@ struct OfficialCertificatesView: View {
8485 statusMessage. contains ( " Success " )
8586 }
8687
88+ private var statusColor : Color {
89+ if statusMessage. contains ( " Downloading " ) {
90+ return . yellow
91+ } else if isSuccess {
92+ return . green
93+ } else {
94+ return . red
95+ }
96+ }
97+
8798 private let dateFormatter : DateFormatter = {
8899 let f = DateFormatter ( )
89100 f. dateStyle = . medium
@@ -95,9 +106,13 @@ struct OfficialCertificatesView: View {
95106 Form {
96107 Section ( " Select Official Certificate " ) {
97108 Picker ( " Certificate " , selection: $selectedRelease) {
98- Text ( " Select a certificate " ) . tag ( nil as Release ? )
99- ForEach ( releases) { release in
100- Text ( cleanName ( release. name) ) . tag ( release as Release ? )
109+ if isLoadingReleases {
110+ Text ( " -- Loading -- " ) . tag ( nil as Release ? )
111+ } else {
112+ Text ( " -- Select a certificate -- " ) . tag ( nil as Release ? )
113+ ForEach ( releases) { release in
114+ Text ( cleanName ( release. name) ) . tag ( release as Release ? )
115+ }
101116 }
102117 }
103118 }
@@ -119,7 +134,10 @@ struct OfficialCertificatesView: View {
119134 . disabled ( selectedRelease == nil || isChecking)
120135 if !statusMessage. isEmpty {
121136 Text ( statusMessage)
122- . foregroundColor ( isSuccess ? . green : . red)
137+ . foregroundColor ( statusColor)
138+ }
139+ if let expiry = expiry {
140+ expiryDisplay ( for: expiry)
123141 }
124142 }
125143 Section {
@@ -144,6 +162,25 @@ struct OfficialCertificatesView: View {
144162 }
145163 }
146164
165+ private func expiryDisplay( for expiry: Date ) -> some View {
166+ let now = Date ( )
167+ let components = Calendar . current. dateComponents ( [ . day] , from: now, to: expiry)
168+ let days = components. day ?? 0
169+ let displayDate = expiry. formattedWithOrdinal ( )
170+ let expiryText : String
171+ let expiryColor : Color
172+ if days > 0 {
173+ expiryText = " Expires on the \( displayDate) "
174+ expiryColor = . green
175+ } else {
176+ expiryText = " Expired on the \( displayDate) "
177+ expiryColor = . red
178+ }
179+ return Text ( expiryText)
180+ . foregroundColor ( expiryColor)
181+ . font ( . caption)
182+ }
183+
147184 private func isoDate( string: String ) -> Date {
148185 let formatter = ISO8601DateFormatter ( )
149186 return formatter. date ( from: string) ?? Date ( )
@@ -185,10 +222,12 @@ struct OfficialCertificatesView: View {
185222 let decoded = try decoder. decode ( [ Release ] . self, from: decodeData)
186223 await MainActor . run {
187224 self . releases = decoded. sorted { isoDate ( string: $0. publishedAt) > isoDate ( string: $1. publishedAt) }
225+ self . isLoadingReleases = false
188226 }
189227 } catch {
190228 await MainActor . run {
191229 self . statusMessage = " Failed to fetch releases: \( error. localizedDescription) "
230+ self . isLoadingReleases = false
192231 }
193232 }
194233 }
@@ -275,7 +314,7 @@ struct OfficialCertificatesView: View {
275314 self . password = pw
276315 self . displayName = dispName
277316 self . expiry = exp
278- self . statusMessage = " Success: Ready to add \( dispName) , expires \( exp ? . formattedWithOrdinal ( ) ?? " Unknown " ) "
317+ self . statusMessage = " Success: Ready to add \( dispName) "
279318 self . isChecking = false
280319 }
281320 } catch {
0 commit comments