124 lines
3.9 KiB
Swift
124 lines
3.9 KiB
Swift
final class SearchOnMapPresenter {
|
|
typealias Response = SearchOnMap.Response
|
|
typealias ViewModel = SearchOnMap.ViewModel
|
|
|
|
weak var view: SearchOnMapView?
|
|
|
|
private var searchState: SearchOnMapState = .searching {
|
|
didSet {
|
|
guard searchState != oldValue else { return }
|
|
didChangeState?(searchState)
|
|
}
|
|
}
|
|
|
|
private var viewModel: ViewModel = .initial
|
|
private var isRouting: Bool
|
|
private var didChangeState: ((SearchOnMapState) -> Void)?
|
|
|
|
init(isRouting: Bool, didChangeState: ((SearchOnMapState) -> Void)?) {
|
|
self.isRouting = isRouting
|
|
self.didChangeState = didChangeState
|
|
didChangeState?(searchState)
|
|
}
|
|
|
|
func process(_ response: SearchOnMap.Response) {
|
|
guard response != .none else { return }
|
|
|
|
if response == .close {
|
|
view?.close()
|
|
searchState = .closed
|
|
return
|
|
}
|
|
|
|
let showSearch = response == .setSearchScreenHidden(false) || response == .showHistoryAndCategory
|
|
guard viewModel.presentationStep != .hidden || showSearch else {
|
|
return
|
|
}
|
|
|
|
let newViewModel = resolve(action: response, with: viewModel)
|
|
if viewModel != newViewModel {
|
|
viewModel = newViewModel
|
|
view?.render(newViewModel)
|
|
searchState = newViewModel.presentationStep.searchState
|
|
}
|
|
}
|
|
|
|
private func resolve(action: Response, with previousViewModel: ViewModel) -> ViewModel {
|
|
var viewModel = previousViewModel
|
|
viewModel.searchingText = nil // should not be nil only when the text is passed to the search field
|
|
|
|
switch action {
|
|
case .startSearching:
|
|
viewModel.isTyping = true
|
|
viewModel.skipSuggestions = false
|
|
viewModel.contentState = .searching
|
|
case .showOnTheMap:
|
|
viewModel.isTyping = false
|
|
viewModel.skipSuggestions = true
|
|
viewModel.presentationStep = isRouting ? .hidden : .halfScreen
|
|
if case .results(var results) = viewModel.contentState, !results.isEmpty {
|
|
results.skipSuggestions()
|
|
viewModel.contentState = .results(results)
|
|
}
|
|
case .setIsTyping(let isSearching):
|
|
viewModel.isTyping = isSearching
|
|
if isSearching {
|
|
viewModel.presentationStep = .fullScreen
|
|
}
|
|
case .showHistoryAndCategory:
|
|
viewModel.isTyping = true
|
|
viewModel.contentState = .historyAndCategory
|
|
viewModel.presentationStep = .fullScreen
|
|
case .showResults(var searchResults, let isSearchCompleted):
|
|
if (viewModel.skipSuggestions) {
|
|
searchResults.skipSuggestions()
|
|
}
|
|
viewModel.contentState = searchResults.isEmpty && isSearchCompleted ? .noResults : .results(searchResults)
|
|
case .selectQuery(let query):
|
|
viewModel.skipSuggestions = false
|
|
viewModel.searchingText = query.text
|
|
viewModel.contentState = .searching
|
|
|
|
switch query.source {
|
|
case .typedText, .suggestion:
|
|
viewModel.isTyping = true
|
|
case .category, .history, .deeplink:
|
|
viewModel.isTyping = false
|
|
viewModel.presentationStep = isRouting ? .hidden : .halfScreen
|
|
@unknown default:
|
|
fatalError("Unknown search text source")
|
|
}
|
|
case .clearSearch:
|
|
viewModel.searchingText = ""
|
|
viewModel.isTyping = true
|
|
viewModel.skipSuggestions = false
|
|
viewModel.contentState = .historyAndCategory
|
|
viewModel.presentationStep = .fullScreen
|
|
case .setSearchScreenHidden(let isHidden):
|
|
viewModel.isTyping = false
|
|
viewModel.presentationStep = isHidden ? .hidden : (isRouting ? .fullScreen : .halfScreen)
|
|
case .setSearchScreenCompact:
|
|
viewModel.isTyping = false
|
|
viewModel.presentationStep = .compact
|
|
case .updatePresentationStep(let step):
|
|
if step == .hidden {
|
|
viewModel.isTyping = false
|
|
}
|
|
viewModel.presentationStep = step
|
|
case .close, .none:
|
|
break
|
|
}
|
|
return viewModel
|
|
}
|
|
}
|
|
|
|
private extension ModalPresentationStep {
|
|
var searchState: SearchOnMapState {
|
|
switch self {
|
|
case .fullScreen, .halfScreen, .compact:
|
|
return .searching
|
|
case .hidden:
|
|
return .hidden
|
|
}
|
|
}
|
|
}
|