2021-07-29 22:34:13 +00:00
|
|
|
import Defaults
|
2022-08-17 15:34:25 +00:00
|
|
|
import Repeat
|
2021-07-29 22:34:13 +00:00
|
|
|
import Siesta
|
|
|
|
import SwiftUI
|
|
|
|
|
2021-09-25 08:18:22 +00:00
|
|
|
final class SearchModel: ObservableObject {
|
2021-10-21 23:29:10 +00:00
|
|
|
@Published var store = Store<[ContentItem]>()
|
2022-01-04 23:18:01 +00:00
|
|
|
@Published var page: SearchPage?
|
2021-09-13 20:41:16 +00:00
|
|
|
|
2021-09-25 08:18:22 +00:00
|
|
|
@Published var query = SearchQuery()
|
2021-09-19 12:42:47 +00:00
|
|
|
@Published var queryText = ""
|
2021-12-06 18:12:33 +00:00
|
|
|
@Published var suggestionsText = ""
|
2021-09-13 20:41:16 +00:00
|
|
|
|
2022-08-17 15:34:25 +00:00
|
|
|
@Published var querySuggestions = [String]()
|
|
|
|
private var suggestionsDebouncer = Debouncer(.milliseconds(200))
|
2021-11-28 14:37:55 +00:00
|
|
|
|
2022-08-04 22:30:09 +00:00
|
|
|
var accounts = AccountsModel()
|
2021-07-29 22:34:13 +00:00
|
|
|
private var resource: Resource!
|
|
|
|
|
|
|
|
var isLoading: Bool {
|
2021-09-25 12:17:58 +00:00
|
|
|
resource?.isLoading ?? false
|
2021-09-13 20:41:16 +00:00
|
|
|
}
|
|
|
|
|
2021-07-29 22:34:13 +00:00
|
|
|
func changeQuery(_ changeHandler: @escaping (SearchQuery) -> Void = { _ in }) {
|
|
|
|
changeHandler(query)
|
|
|
|
|
2022-01-04 23:18:01 +00:00
|
|
|
page = nil
|
2021-07-29 22:34:13 +00:00
|
|
|
|
2021-09-26 17:40:25 +00:00
|
|
|
if !query.isEmpty {
|
2022-08-31 19:24:46 +00:00
|
|
|
resource = accounts.api.search(query, page: nil)
|
|
|
|
resource.addObserver(store)
|
|
|
|
|
2022-01-04 23:18:01 +00:00
|
|
|
loadResource()
|
2021-09-26 17:40:25 +00:00
|
|
|
}
|
2021-09-19 11:06:54 +00:00
|
|
|
}
|
|
|
|
|
2021-09-26 17:40:25 +00:00
|
|
|
func resetQuery(_ query: SearchQuery = SearchQuery()) {
|
2021-09-19 11:06:54 +00:00
|
|
|
self.query = query
|
|
|
|
|
2022-01-04 23:18:01 +00:00
|
|
|
let newResource = accounts.api.search(query, page: nil)
|
|
|
|
guard newResource != resource else {
|
2021-09-19 11:06:54 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-01-04 23:18:01 +00:00
|
|
|
page = nil
|
2021-09-19 11:06:54 +00:00
|
|
|
store.replace([])
|
|
|
|
|
2021-09-26 17:40:25 +00:00
|
|
|
if !query.isEmpty {
|
2022-08-04 22:30:09 +00:00
|
|
|
resource = newResource
|
|
|
|
resource.addObserver(store)
|
2022-01-04 23:18:01 +00:00
|
|
|
loadResource()
|
2021-09-26 17:40:25 +00:00
|
|
|
}
|
2021-07-29 22:34:13 +00:00
|
|
|
}
|
|
|
|
|
2022-01-04 23:18:01 +00:00
|
|
|
func loadResource() {
|
2021-07-29 22:34:13 +00:00
|
|
|
let currentResource = resource!
|
|
|
|
|
2022-01-04 23:18:01 +00:00
|
|
|
resource.load().onSuccess { response in
|
|
|
|
if let page: SearchPage = response.typedContent() {
|
|
|
|
self.page = page
|
|
|
|
self.replace(page.results, for: currentResource)
|
2021-07-29 22:34:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-04 23:18:01 +00:00
|
|
|
func replace(_ items: [ContentItem], for resource: Resource) {
|
2021-07-29 22:34:13 +00:00
|
|
|
if self.resource == resource {
|
2022-01-04 23:18:01 +00:00
|
|
|
store = Store<[ContentItem]>(items)
|
2021-07-29 22:34:13 +00:00
|
|
|
}
|
|
|
|
}
|
2021-09-25 12:17:58 +00:00
|
|
|
|
2022-08-04 22:30:09 +00:00
|
|
|
var suggestionsResource: Resource? { didSet {
|
|
|
|
oldValue?.cancelLoadIfUnobserved()
|
|
|
|
|
|
|
|
objectWillChange.send()
|
|
|
|
}}
|
2021-09-25 12:17:58 +00:00
|
|
|
|
|
|
|
func loadSuggestions(_ query: String) {
|
2022-08-17 15:34:25 +00:00
|
|
|
suggestionsDebouncer.callback = {
|
|
|
|
guard !query.isEmpty else { return }
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
self.accounts.api.searchSuggestions(query: query).load().onSuccess { response in
|
2021-09-25 12:17:58 +00:00
|
|
|
if let suggestions: [String] = response.typedContent() {
|
2022-08-17 15:34:25 +00:00
|
|
|
self.querySuggestions = suggestions
|
|
|
|
} else {
|
|
|
|
self.querySuggestions = []
|
2021-09-25 12:17:58 +00:00
|
|
|
}
|
2021-12-06 18:12:33 +00:00
|
|
|
self.suggestionsText = query
|
2021-09-25 12:17:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-08-17 15:34:25 +00:00
|
|
|
|
|
|
|
suggestionsDebouncer.call()
|
2021-09-25 12:17:58 +00:00
|
|
|
}
|
2022-01-04 23:18:01 +00:00
|
|
|
|
|
|
|
func loadNextPage() {
|
|
|
|
guard var pageToLoad = page, !pageToLoad.last else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if pageToLoad.nextPage.isNil, accounts.app.searchUsesIndexedPages {
|
|
|
|
pageToLoad.nextPage = "2"
|
|
|
|
}
|
|
|
|
|
|
|
|
resource?.removeObservers(ownedBy: store)
|
|
|
|
|
2022-03-28 19:26:38 +00:00
|
|
|
resource = accounts.api.search(query, page: pageToLoad.nextPage)
|
2022-01-04 23:18:01 +00:00
|
|
|
resource.addObserver(store)
|
|
|
|
|
|
|
|
resource
|
|
|
|
.load()
|
|
|
|
.onSuccess { response in
|
|
|
|
if let page: SearchPage = response.typedContent() {
|
|
|
|
var nextPage: Int?
|
|
|
|
if self.accounts.app.searchUsesIndexedPages {
|
|
|
|
nextPage = Int(pageToLoad.nextPage ?? "0")
|
|
|
|
}
|
|
|
|
|
|
|
|
self.page = page
|
|
|
|
|
|
|
|
if self.accounts.app.searchUsesIndexedPages {
|
|
|
|
self.page?.nextPage = String((nextPage ?? 1) + 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
self.replace(self.store.collection + page.results, for: self.resource)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-07-29 22:34:13 +00:00
|
|
|
}
|