diff --git a/src/app/components/Staking/FinalityProviders/FinalityProviderFilter.tsx b/src/app/components/Staking/FinalityProviders/FinalityProviderFilter.tsx index c3d8e2ff..1d0fedd1 100644 --- a/src/app/components/Staking/FinalityProviders/FinalityProviderFilter.tsx +++ b/src/app/components/Staking/FinalityProviders/FinalityProviderFilter.tsx @@ -8,15 +8,15 @@ const options = [ ]; export const FinalityProviderFilter = () => { - const { filterValue, handleFilter, searchValue } = useFinalityProviderState(); + const { filter, handleFilter } = useFinalityProviderState(); return ( ); diff --git a/src/app/components/Staking/FinalityProviders/FinalityProviderTable.tsx b/src/app/components/Staking/FinalityProviders/FinalityProviderTable.tsx index 4f39eda6..83da674e 100644 --- a/src/app/components/Staking/FinalityProviders/FinalityProviderTable.tsx +++ b/src/app/components/Staking/FinalityProviders/FinalityProviderTable.tsx @@ -20,10 +20,9 @@ export const FinalityProviderTable = ({ isFetching, finalityProviders, hasNextPage, - fetchNextPage, - searchValue, - filterValue, hasError, + filter, + fetchNextPage, isRowSelectable, } = useFinalityProviderState(); @@ -83,7 +82,7 @@ export const FinalityProviderTable = ({ return (
void; handleSort: (sortField: string) => void; - handleFilter: (value: string | number) => void; + handleFilter: (key: keyof FilterState, value: string) => void; isRowSelectable: (row: FinalityProvider) => boolean; getFinalityProvider: (btcPkHex: string) => FinalityProvider | null; fetchNextPage: () => void; } +const SORT_DIRECTIONS = { + undefined: "desc", + desc: "asc", + asc: undefined, +} as const; + +const STATUS_FILTERS = { + active: (fp: FinalityProvider) => + fp.state === FinalityProviderStateEnum.ACTIVE, + inactive: (fp: FinalityProvider) => + fp.state !== FinalityProviderStateEnum.ACTIVE, +}; + +const FILTERS = { + search: (fp: FinalityProvider, filter: FilterState) => { + const pattern = new RegExp(filter.search, "i"); + + return ( + pattern.test(fp.description?.moniker ?? "") || pattern.test(fp.btcPk) + ); + }, + status: (fp: FinalityProvider, filter: FilterState) => + filter.status && !filter.search ? STATUS_FILTERS[filter.status](fp) : true, +}; + const defaultState: FinalityProviderState = { - searchValue: "", - filterValue: "", + filter: { search: "", status: "active" }, finalityProviders: [], hasNextPage: false, isFetching: false, hasError: false, isRowSelectable: () => false, - handleSearch: () => {}, handleSort: () => {}, handleFilter: () => {}, getFinalityProvider: () => null, @@ -65,21 +73,16 @@ const defaultState: FinalityProviderState = { const { StateProvider, useState: useFpState } = createStateUtils(defaultState); -function FinalityProviderStateInner({ children }: PropsWithChildren) { +export function FinalityProviderState({ children }: PropsWithChildren) { const searchParams = useSearchParams(); const fpParam = searchParams.get("fp"); - const [searchValue, setSearchValue] = useState(fpParam || ""); - const [filterValue, setFilterValue] = useState( - fpParam ? "" : "active", - ); + const [filter, setFilter] = useState({ + search: fpParam || "", + status: "active", + }); const [sortState, setSortState] = useState({}); - const [previousFilterValue, setPreviousFilterValue] = useState< - string | number - >("active"); - const [isInitialLoad, setIsInitialLoad] = useState(true); - - const debouncedSearch = useDebounce(searchValue, 300); + const debouncedSearch = useDebounce(filter.search, 300); const { data, hasNextPage, fetchNextPage, isFetching, isError } = useFinalityProviders({ @@ -88,26 +91,9 @@ function FinalityProviderStateInner({ children }: PropsWithChildren) { name: debouncedSearch, }); - const handleSearch = useCallback( - (searchTerm: string) => { - if (!searchValue && searchTerm) { - setPreviousFilterValue(filterValue); - } - - setSearchValue(searchTerm); - - if (!(isInitialLoad && fpParam && searchTerm === fpParam)) { - if (searchTerm) { - setFilterValue(""); - } else { - setFilterValue(previousFilterValue); - } - } else { - setIsInitialLoad(false); - } - }, - [searchValue, filterValue, previousFilterValue, isInitialLoad, fpParam], - ); + const handleFilter = useCallback((key: keyof FilterState, value: string) => { + setFilter((state) => ({ ...state, [key]: value })); + }, []); const handleSort = useCallback((sortField: string) => { setSortState(({ field, direction }) => @@ -123,11 +109,6 @@ function FinalityProviderStateInner({ children }: PropsWithChildren) { ); }, []); - const handleFilter = useCallback((value: string | number) => { - setFilterValue(value); - setPreviousFilterValue(value); - }, []); - const isRowSelectable = useCallback((row: FinalityProvider) => { return ( row.state === FinalityProviderStateEnum.ACTIVE || @@ -138,27 +119,10 @@ function FinalityProviderStateInner({ children }: PropsWithChildren) { const filteredFinalityProviders = useMemo(() => { if (!data?.finalityProviders) return []; - return data.finalityProviders.filter((fp: FinalityProvider) => { - if (!fp) return false; - - if (searchValue) { - const searchLower = searchValue.toLowerCase(); - return ( - (fp.description?.moniker?.toLowerCase() || "").includes( - searchLower, - ) || (fp.btcPk?.toLowerCase() || "").includes(searchLower) - ); - } - - const isActive = fp.state === FinalityProviderStateEnum.ACTIVE; - const isInactive = inactiveStatuses.has(fp.state); - - if (filterValue === "active") return isActive; - if (filterValue === "inactive") return isInactive; - // default to active - return true; - }); - }, [data?.finalityProviders, filterValue, searchValue]); + return data.finalityProviders.filter((fp: FinalityProvider) => + Object.values(FILTERS).every((filterFn) => filterFn(fp, filter)), + ); + }, [data?.finalityProviders, filter]); const getFinalityProvider = useCallback( (btcPkHex: string) => @@ -168,13 +132,11 @@ function FinalityProviderStateInner({ children }: PropsWithChildren) { const state = useMemo( () => ({ - searchValue, - filterValue, + filter, finalityProviders: filteredFinalityProviders, isFetching, hasNextPage, hasError: isError, - handleSearch, handleSort, handleFilter, isRowSelectable, @@ -182,13 +144,11 @@ function FinalityProviderStateInner({ children }: PropsWithChildren) { fetchNextPage, }), [ - searchValue, - filterValue, + filter, filteredFinalityProviders, isFetching, hasNextPage, isError, - handleSearch, handleSort, handleFilter, isRowSelectable, @@ -200,14 +160,4 @@ function FinalityProviderStateInner({ children }: PropsWithChildren) { return {children}; } -export function FinalityProviderState({ children }: PropsWithChildren) { - return ( - {children}} - > - {children} - - ); -} - export { useFpState as useFinalityProviderState };