diff --git a/src/screens/Search/stacks/Search/index.tsx b/src/screens/Search/stacks/Search/index.tsx index 8fa397b..c1c3927 100644 --- a/src/screens/Search/stacks/Search/index.tsx +++ b/src/screens/Search/stacks/Search/index.tsx @@ -1,6 +1,6 @@ -import React, { useState, useEffect, useRef, useCallback } from 'react'; +import React, { useState, useEffect, useCallback, useMemo } from 'react'; import Input from '@/components/Input'; -import { ActivityIndicator, Animated, SafeAreaView, View } from 'react-native'; +import { ActivityIndicator, Animated, KeyboardAvoidingView, SafeAreaView, View } from 'react-native'; import styled from 'styled-components/native'; import { useAppDispatch, useTypedSelector } from '@/store'; import Fuse, { IFuseOptions } from 'fuse.js'; @@ -18,7 +18,6 @@ import DownloadIcon from '@/components/DownloadIcon'; import ChevronRight from '@/assets/icons/chevron-right.svg'; import SearchIcon from '@/assets/icons/magnifying-glass.svg'; import { ShadowWrapper } from '@/components/Shadow'; -import { useKeyboardHeight } from '@/utility/useKeyboardHeight'; import { NavigationProp } from '@/screens/types'; import { useNavigationOffsets } from '@/components/SafeNavigatorView'; import BaseAlbumImage from '@/screens/Music/stacks/components/AlbumImage'; @@ -101,17 +100,14 @@ export default function Search() { const offsets = useNavigationOffsets({ includeOverlay: false }); // Prepare state for fuse and albums - const [fuseIsReady, setFuseReady] = useState(false); const [searchTerm, setSearchTerm] = useState(''); const [isLoading, setLoading] = useState(false); const [fuseResults, setFuseResults] = useState([]); const [jellyfinResults, setJellyfinResults] = useState([]); const albums = useTypedSelector(state => state.music.albums.entities); - const fuse = useRef>(); // Prepare helpers const navigation = useNavigation(); - const keyboardHeight = useKeyboardHeight(); const getImage = useGetImage(); const dispatch = useAppDispatch(); @@ -122,10 +118,9 @@ export default function Search() { * more intelligently by removing and adding the changed albums, but this is * an open todo. */ - useEffect(() => { - fuse.current = new Fuse(Object.values(albums) as Album[], fuseOptions); - setFuseReady(true); - }, [albums, setFuseReady]); + const fuse = useMemo(() => ( + new Fuse(Object.values(albums) as Album[], fuseOptions) + ), [albums]); /** * This function retrieves search results from Jellyfin. It is a seperate @@ -175,14 +170,8 @@ export default function Search() { } const retrieveResults = async () => { - // GUARD: In some extraordinary cases, Fuse might not be presented since - // it is assigned via refs. In this case, we can't handle any searching. - if (!fuse.current) { - return; - } - // First set the immediate results from fuse - const fuseResults = fuse.current.search(searchTerm); + const fuseResults = fuse.search(searchTerm); const albums: AlbumResult[] = fuseResults .map(({ item }) => ({ id: item.Id, @@ -214,10 +203,7 @@ export default function Search() { }, [navigation, albums]); const SearchInput = React.useMemo(() => ( - + @@ -269,69 +255,70 @@ export default function Search() { */} - ), [searchTerm, setSearchTerm, defaultStyles, isLoading, keyboardHeight, offsets]); + ), [searchTerm, setSearchTerm, defaultStyles, isLoading]); - // GUARD: We cannot search for stuff unless Fuse is loaded with results. - // Therefore we delay rendering to when we are certain it's there. - if (!fuseIsReady) { - return null; - } + const searchResults = useMemo(() => ([ + ...fuseResults, + ...jellyfinResults, + ]), [fuseResults, jellyfinResults]); return ( - - { - const album = albums[trackAlbum || id]; + + + { + const album = albums[trackAlbum || id]; - // GUARD: If the album cannot be found in the store, we - // cannot display it. - if (!album) { - return null; - } + // GUARD: If the album cannot be found in the store, we + // cannot display it. + if (!album) { + return null; + } - return ( - id={album.Id} onPress={selectAlbum} testID={`search-result-${album.Id}`}> - - - - - - - {trackName || album.Name} - - {(album.AlbumArtist || album.Name) && ( - - {type === 'AlbumArtist' - ? `${t('album')} • ${album.AlbumArtist}` - : `${t('track')} • ${album.AlbumArtist} — ${album.Name}` - } - - )} - - - - - - - - - - ); - }} - keyExtractor={(item) => item.id} - extraData={[searchTerm, albums]} - /> - {(searchTerm.length && !jellyfinResults.length && !fuseResults.length && !isLoading) ? ( - - {t('no-results')} - - ) : null} - {SearchInput} + return ( + id={album.Id} onPress={selectAlbum} testID={`search-result-${album.Id}`}> + + + + + + + {trackName || album.Name} + + {(album.AlbumArtist || album.Name) && ( + + {type === 'AlbumArtist' + ? `${t('album')} • ${album.AlbumArtist}` + : `${t('track')} • ${album.AlbumArtist} — ${album.Name}` + } + + )} + + + + + + + + + + ); + }} + keyExtractor={(item) => item.id} + extraData={[searchTerm, albums]} + /> + {(searchTerm.length && !jellyfinResults.length && !fuseResults.length && !isLoading) ? ( + + {t('no-results')} + + ) : null} + {SearchInput} + ); } \ No newline at end of file