extract search history into feature module
This commit is contained in:
@@ -0,0 +1,93 @@
|
|||||||
|
import {useCallback} from 'react'
|
||||||
|
|
||||||
|
import {useProfilesQuery} from '#/state/queries/profile'
|
||||||
|
import {useSession} from '#/state/session'
|
||||||
|
import {
|
||||||
|
type SearchFilters,
|
||||||
|
serializeHistoryEntry,
|
||||||
|
} from '#/screens/Search/searchParams'
|
||||||
|
import {account, useStorage} from '#/storage'
|
||||||
|
import type * as bsky from '#/types/bsky'
|
||||||
|
|
||||||
|
const MAX_TERMS = 6
|
||||||
|
const MAX_PROFILES = 10
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Per-account recent search history (device storage). Terms are stored as
|
||||||
|
* serialized history entries (plain string, or JSON when filters are
|
||||||
|
* attached); profiles are stored as DIDs and hydrated via useProfilesQuery,
|
||||||
|
* which keeps avatars/names fresh (stale-while-revalidate).
|
||||||
|
*/
|
||||||
|
export function useSearchHistory() {
|
||||||
|
const {currentAccount} = useSession()
|
||||||
|
const [termHistory = [], setTermHistory] = useStorage(account, [
|
||||||
|
currentAccount?.did ?? 'pwi',
|
||||||
|
'searchTermHistory',
|
||||||
|
] as const)
|
||||||
|
const [accountHistory = [], setAccountHistory] = useStorage(account, [
|
||||||
|
currentAccount?.did ?? 'pwi',
|
||||||
|
'searchAccountHistory',
|
||||||
|
])
|
||||||
|
|
||||||
|
const {data: accountHistoryProfiles} = useProfilesQuery({
|
||||||
|
handles: accountHistory,
|
||||||
|
maintainData: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const profiles =
|
||||||
|
accountHistoryProfiles?.profiles.filter(p =>
|
||||||
|
accountHistory.includes(p.did),
|
||||||
|
) ?? []
|
||||||
|
|
||||||
|
const updateSearchHistory = useCallback(
|
||||||
|
(q: string, searchFilters: SearchFilters = {}) => {
|
||||||
|
if (!q) return
|
||||||
|
/*
|
||||||
|
* Store the query plus any advanced-search filters. Term-only searches
|
||||||
|
* serialize to a plain string (back-compatible with existing history);
|
||||||
|
* filtered searches serialize to JSON. Dedupe on the serialized form.
|
||||||
|
*/
|
||||||
|
const item = serializeHistoryEntry(q, searchFilters)
|
||||||
|
const newSearchHistory = [
|
||||||
|
item,
|
||||||
|
...termHistory.filter(search => search !== item),
|
||||||
|
].slice(0, MAX_TERMS)
|
||||||
|
setTermHistory(newSearchHistory)
|
||||||
|
},
|
||||||
|
[termHistory, setTermHistory],
|
||||||
|
)
|
||||||
|
|
||||||
|
const updateProfileHistory = useCallback(
|
||||||
|
(item: bsky.profile.AnyProfileView) => {
|
||||||
|
const newAccountHistory = [
|
||||||
|
item.did,
|
||||||
|
...accountHistory.filter(p => p !== item.did),
|
||||||
|
].slice(0, MAX_PROFILES)
|
||||||
|
setAccountHistory(newAccountHistory)
|
||||||
|
},
|
||||||
|
[accountHistory, setAccountHistory],
|
||||||
|
)
|
||||||
|
|
||||||
|
const deleteSearchHistoryItem = useCallback(
|
||||||
|
(item: string) => {
|
||||||
|
setTermHistory(termHistory.filter(search => search !== item))
|
||||||
|
},
|
||||||
|
[termHistory, setTermHistory],
|
||||||
|
)
|
||||||
|
|
||||||
|
const deleteProfileHistoryItem = useCallback(
|
||||||
|
(item: bsky.profile.AnyProfileView) => {
|
||||||
|
setAccountHistory(accountHistory.filter(p => p !== item.did))
|
||||||
|
},
|
||||||
|
[accountHistory, setAccountHistory],
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
termHistory,
|
||||||
|
profiles,
|
||||||
|
updateSearchHistory,
|
||||||
|
updateProfileHistory,
|
||||||
|
deleteSearchHistoryItem,
|
||||||
|
deleteProfileHistoryItem,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,10 +28,7 @@ import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
|||||||
import {MagnifyingGlassIcon} from '#/lib/icons'
|
import {MagnifyingGlassIcon} from '#/lib/icons'
|
||||||
import {type NavigationProp, type SearchParams} from '#/lib/routes/types'
|
import {type NavigationProp, type SearchParams} from '#/lib/routes/types'
|
||||||
import {listenSoftReset} from '#/state/events'
|
import {listenSoftReset} from '#/state/events'
|
||||||
import {
|
import {unstableCacheProfileView} from '#/state/queries/profile'
|
||||||
unstableCacheProfileView,
|
|
||||||
useProfilesQuery,
|
|
||||||
} from '#/state/queries/profile'
|
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {
|
import {
|
||||||
countActiveFilters,
|
countActiveFilters,
|
||||||
@@ -41,7 +38,6 @@ import {
|
|||||||
parseHistoryEntry,
|
parseHistoryEntry,
|
||||||
readSearchFilters,
|
readSearchFilters,
|
||||||
type SearchFilters,
|
type SearchFilters,
|
||||||
serializeHistoryEntry,
|
|
||||||
withoutFilterParams,
|
withoutFilterParams,
|
||||||
} from '#/screens/Search/searchParams'
|
} from '#/screens/Search/searchParams'
|
||||||
import {
|
import {
|
||||||
@@ -62,7 +58,7 @@ import * as Toast from '#/components/Toast'
|
|||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {useAnalytics} from '#/analytics'
|
import {useAnalytics} from '#/analytics'
|
||||||
import {IS_NATIVE, IS_WEB} from '#/env'
|
import {IS_NATIVE, IS_WEB} from '#/env'
|
||||||
import {account, useStorage} from '#/storage'
|
import {useSearchHistory} from '#/features/searchHistory'
|
||||||
import type * as bsky from '#/types/bsky'
|
import type * as bsky from '#/types/bsky'
|
||||||
import {AdvancedSearchDialog} from './components/AdvancedSearchDialog'
|
import {AdvancedSearchDialog} from './components/AdvancedSearchDialog'
|
||||||
import {AutocompleteResults} from './components/AutocompleteResults'
|
import {AutocompleteResults} from './components/AutocompleteResults'
|
||||||
@@ -116,7 +112,6 @@ export function SearchScreenShell({
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const textInput = useRef<TextInput>(null)
|
const textInput = useRef<TextInput>(null)
|
||||||
const {t: l} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {currentAccount} = useSession()
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
// Get tab parameter from route params
|
// Get tab parameter from route params
|
||||||
@@ -144,61 +139,14 @@ export function SearchScreenShell({
|
|||||||
|
|
||||||
const [showAutocomplete, setShowAutocomplete] = useState(false)
|
const [showAutocomplete, setShowAutocomplete] = useState(false)
|
||||||
|
|
||||||
const [termHistory = [], setTermHistory] = useStorage(account, [
|
const {
|
||||||
currentAccount?.did ?? 'pwi',
|
termHistory,
|
||||||
'searchTermHistory',
|
profiles: historyProfiles,
|
||||||
] as const)
|
updateSearchHistory,
|
||||||
const [accountHistory = [], setAccountHistory] = useStorage(account, [
|
updateProfileHistory,
|
||||||
currentAccount?.did ?? 'pwi',
|
deleteSearchHistoryItem,
|
||||||
'searchAccountHistory',
|
deleteProfileHistoryItem,
|
||||||
])
|
} = useSearchHistory()
|
||||||
|
|
||||||
const {data: accountHistoryProfiles} = useProfilesQuery({
|
|
||||||
handles: accountHistory,
|
|
||||||
maintainData: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
const updateSearchHistory = useCallback(
|
|
||||||
(q: string, searchFilters: SearchFilters = {}) => {
|
|
||||||
if (!q) return
|
|
||||||
/*
|
|
||||||
* Store the query plus any advanced-search filters. Term-only searches
|
|
||||||
* serialize to a plain string (back-compatible with existing history);
|
|
||||||
* filtered searches serialize to JSON. Dedupe on the serialized form.
|
|
||||||
*/
|
|
||||||
const item = serializeHistoryEntry(q, searchFilters)
|
|
||||||
const newSearchHistory = [
|
|
||||||
item,
|
|
||||||
...termHistory.filter(search => search !== item),
|
|
||||||
].slice(0, 6)
|
|
||||||
setTermHistory(newSearchHistory)
|
|
||||||
},
|
|
||||||
[termHistory, setTermHistory],
|
|
||||||
)
|
|
||||||
|
|
||||||
const updateProfileHistory = useCallback(
|
|
||||||
(item: bsky.profile.AnyProfileView) => {
|
|
||||||
const newAccountHistory = [
|
|
||||||
item.did,
|
|
||||||
...accountHistory.filter(p => p !== item.did),
|
|
||||||
].slice(0, 10)
|
|
||||||
setAccountHistory(newAccountHistory)
|
|
||||||
},
|
|
||||||
[accountHistory, setAccountHistory],
|
|
||||||
)
|
|
||||||
|
|
||||||
const deleteSearchHistoryItem = useCallback(
|
|
||||||
(item: string) => {
|
|
||||||
setTermHistory(termHistory.filter(search => search !== item))
|
|
||||||
},
|
|
||||||
[termHistory, setTermHistory],
|
|
||||||
)
|
|
||||||
const deleteProfileHistoryItem = useCallback(
|
|
||||||
(item: bsky.profile.AnyProfileView) => {
|
|
||||||
setAccountHistory(accountHistory.filter(p => p !== item.did))
|
|
||||||
},
|
|
||||||
[accountHistory, setAccountHistory],
|
|
||||||
)
|
|
||||||
|
|
||||||
const {query, filters, setFilters, hasFilters} = useQueryManager({
|
const {query, filters, setFilters, hasFilters} = useQueryManager({
|
||||||
initialQuery: queryParam,
|
initialQuery: queryParam,
|
||||||
@@ -683,11 +631,7 @@ export function SearchScreenShell({
|
|||||||
) : (
|
) : (
|
||||||
<SearchHistory
|
<SearchHistory
|
||||||
searchHistory={termHistory}
|
searchHistory={termHistory}
|
||||||
selectedProfiles={
|
selectedProfiles={historyProfiles}
|
||||||
accountHistoryProfiles?.profiles.filter(p =>
|
|
||||||
accountHistory.includes(p.did),
|
|
||||||
) ?? []
|
|
||||||
}
|
|
||||||
onItemClick={handleHistoryItemClick}
|
onItemClick={handleHistoryItemClick}
|
||||||
onProfileClick={handleProfileClick}
|
onProfileClick={handleProfileClick}
|
||||||
onRemoveItemClick={deleteSearchHistoryItem}
|
onRemoveItemClick={deleteSearchHistoryItem}
|
||||||
|
|||||||
Reference in New Issue
Block a user