From 61164b6dd8343c8699d091dd5fccc55beb0b90b9 Mon Sep 17 00:00:00 2001 From: Christian Cleberg Date: Sat, 11 Apr 2026 19:47:40 -0500 Subject: feat: add contribution calendar to user profiles Add a trailing 365-day contribution heatmap to user profile views, backed by HutchStatsService. Includes calendar model, view model, SwiftUI calendar view, app configuration, and unit tests. --- Hutch/App/AppConfiguration.swift | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 Hutch/App/AppConfiguration.swift (limited to 'Hutch/App/AppConfiguration.swift') diff --git a/Hutch/App/AppConfiguration.swift b/Hutch/App/AppConfiguration.swift new file mode 100644 index 0000000..7824371 --- /dev/null +++ b/Hutch/App/AppConfiguration.swift @@ -0,0 +1,42 @@ +import Foundation + +struct AppConfiguration: Sendable { + static let defaultHutchStatsBaseURL = URL(string: "https://hutch-stats.zerolabs.sh")! + static let hutchStatsBaseURLEnvironmentKey = "HUTCH_STATS_BASE_URL" + + let hutchStatsBaseURL: URL + + init( + userDefaults: UserDefaults = .standard, + processInfo: ProcessInfo = .processInfo, + environment: [String: String]? = nil + ) { + let resolvedEnvironment = environment ?? processInfo.environment + + if + let rawValue = resolvedEnvironment[Self.hutchStatsBaseURLEnvironmentKey], + let url = Self.normalizedURL(from: rawValue) + { + self.hutchStatsBaseURL = url + } else if + let rawValue = userDefaults.string(forKey: AppStorageKeys.hutchStatsBaseURL), + let url = Self.normalizedURL(from: rawValue) + { + self.hutchStatsBaseURL = url + } else { + self.hutchStatsBaseURL = Self.defaultHutchStatsBaseURL + } + } + + private static func normalizedURL(from rawValue: String) -> URL? { + guard var components = URLComponents(string: rawValue.trimmingCharacters(in: .whitespacesAndNewlines)) else { + return nil + } + + if components.path.isEmpty { + components.path = "/" + } + + return components.url + } +} -- cgit v1.2.3