import Adwaita import LuminateCore struct SearchView: View { var client: JellyfinClient var userId: String @State private var searchText = "" @State private var results: [Components.Schemas.SearchHint] = [] @State private var isSearching = false var view: Body { VStack { SearchBar("Search", text: $searchText) .onSubmit { performSearch() } .onChange { debounceSearch() } if isSearching { Spinner() .padding(20) } else { ScrollView { VStack { ForEach(results) { hint in SearchResultRow(hint: hint, client: client) } } } } } .padding() } private func debounceSearch() { Task { try? await Task.sleep(nanoseconds: 300_000_000) if !searchText.isEmpty { performSearch() } } } private func performSearch() { guard !searchText.isEmpty else { results = [] return } isSearching = true Task { let result = try? await client.getSearchHints( searchTerm: searchText, userId: userId, limit: 50 ) await MainActor.run { results = result?.SearchHints ?? [] isSearching = false } } } } extension Components.Schemas.SearchHint: Identifiable { public var id: String { Id ?? ItemId ?? UUID().uuidString } } struct SearchResultRow: View { var hint: Components.Schemas.SearchHint var client: JellyfinClient @State private var imageData: Data? var view: Body { HStack { if let data = imageData { Picture() .data(data) .frame(minWidth: 80, maxWidth: 80, minHeight: 120, maxHeight: 120) .style("card") } else { Box(spacing: 0) {} .frame(minWidth: 80, maxWidth: 80, minHeight: 120, maxHeight: 120) .style("card") } VStack { Text(hint.Name ?? "") .style("body") .halign(.start) if let type = hint._Type?.value1 { Text("\(type)") .style("caption") .dim() .halign(.start) } if let year = hint.ProductionYear { Text("\(year)") .style("caption") .halign(.start) } Text(hint.runtimeString) .style("caption") .halign(.start) } .hexpand(true) } .padding(5, .vertical) .onAppear { loadImage() } } private func loadImage() { guard let tag = hint.PrimaryImageTag, let itemId = hint.Id ?? hint.ItemId else { return } Task { guard let url = await client.imageURL( itemId: itemId, imageType: .Primary, tag: tag, maxWidth: 160 ) else { return } let service = ImageService() imageData = try? await service.loadImage(url: url) } } }