summaryrefslogtreecommitdiff
path: root/Hutch/Views/Home/HomeViewModel.swift
diff options
context:
space:
mode:
Diffstat (limited to 'Hutch/Views/Home/HomeViewModel.swift')
-rw-r--r--Hutch/Views/Home/HomeViewModel.swift78
1 files changed, 60 insertions, 18 deletions
diff --git a/Hutch/Views/Home/HomeViewModel.swift b/Hutch/Views/Home/HomeViewModel.swift
index ec54759..4bd4f32 100644
--- a/Hutch/Views/Home/HomeViewModel.swift
+++ b/Hutch/Views/Home/HomeViewModel.swift
@@ -172,6 +172,9 @@ struct HomeBuildItem: Identifiable, Hashable, Sendable {
@Observable
@MainActor
final class HomeViewModel {
+ nonisolated static let defaultFailedBuildLookbackDays = 7
+ nonisolated static let allowedFailedBuildLookbackDays = [1, 3, 7, 14, 30]
+
private(set) var projects: [Project] = []
var assignedTickets: [HomeAssignedTicket] = []
var recentBuilds: [HomeBuildItem] = []
@@ -434,14 +437,7 @@ final class HomeViewModel {
}
var failedBuildCount: Int {
- recentBuilds.filter {
- switch $0.job.status {
- case .failed, .timeout:
- return true
- default:
- return false
- }
- }.count
+ recentFailedBuilds().count
}
var activeBuildCount: Int {
@@ -532,6 +528,19 @@ final class HomeViewModel {
return parts.joined(separator: " • ")
}
+ func recentFailedBuilds(
+ lookbackDays: Int? = nil,
+ now: Date = .now,
+ calendar: Calendar = .current
+ ) -> [HomeBuildItem] {
+ Self.failedBuilds(
+ in: recentBuilds,
+ lookbackDays: lookbackDays ?? Self.failedBuildLookbackDays(),
+ now: now,
+ calendar: calendar
+ )
+ }
+
var systemSummaryText: String {
guard let systemStatusSnapshot else {
return systemStatusErrorMessage ?? "System status unavailable"
@@ -949,20 +958,12 @@ final class HomeViewModel {
}
private func persistNeedsAttentionSnapshot() {
+ let failedBuildCount = recentFailedBuilds().count
NeedsAttentionSnapshotStore.save(
NeedsAttentionSnapshot(
unreadInboxThreads: unreadInboxThreadCount,
assignedOpenTickets: assignedTicketsError == nil ? assignedTickets.count : nil,
- failedBuilds: recentBuildsError == nil
- ? recentBuilds.filter {
- switch $0.job.status {
- case .failed, .timeout:
- true
- default:
- false
- }
- }.count
- : nil,
+ failedBuilds: recentBuildsError == nil ? failedBuildCount : nil,
updatedAt: .now
),
accountID: accountID
@@ -1013,6 +1014,47 @@ final class HomeViewModel {
}
}
+ nonisolated static func failedBuildLookbackDays(defaults: UserDefaults = .standard) -> Int {
+ let value = defaults.object(forKey: AppStorageKeys.homeFailedBuildLookbackDays) as? Int
+ guard let value, allowedFailedBuildLookbackDays.contains(value) else {
+ return defaultFailedBuildLookbackDays
+ }
+ return value
+ }
+
+ nonisolated static func failedBuilds(
+ in builds: [HomeBuildItem],
+ lookbackDays: Int,
+ now: Date = .now,
+ calendar: Calendar = .current
+ ) -> [HomeBuildItem] {
+ let normalizedLookbackDays = allowedFailedBuildLookbackDays.contains(lookbackDays)
+ ? lookbackDays
+ : defaultFailedBuildLookbackDays
+ let startOfToday = calendar.startOfDay(for: now)
+ let windowStart = calendar.date(byAdding: .day, value: -(normalizedLookbackDays - 1), to: startOfToday) ?? startOfToday
+
+ return builds.filter { build in
+ guard build.job.updated >= windowStart else { return false }
+ switch build.job.status {
+ case .failed, .timeout:
+ return true
+ default:
+ return false
+ }
+ }
+ }
+
+ nonisolated static func failedBuildLookbackLabel(days: Int) -> String {
+ let normalizedDays = allowedFailedBuildLookbackDays.contains(days)
+ ? days
+ : defaultFailedBuildLookbackDays
+ if normalizedDays == 1 {
+ return "today"
+ }
+ return "last \(normalizedDays) days"
+ }
+
nonisolated static func sortBuildItemsForTriage(_ lhs: HomeBuildItem, _ rhs: HomeBuildItem) -> Bool {
let lhsPriority = buildPriority(for: lhs.job.status)
let rhsPriority = buildPriority(for: rhs.job.status)