
- Comprehensive README update documenting v2.0 architectural changes - Updated git remote to ssh://rockvilleav@git.rockvilletollandsda.church:10443/RTSDA/RTSDA-iOS.git - Documented unified ChurchService and 60% code reduction - Added new features: Home Feed, responsive reading, enhanced UI - Corrected license information (GPL v3 with church content copyright) - Updated build instructions and technical stack details
179 lines
6.9 KiB
Swift
179 lines
6.9 KiB
Swift
import SwiftUI
|
|
|
|
struct WatchView: View {
|
|
@Environment(ChurchDataService.self) private var dataService
|
|
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
|
@State private var searchText = ""
|
|
@State private var selectedTab: WatchTab = .sermons
|
|
|
|
|
|
private let grayBackgroundColor: Color = {
|
|
#if os(iOS)
|
|
Color(.systemGray6)
|
|
#else
|
|
Color.gray.opacity(0.2)
|
|
#endif
|
|
}()
|
|
|
|
enum WatchTab: String, CaseIterable {
|
|
case sermons = "Sermons"
|
|
case liveArchives = "Live Archives"
|
|
|
|
var icon: String {
|
|
switch self {
|
|
case .sermons:
|
|
return "play.rectangle.fill"
|
|
case .liveArchives:
|
|
return "dot.radiowaves.left.and.right"
|
|
}
|
|
}
|
|
}
|
|
|
|
var currentContent: [Sermon] {
|
|
switch selectedTab {
|
|
case .sermons:
|
|
return dataService.sermons
|
|
case .liveArchives:
|
|
return dataService.livestreamArchives
|
|
}
|
|
}
|
|
|
|
var filteredContent: [Sermon] {
|
|
let content = currentContent
|
|
|
|
// Apply search text filter
|
|
return SearchUtils.searchSermons(content, searchText: searchText, contentType: selectedTab.rawValue.lowercased())
|
|
}
|
|
|
|
|
|
|
|
var body: some View {
|
|
VStack(spacing: 0) {
|
|
// Toggle buttons
|
|
HStack(spacing: 12) {
|
|
ForEach(WatchTab.allCases, id: \.self) { tab in
|
|
Button(action: {
|
|
selectedTab = tab
|
|
Task {
|
|
switch tab {
|
|
case .sermons:
|
|
await dataService.loadAllSermons()
|
|
case .liveArchives:
|
|
await dataService.loadAllLivestreamArchives()
|
|
}
|
|
}
|
|
}) {
|
|
HStack(spacing: 8) {
|
|
Image(systemName: tab.icon)
|
|
.font(horizontalSizeClass == .regular ? .body : .subheadline)
|
|
Text(tab.rawValue)
|
|
.font(.system(size: horizontalSizeClass == .regular ? 18 : 16, weight: .medium))
|
|
}
|
|
.foregroundColor(selectedTab == tab ? .white : .secondary)
|
|
.padding(.horizontal, horizontalSizeClass == .regular ? 32 : 20)
|
|
.padding(.vertical, horizontalSizeClass == .regular ? 20 : 14)
|
|
.background(
|
|
selectedTab == tab ?
|
|
Color(hex: getBrandColor()) :
|
|
Color.clear,
|
|
in: RoundedRectangle(cornerRadius: horizontalSizeClass == .regular ? 12 : 10)
|
|
)
|
|
.overlay(
|
|
RoundedRectangle(cornerRadius: horizontalSizeClass == .regular ? 12 : 10)
|
|
.strokeBorder(selectedTab == tab ? Color.clear : .secondary.opacity(0.3), lineWidth: 1)
|
|
)
|
|
.contentShape(RoundedRectangle(cornerRadius: horizontalSizeClass == .regular ? 12 : 10))
|
|
}
|
|
.buttonStyle(.plain)
|
|
}
|
|
Spacer()
|
|
}
|
|
.padding(.horizontal, 20)
|
|
.padding(.top, 16)
|
|
.padding(.bottom, 12)
|
|
|
|
// Search bar
|
|
HStack {
|
|
Image(systemName: "magnifyingglass")
|
|
.foregroundColor(.secondary)
|
|
|
|
TextField(selectedTab == .sermons ? "Search sermons..." : "Search live archives...", text: $searchText)
|
|
.textFieldStyle(PlainTextFieldStyle())
|
|
|
|
if !searchText.isEmpty {
|
|
Button(action: { searchText = "" }) {
|
|
Image(systemName: "xmark.circle.fill")
|
|
.foregroundColor(.secondary)
|
|
}
|
|
}
|
|
}
|
|
.padding(.horizontal, 16)
|
|
.padding(.vertical, 12)
|
|
.background(grayBackgroundColor)
|
|
.cornerRadius(12)
|
|
.padding(.horizontal, 20)
|
|
.padding(.bottom, 16)
|
|
|
|
// Content
|
|
if dataService.isLoading {
|
|
Spacer()
|
|
ProgressView(selectedTab == .sermons ? "Loading sermons..." : "Loading live archives...")
|
|
Spacer()
|
|
} else if filteredContent.isEmpty {
|
|
Spacer()
|
|
VStack(spacing: 16) {
|
|
Image(systemName: selectedTab == .sermons ? "play.rectangle" : "dot.radiowaves.left.and.right")
|
|
.font(.system(size: 50))
|
|
.foregroundColor(.secondary)
|
|
|
|
Text(selectedTab == .sermons ? "No sermons found" : "No live archives available")
|
|
.font(.headline)
|
|
.foregroundColor(.secondary)
|
|
|
|
if !searchText.isEmpty {
|
|
Text("Try adjusting your search terms")
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
.multilineTextAlignment(.center)
|
|
}
|
|
}
|
|
.padding()
|
|
Spacer()
|
|
} else {
|
|
ScrollView {
|
|
LazyVStack(spacing: 16) {
|
|
// Live stream card - only show when stream is live
|
|
if dataService.isStreamLive {
|
|
LiveStreamCard()
|
|
.padding(.horizontal, 20)
|
|
}
|
|
|
|
ForEach(filteredContent, id: \.id) { sermon in
|
|
SermonCard(sermon: sermon, style: .watch)
|
|
}
|
|
}
|
|
.padding(.horizontal, 20)
|
|
.padding(.bottom, 100)
|
|
}
|
|
.refreshable {
|
|
switch selectedTab {
|
|
case .sermons:
|
|
await dataService.loadAllSermons()
|
|
case .liveArchives:
|
|
await dataService.loadAllLivestreamArchives()
|
|
}
|
|
// Always refresh stream status when pulling to refresh
|
|
await dataService.loadStreamStatus()
|
|
}
|
|
}
|
|
}
|
|
.navigationTitle("Watch")
|
|
.task {
|
|
// Load both sermons, live archives, and stream status on initial load
|
|
await dataService.loadAllSermons()
|
|
await dataService.loadAllLivestreamArchives()
|
|
await dataService.loadStreamStatus()
|
|
}
|
|
|
|
}
|
|
} |