yattee/Model/Search/SearchModel.swift

176 lines
4.8 KiB
Swift
Raw Permalink Normal View History

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 {
static var shared = SearchModel()
@Published var store = Store<[ContentItem]>()
@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
@Published var focused = false
@Default(.showSearchSuggestions) private var showSearchSuggestions
#if os(iOS)
var textField: UITextField!
#elseif os(macOS)
var textField: NSTextField!
#endif
var accounts: AccountsModel { .shared }
2021-07-29 22:34:13 +00:00
private var resource: Resource!
init() {
#if os(iOS)
addKeyboardDidHideNotificationObserver()
#endif
}
deinit {
#if os(iOS)
removeKeyboardDidHideNotificationObserver()
#endif
}
2021-07-29 22:34:13 +00:00
var isLoading: Bool {
2021-09-25 12:17:58 +00:00
resource?.isLoading ?? false
2021-09-13 20:41:16 +00:00
}
2022-12-09 00:15:19 +00:00
func reloadQuery() {
changeQuery()
}
2021-07-29 22:34:13 +00:00
func changeQuery(_ changeHandler: @escaping (SearchQuery) -> Void = { _ in }) {
changeHandler(query)
page = nil
2021-07-29 22:34:13 +00:00
if !query.isEmpty {
2022-08-31 19:24:46 +00:00
resource = accounts.api.search(query, page: nil)
resource.addObserver(store)
loadResource()
}
2021-09-19 11:06:54 +00:00
}
func resetQuery(_ query: SearchQuery = SearchQuery()) {
2021-09-19 11:06:54 +00:00
self.query = query
let newResource = accounts.api.search(query, page: nil)
guard newResource != resource else {
2021-09-19 11:06:54 +00:00
return
}
page = nil
2021-09-19 11:06:54 +00:00
store.replace([])
if !query.isEmpty {
resource = newResource
resource.addObserver(store)
loadResource()
}
2021-07-29 22:34:13 +00:00
}
func loadResource() {
2021-07-29 22:34:13 +00:00
let currentResource = resource!
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
}
}
}
func replace(_ items: [ContentItem], for resource: Resource) {
2021-07-29 22:34:13 +00:00
if self.resource == resource {
store = Store<[ContentItem]>(items)
2021-07-29 22:34:13 +00:00
}
}
2021-09-25 12:17:58 +00:00
var suggestionsResource: Resource? { didSet {
oldValue?.cancelLoadIfUnobserved()
objectWillChange.send()
}}
2021-09-25 12:17:58 +00:00
func loadSuggestions(_ query: String) {
guard accounts.app.supportsSearchSuggestions, showSearchSuggestions else {
2022-12-09 00:15:19 +00:00
querySuggestions.removeAll()
return
}
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
}
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)
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)
}
}
}
#if os(iOS)
private func addKeyboardDidHideNotificationObserver() {
NotificationCenter.default.addObserver(self, selector: #selector(onKeyboardDidHide), name: UIResponder.keyboardDidHideNotification, object: nil)
}
@objc func onKeyboardDidHide() {
focused = false
}
private func removeKeyboardDidHideNotificationObserver() {
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardDidHideNotification, object: nil)
}
#endif
2021-07-29 22:34:13 +00:00
}