🚸 Add debounce mechanism to stop name input

This commit is contained in:
2023-04-22 12:31:26 +02:00
parent 61610fa2ba
commit e81f81b7a7

View File

@@ -43,7 +43,6 @@ interface SearchStore {
getSearchText: () => string; getSearchText: () => string;
setSearchText: (text: string, businessDataStore: BusinessDataStore) => Promise<void>; setSearchText: (text: string, businessDataStore: BusinessDataStore) => Promise<void>;
isSearchInProgress: () => boolean;
getFoundStops: () => Stop[]; getFoundStops: () => Stop[];
setFoundStops: (stops: Stop[]) => void; setFoundStops: (stops: Stop[]) => void;
@@ -65,11 +64,14 @@ interface SearchStore {
const SearchContext = createContext<SearchStore>(); const SearchContext = createContext<SearchStore>();
function SearchProvider(props: { children: JSX.Element }) { function SearchProvider(props: { children: JSX.Element }) {
const searchTextDelayMs = 1500;
type Store = { type Store = {
searchText: string; searchText: string;
searchInProgress: boolean; searchPromise: Promise<void> | undefined;
foundStops: Stop[]; foundStops: Stop[];
displayedPanelId: number; displayedPanelId: number;
panels: PositionedPanel[]; panels: PositionedPanel[];
@@ -79,7 +81,7 @@ function SearchProvider(props: { children: JSX.Element }) {
const [store, setStore] = createStore<Store>({ const [store, setStore] = createStore<Store>({
searchText: "", searchText: "",
searchInProgress: false, searchPromise: undefined,
foundStops: [], foundStops: [],
displayedPanelId: 0, displayedPanelId: 0,
panels: [], panels: [],
@@ -91,22 +93,30 @@ function SearchProvider(props: { children: JSX.Element }) {
return store.searchText; return store.searchText;
} }
const setSearchText = async (text: string, businessDataStore: BusinessDataStore): Promise<void> => { const debounce = async (fn: (...args: any[]) => Promise<void>, delayMs: number) => {
setStore('searchInProgress', true); let timerId: number;
return new Promise((...args) => {
clearTimeout(timerId);
timerId = setTimeout(fn, delayMs, ...args);
});
}
const setSearchText = async (text: string, businessDataStore: BusinessDataStore): Promise<void> => {
setStore('searchText', text); setStore('searchText', text);
if (store.searchPromise === undefined) {
const { searchStopByName } = businessDataStore; const { searchStopByName } = businessDataStore;
console.log("store.searchText=", store.searchText); const promise: Promise<void> = debounce(async (onSuccess: () => void) => {
console.log(`Fetching data for "${store.searchText}" stop name`);
const stopsById = await searchStopByName(store.searchText); const stopsById = await searchStopByName(store.searchText);
console.log("stopsById=", stopsById); console.log("stopsById=", stopsById);
setFoundStops(Object.values(stopsById)); setFoundStops(Object.values(stopsById));
onSuccess();
setStore('searchInProgress', false); }, searchTextDelayMs).then(() => {
setStore('searchPromise', undefined);
});
setStore('searchPromise', promise);
} }
const isSearchInProgress = (): boolean => {
return store.searchInProgress;
} }
const getFoundStops = (): Stop[] => { const getFoundStops = (): Stop[] => {
@@ -161,7 +171,7 @@ function SearchProvider(props: { children: JSX.Element }) {
return ( return (
<SearchContext.Provider value={{ <SearchContext.Provider value={{
getSearchText, setSearchText, isSearchInProgress, getSearchText, setSearchText,
getFoundStops, setFoundStops, getFoundStops, setFoundStops,
getDisplayedPanelId, setDisplayedPanelId, getDisplayedPanelId, setDisplayedPanelId,
getPanels, setPanels, getPanels, setPanels,
@@ -182,13 +192,12 @@ const Header: VoidComponent<{ title: string, minCharsNb: number }> = (props) =>
if (businessDataStore === undefined || searchStore === undefined) if (businessDataStore === undefined || searchStore === undefined)
return <div />; return <div />;
const { isSearchInProgress, setSearchText } = searchStore; const { setSearchText } = searchStore;
const onStopNameInput: JSX.EventHandler<HTMLInputElement, InputEvent> = async (event): Promise<void> => { const onStopNameInput: JSX.EventHandler<HTMLInputElement, InputEvent> = async (event): Promise<void> => {
/* TODO: Add a tempo before fetching stop for giving time to user to finish his request */ /* TODO: Add a tempo before fetching stop for giving time to user to finish his request */
const stopName = event.currentTarget.value; const stopName = event.currentTarget.value;
if (stopName.length >= props.minCharsNb) { if (stopName.length >= props.minCharsNb) {
console.log(`Fetching data for "${stopName}" stop name`);
await setSearchText(stopName, businessDataStore); await setSearchText(stopName, businessDataStore);
} }
} }
@@ -205,7 +214,7 @@ const Header: VoidComponent<{ title: string, minCharsNb: number }> = (props) =>
<div class="inputGroup"> <div class="inputGroup">
<InputGroup > <InputGroup >
<InputLeftAddon>🚉 🚏</InputLeftAddon> <InputLeftAddon>🚉 🚏</InputLeftAddon>
<Input onInput={onStopNameInput} readOnly={isSearchInProgress()} placeholder="Stop name..." /> <Input onInput={onStopNameInput} placeholder="Stop name..." />
</InputGroup> </InputGroup>
</div> </div>
</div > </div >