diff options
Diffstat (limited to 'Hutch/Models/Project.swift')
| -rw-r--r-- | Hutch/Models/Project.swift | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/Hutch/Models/Project.swift b/Hutch/Models/Project.swift index 54c0318..678d52e 100644 --- a/Hutch/Models/Project.swift +++ b/Hutch/Models/Project.swift @@ -115,11 +115,104 @@ struct Project: Identifiable, Hashable, Sendable { return nil } + var displayName: String { + Self.normalizedText(name) ?? "Untitled Project" + } + + var displayDescription: String? { + Self.normalizedText(description) + } + + var displayTags: [String] { + var seen = Set<String>() + return tags.compactMap { tag in + guard let normalized = Self.normalizedText(tag) else { return nil } + let key = normalized.lowercased() + guard seen.insert(key).inserted else { return nil } + return normalized + } + } + + var websiteURL: URL? { + guard let website = Self.normalizedText(website) else { return nil } + return URL(string: website) + } + + var hasLinkedResources: Bool { + !sources.isEmpty || !trackers.isEmpty || !mailingLists.isEmpty + } + + var metadataLine: String { + var parts = [visibility.displayName, updated.relativeDescription] + if let summary = resourceSummary { + parts.append(summary) + } + return parts.joined(separator: " • ") + } + private static func resourceCountText(count: Int, singular: String) -> String? { guard count > 0 else { return nil } let label = count == 1 ? singular : "\(singular)s" return "\(count) \(label)" } + + fileprivate static func normalizedText(_ value: String?) -> String? { + guard let trimmed = value?.trimmingCharacters(in: .whitespacesAndNewlines), + !trimmed.isEmpty else { + return nil + } + return trimmed + } +} + +extension Project.MailingList { + var displayName: String { + Project.normalizedText(name) ?? "Untitled Mailing List" + } + + var displayDescription: String? { + Project.normalizedText(description) + } + + var ownerDisplayName: String { + Project.normalizedText(owner.canonicalName) ?? "~unknown" + } +} + +extension Project.SourceRepo { + var displayName: String { + Project.normalizedText(name) ?? "Untitled Repository" + } + + var displayDescription: String? { + Project.normalizedText(description) + } + + var ownerDisplayName: String { + Project.normalizedText(owner.canonicalName) ?? "~unknown" + } + + var webURL: URL? { + SRHTWebURL.projectSource(self) + } +} + +extension Project.Tracker { + var displayName: String { + Project.normalizedText(name) ?? "Untitled Tracker" + } + + var displayDescription: String? { + Project.normalizedText(description) + } + + var ownerDisplayName: String { + Project.normalizedText(owner.canonicalName) ?? "~unknown" + } + + var webURL: URL? { + SRHTWebURL.tracker(ownerUsername: ownerUsername, trackerName: name) + } } extension String { @@ -127,3 +220,16 @@ extension String { hasPrefix("~") ? String(dropFirst()) : self } } + +extension Visibility { + var displayName: String { + switch self { + case .public: + "Public" + case .unlisted: + "Unlisted" + case .private: + "Private" + } + } +} |
