151 lines
4.9 KiB
Swift
151 lines
4.9 KiB
Swift
import SwiftUI
|
|
|
|
struct BulletinListView: View {
|
|
@StateObject private var viewModel = BulletinViewModel()
|
|
|
|
var body: some View {
|
|
NavigationView {
|
|
Group {
|
|
if viewModel.isLoading {
|
|
ProgressView()
|
|
} else if let error = viewModel.error {
|
|
VStack {
|
|
Text("Error loading bulletins")
|
|
.font(.headline)
|
|
.foregroundColor(.red)
|
|
Text(error.localizedDescription)
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
Button("Retry") {
|
|
Task {
|
|
await viewModel.loadBulletins()
|
|
}
|
|
}
|
|
.padding()
|
|
.background(Color.blue)
|
|
.foregroundColor(.white)
|
|
.cornerRadius(8)
|
|
}
|
|
.padding()
|
|
} else if viewModel.bulletins.isEmpty {
|
|
VStack {
|
|
Text("No Bulletins")
|
|
.font(.headline)
|
|
Text("No bulletins are available at this time.")
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
.padding()
|
|
} else {
|
|
List(viewModel.bulletins) { bulletin in
|
|
NavigationLink(destination: BulletinDetailView(bulletin: bulletin)) {
|
|
BulletinRowView(bulletin: bulletin)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.navigationTitle("Church Bulletins")
|
|
.task {
|
|
await viewModel.loadBulletins()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct BulletinSectionView: View {
|
|
let section: BulletinSection
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
Text(section.title)
|
|
.font(.headline)
|
|
|
|
if section.title == "Scripture Reading" {
|
|
ScriptureReadingView(content: section.content)
|
|
} else {
|
|
BulletinContentText(content: section.content)
|
|
}
|
|
}
|
|
.padding()
|
|
.background(Color(.systemBackground))
|
|
.cornerRadius(10)
|
|
.shadow(radius: 2)
|
|
}
|
|
}
|
|
|
|
struct ScriptureReadingView: View {
|
|
let content: String
|
|
|
|
var body: some View {
|
|
VStack(spacing: 8) {
|
|
ForEach(content.components(separatedBy: .newlines), id: \.self) { line in
|
|
if !line.isEmpty {
|
|
Text(line)
|
|
.font(.body)
|
|
.foregroundColor(line.contains("Acts") ? .primary : .secondary)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct BulletinContentText: View {
|
|
let content: String
|
|
|
|
var formattedContent: [(label: String?, value: String)] {
|
|
content.components(separatedBy: .newlines)
|
|
.map { line -> (String?, String) in
|
|
let parts = line.split(separator: ":", maxSplits: 1).map(String.init)
|
|
if parts.count == 2 {
|
|
return (parts[0].trimmingCharacters(in: .whitespaces),
|
|
parts[1].trimmingCharacters(in: .whitespaces))
|
|
}
|
|
return (nil, line)
|
|
}
|
|
.filter { !$0.1.isEmpty }
|
|
}
|
|
|
|
var body: some View {
|
|
VStack(spacing: 12) {
|
|
ForEach(formattedContent, id: \.1) { item in
|
|
if let label = item.label {
|
|
VStack(spacing: 4) {
|
|
Text(label)
|
|
.font(.headline)
|
|
.foregroundColor(.primary)
|
|
Text(item.value)
|
|
.font(.body)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
} else {
|
|
Text(item.value)
|
|
.font(.body)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct BulletinRowView: View {
|
|
let bulletin: Bulletin
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
Text(bulletin.date.formatted(date: .long, time: .omitted))
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
|
|
Text(bulletin.title)
|
|
.font(.headline)
|
|
.lineLimit(2)
|
|
|
|
if let pdf = bulletin.pdf, !pdf.isEmpty {
|
|
Label("PDF Available", systemImage: "doc.fill")
|
|
.font(.caption)
|
|
.foregroundColor(.blue)
|
|
}
|
|
}
|
|
.padding(.vertical, 8)
|
|
}
|
|
} |