diff --git a/src/app/components/versions/ChangelogList.tsx b/src/app/components/versions/ChangelogList.tsx index c4a94593..48246ffb 100644 --- a/src/app/components/versions/ChangelogList.tsx +++ b/src/app/components/versions/ChangelogList.tsx @@ -1,29 +1,26 @@ import { useMemo, useState } from 'preact/hooks' import { Btn, TextInput } from '..' import { useLocale } from '../../contexts' +import { useSearchParam } from '../../hooks' +import { useTags } from '../../hooks/useTags' import type { Change } from '../../services' import { ChangelogEntry } from './ChangelogEntry' import { ChangelogTag } from './ChangelogTag' +const SEARCH_KEY = 'search' + interface Props { changes: Change[] | undefined, defaultOrder: 'asc' | 'desc', } export function ChangelogList({ changes, defaultOrder }: Props) { const { locale } = useLocale() - - const [search, setSearch] = useState('') - const [tags, setTags] = useState([]) - const toggleTag = (tag: string) => { - if (!tags.includes(tag)) { - setTags([...tags, tag]) - } else { - setTags(tags.filter(t => t !== tag)) - } - } + + const [search, setSearch] = useSearchParam(SEARCH_KEY) + const [tags, toggleTag] = useTags() const filteredChangelogs = useMemo(() => { - const query = search.split(' ').map(q => q.trim().toLowerCase()).filter(q => q.length > 0) + const query = (search ?? '').split(' ').map(q => q.trim().toLowerCase()).filter(q => q.length > 0) if (query.length === 0 && tags.length === 0) return changes return changes?.filter(change => { if (!tags.every(tag => change.tags.includes(tag))) { @@ -48,11 +45,11 @@ export function ChangelogList({ changes, defaultOrder }: Props) { return <>
+ value={search} onChange={v => setSearch(v, true)} /> setSort(!sort)} />
{tags.length > 0 &&
- {tags.map(tag => setTags(tags.filter(t => t !== tag))} />)} + {tags.map(tag => toggleTag(tag)} />)}
}
{sortedChangelogs === undefined diff --git a/src/app/components/versions/VersionList.tsx b/src/app/components/versions/VersionList.tsx index a23f7006..90a479c7 100644 --- a/src/app/components/versions/VersionList.tsx +++ b/src/app/components/versions/VersionList.tsx @@ -1,9 +1,12 @@ import { useMemo, useState } from 'preact/hooks' import { Checkbox, TextInput } from '..' import { useLocale } from '../../contexts' +import { useSearchParam } from '../../hooks' import type { VersionMeta } from '../../services' import { VersionEntry } from './VersionEntry' +const SEARCH_KEY = 'search' + interface Props { versions: VersionMeta[] link?: (id: string) => string @@ -12,11 +15,11 @@ export function VersionList({ versions, link }: Props) { const { locale } = useLocale() const [snapshots, setSnapshots] = useState(true) - const [search, setSearch] = useState('') + const [search, setSearch] = useSearchParam(SEARCH_KEY) const filteredVersions = useMemo(() => versions.filter(v => { if (v.type === 'snapshot' && !snapshots) return false - return v.id.includes(search) + return v.id.includes(search ?? '') }), [versions, snapshots, search]) diff --git a/src/app/hooks/useTags.ts b/src/app/hooks/useTags.ts new file mode 100644 index 00000000..004b089c --- /dev/null +++ b/src/app/hooks/useTags.ts @@ -0,0 +1,20 @@ +import { useMemo } from 'preact/hooks' +import { useSearchParam } from './useSearchParam' + +const TAG_KEY = 'tags' +const TAG_SEP = '|' + +export function useTags(): [string[], (tag: string, force?: boolean) => void] { + const [tags, setTags] = useSearchParam(TAG_KEY) + const activeTags = useMemo(() => tags?.split(TAG_SEP) ?? [], [tags]) + + const toggleTag = (tag: string, force?: boolean) => { + if (force === false || (activeTags.includes(tag) && force !== true)) { + setTags(activeTags.filter(t => t !== tag).join(TAG_SEP), true) + } else { + setTags([...activeTags, tag].sort().join(TAG_SEP), true) + } + } + + return [activeTags, toggleTag] +} diff --git a/src/app/pages/Guides.tsx b/src/app/pages/Guides.tsx index 2560b426..15b77d7d 100644 --- a/src/app/pages/Guides.tsx +++ b/src/app/pages/Guides.tsx @@ -2,7 +2,7 @@ import { useMemo, useState } from 'preact/hooks' import config from '../../config.json' import { Btn, BtnMenu, ChangelogTag, GuideCard, TextInput } from '../components' import { useLocale, useTitle, useVersion } from '../contexts' -import { useSearchParam } from '../hooks' +import { useTags } from '../hooks/useTags' import type { VersionId } from '../services' interface Guide { @@ -14,9 +14,6 @@ interface Guide { declare var __GUIDES__: Guide[] -const TAG_KEY = 'tags' -const TAG_SEP = '|' - interface Props { path?: string } @@ -26,15 +23,7 @@ export function Guides({}: Props) { useTitle(locale('title.guides')) const [search, setSearch] = useState('') - const [tags, setTags] = useSearchParam(TAG_KEY) - const activeTags = useMemo(() => tags?.split(TAG_SEP) ?? [], [tags]) - const toggleTag = (tag: string) => { - if (activeTags.includes(tag)) { - setTags(activeTags.filter(t => t !== tag).join(TAG_SEP)) - } else { - setTags([...activeTags, tag].sort().join(TAG_SEP)) - } - } + const [activeTags, toggleTag] = useTags() const [versionFilter, setVersionFiler] = useState(false) @@ -59,7 +48,7 @@ export function Guides({}: Props) { return content.includes(q) }) }) - }, [versionedGuides, search, tags]) + }, [versionedGuides, search, activeTags]) return