fix: fix runtime error when clearing backlinks filter (#105)

This commit is contained in:
Ben Senescu 2026-04-09 16:43:47 -04:00 committed by GitHub
parent 4137b386ab
commit 53505c0a5b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 39 additions and 31 deletions

View File

@ -324,6 +324,7 @@ export function BacklinksFilterPanel({
) : null} ) : null}
</div> </div>
<button <button
type="button"
className="btn btn-xs btn-ghost gap-1" className="btn btn-xs btn-ghost gap-1"
onClick={current.reset} onClick={current.reset}
disabled={current.activeFilterCount === 0} disabled={current.activeFilterCount === 0}

View File

@ -7,9 +7,9 @@ export type BacklinksTabFilterValues = {
maxLinkAuthority: string; maxLinkAuthority: string;
minSpamScore: string; minSpamScore: string;
maxSpamScore: string; maxSpamScore: string;
linkType: "" | "dofollow" | "nofollow"; linkType: string;
hideLost: "" | "true"; hideLost: string;
hideBroken: "" | "true"; hideBroken: string;
}; };
export type ReferringDomainsFilterValues = { export type ReferringDomainsFilterValues = {

View File

@ -12,72 +12,79 @@ import { countActiveFilters } from "./backlinksFiltering";
const STORAGE_KEY_PREFIX = "backlinks-filters:"; const STORAGE_KEY_PREFIX = "backlinks-filters:";
type FilterValues = Record<string, string>;
function isRecord(value: unknown): value is Record<string, unknown> { function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null; return typeof value === "object" && value !== null;
} }
function loadFromStorage<T extends Record<string, string>>( function loadFromStorage<T extends FilterValues>(tab: string, fallback: T): T {
tab: string, const fallbackClone = { ...fallback };
fallback: T,
): T {
try { try {
const raw = localStorage.getItem(`${STORAGE_KEY_PREFIX}${tab}`); const raw = localStorage.getItem(`${STORAGE_KEY_PREFIX}${tab}`);
if (!raw) return fallback; if (!raw) return fallbackClone;
const parsed: unknown = JSON.parse(raw); const parsed: unknown = JSON.parse(raw);
if (!isRecord(parsed)) return fallback; if (!isRecord(parsed)) return fallbackClone;
const result = { ...fallback };
for (const key of Object.keys(fallback)) { const result = { ...fallbackClone };
const val = parsed[key]; for (const key in fallback) {
if (typeof val === "string") { const value = parsed[key];
(result as Record<string, string>)[key] = val; if (typeof value === "string") {
Object.assign(result, { [key]: value });
} }
} }
return result; return result;
} catch { } catch {
return fallback; return fallbackClone;
} }
} }
function saveToStorage(tab: string, values: Record<string, string>) { function saveToStorage(tab: string, values: FilterValues) {
try { try {
localStorage.setItem(`${STORAGE_KEY_PREFIX}${tab}`, JSON.stringify(values)); localStorage.setItem(`${STORAGE_KEY_PREFIX}${tab}`, JSON.stringify(values));
} catch { } catch {
// storage full silently ignore // storage full - silently ignore
} }
} }
function useTabFilterForm<T extends Record<string, string>>( function useTabFilters<T extends FilterValues>(tab: string, emptyValues: T) {
tab: string, const [defaultValues] = useState<T>(() =>
emptyValues: T, loadFromStorage(tab, { ...emptyValues }),
) { );
const form = useForm({ defaultValues: loadFromStorage(tab, emptyValues) }); const form = useForm({ defaultValues });
const values = useStore(form.store, (s) => s.values); const values = useStore(form.store, (state) => state.values);
useEffect(() => { useEffect(() => {
saveToStorage(tab, values); saveToStorage(tab, values);
}, [tab, values]); }, [tab, values]);
const reset = useCallback(() => { const reset = useCallback(() => {
form.reset(emptyValues); form.reset({ ...emptyValues }, { keepDefaultValues: true });
}, [form, emptyValues]); }, [emptyValues, form]);
const activeFilterCount = countActiveFilters(values); return {
form,
return { form, values, reset, activeFilterCount }; values,
reset,
activeFilterCount: countActiveFilters(values),
};
} }
export function useBacklinksFilters() { export function useBacklinksFilters() {
const [showFilters, setShowFilters] = useState(false); const [showFilters, setShowFilters] = useState(false);
const backlinks = useTabFilterForm<BacklinksTabFilterValues>( const backlinks = useTabFilters<BacklinksTabFilterValues>(
"backlinks", "backlinks",
EMPTY_BACKLINKS_FILTERS, EMPTY_BACKLINKS_FILTERS,
); );
const domains = useTabFilterForm<ReferringDomainsFilterValues>( const domains = useTabFilters<ReferringDomainsFilterValues>(
"domains", "domains",
EMPTY_REFERRING_DOMAINS_FILTERS, EMPTY_REFERRING_DOMAINS_FILTERS,
); );
const pages = useTabFilterForm<TopPagesFilterValues>( const pages = useTabFilters<TopPagesFilterValues>(
"pages", "pages",
EMPTY_TOP_PAGES_FILTERS, EMPTY_TOP_PAGES_FILTERS,
); );