stremio/apps/ios/VMC/UI/SearchScreen.swift
Jos Vooges | STH 252bce838b Add native VMC iOS client with VLC playback.
SwiftUI app against master API: link, profiles, browse, live TV, favorites sync, and TestFlight-ready assets.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-07 19:16:29 +02:00

101 lines
3.7 KiB
Swift

import SwiftUI
struct SearchScreen: View {
@EnvironmentObject private var app: AppModel
@State private var query = ""
@State private var results: [MediaItem] = []
@State private var error: String?
@State private var searching = false
private let cols = [GridItem(.adaptive(minimum: 104), spacing: 12)]
var body: some View {
VStack(spacing: 0) {
HStack(spacing: 10) {
Image(systemName: "magnifyingglass")
.foregroundStyle(McColors.textDim)
TextField("Films, series…", text: $query)
.textInputAutocapitalization(.never)
.autocorrectionDisabled()
.foregroundStyle(McColors.text)
.submitLabel(.search)
.onSubmit { Task { await runSearch() } }
if !query.isEmpty {
Button {
query = ""
results = []
} label: {
Image(systemName: "xmark.circle.fill")
.foregroundStyle(McColors.textDim)
}
}
}
.padding(12)
.background(McColors.surface, in: RoundedRectangle(cornerRadius: 14, style: .continuous))
.padding(.horizontal, 16)
.padding(.top, 8)
.padding(.bottom, 12)
if searching {
ProgressView().tint(.white).padding(.top, 40)
Spacer()
} else if let error {
Text(error).foregroundStyle(McColors.danger).padding()
Spacer()
} else if results.isEmpty && query.count >= 2 {
ContentUnavailableView("Geen resultaten", systemImage: "magnifyingglass")
} else if results.isEmpty {
ContentUnavailableView("Zoeken in VMC", systemImage: "sparkle.magnifyingglass", description: Text("Typ minstens 2 tekens."))
} else {
ScrollView {
LazyVGrid(columns: cols, spacing: 14) {
ForEach(results) { item in
NavigationLink(value: item) {
PosterCard(item: item, width: 104)
}
.buttonStyle(.plain)
}
}
.padding(.horizontal, 16)
.padding(.bottom, 24)
}
.navigationDestination(for: MediaItem.self) { MediaRouter(item: $0) }
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(McColors.bg.ignoresSafeArea())
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .principal) {
BrandHeader(subtitle: "ZOEKEN")
}
}
.toolbarBackground(McColors.bg, for: .navigationBar)
.toolbarColorScheme(.dark, for: .navigationBar)
.onChange(of: query) { _, newValue in
Task {
try? await Task.sleep(nanoseconds: 350_000_000)
guard newValue == query else { return }
await runSearch()
}
}
}
private func runSearch() async {
let q = query.trimmingCharacters(in: .whitespacesAndNewlines)
guard q.count >= 2 else {
results = []
error = nil
return
}
searching = true
error = nil
do {
results = try await app.api.search(query: q)
searching = false
} catch {
self.error = error.localizedDescription
searching = false
}
}
}