Skip to content

Commit 48dbd79

Browse files
committed
refactor: improve update handling and UI feedback in SettingsUpdaterModel and UpdatesView
1 parent 6d2b590 commit 48dbd79

4 files changed

Lines changed: 271 additions & 61 deletions

File tree

Binary file not shown.

Micmute/Utilities/SettingsUpdaterModel.swift

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ final class SettingsUpdaterModel: ObservableObject {
3737
static let frequency = "com.rokartur.micmute.updater.frequency"
3838
static let nextCheck = "com.rokartur.micmute.updater.nextCheck"
3939
static let lastSeenRelease = "com.rokartur.micmute.updater.lastSeenRelease"
40-
static let lastFetch = "com.rokartur.micmute.updater.lastFetch"
40+
static let lastFetch = "com.rokartur.micmute.updater.lastFetch"
4141
}
4242

4343
private enum FetchReason {
@@ -291,10 +291,11 @@ final class SettingsUpdaterModel: ObservableObject {
291291
}
292292

293293
private func handleFetchedReleases(_ releases: [GitHubReleaseDTO]) {
294-
let filtered = releases.filter { !$0.draft && !$0.prerelease }
295-
self.releases = filtered.map { SettingsRelease(dto: $0) }
296-
updateAvailable = determineUpdateAvailability(from: filtered.first)
297-
updateAnnouncementState(using: filtered.first)
294+
let visible = releases.filter { !$0.draft && !$0.prerelease }
295+
self.releases = visible.map { SettingsRelease(dto: $0) }
296+
let latest = visible.first
297+
updateAvailable = determineUpdateAvailability(from: latest)
298+
updateAnnouncementState(using: latest)
298299

299300
if progressValue > 0 {
300301
progressValue = min(progressValue + 0.2, 1.0)
@@ -628,6 +629,7 @@ struct SettingsRelease: Identifiable {
628629
let body: String
629630
let publishedAt: Date?
630631
let assets: [Asset]
632+
let isPrerelease: Bool
631633

632634
init(dto: GitHubReleaseDTO) {
633635
self.id = dto.id
@@ -636,6 +638,7 @@ struct SettingsRelease: Identifiable {
636638
self.body = dto.body ?? ""
637639
self.publishedAt = dto.publishedAt
638640
self.assets = dto.assets.map { Asset(dto: $0) }
641+
self.isPrerelease = dto.prerelease
639642
}
640643

641644
var displayTitle: String {
@@ -716,46 +719,54 @@ private extension UpdaterError {
716719
var userFacingMessage: String {
717720
switch self {
718721
case .invalidResponse:
719-
return "GitHub returned an unexpected response."
722+
return "Micmute couldn't understand GitHub's response."
720723
case .invalidDownloadURL:
721-
return "No valid download URL was found for the selected release."
724+
return "The release is missing a valid download link."
722725
case .cannotAccessApplicationSupport:
723-
return "Micmute couldn't access Application Support."
724-
case .failedToExtractArchive(let message):
725-
return message.isEmpty ? "Micmute couldn't extract the update." : "Extraction failed: \(message)"
726+
return "Micmute can't access Application Support."
727+
case .failedToExtractArchive(let reason):
728+
return "Couldn't unpack the update (\(reason))."
726729
case .applicationBundleNotFound:
727-
return "The downloaded archive didn't contain a Micmute app."
730+
return "Micmute couldn't find the app bundle inside the archive."
728731
case .installationFailed(let message):
729-
return message.isEmpty ? "Installing the update failed." : message
732+
return message.isEmpty ? "Update installation failed." : message
730733
case .installationCancelled:
731-
return "Installation cancelled."
734+
return "Installation cancelled"
732735
}
733736
}
734737

735738
var logDescription: String {
736739
switch self {
737740
case .invalidResponse:
738-
return "Invalid HTTP response while fetching release data"
741+
return "Invalid response from GitHub"
739742
case .invalidDownloadURL:
740-
return "Release asset missing a usable download URL"
743+
return "Missing or invalid download URL"
741744
case .cannotAccessApplicationSupport:
742-
return "Application Support directory not accessible"
743-
case .failedToExtractArchive(let message):
744-
return "Archive extraction failed: \(message)"
745+
return "Cannot access Application Support directory"
746+
case .failedToExtractArchive(let reason):
747+
return "Extraction failed: \(reason)"
745748
case .applicationBundleNotFound:
746-
return "Extracted archive did not contain an .app bundle"
749+
return "Application bundle not found in archive"
747750
case .installationFailed(let message):
748-
return "Application installation failed: \(message)"
751+
return "Installation failed: \(message)"
749752
case .installationCancelled:
750-
return "Installation cancelled by the user"
753+
return "Installation cancelled by user"
751754
}
752755
}
753756
}
754757

755758
private extension Bundle {
756759
var name: String {
757-
object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
758-
?? object(forInfoDictionaryKey: "CFBundleName") as? String
759-
?? bundleURL.deletingPathExtension().lastPathComponent
760+
if let displayName = object(forInfoDictionaryKey: "CFBundleDisplayName") as? String,
761+
!displayName.isEmpty {
762+
return displayName
763+
}
764+
765+
if let bundleName = object(forInfoDictionaryKey: "CFBundleName") as? String,
766+
!bundleName.isEmpty {
767+
return bundleName
768+
}
769+
770+
return bundleURL.deletingPathExtension().lastPathComponent
760771
}
761772
}

Micmute/Views/MarkdownReleasesView.swift

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,10 @@ struct MarkdownReleasesView: View {
4848

4949
@ViewBuilder
5050
private func releaseView(for release: SettingsRelease) -> some View {
51+
let isLatest = release.id == releases.first?.id
52+
5153
VStack(alignment: .leading, spacing: 12) {
52-
header(for: release)
54+
header(for: release, isLatest: isLatest)
5355

5456
if let markdown = release.githubMarkdownBody() {
5557
Text(markdown)
@@ -107,9 +109,18 @@ struct MarkdownReleasesView: View {
107109
}
108110

109111
@ViewBuilder
110-
private func header(for release: SettingsRelease) -> some View {
111-
Text(release.displayTitle)
112-
.font(.headline)
113-
.fontWeight(.semibold)
112+
private func header(for release: SettingsRelease, isLatest: Bool) -> some View {
113+
HStack(alignment: .center, spacing: 10) {
114+
Text(release.displayTitle)
115+
.font(.headline)
116+
.fontWeight(.semibold)
117+
118+
Spacer(minLength: 6)
119+
120+
if isLatest {
121+
ReleaseBadge(label: "Latest", style: .latest)
122+
.transition(.scale.combined(with: .opacity))
123+
}
124+
}
114125
}
115126
}

0 commit comments

Comments
 (0)