🚸 Add debounce mechanism to stop name input
This commit is contained in:
@@ -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 >
|
||||||
|
Reference in New Issue
Block a user