Search: Fix keyboard lag with searchText

Make searchText a local variable passed to ScrapingModel to prevent
extreme keyboard lag and CPU usage when tracking this EnvironmentObject.

Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
kingbri 2023-04-09 21:37:14 -04:00
parent 4f303e1c1e
commit 75be076e0b
3 changed files with 13 additions and 7 deletions

View file

@ -22,7 +22,7 @@ class ScrapingViewModel: ObservableObject {
runningSearchTask = nil runningSearchTask = nil
} }
@Published var searchText: String = "" var cleanedSearchText: String = ""
@Published var searchResults: [SearchResult] = [] @Published var searchResults: [SearchResult] = []
// Only add results with valid magnet hashes to the search results array // Only add results with valid magnet hashes to the search results array
@ -66,7 +66,7 @@ class ScrapingViewModel: ObservableObject {
await logManager?.error(description, showToast: false) await logManager?.error(description, showToast: false)
} }
public func scanSources(sources: [Source], debridManager: DebridManager) async { public func scanSources(sources: [Source], searchText: String, debridManager: DebridManager) async {
await logManager?.info("Started scanning sources for query \"\(searchText)\"") await logManager?.info("Started scanning sources for query \"\(searchText)\"")
if sources.isEmpty { if sources.isEmpty {
@ -78,6 +78,8 @@ class ScrapingViewModel: ObservableObject {
return return
} }
cleanedSearchText = searchText.lowercased()
if await !debridManager.enabledDebrids.isEmpty { if await !debridManager.enabledDebrids.isEmpty {
await debridManager.clearIAValues() await debridManager.clearIAValues()
} }
@ -152,7 +154,7 @@ class ScrapingViewModel: ObservableObject {
// Default to HTML scraping // Default to HTML scraping
let preferredParser = SourcePreferredParser(rawValue: source.preferredParser) ?? .none let preferredParser = SourcePreferredParser(rawValue: source.preferredParser) ?? .none
guard let encodedQuery = searchText.lowercased().addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else { guard let encodedQuery = cleanedSearchText.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else {
await sendSourceError("\(source.name): Could not process search query, invalid characters present.") await sendSourceError("\(source.name): Could not process search query, invalid characters present.")
return nil return nil
@ -933,7 +935,7 @@ class ScrapingViewModel: ObservableObject {
func runRegex(parsedValue: String, regexString: String) -> String? { func runRegex(parsedValue: String, regexString: String) -> String? {
// TODO: Maybe dynamically parse flags // TODO: Maybe dynamically parse flags
let replacedRegexString = regexString let replacedRegexString = regexString
.replacingOccurrences(of: "{query}", with: searchText.lowercased()) .replacingOccurrences(of: "{query}", with: cleanedSearchText)
guard guard
let matchedRegex = try? Regex( let matchedRegex = try? Regex(

View file

@ -17,13 +17,15 @@ struct SearchResultsView: View {
@AppStorage("Behavior.UsesRandomSearchText") var usesRandomSearchText: Bool = false @AppStorage("Behavior.UsesRandomSearchText") var usesRandomSearchText: Bool = false
@Binding var searchText: String
var body: some View { var body: some View {
ForEach(scrapingModel.searchResults, id: \.self) { result in ForEach(scrapingModel.searchResults, id: \.self) { result in
if pluginManager.filteredInstalledSources.isEmpty || pluginManager.filteredInstalledSources.contains(where: { result.source == $0.name }) { if pluginManager.filteredInstalledSources.isEmpty || pluginManager.filteredInstalledSources.contains(where: { result.source == $0.name }) {
SearchResultButtonView(result: result) SearchResultButtonView(result: result)
} }
} }
.onChange(of: scrapingModel.searchText) { newText in .onChange(of: searchText) { newText in
if newText.isEmpty, isSearching { if newText.isEmpty, isSearching {
navModel.getSearchPrompt() navModel.getSearchPrompt()
} }

View file

@ -21,6 +21,7 @@ struct ContentView: View {
sortDescriptors: [] sortDescriptors: []
) var sources: FetchedResults<Source> ) var sources: FetchedResults<Source>
@State private var searchText = ""
@State private var isSearching = false @State private var isSearching = false
@State private var isEditingSearch = false @State private var isEditingSearch = false
@State private var dismissAction: () -> Void = {} @State private var dismissAction: () -> Void = {}
@ -28,7 +29,7 @@ struct ContentView: View {
var body: some View { var body: some View {
NavView { NavView {
List { List {
SearchResultsView() SearchResultsView(searchText: $searchText)
} }
.listStyle(.insetGrouped) .listStyle(.insetGrouped)
.inlinedList(inset: 20) .inlinedList(inset: 20)
@ -49,7 +50,7 @@ struct ContentView: View {
} }
} }
.expandedSearchable( .expandedSearchable(
text: $scrapingModel.searchText, text: $searchText,
isSearching: $isSearching, isSearching: $isSearching,
isEditingSearch: $isEditingSearch, isEditingSearch: $isEditingSearch,
prompt: navModel.searchPrompt, prompt: navModel.searchPrompt,
@ -89,6 +90,7 @@ struct ContentView: View {
(pluginManager.filteredInstalledSources.isEmpty ? (pluginManager.filteredInstalledSources.isEmpty ?
sources.compactMap { $0 } : sources.compactMap { $0 } :
pluginManager.filteredInstalledSources), pluginManager.filteredInstalledSources),
searchText: searchText,
debridManager: debridManager debridManager: debridManager
) )