mirror of
https://github.com/cranci1/Sora.git
synced 2026-04-12 20:40:21 +00:00
search history?
This commit is contained in:
parent
6ff7fe06e2
commit
d74a32c918
2 changed files with 106 additions and 5 deletions
|
|
@ -102,7 +102,7 @@ extension JSController {
|
|||
// Enhanced session configuration
|
||||
let sessionConfig = URLSessionConfiguration.default
|
||||
sessionConfig.timeoutIntervalForRequest = 60.0
|
||||
sessionConfig.timeoutIntervalForResource = 1800.0 // 30 minutes for large files
|
||||
sessionConfig.timeoutIntervalForResource = 1800.0
|
||||
sessionConfig.httpMaximumConnectionsPerHost = 1
|
||||
sessionConfig.allowsCellularAccess = true
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ struct SearchView: View {
|
|||
@State private var hasNoResults = false
|
||||
@State private var isLandscape: Bool = UIDevice.current.orientation.isLandscape
|
||||
@State private var isModuleSelectorPresented = false
|
||||
@State private var searchHistory: [String] = []
|
||||
@State private var isSearchFieldFocused = false
|
||||
|
||||
private var selectedModule: ScrapingModule? {
|
||||
guard let id = selectedModuleId else { return nil }
|
||||
|
|
@ -64,7 +66,7 @@ struct SearchView: View {
|
|||
let columnsCount = determineColumns()
|
||||
VStack(spacing: 0) {
|
||||
HStack {
|
||||
SearchBar(text: $searchText, onSearchButtonClicked: performSearch)
|
||||
SearchBar(text: $searchText, onSearchButtonClicked: performSearch, isFocused: $isSearchFieldFocused)
|
||||
.padding(.leading)
|
||||
.padding(.trailing, searchText.isEmpty ? 16 : 0)
|
||||
.disabled(selectedModule == nil)
|
||||
|
|
@ -73,6 +75,7 @@ struct SearchView: View {
|
|||
if !searchText.isEmpty {
|
||||
Button("Cancel") {
|
||||
searchText = ""
|
||||
isSearchFieldFocused = false
|
||||
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
|
||||
}
|
||||
.padding(.trailing)
|
||||
|
|
@ -80,6 +83,60 @@ struct SearchView: View {
|
|||
}
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
HStack {
|
||||
Text("Recent Searches")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
Spacer()
|
||||
Button("Clear") {
|
||||
clearSearchHistory()
|
||||
}
|
||||
.font(.caption)
|
||||
.foregroundColor(.accentColor)
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.top, 8)
|
||||
|
||||
ForEach(Array(searchHistory.enumerated()), id: \.offset) { index, searchTerm in
|
||||
Button(action: {
|
||||
searchText = searchTerm
|
||||
isSearchFieldFocused = false
|
||||
performSearch()
|
||||
}) {
|
||||
HStack {
|
||||
Image(systemName: "clock")
|
||||
.foregroundColor(.secondary)
|
||||
.font(.caption)
|
||||
Text(searchTerm)
|
||||
.foregroundColor(.primary)
|
||||
Spacer()
|
||||
Button(action: {
|
||||
removeFromHistory(at: index)
|
||||
}) {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.foregroundColor(.secondary)
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
|
||||
if index < searchHistory.count - 1 {
|
||||
Divider()
|
||||
.padding(.leading, 40)
|
||||
}
|
||||
}
|
||||
}
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(8)
|
||||
.shadow(color: Color.black.opacity(0.1), radius: 2, y: 1)
|
||||
.padding(.horizontal)
|
||||
.padding(.top, 4)
|
||||
}
|
||||
|
||||
if selectedModule == nil {
|
||||
VStack(spacing: 8) {
|
||||
Image(systemName: "questionmark.app")
|
||||
|
|
@ -221,6 +278,9 @@ struct SearchView: View {
|
|||
}
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
.onAppear {
|
||||
loadSearchHistory()
|
||||
}
|
||||
.onChange(of: selectedModuleId) { _ in
|
||||
if !searchText.isEmpty {
|
||||
performSearch()
|
||||
|
|
@ -251,6 +311,9 @@ struct SearchView: View {
|
|||
return
|
||||
}
|
||||
|
||||
addToSearchHistory(searchText)
|
||||
isSearchFieldFocused = false
|
||||
|
||||
isSearching = true
|
||||
hasNoResults = false
|
||||
searchItems = []
|
||||
|
|
@ -282,6 +345,39 @@ struct SearchView: View {
|
|||
}
|
||||
}
|
||||
|
||||
private func loadSearchHistory() {
|
||||
searchHistory = UserDefaults.standard.stringArray(forKey: "searchHistory") ?? []
|
||||
}
|
||||
|
||||
private func saveSearchHistory() {
|
||||
UserDefaults.standard.set(searchHistory, forKey: "searchHistory")
|
||||
}
|
||||
|
||||
private func addToSearchHistory(_ term: String) {
|
||||
let trimmedTerm = term.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !trimmedTerm.isEmpty else { return }
|
||||
|
||||
searchHistory.removeAll { $0.lowercased() == trimmedTerm.lowercased() }
|
||||
searchHistory.insert(trimmedTerm, at: 0)
|
||||
|
||||
if searchHistory.count > 10 {
|
||||
searchHistory = Array(searchHistory.prefix(10))
|
||||
}
|
||||
|
||||
saveSearchHistory()
|
||||
}
|
||||
|
||||
private func removeFromHistory(at index: Int) {
|
||||
guard index < searchHistory.count else { return }
|
||||
searchHistory.remove(at: index)
|
||||
saveSearchHistory()
|
||||
}
|
||||
|
||||
private func clearSearchHistory() {
|
||||
searchHistory.removeAll()
|
||||
saveSearchHistory()
|
||||
}
|
||||
|
||||
private func updateOrientation() {
|
||||
DispatchQueue.main.async {
|
||||
isLandscape = UIDevice.current.orientation.isLandscape
|
||||
|
|
@ -335,19 +431,24 @@ struct SearchView: View {
|
|||
struct SearchBar: View {
|
||||
@State private var debounceTimer: Timer?
|
||||
@Binding var text: String
|
||||
@Binding var isFocused: Bool
|
||||
var onSearchButtonClicked: () -> Void
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
TextField("Search...", text: $text, onCommit: onSearchButtonClicked)
|
||||
TextField("Search...", text: $text, onEditingChanged: { isEditing in
|
||||
isFocused = isEditing
|
||||
}, onCommit: onSearchButtonClicked)
|
||||
.padding(7)
|
||||
.padding(.horizontal, 25)
|
||||
.background(Color(.systemGray6))
|
||||
.cornerRadius(8)
|
||||
.onChange(of: text){newValue in
|
||||
debounceTimer?.invalidate()
|
||||
debounceTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { _ in
|
||||
onSearchButtonClicked()
|
||||
if !newValue.isEmpty {
|
||||
debounceTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { _ in
|
||||
onSearchButtonClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
.overlay(
|
||||
|
|
|
|||
Loading…
Reference in a new issue