import SwiftUI struct HomeFeedView: View { @Environment(ChurchDataService.self) private var dataService @Environment(\.horizontalSizeClass) private var horizontalSizeClass var body: some View { if horizontalSizeClass == .regular { // iPad: Enhanced entertainment-focused layout iPadHomeFeedView() } else { // iPhone: Current compact layout iPhoneHomeFeedView() } } } struct iPadHomeFeedView: View { @Environment(ChurchDataService.self) private var dataService var body: some View { ScrollView { LazyVStack(spacing: 32) { // Hero Section - Latest Featured Sermon if let latestSermon = dataService.sermons.first { VStack(alignment: .leading, spacing: 0) { HStack { VStack(alignment: .leading, spacing: 8) { Text("LATEST MESSAGE") .font(.caption) .fontWeight(.bold) .foregroundColor(Color(hex: getBrandColor())) .textCase(.uppercase) .tracking(1) Text("Featured Sermon") .font(.system(size: 32, weight: .bold)) .foregroundColor(.primary) } Spacer() NavigationLink("Browse All") { WatchView() } .font(.system(size: 16, weight: .medium)) .foregroundColor(Color(hex: getBrandColor())) } .padding(.horizontal, 24) .padding(.top, 16) Button { if latestSermon.videoUrl != nil, let url = URL(string: getOptimalStreamingUrl(mediaId: latestSermon.id)) { SharedVideoManager.shared.playVideo(url: url, title: latestSermon.title, artworkURL: latestSermon.thumbnail) } } label: { HeroSermonCard(sermon: latestSermon) } .buttonStyle(PlainButtonStyle()) .padding(.top, 20) } } // Recent Sermons Grid if dataService.sermons.count > 1 { VStack(alignment: .leading, spacing: 20) { HStack { Text("Recent Sermons") .font(.system(size: 28, weight: .bold)) Spacer() NavigationLink("View All") { WatchView() } .font(.system(size: 16, weight: .medium)) .foregroundColor(Color(hex: getBrandColor())) } .padding(.horizontal, 24) // 2x2 grid layout for iPads LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 16), count: 2), spacing: 16) { ForEach(Array(dataService.sermons.dropFirst().prefix(4)), id: \.id) { sermon in Button { if sermon.videoUrl != nil, let url = URL(string: getOptimalStreamingUrl(mediaId: sermon.id)) { SharedVideoManager.shared.playVideo(url: url, title: sermon.title, artworkURL: sermon.thumbnail) } } label: { SermonGridCard(sermon: sermon) } .buttonStyle(PlainButtonStyle()) } } .padding(.horizontal, 24) } } // Upcoming Events Section if !dataService.events.isEmpty { VStack(alignment: .leading, spacing: 20) { HStack { VStack(alignment: .leading, spacing: 4) { Text("WHAT'S HAPPENING") .font(.caption) .fontWeight(.bold) .foregroundColor(Color(hex: getBrandColor())) .textCase(.uppercase) .tracking(1) Text("Upcoming Events") .font(.system(size: 28, weight: .bold)) } Spacer() NavigationLink("All Events") { EventsListView() } .font(.system(size: 16, weight: .medium)) .foregroundColor(Color(hex: getBrandColor())) } .padding(.horizontal, 24) ScrollView(.horizontal, showsIndicators: false) { HStack(spacing: 16) { ForEach(dataService.events.prefix(5), id: \.id) { event in NavigationLink { EventDetailViewWrapper(event: event) } label: { ChurchEventHighlightCard(event: event) .frame(width: 300) } .buttonStyle(.plain) } } .padding(.horizontal, 24) } } } // Quick Actions removed - functionality available via main navigation tabs } .padding(.vertical, 20) .padding(.bottom, 100) } .navigationTitle("Welcome to RTSDA") .navigationBarTitleDisplayMode(.large) .refreshable { await dataService.loadHomeFeed() } } } struct iPhoneHomeFeedView: View { @Environment(ChurchDataService.self) private var dataService var body: some View { ScrollView { LazyVStack(spacing: 20) { if !dataService.sermons.isEmpty { VStack(alignment: .leading, spacing: 16) { HStack { Text("Featured Sermons") .font(.system(size: 24, weight: .bold)) Spacer() NavigationLink("See All") { WatchView() } .font(.subheadline) .foregroundColor(.blue) } ScrollView(.horizontal, showsIndicators: false) { HStack(spacing: 16) { ForEach(dataService.sermons.prefix(5), id: \.id) { sermon in SermonCard(sermon: sermon, style: .feed) .frame(width: 250) } } .padding(.horizontal, 20) } } } if !dataService.events.isEmpty { VStack(alignment: .leading, spacing: 16) { HStack { Text("Upcoming Events") .font(.system(size: 24, weight: .bold)) Spacer() NavigationLink("See All") { EventsListView() } .font(.subheadline) .foregroundColor(.blue) } ForEach(dataService.events.prefix(3), id: \.id) { event in FeedItemCard(item: FeedItem(type: .event(event), timestamp: Date())) } } } } .padding(.horizontal, 20) .padding(.bottom, 100) } .navigationTitle("Welcome to RTSDA") .navigationBarTitleDisplayMode(.large) .refreshable { await dataService.loadHomeFeed() } } } // MARK: - iPad-specific Cards struct HeroSermonCard: View { let sermon: Sermon var body: some View { ZStack { // Background image with gradient overlay AsyncImage(url: URL(string: sermon.thumbnail ?? "")) { image in image .resizable() .aspectRatio(contentMode: .fill) .offset(y: 20) // Shift DOWN to show heads instead of cutting them off .scaleEffect(0.9) // Zoom out to ensure heads are visible } placeholder: { Rectangle() .fill(LinearGradient( colors: [Color(hex: getBrandColor()), Color(hex: getBrandColor()).opacity(0.7)], startPoint: .topLeading, endPoint: .bottomTrailing )) } .frame(height: 450) .clipped() // Gradient overlay LinearGradient( colors: [Color.clear, Color.black.opacity(0.8)], startPoint: .top, endPoint: .bottom ) // Content overlay VStack(alignment: .leading) { Spacer() HStack { VStack(alignment: .leading, spacing: 12) { // Play button Image(systemName: "play.circle.fill") .font(.system(size: 60)) .foregroundColor(.white) .shadow(color: .black.opacity(0.3), radius: 4) VStack(alignment: .leading, spacing: 8) { Text(sermon.title) .font(.system(size: 28, weight: .bold)) .foregroundColor(.white) .lineLimit(2) .shadow(color: .black.opacity(0.5), radius: 2) HStack(spacing: 16) { if !sermon.speaker.isEmpty { HStack(spacing: 4) { Image(systemName: "person.fill") .font(.caption) Text(sermon.speaker) .font(.subheadline) .fontWeight(.medium) } } if let duration = sermon.durationFormatted { HStack(spacing: 4) { Image(systemName: "clock.fill") .font(.caption) Text(duration) .font(.subheadline) } } Text(sermon.formattedDate) .font(.subheadline) } .foregroundColor(.white.opacity(0.9)) .shadow(color: .black.opacity(0.3), radius: 1) } } Spacer() } .padding(32) } } .cornerRadius(16) .shadow(color: .black.opacity(0.1), radius: 8, x: 0, y: 4) .padding(.horizontal, 24) } } struct SermonGridCard: View { let sermon: Sermon var body: some View { VStack(alignment: .leading, spacing: 12) { // Thumbnail ZStack { AsyncImage(url: URL(string: sermon.thumbnail ?? "")) { image in image .resizable() .aspectRatio(contentMode: .fill) .offset(y: 10) // Shift DOWN to show heads instead of cutting them off .scaleEffect(0.95) // Zoom out to ensure heads are visible } placeholder: { Rectangle() .fill(LinearGradient( colors: [Color(hex: getBrandColor()), Color(hex: getBrandColor()).opacity(0.7)], startPoint: .topLeading, endPoint: .bottomTrailing )) } .frame(height: 200) .clipped() // Play button overlay Circle() .fill(.black.opacity(0.6)) .frame(width: 50, height: 50) .overlay { Image(systemName: "play.fill") .font(.title2) .foregroundColor(.white) .offset(x: 2) // Visual centering } } .cornerRadius(12) // Content VStack(alignment: .leading, spacing: 8) { Text(sermon.title) .font(.system(size: 16, weight: .semibold)) .lineLimit(2) .multilineTextAlignment(.leading) HStack { if !sermon.speaker.isEmpty { Text(sermon.speaker) .font(.system(size: 14)) .foregroundColor(.secondary) .lineLimit(1) } Spacer() if let duration = sermon.durationFormatted { Text(duration) .font(.system(size: 14)) .foregroundColor(.secondary) } } Text(sermon.formattedDate) .font(.system(size: 13)) .foregroundColor(.secondary) } .padding(.horizontal, 4) } .frame(height: 300) .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 16)) .shadow(color: .black.opacity(0.05), radius: 8, x: 0, y: 2) } } struct ChurchEventHighlightCard: View { let event: ChurchEvent var body: some View { HStack(spacing: 16) { // Date indicator VStack(spacing: 4) { Text(event.dayOfMonth) .font(.system(size: 24, weight: .bold)) .foregroundColor(Color(hex: getBrandColor())) Text(event.monthAbbreviation) .font(.system(size: 12, weight: .semibold)) .foregroundColor(.secondary) .textCase(.uppercase) } .frame(width: 50) .padding(.vertical, 12) .background(Color(hex: getBrandColor()).opacity(0.1), in: RoundedRectangle(cornerRadius: 12)) VStack(alignment: .leading, spacing: 8) { Text(event.title) .font(.system(size: 18, weight: .semibold)) .lineLimit(2) if !event.description.isEmpty { Text(event.description.stripHtml()) .font(.system(size: 14)) .foregroundColor(.secondary) .lineLimit(2) } HStack(spacing: 8) { Image(systemName: "clock.fill") .font(.caption) .foregroundColor(Color(hex: getBrandColor())) Text(event.timeString) .font(.system(size: 13, weight: .medium)) .foregroundColor(.secondary) if !event.location.isEmpty { Image(systemName: "location.fill") .font(.caption) .foregroundColor(Color(hex: getBrandColor())) Text(event.location) .font(.system(size: 13, weight: .medium)) .foregroundColor(.secondary) .lineLimit(1) } } } Spacer() } .padding(20) .frame(height: 140) .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 16)) .shadow(color: .black.opacity(0.05), radius: 6, x: 0, y: 2) } } // HomeQuickActionCard removed - no longer needed // MARK: - Extensions // All date/time formatting now handled by Rust church-core crate (RTSDA Architecture Rules compliance) // ChurchEvent now includes dayOfMonth, monthAbbreviation, and timeString fields directly from Rust extension String { func stripHtml() -> String { return self.replacingOccurrences(of: "<[^>]+>", with: "", options: .regularExpression, range: nil) } }