Commit f67145f3 authored by tom's avatar tom

save search term and category in the url

parent 621f86f5
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import _pickBy from 'lodash/pickBy';
import _unique from 'lodash/uniq'; import _unique from 'lodash/uniq';
import React, { useCallback, useEffect, useState } from 'react'; import { useRouter } from 'next/router';
import React from 'react';
import type { MarketplaceAppOverview } from 'types/client/marketplace'; import type { MarketplaceAppOverview } from 'types/client/marketplace';
import { MarketplaceCategory } from 'types/client/marketplace'; import { MarketplaceCategory } from 'types/client/marketplace';
...@@ -9,6 +11,7 @@ import appConfig from 'configs/app/config'; ...@@ -9,6 +11,7 @@ import appConfig from 'configs/app/config';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import useDebounce from 'lib/hooks/useDebounce'; import useDebounce from 'lib/hooks/useDebounce';
import useApiFetch from 'lib/hooks/useFetch'; import useApiFetch from 'lib/hooks/useFetch';
import getQueryParamString from 'lib/router/getQueryParamString';
const favoriteAppsLocalStorageKey = 'favoriteApps'; const favoriteAppsLocalStorageKey = 'favoriteApps';
...@@ -31,10 +34,14 @@ function isAppCategoryMatches(category: string, app: MarketplaceAppOverview, fav ...@@ -31,10 +34,14 @@ function isAppCategoryMatches(category: string, app: MarketplaceAppOverview, fav
} }
export default function useMarketplace() { export default function useMarketplace() {
const [ selectedAppId, setSelectedAppId ] = useState<string | null>(null); const router = useRouter();
const [ selectedCategoryId, setSelectedCategoryId ] = useState<string>(MarketplaceCategory.ALL); const defaultCategoryId = getQueryParamString(router.query.category);
const [ filterQuery, setFilterQuery ] = useState(''); const defaultFilterQuery = getQueryParamString(router.query.filter);
const [ favoriteApps, setFavoriteApps ] = useState<Array<string>>([]);
const [ selectedAppId, setSelectedAppId ] = React.useState<string | null>(null);
const [ selectedCategoryId, setSelectedCategoryId ] = React.useState<string>(MarketplaceCategory.ALL);
const [ filterQuery, setFilterQuery ] = React.useState(defaultFilterQuery);
const [ favoriteApps, setFavoriteApps ] = React.useState<Array<string>>([]);
const apiFetch = useApiFetch(); const apiFetch = useApiFetch();
const { isLoading, isError, error, data } = useQuery<unknown, ResourceError<unknown>, Array<MarketplaceAppOverview>>( const { isLoading, isError, error, data } = useQuery<unknown, ResourceError<unknown>, Array<MarketplaceAppOverview>>(
...@@ -42,9 +49,10 @@ export default function useMarketplace() { ...@@ -42,9 +49,10 @@ export default function useMarketplace() {
async() => apiFetch(appConfig.marketplaceConfigUrl || ''), async() => apiFetch(appConfig.marketplaceConfigUrl || ''),
{ {
select: (data) => (data as Array<MarketplaceAppOverview>).sort((a, b) => a.title.localeCompare(b.title)), select: (data) => (data as Array<MarketplaceAppOverview>).sort((a, b) => a.title.localeCompare(b.title)),
staleTime: Infinity,
}); });
const handleFavoriteClick = useCallback((id: string, isFavorite: boolean) => { const handleFavoriteClick = React.useCallback((id: string, isFavorite: boolean) => {
const favoriteApps = getFavoriteApps(); const favoriteApps = getFavoriteApps();
if (isFavorite) { if (isFavorite) {
...@@ -58,14 +66,14 @@ export default function useMarketplace() { ...@@ -58,14 +66,14 @@ export default function useMarketplace() {
} }
}, [ ]); }, [ ]);
const showAppInfo = useCallback((id: string) => { const showAppInfo = React.useCallback((id: string) => {
setSelectedAppId(id); setSelectedAppId(id);
}, []); }, []);
const debouncedFilterQuery = useDebounce(filterQuery, 500); const debouncedFilterQuery = useDebounce(filterQuery, 500);
const clearSelectedAppId = useCallback(() => setSelectedAppId(null), []); const clearSelectedAppId = React.useCallback(() => setSelectedAppId(null), []);
const handleCategoryChange = useCallback((newCategory: string) => { const handleCategoryChange = React.useCallback((newCategory: string) => {
setSelectedCategoryId(newCategory); setSelectedCategoryId(newCategory);
}, []); }, []);
...@@ -77,13 +85,38 @@ export default function useMarketplace() { ...@@ -77,13 +85,38 @@ export default function useMarketplace() {
return _unique(data?.map(app => app.categories).flat()) || []; return _unique(data?.map(app => app.categories).flat()) || [];
}, [ data ]); }, [ data ]);
useEffect(() => { React.useEffect(() => {
setFavoriteApps(getFavoriteApps()); setFavoriteApps(getFavoriteApps());
}, [ ]); }, [ ]);
React.useEffect(() => {
if (!isLoading && !isError) {
const isValidDefaultCategory = categories.includes(defaultCategoryId);
isValidDefaultCategory && setSelectedCategoryId(defaultCategoryId);
}
// run only when data is loaded
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ isLoading ]);
React.useEffect(() => {
const query = _pickBy({
category: selectedCategoryId === MarketplaceCategory.ALL ? undefined : selectedCategoryId,
filter: debouncedFilterQuery,
}, Boolean);
router.replace(
{ pathname: '/apps', query },
undefined,
{ shallow: true },
);
// omit router in the deps because router.push() somehow modifies it
// and we get infinite re-renders then
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ debouncedFilterQuery, selectedCategoryId ]);
return React.useMemo(() => ({ return React.useMemo(() => ({
selectedCategoryId, selectedCategoryId,
onCategoryChange: handleCategoryChange, onCategoryChange: handleCategoryChange,
filterQuery: debouncedFilterQuery,
onSearchInputChange: setFilterQuery, onSearchInputChange: setFilterQuery,
isLoading, isLoading,
isError, isError,
...@@ -108,5 +141,6 @@ export default function useMarketplace() { ...@@ -108,5 +141,6 @@ export default function useMarketplace() {
isError, isError,
isLoading, isLoading,
showAppInfo, showAppInfo,
debouncedFilterQuery,
]); ]);
} }
...@@ -19,6 +19,7 @@ const Marketplace = () => { ...@@ -19,6 +19,7 @@ const Marketplace = () => {
selectedCategoryId, selectedCategoryId,
categories, categories,
onCategoryChange, onCategoryChange,
filterQuery,
onSearchInputChange, onSearchInputChange,
showAppInfo, showAppInfo,
displayedApps, displayedApps,
...@@ -46,7 +47,12 @@ const Marketplace = () => { ...@@ -46,7 +47,12 @@ const Marketplace = () => {
onSelect={ onCategoryChange } onSelect={ onCategoryChange }
/> />
<FilterInput onChange={ onSearchInputChange } marginBottom={{ base: '4', lg: '6' }} placeholder="Find app"/> <FilterInput
initialValue={ filterQuery }
onChange={ onSearchInputChange }
marginBottom={{ base: '4', lg: '6' }}
placeholder="Find app"
/>
</Box> </Box>
{ isLoading ? <MarketplaceListSkeleton/> : ( { isLoading ? <MarketplaceListSkeleton/> : (
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment