refactor(documents): extract buildSearchParams — address @felixbrandt
triggerSearch (local state, filter change) and buildPageHref (server data, page nav) were each iterating over the same ~10 filter params. Any new filter would have had to land in two places. buildSearchParams is now the single source of truth for which params the /documents URL understands; both callers just pass their snapshot and an optional targetPage. (#316) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -36,46 +36,84 @@ let showAdvanced = $state(untrack(hasAdvancedFilters));
|
|||||||
|
|
||||||
let searchTimer: ReturnType<typeof setTimeout>;
|
let searchTimer: ReturnType<typeof setTimeout>;
|
||||||
|
|
||||||
|
type FilterSnapshot = {
|
||||||
|
q: string;
|
||||||
|
from: string;
|
||||||
|
to: string;
|
||||||
|
senderId: string;
|
||||||
|
receiverId: string;
|
||||||
|
tags: string[];
|
||||||
|
sort: string;
|
||||||
|
dir: string;
|
||||||
|
tagQ: string;
|
||||||
|
tagOp: 'AND' | 'OR';
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a URLSearchParams from a filter snapshot. Single source of truth for
|
||||||
|
* which params the `/documents` URL understands — add a filter here and both
|
||||||
|
* filter-change nav (triggerSearch) and page nav (buildPageHref) will pick it
|
||||||
|
* up. `page` is appended only when > 0 so the default page 0 stays out of the
|
||||||
|
* URL, keeping the filter-change-resets-to-page-0 behaviour implicit.
|
||||||
|
*/
|
||||||
|
function buildSearchParams(filters: FilterSnapshot, targetPage?: number): SvelteURLSearchParams {
|
||||||
|
const params = new SvelteURLSearchParams();
|
||||||
|
if (filters.q) params.set('q', filters.q);
|
||||||
|
if (filters.from) params.set('from', filters.from);
|
||||||
|
if (filters.to) params.set('to', filters.to);
|
||||||
|
if (filters.senderId) params.set('senderId', filters.senderId);
|
||||||
|
if (filters.receiverId) params.set('receiverId', filters.receiverId);
|
||||||
|
filters.tags.forEach((tag) => params.append('tag', tag));
|
||||||
|
if (filters.sort) params.set('sort', filters.sort);
|
||||||
|
if (filters.dir) params.set('dir', filters.dir);
|
||||||
|
if (filters.tagQ) params.set('tagQ', filters.tagQ);
|
||||||
|
if (filters.tagOp === 'OR') params.set('tagOp', 'OR');
|
||||||
|
if (targetPage !== undefined && targetPage > 0) params.set('page', String(targetPage));
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rebuilds the URL from the CURRENT local filter state. `page` is intentionally
|
* Rebuilds the URL from the CURRENT local filter state. `page` is intentionally
|
||||||
* not carried over — any filter change implicitly resets back to page 0, which
|
* not carried over — any filter change implicitly resets back to page 0.
|
||||||
* is the expected behaviour. For page-only navigation use {@link buildPageHref}
|
|
||||||
* instead, which preserves every filter from the server `data`.
|
|
||||||
*/
|
*/
|
||||||
function triggerSearch() {
|
function triggerSearch() {
|
||||||
const params = new SvelteURLSearchParams();
|
const params = buildSearchParams({
|
||||||
if (q) params.set('q', q);
|
q,
|
||||||
if (from) params.set('from', from);
|
from,
|
||||||
if (to) params.set('to', to);
|
to,
|
||||||
if (senderId) params.set('senderId', senderId);
|
senderId,
|
||||||
if (receiverId) params.set('receiverId', receiverId);
|
receiverId,
|
||||||
tagNames.forEach((tag) => params.append('tag', tag.name));
|
tags: tagNames.map((t) => t.name),
|
||||||
if (sort) params.set('sort', sort);
|
sort,
|
||||||
if (dir) params.set('dir', dir);
|
dir,
|
||||||
if (tagQ) params.set('tagQ', tagQ);
|
tagQ,
|
||||||
if (tagOperator === 'OR') params.set('tagOp', 'OR');
|
tagOp: tagOperator
|
||||||
|
});
|
||||||
goto(`/documents?${params.toString()}`, { keepFocus: true, noScroll: true });
|
goto(`/documents?${params.toString()}`, { keepFocus: true, noScroll: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the href for a Pagination prev/next link. Preserves every current
|
* Builds the href for a Pagination prev/next link. Preserves every filter
|
||||||
* filter param and only updates `page`. Uses a normal <a href> (not goto)
|
* param from server `data` and updates `page`. Uses a normal <a href> (not
|
||||||
* so SvelteKit's default scroll restoration brings the user to the top of
|
* goto) so SvelteKit's default scroll restoration brings the user to the top
|
||||||
* the new slice — the expected behaviour for page navigation.
|
* of the new slice — the expected behaviour for page navigation.
|
||||||
*/
|
*/
|
||||||
function buildPageHref(targetPage: number): string {
|
function buildPageHref(targetPage: number): string {
|
||||||
const params = new SvelteURLSearchParams();
|
const params = buildSearchParams(
|
||||||
if (data.q) params.set('q', data.q);
|
{
|
||||||
if (data.from) params.set('from', data.from);
|
q: data.q || '',
|
||||||
if (data.to) params.set('to', data.to);
|
from: data.from || '',
|
||||||
if (data.senderId) params.set('senderId', data.senderId);
|
to: data.to || '',
|
||||||
if (data.receiverId) params.set('receiverId', data.receiverId);
|
senderId: data.senderId || '',
|
||||||
(data.tags || []).forEach((t: string) => params.append('tag', t));
|
receiverId: data.receiverId || '',
|
||||||
if (data.sort) params.set('sort', data.sort);
|
tags: data.tags || [],
|
||||||
if (data.dir) params.set('dir', data.dir);
|
sort: data.sort || '',
|
||||||
if (data.tagQ) params.set('tagQ', data.tagQ);
|
dir: data.dir || '',
|
||||||
if (data.tagOp === 'OR') params.set('tagOp', 'OR');
|
tagQ: data.tagQ || '',
|
||||||
if (targetPage > 0) params.set('page', String(targetPage));
|
tagOp: (data.tagOp as 'AND' | 'OR') || 'AND'
|
||||||
|
},
|
||||||
|
targetPage
|
||||||
|
);
|
||||||
const qs = params.toString();
|
const qs = params.toString();
|
||||||
return qs ? `/documents?${qs}` : '/documents';
|
return qs ? `/documents?${qs}` : '/documents';
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user