From a719e309ad1bf1668bfa9cf46d40cd34156f2c08 Mon Sep 17 00:00:00 2001 From: Lei Nelissen Date: Mon, 16 May 2022 22:17:00 +0200 Subject: [PATCH] Add stubs for filters in search --- src/assets/icons/collection.svg | 3 + src/assets/icons/microphone.svg | 3 + src/assets/icons/note.svg | 3 + .../Search/components/SelectableFilter.tsx | 55 ++++++++ src/screens/Search/index.tsx | 121 ++++++++++++------ 5 files changed, 143 insertions(+), 42 deletions(-) create mode 100644 src/assets/icons/collection.svg create mode 100644 src/assets/icons/microphone.svg create mode 100644 src/assets/icons/note.svg create mode 100644 src/screens/Search/components/SelectableFilter.tsx diff --git a/src/assets/icons/collection.svg b/src/assets/icons/collection.svg new file mode 100644 index 0000000..d017c0c --- /dev/null +++ b/src/assets/icons/collection.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/microphone.svg b/src/assets/icons/microphone.svg new file mode 100644 index 0000000..8446db3 --- /dev/null +++ b/src/assets/icons/microphone.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/note.svg b/src/assets/icons/note.svg new file mode 100644 index 0000000..ec9765b --- /dev/null +++ b/src/assets/icons/note.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/screens/Search/components/SelectableFilter.tsx b/src/screens/Search/components/SelectableFilter.tsx new file mode 100644 index 0000000..3a53315 --- /dev/null +++ b/src/screens/Search/components/SelectableFilter.tsx @@ -0,0 +1,55 @@ +import useDefaultStyles from 'components/Colors'; +import { Text } from 'components/Typography'; +import { THEME_COLOR } from 'CONSTANTS'; +import React from 'react'; +import { SvgProps } from 'react-native-svg'; +import styled, { css } from 'styled-components/native'; + +const Container = styled.TouchableOpacity<{ active?: boolean }>` + border-radius: 80px; + padding: 6px 12px; + margin-right: 2px; + display: flex; + flex-direction: row; + align-items: center; +`; + +const Label = styled(Text)<{ active?: boolean }>` + margin-left: 6px; + opacity: 0.5; + + ${(props) => props.active && css` + opacity: 1; + font-weight: 500; + color: ${THEME_COLOR}; + `} +`; + +interface Props { + icon: React.FC; + text: string; + active: boolean; + onPress: () => void; +} + +function SelectableFilter({ + icon: Icon, + text, + active, + onPress, +}: Props) { + const defaultStyles = useDefaultStyles(); + + return ( + + + + + ); +} + +export default SelectableFilter; \ No newline at end of file diff --git a/src/screens/Search/index.tsx b/src/screens/Search/index.tsx index af7dc93..f4e1ad2 100644 --- a/src/screens/Search/index.tsx +++ b/src/screens/Search/index.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect, useRef, useCallback } from 'react'; import Input from 'components/Input'; -import { ActivityIndicator, SafeAreaView, TextInput, View } from 'react-native'; +import { ActivityIndicator, SafeAreaView, View } from 'react-native'; import styled from 'styled-components/native'; import { useTypedSelector } from 'store'; import Fuse from 'fuse.js'; @@ -9,7 +9,6 @@ import { FlatList } from 'react-native-gesture-handler'; import TouchableHandler from 'components/TouchableHandler'; import { useNavigation } from '@react-navigation/native'; import { useGetImage } from 'utility/JellyfinApi'; -import { MusicNavigationProp } from '../types'; import FastImage from 'react-native-fast-image'; import { t } from '@localisation'; import useDefaultStyles from 'components/Colors'; @@ -17,23 +16,33 @@ import { searchAndFetchAlbums } from 'store/music/actions'; import { debounce } from 'lodash'; import { useDispatch } from 'react-redux'; import { Text } from 'components/Typography'; +import { MusicNavigationProp } from 'screens/Music/types'; 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 MicrophoneIcon from 'assets/icons/microphone.svg'; +// import AlbumIcon from 'assets/icons/collection.svg'; +// import TrackIcon from 'assets/icons/note.svg'; +// import PlaylistIcon from 'assets/icons/note-list.svg'; +// import StreamIcon from 'assets/icons/cloud.svg'; +// import LocalIcon from 'assets/icons/internal-drive.svg'; +// import SelectableFilter from './components/SelectableFilter'; const Container = styled.View` - padding: 0 32px; - position: relative; + padding: 4px 32px 0 32px; + margin-bottom: 0px; + padding-bottom: 0px; + border-top-width: 1px; `; -const FullSizeContainer = styled(Container)` +const FullSizeContainer = styled.View` flex: 1; `; const Loading = styled.View` position: absolute; - right: 32px; + right: 12px; top: 0; height: 100%; flex: 1; @@ -61,10 +70,18 @@ const SearchResult = styled.View` height: 54px; `; -const fuseOptions = { +const SearchIndicator = styled(SearchIcon)` + position: absolute; + left: 16px; + top: 26px; +`; + + +const fuseOptions: Fuse.IFuseOptions = { keys: ['Name', 'AlbumArtist', 'AlbumArtists', 'Artists'], threshold: 0.1, includeScore: true, + fieldNormWeight: 1, }; type AudioResult = { @@ -86,17 +103,14 @@ type CombinedResults = (AudioResult | AlbumResult)[]; export default function Search() { const defaultStyles = useDefaultStyles(); - // Prepare state + // 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>(); - const searchElement = useRef(null); // Prepare helpers const navigation = useNavigation(); @@ -197,40 +211,62 @@ export default function Search() { retrieveResults(); }, [searchTerm, setFuseResults, setLoading, fuse, fetchJellyfinResults]); - // Automatically focus on the text input on mount - useEffect(() => { - // Give the timeout a slight delay so the component has a chance to actually - // render the text input field. - setTimeout(() => searchElement.current?.focus(), 10); - }, []); - // Handlers const selectAlbum = useCallback((id: string) => navigation.navigate('Album', { id, album: albums[id] as Album }), [navigation, albums] ); const HeaderComponent = React.useMemo(() => ( - - - - {isLoading && } - + + + + + + {isLoading && } + + + {/* + + + + + + + + + */} + ), [searchTerm, setSearchTerm, defaultStyles, isLoading]); - // const FooterComponent = React.useMemo(() => ( - // - // {(searchTerm.length && !jellyfinResults.length && !fuseResults.length && !isLoading) - // ? {t('no-results')} - // : null} - // - // ), [searchTerm, jellyfinResults, fuseResults, 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) { @@ -240,7 +276,7 @@ export default function Search() { return ( { const album = albums[trackAlbum || id]; @@ -254,7 +290,9 @@ export default function Search() { return ( id={album.Id} onPress={selectAlbum}> - + + + {trackName || album.Name} @@ -277,8 +315,6 @@ export default function Search() { ); }} keyExtractor={(item) => item.id} - ListHeaderComponent={HeaderComponent} - // ListFooterComponent={FooterComponent} extraData={[searchTerm, albums]} /> {(searchTerm.length && !jellyfinResults.length && !fuseResults.length && !isLoading) ? ( @@ -286,6 +322,7 @@ export default function Search() { {t('no-results')} ) : null} + {HeaderComponent} ); } \ No newline at end of file