import SwiftUI import OrgSocialKit struct SearchView: View { @State private var viewModel = SearchViewModel() @AppStorage("useRelay") private var useRelay = true var body: some View { NavigationStack { VStack(spacing: 0) { searchBar Divider() content } .navigationTitle("Search") .navigationBarTitleDisplayMode(.large) .navigationDestination(for: URL.self) { ProfileView(feedURL: $0) } .navigationDestination(for: ThreadRoute.self) { ThreadView(postURL: $0.postURL, relayURL: $0.relayURL) } } } private var searchBar: some View { VStack(spacing: 8) { HStack(spacing: 8) { HStack(spacing: 6) { Image(systemName: "magnifyingglass").foregroundStyle(.secondary) TextField(viewModel.isTagSearch ? "Tag name…" : "Search posts…", text: $viewModel.query) .autocorrectionDisabled() .textInputAutocapitalization(.never) .submitLabel(.search) .onSubmit { if useRelay { Task { await viewModel.search() } } } if !viewModel.query.isEmpty { Button { viewModel.query = "" } label: { Image(systemName: "xmark.circle.fill").foregroundStyle(.secondary) } .buttonStyle(.plain) } } .padding(8) .background(Color.secondary.opacity(0.1), in: RoundedRectangle(cornerRadius: 10)) Button(viewModel.isTagSearch ? "#Tag" : "Text") { viewModel.isTagSearch.toggle() } .font(.caption.weight(.semibold)) .padding(.horizontal, 10).padding(.vertical, 8) .background(Color.accentColor.opacity(0.1), in: RoundedRectangle(cornerRadius: 10)) .foregroundStyle(Color.accentColor) } Button { Task { await viewModel.search() } } label: { Label("Search", systemImage: "magnifyingglass") .frame(maxWidth: .infinity) } .buttonStyle(.borderedProminent) .disabled(viewModel.query.trimmingCharacters(in: .whitespaces).isEmpty || viewModel.isLoading || !useRelay) if !useRelay { Text("Enable Use Relay in Settings to search.") .font(.caption) .foregroundStyle(.secondary) } } .padding() } @ViewBuilder private var content: some View { if viewModel.isLoading { ProgressView().frame(maxWidth: .infinity, maxHeight: .infinity) } else if let error = viewModel.errorMessage { ContentUnavailableView { Label("Search failed", systemImage: "exclamationmark.triangle") } description: { Text(error) } } else if viewModel.results.isEmpty && !viewModel.query.isEmpty { ContentUnavailableView( "No results", systemImage: "magnifyingglass", description: Text("No posts found for \"\(viewModel.query)\".") ) } else if viewModel.results.isEmpty { ContentUnavailableView( "Search posts", systemImage: "magnifyingglass", description: Text("Enter a keyword or tag to search posts on the relay.") ) } else { List { ForEach(viewModel.results, id: \.timestamp) { post in PostRowView(post: post) .listRowSeparator(.visible) .listRowSeparatorTint(Color.secondary.opacity(0.2)) .listRowInsets(EdgeInsets(top: 12, leading: 16, bottom: 12, trailing: 16)) .listRowBackground(Color.clear) } } .listStyle(.plain) } } }