diff --git a/Ruddarr/Utilities/Helpers.swift b/Ruddarr/Utilities/Helpers.swift index 754f6ad2..35434878 100644 --- a/Ruddarr/Utilities/Helpers.swift +++ b/Ruddarr/Utilities/Helpers.swift @@ -1,6 +1,25 @@ import Foundation import CloudKit +struct SearchRequest: Equatable { + private let id = UUID() + + let query: String + let isDebounced: Bool + + init(query: String, isDebounced: Bool) { + self.query = query + self.isDebounced = isDebounced + } + + func waitForDebounce() async -> Bool { + guard isDebounced else { return true } + + try? await Task.sleep(for: .milliseconds(250)) + return !Task.isCancelled + } +} + protocol OptionalProtocol { associatedtype Wrapped var wrappedValue: Wrapped? { get set } diff --git a/Ruddarr/Views/Movies/Search/MovieSearchView.swift b/Ruddarr/Views/Movies/Search/MovieSearchView.swift index 88b952b3..2e5e2d49 100644 --- a/Ruddarr/Views/Movies/Search/MovieSearchView.swift +++ b/Ruddarr/Views/Movies/Search/MovieSearchView.swift @@ -1,14 +1,12 @@ import SwiftUI -import Combine struct MovieSearchView: View { @State var searchQuery: String @State private var searchPresented: Bool = true + @State private var searchRequest: SearchRequest? @Environment(RadarrInstance.self) private var instance - let searchTextPublisher = PassthroughSubject() - var body: some View { @Bindable var discovery = Discovery.shared @Bindable var movieLookup = instance.lookup @@ -56,13 +54,13 @@ struct MovieSearchView: View { await discovery.fetch(.movies) } .onSubmit(of: .search) { - searchTextPublisher.send(searchQuery) + performSearch() } .onChange(of: searchQuery, initial: true, handleSearchQueryChange) - .onReceive( - searchTextPublisher.debounce(for: .milliseconds(250), scheduler: DispatchQueue.main) - ) { _ in - performSearch() + .task(id: searchRequest) { + guard let searchRequest, await searchRequest.waitForDebounce() else { return } + + await instance.lookup.search(query: searchRequest.query) } .alert( isPresented: instance.lookup.errorBinding, @@ -81,10 +79,8 @@ struct MovieSearchView: View { } } - func performSearch() { - Task { - await instance.lookup.search(query: searchQuery) - } + func performSearch(debounced: Bool = false) { + searchRequest = SearchRequest(query: searchQuery, isDebounced: debounced) } func handleSearchQueryChange(oldQuery: String, newQuery: String) { @@ -99,7 +95,7 @@ struct MovieSearchView: View { } else if oldQuery == newQuery { performSearch() // always perform initial search } else { - searchTextPublisher.send(searchQuery) + performSearch(debounced: true) } } } diff --git a/Ruddarr/Views/Series/Search/SeriesSearchView.swift b/Ruddarr/Views/Series/Search/SeriesSearchView.swift index f32aa1d9..7d36c39f 100644 --- a/Ruddarr/Views/Series/Search/SeriesSearchView.swift +++ b/Ruddarr/Views/Series/Search/SeriesSearchView.swift @@ -1,14 +1,12 @@ import SwiftUI -import Combine struct SeriesSearchView: View { @State var searchQuery: String @State private var searchPresented: Bool = true + @State private var searchRequest: SearchRequest? @Environment(SonarrInstance.self) private var instance - let searchTextPublisher = PassthroughSubject() - var body: some View { @Bindable var discovery = Discovery.shared @Bindable var seriesLookup = instance.lookup @@ -54,13 +52,13 @@ struct SeriesSearchView: View { await discovery.fetch(.series) } .onSubmit(of: .search) { - searchTextPublisher.send(searchQuery) + performSearch() } .onChange(of: searchQuery, initial: true, handleSearchQueryChange) - .onReceive( - searchTextPublisher.debounce(for: .milliseconds(250), scheduler: DispatchQueue.main) - ) { _ in - performSearch() + .task(id: searchRequest) { + guard let searchRequest, await searchRequest.waitForDebounce() else { return } + + await instance.lookup.search(query: searchRequest.query) } .alert( isPresented: instance.lookup.errorBinding, @@ -79,10 +77,8 @@ struct SeriesSearchView: View { } } - func performSearch() { - Task { @MainActor in - await instance.lookup.search(query: searchQuery) - } + func performSearch(debounced: Bool = false) { + searchRequest = SearchRequest(query: searchQuery, isDebounced: debounced) } func handleSearchQueryChange(oldQuery: String, newQuery: String) { @@ -97,7 +93,7 @@ struct SeriesSearchView: View { } else if oldQuery == newQuery { performSearch() // always perform initial search } else { - searchTextPublisher.send(searchQuery) + performSearch(debounced: true) } } }