import SwiftUI import FlixyDesign import MediaKit /// Sucht über alle Mediatheken des Servers. /// /// **Nur noch Darstellung.** Entprellen, fragen und merken liegen in /// `SearchFeed`, weil der Fernseher dieselben Fragen an denselben Server /// stellt. Siehe dort, warum das kein Beiwerk ist. struct SearchView: View { @Environment(FlixySession.self) private var session @State private var text = "" @State private var feed = SearchFeed() var body: some View { Group { if text.isEmpty { recentSearches } else if feed.hubs.isEmpty && !feed.isSearching { ContentUnavailableView.search(text: text) } else { results } } .background(Color.flixyCanvas) .navigationTitle(Text("search.title")) .flixyInlineTitle() .searchable(text: $text) // Jeder Tastendruck löst eine Anfrage aus — aber erst nach einer // kurzen Pause. Ohne sie schickt „Blade Runner" zwölf Anfragen los, // von denen elf verworfen werden. .task(id: text) { await feed.search(text, from: session.backend) } // **Hier fehlte er.** Startseite und Mediathek hinterlegen das längst; // in der Suche nicht, und deshalb wirkte „als gesehen markieren" aus // dem Kontextmenü zwar am Server, aber die Kachel lud nie neu — es sah // aus, als wäre nichts passiert. .environment(\.flixyItemChanged, { await feed.search(text, from: session.backend) }) } private var results: some View { ScrollView { LazyVStack(alignment: .leading, spacing: FlixySpacing.section) { ForEach(feed.hubs) { hub in // **Antippen führt auf die Seite, nicht in den Film.** Die // Suche benutzt dieselbe Reihe wie die Startseite — und // damit war sie bisher auch dieselbe Geste. Wer sucht, // weiß aber noch nicht, was er sehen will; der erste // Treffer ist eine Vermutung des Servers. Starten geht // weiter, über langes Drücken. MediaRow(row: hub, opensDetails: true) } } .padding(.vertical, FlixySpacing.loose) } .flixyRevealsTopChrome() } @ViewBuilder private var recentSearches: some View { if feed.recent.isEmpty { ContentUnavailableView { Label("search.title", systemImage: "magnifyingglass") } description: { Text("search.hint") } } else { List { Section { ForEach(feed.recent, id: \.self) { entry in Button { text = entry } label: { Label(entry, systemImage: "clock.arrow.circlepath") .foregroundStyle(Color.flixyTextSecondary) } } } header: { Text("search.recent") } footer: { Button("search.forgetRecent", role: .destructive) { feed.forgetRecent() } .font(.flixyCaption) } .listRowBackground(Color.flixySurface) } .scrollContentBackground(.hidden) .flixyRevealsTopChrome() } } }