mirror of
https://github.com/Jellify-Music/App.git
synced 2026-03-18 11:10:59 -05:00
refactor: search query hook (#1014)
* refactor: search query hook update staleTime and gcTime for search query move query into queries folder alongside function call update usage in search component * debounce searches properly
This commit is contained in:
19
src/api/queries/search/index.ts
Normal file
19
src/api/queries/search/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { QueryKeys } from '../../../enums/query-keys'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { fetchSearchResults } from './utils'
|
||||
import { ONE_MINUTE } from '../../../constants/query-client'
|
||||
import { useJellifyLibrary } from '../../../stores'
|
||||
|
||||
const useSearchResults = (searchString: string | undefined) => {
|
||||
const [library] = useJellifyLibrary()
|
||||
|
||||
return useQuery({
|
||||
queryKey: [QueryKeys.Search, library?.musicLibraryId, searchString],
|
||||
queryFn: () => fetchSearchResults(library?.musicLibraryId, searchString),
|
||||
staleTime: ONE_MINUTE * 10, // Cache results for 10 minutes
|
||||
gcTime: ONE_MINUTE * 15, // Garbage collect after 15 minutes
|
||||
enabled: !!library?.musicLibraryId && !!searchString, // Only run if we have a library ID and a search string
|
||||
})
|
||||
}
|
||||
|
||||
export default useSearchResults
|
||||
@@ -1,9 +1,8 @@
|
||||
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||
import { getItemsApi } from '@jellyfin/sdk/lib/utils/api'
|
||||
import { isEmpty, isUndefined, trim } from 'lodash'
|
||||
import QueryConfig from '../../configs/query.config'
|
||||
import { Api } from '@jellyfin/sdk'
|
||||
import { JellifyUser } from '../../types/JellifyUser'
|
||||
import QueryConfig from '../../../../configs/query.config'
|
||||
import { getApi, getUser } from '../../../../stores'
|
||||
/**
|
||||
* Performs a search for items against the Jellyfin server, trimming whitespace
|
||||
* around the search term for the best possible results.
|
||||
@@ -11,12 +10,13 @@ import { JellifyUser } from '../../types/JellifyUser'
|
||||
* @returns A promise of a BaseItemDto array, be it empty or not
|
||||
*/
|
||||
export async function fetchSearchResults(
|
||||
api: Api | undefined,
|
||||
user: JellifyUser | undefined,
|
||||
libraryId: string | undefined,
|
||||
searchString: string | undefined,
|
||||
): Promise<BaseItemDto[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const api = getApi()
|
||||
const user = getUser()
|
||||
|
||||
if (isEmpty(searchString)) resolve([])
|
||||
|
||||
if (isUndefined(api)) return reject('Client instance not set')
|
||||
@@ -1,11 +1,8 @@
|
||||
import React, { useState } from 'react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import Input from '../Global/helpers/input'
|
||||
import { H5, Text } from '../Global/helpers/text'
|
||||
import ItemRow from '../Global/components/item-row'
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
|
||||
import { QueryKeys } from '../../enums/query-keys'
|
||||
import { fetchSearchResults } from '../../api/queries/search'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { getToken, H3, Spinner, YStack } from 'tamagui'
|
||||
import Suggestions from './suggestions'
|
||||
import { isEmpty } from 'lodash'
|
||||
@@ -13,7 +10,6 @@ import HorizontalCardList from '../Global/components/horizontal-list'
|
||||
import ItemCard from '../Global/components/item-card'
|
||||
import SearchParamList from '../../screens/Search/types'
|
||||
import { closeAllSwipeableRows } from '../Global/components/swipeable-row-registry'
|
||||
import { getApi, getUser, useJellifyLibrary } from '../../stores'
|
||||
import { FlashList } from '@shopify/flash-list'
|
||||
import navigationRef from '../../../navigation'
|
||||
import { StackActions } from '@react-navigation/native'
|
||||
@@ -22,41 +18,35 @@ import Track from '../Global/components/Track'
|
||||
import { pickRandomItemFromArray } from '../../utils/parsing/random'
|
||||
import { SEARCH_PLACEHOLDERS } from '../../configs/placeholder.config'
|
||||
import { formatArtistName } from '../../utils/formatting/artist-names'
|
||||
import useSearchResults from '../../api/queries/search'
|
||||
|
||||
export default function Search({
|
||||
navigation,
|
||||
}: {
|
||||
navigation: NativeStackNavigationProp<SearchParamList, 'SearchScreen'>
|
||||
}): React.JSX.Element {
|
||||
const api = getApi()
|
||||
const user = getUser()
|
||||
const [library] = useJellifyLibrary()
|
||||
/**
|
||||
* Raw text input value from the user, updates immediately as they type
|
||||
*/
|
||||
const [inputValue, setInputValue] = useState<string | undefined>(undefined)
|
||||
|
||||
/**
|
||||
* Debounced search string that updates 500ms after the user stops typing, used to trigger the search query
|
||||
* which is keyed off of this value for caching.
|
||||
*/
|
||||
const [searchString, setSearchString] = useState<string | undefined>(undefined)
|
||||
|
||||
const {
|
||||
data: items,
|
||||
refetch,
|
||||
isFetching: fetchingResults,
|
||||
} = useQuery({
|
||||
queryKey: [QueryKeys.Search, library?.musicLibraryId, searchString],
|
||||
queryFn: () => fetchSearchResults(api, user, library?.musicLibraryId, searchString),
|
||||
})
|
||||
useEffect(() => {
|
||||
const timeout = setTimeout(() => {
|
||||
setSearchString(inputValue || undefined)
|
||||
}, 500)
|
||||
return () => clearTimeout(timeout)
|
||||
}, [inputValue])
|
||||
|
||||
const search = () => {
|
||||
let timeout: ReturnType<typeof setTimeout>
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout)
|
||||
timeout = setTimeout(() => {
|
||||
refetch()
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
const { data: items, isFetching: fetchingResults } = useSearchResults(searchString)
|
||||
|
||||
const handleSearchStringUpdate = (value: string | undefined) => {
|
||||
setSearchString(value)
|
||||
search()
|
||||
setInputValue(value || undefined)
|
||||
}
|
||||
|
||||
const handleScrollBeginDrag = () => {
|
||||
@@ -90,7 +80,7 @@ export default function Search({
|
||||
<Input
|
||||
placeholder={placeholder}
|
||||
onChangeText={handleSearchStringUpdate}
|
||||
value={searchString}
|
||||
value={inputValue}
|
||||
testID='search-input'
|
||||
clearButtonMode='always'
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user