import SwiftUI struct NewsItemView: View { @Environment(HotlineState.self) private var model: HotlineState var news: NewsInfo let depth: Int static var dateFormatter: DateFormatter = { var dateFormatter = DateFormatter() dateFormatter.dateStyle = .long dateFormatter.timeStyle = .short dateFormatter.timeZone = .gmt return dateFormatter }() static var relativeDateFormatter: RelativeDateTimeFormatter = { var formatter = RelativeDateTimeFormatter() formatter.unitsStyle = .full formatter.dateTimeStyle = .named formatter.formattingContext = .listItem return formatter }() var body: some View { HStack(alignment: .center, spacing: 0) { if news.expandable { Button { news.expanded.toggle() } label: { Text(Image(systemName: news.expanded ? "chevron.down" : "chevron.right")) .bold() .font(.system(size: 10)) .opacity(0.5) .frame(alignment: .center) } .buttonStyle(.plain) .frame(width: 10) .padding(.leading, 4) .padding(.trailing, 8) } else { Spacer() .frame(width: 10) .padding(.leading, 4) .padding(.trailing, 8) } // Tree indent Spacer() .frame(width: (CGFloat(depth) * 22)) switch news.type { case .category: Image("News Category") .resizable() .frame(width: 16, height: 16, alignment: .center) .padding(.trailing, 6) case .bundle: Image("News Bundle") .resizable() .frame(width: 16, height: 16, alignment: .center) .padding(.trailing, 6) case .article: EmptyView() } Text(news.name) .fontWeight( (news.type == .bundle || news.type == .category || !news.read) ? .semibold : .regular ) .lineLimit(1) .truncationMode(.tail) if news.type == .article && news.articleUsername != nil { Text(news.articleUsername!) .foregroundStyle(.secondary) .lineLimit(1) .padding(.leading, 8) } Spacer() if news.type == .category && news.count > 0 { Text("^[\(news.count) Post](inflect: true)") .lineLimit(1) .foregroundStyle(.secondary) .padding(.trailing, 8) } else if news.type == .bundle && news.count > 0 { Text("^[\(news.count) Category](inflect: true)") .lineLimit(1) .foregroundStyle(.secondary) .padding(.trailing, 8) } // if news.type == .bundle { // Text("\(news.count)") // .lineLimit(1) // .foregroundStyle(.tertiary) // .padding(.trailing, 8) // ZStack { // Text("^[\(news.count) \(news.type == .bundle ? "Category" : "Post")](inflect: true)") // Text("\(news.count)") // .foregroundStyle(.clear) //// .font(.caption) // .lineLimit(1) // .padding([.leading, .trailing], 8) // .padding([.top, .bottom], 2) // .background(.secondary) // .clipShape(Capsule()) // // Text("\(news.count)") // .foregroundStyle(.white) //// .font(.caption) // .lineLimit(1) // .padding([.leading, .trailing], 8) // .padding([.top, .bottom], 2) // .blendMode(.destinationOut) // } // .drawingGroup(opaque: false) // } else if news.type == .article && news.articleUsername != nil { if let d = news.articleDate { Text(NewsItemView.relativeDateFormatter.localizedString(for: d, relativeTo: Date.now)) .lineLimit(1) .foregroundStyle(.secondary) .padding(.trailing, 8) } } } .frame(maxWidth: .infinity, maxHeight: .infinity) .onChange(of: news.expanded) { guard news.expanded, news.type == .bundle || news.type == .category else { return } Task { try? await model.getNewsList(at: news.path) } } if news.expanded { ForEach(news.children.reversed(), id: \.self) { childNews in NewsItemView(news: childNews, depth: self.depth + 1).tag(childNews.id) } } } } #Preview { NewsItemView(news: NewsInfo(hotlineNewsArticle: HotlineNewsArticle(id: 0, parentID: 0, flags: 0, title: "Title", username: "username", date: Date.now, flavors: [("", 1)], path: ["Guest"])), depth: 0) .environment(HotlineState()) }