Files
simple-react-app-kit/packages/search/src/use-search-history.ts
T

102 lines
2.5 KiB
TypeScript
Raw Normal View History

import * as React from "react"
interface UseSearchHistoryOptions {
limit: number
storageKey?: string
}
export function useSearchHistory({
limit,
storageKey,
}: UseSearchHistoryOptions) {
const [items, setItems] = React.useState<readonly string[]>(() =>
readSearchHistory(storageKey, limit)
)
React.useEffect(() => {
const syncFromStorage = () => setItems(readSearchHistory(storageKey, limit))
const handleStorage = (event: StorageEvent) => {
if (event.key === storageKey) syncFromStorage()
}
syncFromStorage()
if (!storageKey || typeof window === "undefined") return
window.addEventListener("storage", handleStorage)
return () => window.removeEventListener("storage", handleStorage)
}, [limit, storageKey])
const update = React.useCallback(
(updater: (current: readonly string[]) => readonly string[]) => {
setItems((current) => {
const next = updater(current).slice(0, limit)
writeSearchHistory(storageKey, next)
return next
})
},
[limit, storageKey]
)
const add = React.useCallback(
(query: string) => {
const normalizedQuery = query.trim()
if (!normalizedQuery) return
update((current) => [
normalizedQuery,
...current.filter((item) => item !== normalizedQuery),
])
},
[update]
)
const remove = React.useCallback(
(query: string) =>
update((current) => current.filter((item) => item !== query)),
[update]
)
const clear = React.useCallback(() => update(() => []), [update])
return { add, clear, items, remove }
}
function readSearchHistory(storageKey: string | undefined, limit: number) {
if (!storageKey || typeof window === "undefined") return []
try {
const stored = window.localStorage.getItem(storageKey)
if (!stored) return []
const parsed: unknown = JSON.parse(stored)
if (!Array.isArray(parsed)) return []
return Array.from(
new Set(
parsed
.filter((item): item is string => typeof item === "string")
.map((item) => item.trim())
.filter(Boolean)
)
).slice(0, limit)
} catch {
return []
}
}
function writeSearchHistory(
storageKey: string | undefined,
items: readonly string[]
) {
if (!storageKey || typeof window === "undefined") return
try {
if (items.length === 0) {
window.localStorage.removeItem(storageKey)
} else {
window.localStorage.setItem(storageKey, JSON.stringify(items))
}
} catch {
// Storage can be disabled or unavailable in private browsing contexts.
}
}