import { debounce } from "lodash-es"; import { action, makeObservable, observable, override } from "mobx"; import AbstractConstructor from "../../Core/AbstractConstructor"; import Model from "../../Models/Definition/Model"; import SearchProviderResult from "../../Models/SearchProviders/SearchProviderResults"; import SearchProviderTraits from "../../Traits/SearchProviders/SearchProviderTraits"; type SearchProviderModel = Model; function SearchProviderMixin< T extends AbstractConstructor >(Base: T) { abstract class SearchProviderMixin extends Base { abstract get type(): string; protected debounceTime = 1000; private _debouncedSearch: ReturnType; constructor(...args: any[]) { super(...args); makeObservable(this); this.searchResult = new SearchProviderResult(this); this._debouncedSearch = debounce((searchText: string) => { this.performSearch(searchText); }, this.debounceTime); } @override get minCharacters() { return super.minCharacters ?? this.terria.searchBarModel.minCharacters; } @observable public searchResult: SearchProviderResult; protected abstract logEvent(searchText: string): void; protected abstract doSearch( searchText: string, results: SearchProviderResult ): Promise; @action cancelSearch() { this._debouncedSearch.cancel(); this.searchResult.isCanceled = true; this.searchResult = new SearchProviderResult(this); } @action async search( searchText: string, manuallyTriggered?: boolean ): Promise { this.searchResult.isWaitingToStartSearch = true; if (!this.shouldRunSearch(searchText)) { this._debouncedSearch.cancel(); this.searchResult.isSearching = false; this.searchResult.message = { content: "translate#viewModels.searchMinCharacters", params: { count: this.minCharacters } }; this.searchResult.isWaitingToStartSearch = false; return; } if (manuallyTriggered) { this._debouncedSearch.cancel(); await this.performSearch(searchText); } else { await this._debouncedSearch(searchText); } } @action private async performSearch(searchText: string): Promise { this.logEvent(searchText); this.searchResult.isWaitingToStartSearch = false; this.searchResult.isSearching = true; await this.doSearch(searchText, this.searchResult); this.searchResult.isSearching = false; } private shouldRunSearch(searchText: string) { if ( searchText === undefined || /^\s*$/.test(searchText) || (this.minCharacters && searchText.length < this.minCharacters) ) { return false; } return true; } get hasSearchProviderMixin() { return true; } } return SearchProviderMixin; } namespace SearchProviderMixin { export interface Instance extends InstanceType< ReturnType > {} export function isMixedInto(model: any): model is Instance { return model && model.hasSearchProviderMixin; } } export default SearchProviderMixin;