diff --git a/src/app/routes/ProjectDetailsPage.tsx b/src/app/routes/ProjectDetailsPage.tsx index eeaf67a..da2e94a 100644 --- a/src/app/routes/ProjectDetailsPage.tsx +++ b/src/app/routes/ProjectDetailsPage.tsx @@ -25,8 +25,8 @@ export function ProjectDetailsPage() { }>(); const [, navigate] = useLocation(); const [searchParams] = useSearchParams(); - const group = searchParams.get('group'); - const projectsHref = `/years/${yearId}/projects${group ? `?group=${encodeURIComponent(group)}` : ''}`; + const projectsSearch = searchParams.toString(); + const projectsHref = `/years/${yearId}/projects${projectsSearch ? `?${projectsSearch}` : ''}`; const project = useProject(projectId); const ballotYearId = project.data?.project.yearId ?? yearId; const ballot = useBallotStatus(ballotYearId, project.data?.project.kind === 'project'); diff --git a/src/app/routes/ProjectsPage.tsx b/src/app/routes/ProjectsPage.tsx index 2c3f81e..436ff43 100644 --- a/src/app/routes/ProjectsPage.tsx +++ b/src/app/routes/ProjectsPage.tsx @@ -36,15 +36,21 @@ function saveProjectsView(view: ProjectsView) { export function ProjectsPage({isAdmin = false}: {isAdmin?: boolean}) { const {yearId} = useParams<{yearId: string}>(); const [searchParams, setSearchParams] = useSearchParams(); - const [kind, setKind] = useState<'project' | 'idea'>('project'); + const [defaultView] = useState(getProjectsView); + const kind = searchParams.get('kind') === 'idea' ? 'idea' : 'project'; const group = searchParams.get('group') ?? ''; - const [category, setCategory] = useState(''); - const [hasVideoOnly, setHasVideoOnly] = useState(false); - const [searchInput, setSearchInput] = useState(''); - const [search, setSearch] = useState(''); + const category = searchParams.get('category') ?? ''; + const hasVideoOnly = searchParams.get('hasVideo') === 'true'; + const searchParam = searchParams.get('q') ?? ''; + const [searchInput, setSearchInput] = useState(searchParam); + const previousSearchParam = useRef(searchParam); + const pendingSearchParam = useRef(undefined); + const search = searchParam.trim(); + const viewParam = searchParams.get('view'); + const view: ProjectsView = + viewParam === 'grid' || viewParam === 'list' ? viewParam : defaultView; const [cursor, setCursor] = useState(); const [cursorHistory, setCursorHistory] = useState>([]); - const [view, setView] = useState(getProjectsView); const resultStart = useRef(null); const paginationRequestPending = useRef(false); const year = useYear(yearId); @@ -80,26 +86,40 @@ export function ProjectsPage({isAdmin = false}: {isAdmin?: boolean}) { setCursorHistory([]); }; - const selectGroup = (groupId: string) => { + const setFilter = ( + name: string, + value: string | undefined, + options?: {replace?: boolean}, + ) => { setSearchParams((current) => { const next = new URLSearchParams(current); - if (groupId) next.set('group', groupId); - else next.delete('group'); + if (value) next.set(name, value); + else next.delete(name); return next; - }); - resetPagination(); + }, options); }; useEffect(() => { + if (previousSearchParam.current !== searchParam) { + previousSearchParam.current = searchParam; + const internalUpdate = pendingSearchParam.current === searchParam; + pendingSearchParam.current = undefined; + if (!internalUpdate) setSearchInput(searchParam); + return; + } + if (searchInput === searchParam) return; + const timeout = window.setTimeout(() => { - setSearch(searchInput.trim()); + const nextSearchParam = searchInput.trim(); + pendingSearchParam.current = nextSearchParam; + setFilter('q', nextSearchParam || undefined, {replace: true}); }, SEARCH_DEBOUNCE_MS); return () => window.clearTimeout(timeout); - }, [searchInput]); + }, [searchInput, searchParam]); useEffect(() => { resetPagination(); - }, [yearId, search, group]); + }, [yearId, kind, search, group, category, hasVideoOnly]); useEffect(() => { if ( @@ -135,7 +155,10 @@ export function ProjectsPage({isAdmin = false}: {isAdmin?: boolean}) { ? 'browse the finished projects, teams, and award winners.' : 'see what everyone is building, join a team, or share an idea.'}

- +
{(isAdmin || year.data.year.submissionsClosed) && ( @@ -161,6 +184,7 @@ export function ProjectsPage({isAdmin = false}: {isAdmin?: boolean}) { data={ballot.data} error={ballot.error} loading={ballot.isLoading} + detailsSearch={searchParams.toString()} /> )}
{ resetPagination(); + setFilter('q', undefined, {replace: true}); setSearchInput(''); - setSearch(''); }} > clear @@ -209,7 +233,7 @@ export function ProjectsPage({isAdmin = false}: {isAdmin?: boolean}) {