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}) {