serach debouncing, hiding result categories

This commit is contained in:
austinried 2021-07-26 13:44:55 +09:00
parent 3615ec9ab6
commit c12ff2c08c
8 changed files with 119 additions and 42 deletions

View File

@ -1,4 +1,4 @@
import { AlbumListItem, Artist, PlaylistListItem, Song } from '@app/models/music'
import { ListableItem } from '@app/models/music'
import { currentTrackAtom } from '@app/state/trackplayer'
import colors from '@app/styles/colors'
import font from '@app/styles/font'
@ -37,7 +37,7 @@ const TitleText = React.memo<{
})
const ListItem: React.FC<{
item: Song | AlbumListItem | Artist | PlaylistListItem
item: ListableItem
onPress?: (event: GestureResponderEvent) => void
showArt?: boolean
showStar?: boolean

View File

@ -1,17 +1,18 @@
import font from '@app/styles/font'
import React from 'react'
import { Text, View, StyleSheet } from 'react-native'
import { Text, View, StyleSheet, ViewStyle } from 'react-native'
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'
const NothingHere = React.memo<{
height?: number
width?: number
}>(({ height, width }) => {
style?: ViewStyle
}>(({ height, width, style }) => {
height = height || 200
width = width || 200
return (
<View style={[styles.container, { height, width }]}>
<View style={[styles.container, { height, width }, style]}>
<Icon name="music-rest-quarter" color={styles.text.color} size={width / 2} />
<Text style={[styles.text, { fontSize: width / 8 }]}>Nothing here...</Text>
</View>

View File

@ -67,6 +67,8 @@ export interface Song {
coverArt?: string
}
export type ListableItem = Song | AlbumListItem | Artist | PlaylistListItem
export type DownloadedSong = {
id: string
type: 'song'

View File

@ -1,12 +1,14 @@
import React from 'react'
import { Text, View, StyleSheet } from 'react-native'
import { BottomTabBarProps } from '@react-navigation/bottom-tabs'
import colors from '@app/styles/colors'
import FastImage from 'react-native-fast-image'
import NowPlayingBar from '@app/components/NowPlayingBar'
import PressableOpacity from '@app/components/PressableOpacity'
import font from '@app/styles/font'
import colors from '@app/styles/colors'
import dimensions from '@app/styles/dimensions'
import font from '@app/styles/font'
import { BottomTabBarProps } from '@react-navigation/bottom-tabs'
import { BottomTabNavigationEventMap } from '@react-navigation/bottom-tabs/lib/typescript/src/types'
import { NavigationHelpers, ParamListBase } from '@react-navigation/native'
import React from 'react'
import { StyleSheet, Text, View } from 'react-native'
import FastImage from 'react-native-fast-image'
type TabButtonImage = {
regular: number
@ -38,7 +40,7 @@ const BottomTabButton = React.memo<{
name: string
isFocused: boolean
img: TabButtonImage
navigation: any
navigation: NavigationHelpers<ParamListBase, BottomTabNavigationEventMap>
}>(({ routeKey, label, name, isFocused, img, navigation }) => {
const onPress = () => {
const event = navigation.emit({
@ -53,7 +55,6 @@ const BottomTabButton = React.memo<{
}
return (
// <PressableOpacity onPress={onPress} style={styles.button} ripple={true} rippleColor="rgba(100,100,100,0.18)">
<PressableOpacity onPress={onPress} style={styles.button}>
<FastImage
source={isFocused ? img.fill : img.regular}

View File

@ -1,47 +1,92 @@
import GradientScrollView from '@app/components/GradientScrollView'
import Header from '@app/components/Header'
import ListItem from '@app/components/ListItem'
import { searchResultsAtom, useUpdateSearchResults } from '@app/state/music'
import NothingHere from '@app/components/NothingHere'
import { ListableItem, SearchResults } from '@app/models/music'
import { searchResultsAtom, searchResultsUpdatingAtom, useUpdateSearchResults } from '@app/state/music'
import colors from '@app/styles/colors'
import font from '@app/styles/font'
import { useAtomValue } from 'jotai/utils'
import React, { useState } from 'react'
import { StatusBar, StyleSheet, View, TextInput } from 'react-native'
import debounce from 'lodash.debounce'
import React, { useCallback, useMemo, useState } from 'react'
import { ActivityIndicator, StatusBar, StyleSheet, TextInput, View } from 'react-native'
function getSubtitle(item: ListableItem) {
switch (item.itemType) {
case 'playlist':
return item.comment
case 'album':
case 'song':
return item.artist
default:
return undefined
}
}
const ResultsCategory = React.memo<{
name: string
items: ListableItem[]
}>(({ name, items }) => {
if (items.length === 0) {
return <></>
}
return (
<>
<Header>{name}</Header>
{items.map(a => (
<ListItem key={a.id} item={a} showArt={true} showStar={false} subtitle={getSubtitle(a)} />
))}
</>
)
})
const Results = React.memo<{
results: SearchResults
}>(({ results }) => {
return (
<>
<ResultsCategory name="Artists" items={results.artists} />
<ResultsCategory name="Albums" items={results.albums} />
<ResultsCategory name="Songs" items={results.songs} />
</>
)
})
const Search = () => {
const [text, setText] = useState('')
const updateSearch = useUpdateSearchResults()
const updating = useAtomValue(searchResultsUpdatingAtom)
const results = useAtomValue(searchResultsAtom)
const onSubmitEditing = () => {
console.log(text)
updateSearch(text)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
const debouncedonUpdateSearch = useMemo(() => debounce(updateSearch, 400), [])
const onChangeText = useCallback(
(value: string) => {
setText(value)
debouncedonUpdateSearch(value)
},
[setText, debouncedonUpdateSearch],
)
const resultsCount = results.albums.length + results.artists.length + results.songs.length
return (
<GradientScrollView style={styles.scroll} contentContainerStyle={styles.scrollContentContainer}>
<View style={styles.content}>
<TextInput
style={styles.textInput}
placeholder="Search"
placeholderTextColor="grey"
selectionColor={colors.text.secondary}
value={text}
onChangeText={setText}
onSubmitEditing={onSubmitEditing}
/>
<Header>Artists</Header>
{results.artists.map(a => (
<ListItem key={a.id} item={a} showArt={true} showStar={false} />
))}
<Header>Albums</Header>
{results.albums.map(a => (
<ListItem key={a.id} item={a} showArt={true} showStar={false} subtitle={a.artist} />
))}
<Header>Songs</Header>
{results.songs.map(a => (
<ListItem key={a.id} item={a} showArt={true} showStar={false} subtitle={a.artist} />
))}
<View style={styles.inputBar}>
<TextInput
style={styles.textInput}
placeholder="Search"
placeholderTextColor="grey"
selectionColor={colors.text.secondary}
value={text}
onChangeText={onChangeText}
/>
<ActivityIndicator animating={updating} size="small" color={colors.text.secondary} style={styles.activity} />
</View>
{resultsCount > 0 ? <Results results={results} /> : <NothingHere style={styles.noResults} />}
</View>
</GradientScrollView>
)
@ -56,14 +101,28 @@ const styles = StyleSheet.create({
},
content: {
paddingHorizontal: 20,
alignItems: 'stretch',
},
inputBar: {
justifyContent: 'center',
},
activity: {
position: 'absolute',
right: 16,
bottom: 15,
},
textInput: {
width: '100%',
backgroundColor: '#515151',
fontFamily: font.regular,
fontSize: 18,
color: colors.text.primary,
marginTop: 20,
paddingHorizontal: 12,
paddingRight: 46,
},
noResults: {
width: '100%',
},
itemText: {
color: colors.text.primary,

View File

@ -114,7 +114,7 @@ export const useUpdateSearchResults = () => {
}
return async (query: string) => {
if (updating) {
if (updating || query.length < 2) {
return
}
setUpdating(true)

View File

@ -18,6 +18,7 @@
"@react-navigation/native": "^5.9.4",
"fast-deep-equal": "^3.1.3",
"jotai": "^1.1.0",
"lodash.debounce": "^4.0.8",
"md5": "^2.3.0",
"react": "17.0.1",
"react-native": "0.64.1",
@ -41,6 +42,7 @@
"@babel/runtime": "^7.12.5",
"@react-native-community/eslint-config": "^2.0.0",
"@types/jest": "^26.0.23",
"@types/lodash.debounce": "^4.0.6",
"@types/md5": "^2.3.0",
"@types/react-native": "^0.64.5",
"@types/react-native-vector-icons": "^6.4.7",

View File

@ -1293,6 +1293,18 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
"@types/lodash.debounce@^4.0.6":
version "4.0.6"
resolved "https://registry.yarnpkg.com/@types/lodash.debounce/-/lodash.debounce-4.0.6.tgz#c5a2326cd3efc46566c47e4c0aa248dc0ee57d60"
integrity sha512-4WTmnnhCfDvvuLMaF3KV4Qfki93KebocUF45msxhYyjMttZDQYzHkO639ohhk8+oco2cluAFL3t5+Jn4mleylQ==
dependencies:
"@types/lodash" "*"
"@types/lodash@*":
version "4.14.171"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.171.tgz#f01b3a5fe3499e34b622c362a46a609fdb23573b"
integrity sha512-7eQ2xYLLI/LsicL2nejW9Wyko3lcpN6O/z0ZLHrEQsg280zIdCv1t/0m6UtBjUHokCGBQ3gYTbHzDkZ1xOBwwg==
"@types/md5@^2.3.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@types/md5/-/md5-2.3.0.tgz#3b6a623091160f4dc75be3173e25f2110dc3fa1f"