import Foundation final class Post: ObservableObject { let id: String @Published var isDraft: Bool @Published var createdDate: Date @Published var startDate: Date @Published var hasEndDate: Bool @Published var endDate: Date @Published var tags: [Tag] @Published var german: LocalizedPost @Published var english: LocalizedPost /// The page linked to by this post @Published var linkedPage: Page? init(id: String, isDraft: Bool, createdDate: Date, startDate: Date, endDate: Date?, tags: [Tag], german: LocalizedPost, english: LocalizedPost, linkedPage: Page? = nil) { self.id = id self.isDraft = isDraft self.createdDate = createdDate self.startDate = startDate self.hasEndDate = endDate != nil self.endDate = endDate ?? startDate self.tags = tags self.german = german self.english = english self.linkedPage = linkedPage } func localized(in language: ContentLanguage) -> LocalizedPost { switch language { case .english: return english case .german: return german } } } extension Post: Identifiable { } extension Post: Equatable { static func == (lhs: Post, rhs: Post) -> Bool { lhs.id == rhs.id } } extension Post: Hashable { func hash(into hasher: inout Hasher) { hasher.combine(id) } } // MARK: Feed entry extension Post { private static let englishDate: DateFormatter = { let df = DateFormatter() df.locale = .init(identifier: "en") df.dateFormat = "d. MMMM yyyy" return df }() private static let germanDate: DateFormatter = { let df = DateFormatter() df.locale = .init(identifier: "de") df.dateFormat = "d. MMMM yyyy" return df }() private static let englishDayAndMonth: DateFormatter = { let df = DateFormatter() df.locale = .init(identifier: "en") df.dateFormat = "d. MMMM" return df }() private static let germanDayAndMonth: DateFormatter = { let df = DateFormatter() df.locale = .init(identifier: "de") df.dateFormat = "d. MMMM" return df }() private static let day: DateFormatter = { let df = DateFormatter() df.dateFormat = "d." return df }() private static func dayAndMonth(of date: Date, in language: ContentLanguage) -> String { switch language { case .english: return englishDayAndMonth.string(from: date) case .german: return germanDayAndMonth.string(from: date) } } private static func dateString(for date: Date, in language: ContentLanguage) -> String { switch language { case .english: return englishDate.string(from: date) case .german: return germanDate.string(from: date) } } private func datePrefixString(in language: ContentLanguage) -> String { guard Calendar.current.isDate(startDate, equalTo: endDate, toGranularity: .year) else { // Different year, return full string return startDate.formatted(date: .long, time: .omitted) } guard Calendar.current.isDate(startDate, equalTo: endDate, toGranularity: .month) else { // Different month return Post.dayAndMonth(of: startDate, in: language) } return Post.day.string(from: startDate) } func dateText(in language: ContentLanguage) -> String { guard hasEndDate else { return Post.dateString(for: startDate, in: language) } let endText = Post.dateString(for: endDate, in: language) return "\(datePrefixString(in: language)) - \(endText)" } private func paragraphs(in language: ContentLanguage) -> [String] { localized(in: language).content .components(separatedBy: "\n") .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } .filter { $0 != "" } } func linkToPageInFeed(for language: ContentLanguage) -> FeedEntryData.Link? { nil //.init(url: <#T##String#>, text: <#T##String#>) } func feedEntry(for language: ContentLanguage) -> FeedEntryData { let post = localized(in: language) return .init( entryId: "\(id)", title: post.title, textAboveTitle: dateText(in: language), link: linkToPageInFeed(for: language), tags: tags.map { $0.data(in: language) }, text: paragraphs(in: language), images: post.images.map { $0.feedEntryImage(for: language) }) } }