import ManaCore import SwiftUI /// Browse-View: Suche, Sortier-Picker, Sprachfilter, Liste der Resultate. struct BrowseView: View { @Environment(AuthClient.self) private var auth @State private var store: MarketplaceStore? @State private var queryText: String = "" var body: some View { ZStack { CardsTheme.background.ignoresSafeArea() VStack(spacing: 0) { filters Divider().background(CardsTheme.border) resultsList } } .navigationTitle("Durchsuchen") #if os(iOS) .navigationBarTitleDisplayMode(.inline) #endif .searchable( text: $queryText, placement: .navigationBarDrawer(displayMode: .always), prompt: "Decks suchen" ) .onSubmit(of: .search) { store?.browseQuery = queryText Task { await store?.browse() } } .task { if store == nil { store = MarketplaceStore(auth: auth) await store?.browse() } } } @ViewBuilder private var filters: some View { if let store { HStack { Picker("Sortierung", selection: Binding( get: { store.browseSort }, set: { newValue in store.browseSort = newValue Task { await store.browse() } } )) { ForEach(MarketplaceSort.allCases, id: \.self) { sort in Text(sort.label).tag(sort) } } .pickerStyle(.segmented) } .padding(.horizontal, 16) .padding(.vertical, 8) } } @ViewBuilder private var resultsList: some View { if let store { if store.isLoadingBrowse, store.browseResults.isEmpty { Spacer() ProgressView() .tint(CardsTheme.primary) Spacer() } else if store.browseResults.isEmpty { ContentUnavailableView( "Keine Decks gefunden", systemImage: "magnifyingglass", description: Text("Versuche eine andere Suche oder Sortierung.") ) .foregroundStyle(CardsTheme.foreground) } else { List { ForEach(store.browseResults) { entry in NavigationLink(value: MarketplaceRoute.publicDeck(slug: entry.slug)) { BrowseRow(entry: entry) } .listRowBackground(Color.clear) .listRowSeparator(.hidden) .listRowInsets(EdgeInsets(top: 4, leading: 16, bottom: 4, trailing: 16)) } } .listStyle(.plain) .scrollContentBackground(.hidden) .refreshable { await store.browse() } } } } } struct BrowseRow: View { let entry: PublicDeckEntry var body: some View { HStack(spacing: 12) { VStack(alignment: .leading, spacing: 4) { HStack { Text(entry.title) .font(.headline) .foregroundStyle(CardsTheme.foreground) if entry.isFeatured { Image(systemName: "star.fill") .font(.caption) .foregroundStyle(CardsTheme.warning) } } if let description = entry.description, !description.isEmpty { Text(description) .font(.caption) .foregroundStyle(CardsTheme.mutedForeground) .lineLimit(2) } HStack(spacing: 12) { Label("\(entry.cardCount)", systemImage: "rectangle.stack") Label("\(entry.starCount)", systemImage: "star") if entry.isPaid { Label("\(entry.priceCredits)", systemImage: "creditcard") .foregroundStyle(CardsTheme.primary) } if let language = entry.language { Text(language.uppercased()) .font(.caption2.weight(.semibold)) .padding(.horizontal, 4) .padding(.vertical, 1) .background(CardsTheme.muted, in: Capsule()) } } .font(.caption2) .foregroundStyle(CardsTheme.mutedForeground) } Spacer() Image(systemName: "chevron.right") .font(.footnote) .foregroundStyle(CardsTheme.mutedForeground) } .padding(.vertical, 8) } }