import SwiftUI import HaloDesign import MediaKit /// Die Startseite: Aufmacher oben, darunter die Reihen vom Server. /// /// **Nur noch Darstellung.** Was auf der Startseite steht — laden, ordnen, /// den Aufmacher auslosen — liegt in `HomeFeed`, weil der Fernseher dieselben /// Reihen zeigt und die Logik sonst zweimal dastünde. Kopiert wäre dabei /// ausgerechnet der Teil gewesen, an dem schon mehrere Fehler steckten. struct HomeView: View { @Environment(HaloSession.self) private var session @Environment(HomeLayoutSettings.self) private var layout @State private var feed = HomeFeed() var body: some View { Group { switch feed.state { case .loading: ProgressView().frame(maxWidth: .infinity, maxHeight: .infinity) case .failed(let message): ContentUnavailableView { Label("error.title", systemImage: "exclamationmark.triangle") } description: { Text(message) } actions: { Button("action.retry") { Task { await feed.load(from: session.backend) } } } case .loaded: let hubs = feed.rows(for: layout) if hubs.isEmpty { ContentUnavailableView { Label("home.empty.title", systemImage: "film.stack") } description: { Text("home.empty.description") } } else { content(hubs: hubs) } } } .background(Color.haloCanvas) // Kein Titel: der Name der App gehört auf den Homescreen, nicht // über jeden Bildschirm. Darunter liegt der Aufmacher, und der sagt // ohnehin mehr. .navigationTitle("") .haloInlineTitle() // Gar kein Hintergrund statt eines weichgezeichneten: die Knöpfe // bringen ihre eigene Glasfläche mit und bleiben auch über dem Bild // lesbar. `.ultraThinMaterial` verschleierte dagegen den halben // Aufmacher. .haloClearToolbarBackground() // Gar keine Navigationsleiste auf der Startseite. // // Sie war leer — kein Titel, keine Knöpfe — und belegte trotzdem ihre // Höhe. Der Aufmacher fing dadurch tiefer an als die Seitenleiste // daneben, und genau diese Kante sah man. Auf den geschobenen // Bildschirmen bleibt sie, dort trägt sie den Zurück-Weg. .toolbar(.hidden, for: .navigationBar) .task(id: session.serverName) { await feed.load(from: session.backend) } .refreshable { await feed.load(from: session.backend) } // Was eine Kachel am Server ändert, ändert auch, was hier steht: // eine abgehakte Folge gehört nicht mehr in „Weiterschauen". .environment(\.haloItemChanged, { await feed.load(from: session.backend) }) } private func content(hubs: [MediaRowGroup]) -> some View { ScrollView { LazyVStack(alignment: .leading, spacing: HaloSpacing.section) { let featured = feed.heroItems(for: layout) if !featured.isEmpty { HeroCarousel(items: featured) } ForEach(hubs) { hub in MediaRow(row: hub) } } .padding(.bottom, HaloSpacing.section) } .scrollIndicators(.never) .haloRevealsTopChrome() } }