mirror of
https://github.com/austinried/subtracks.git
synced 2026-02-10 15:02:42 +01:00
remove unused + (slightly) rework search
This commit is contained in:
@@ -21,12 +21,6 @@ export interface Album extends AlbumListItem {
|
|||||||
year?: number
|
year?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SearchResults {
|
|
||||||
artists: Artist[]
|
|
||||||
albums: AlbumListItem[]
|
|
||||||
songs: Song[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PlaylistListItem {
|
export interface PlaylistListItem {
|
||||||
itemType: 'playlist'
|
itemType: 'playlist'
|
||||||
id: string
|
id: string
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ import ListItem from '@app/components/ListItem'
|
|||||||
import NothingHere from '@app/components/NothingHere'
|
import NothingHere from '@app/components/NothingHere'
|
||||||
import TextInput from '@app/components/TextInput'
|
import TextInput from '@app/components/TextInput'
|
||||||
import { useActiveServerRefresh } from '@app/hooks/server'
|
import { useActiveServerRefresh } from '@app/hooks/server'
|
||||||
import { ListableItem, SearchResults, Song } from '@app/models/music'
|
import { Album, Artist, mapById, SearchResults, Song } from '@app/state/library'
|
||||||
import { selectMusic } from '@app/state/music'
|
import { useStore, useStoreDeep } from '@app/state/store'
|
||||||
import { useStore } from '@app/state/store'
|
|
||||||
import { selectTrackPlayer } from '@app/state/trackplayer'
|
import { selectTrackPlayer } from '@app/state/trackplayer'
|
||||||
import colors from '@app/styles/colors'
|
import colors from '@app/styles/colors'
|
||||||
import font from '@app/styles/font'
|
import font from '@app/styles/font'
|
||||||
@@ -42,8 +41,27 @@ const SongItem = React.memo<{ item: Song }>(({ item }) => {
|
|||||||
const ResultsCategory = React.memo<{
|
const ResultsCategory = React.memo<{
|
||||||
name: string
|
name: string
|
||||||
query: string
|
query: string
|
||||||
items: ListableItem[]
|
ids: string[]
|
||||||
}>(({ name, query, items }) => {
|
type: 'artist' | 'album' | 'song'
|
||||||
|
}>(({ name, query, type, ids }) => {
|
||||||
|
const items: (Album | Artist | Song)[] = useStoreDeep(
|
||||||
|
useCallback(
|
||||||
|
store => {
|
||||||
|
switch (type) {
|
||||||
|
case 'album':
|
||||||
|
return mapById(store.entities.albums, ids)
|
||||||
|
case 'artist':
|
||||||
|
return mapById(store.entities.artists, ids)
|
||||||
|
case 'song':
|
||||||
|
return mapById(store.entities.songs, ids)
|
||||||
|
default:
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[ids, type],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
|
|
||||||
if (items.length === 0) {
|
if (items.length === 0) {
|
||||||
@@ -54,8 +72,8 @@ const ResultsCategory = React.memo<{
|
|||||||
<>
|
<>
|
||||||
<Header>{name}</Header>
|
<Header>{name}</Header>
|
||||||
{items.map(a =>
|
{items.map(a =>
|
||||||
a.itemType === 'song' ? (
|
type === 'song' ? (
|
||||||
<SongItem key={a.id} item={a} />
|
<SongItem key={a.id} item={a as Song} />
|
||||||
) : (
|
) : (
|
||||||
<ListItem key={a.id} item={a} showArt={true} showStar={false} />
|
<ListItem key={a.id} item={a} showArt={true} showStar={false} />
|
||||||
),
|
),
|
||||||
@@ -78,15 +96,15 @@ const Results = React.memo<{
|
|||||||
}>(({ results, query }) => {
|
}>(({ results, query }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ResultsCategory name="Artists" query={query} items={results.artists} />
|
<ResultsCategory name="Artists" query={query} type={'artist'} ids={results.artists} />
|
||||||
<ResultsCategory name="Albums" query={query} items={results.albums} />
|
<ResultsCategory name="Albums" query={query} type={'album'} ids={results.albums} />
|
||||||
<ResultsCategory name="Songs" query={query} items={results.songs} />
|
<ResultsCategory name="Songs" query={query} type={'song'} ids={results.songs} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const Search = () => {
|
const Search = () => {
|
||||||
const fetchSearchResults = useStore(selectMusic.fetchSearchResults)
|
const fetchSearchResults = useStore(store => store.fetchLibrarySearchResults)
|
||||||
const [results, setResults] = useState<SearchResults>({ artists: [], albums: [], songs: [] })
|
const [results, setResults] = useState<SearchResults>({ artists: [], albums: [], songs: [] })
|
||||||
const [refreshing, setRefreshing] = useState(false)
|
const [refreshing, setRefreshing] = useState(false)
|
||||||
const [text, setText] = useState('')
|
const [text, setText] = useState('')
|
||||||
@@ -118,7 +136,7 @@ const Search = () => {
|
|||||||
() =>
|
() =>
|
||||||
debounce(async (query: string) => {
|
debounce(async (query: string) => {
|
||||||
setRefreshing(true)
|
setRefreshing(true)
|
||||||
setResults(await fetchSearchResults(query))
|
setResults(await fetchSearchResults({ query, albumCount: 5, artistCount: 5, songCount: 5 }))
|
||||||
setRefreshing(false)
|
setRefreshing(false)
|
||||||
}, 400),
|
}, 400),
|
||||||
[fetchSearchResults],
|
[fetchSearchResults],
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import GradientFlatList from '@app/components/GradientFlatList'
|
import GradientFlatList from '@app/components/GradientFlatList'
|
||||||
import ListItem from '@app/components/ListItem'
|
import ListItem from '@app/components/ListItem'
|
||||||
import { useFetchPaginatedList } from '@app/hooks/list'
|
import { useFetchPaginatedList } from '@app/hooks/list'
|
||||||
import { AlbumListItem, Artist, Song } from '@app/models/music'
|
import { Album, Artist, Song, mapById } from '@app/state/library'
|
||||||
import { selectMusic } from '@app/state/music'
|
import { useStore, useStoreDeep } from '@app/state/store'
|
||||||
import { useStore } from '@app/state/store'
|
|
||||||
import { selectTrackPlayer } from '@app/state/trackplayer'
|
import { selectTrackPlayer } from '@app/state/trackplayer'
|
||||||
|
import { Search3Params } from '@app/subsonic/params'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import React, { useCallback, useEffect } from 'react'
|
import React, { useCallback, useEffect } from 'react'
|
||||||
import { StyleSheet } from 'react-native'
|
import { StyleSheet } from 'react-native'
|
||||||
|
|
||||||
type SearchListItemType = AlbumListItem | Song | Artist
|
type SearchListItemType = Album | Song | Artist
|
||||||
|
|
||||||
const ResultsListItem: React.FC<{ item: SearchListItemType }> = ({ item }) => {
|
const ResultsListItem: React.FC<{ item: SearchListItemType }> = ({ item }) => {
|
||||||
const setQueue = useStore(selectTrackPlayer.setQueue)
|
const setQueue = useStore(selectTrackPlayer.setQueue)
|
||||||
@@ -40,11 +40,28 @@ const SearchResultsView: React.FC<{
|
|||||||
type: 'album' | 'artist' | 'song'
|
type: 'album' | 'artist' | 'song'
|
||||||
}> = ({ query, type }) => {
|
}> = ({ query, type }) => {
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
const fetchSearchResults = useStore(selectMusic.fetchSearchResults)
|
const fetchSearchResults = useStore(store => store.fetchLibrarySearchResults)
|
||||||
const { list, refreshing, refresh, fetchNextPage } = useFetchPaginatedList<SearchListItemType>(
|
const { list, refreshing, refresh, fetchNextPage } = useFetchPaginatedList(
|
||||||
useCallback(
|
useCallback(
|
||||||
(size, offset) =>
|
async (size, offset) => {
|
||||||
fetchSearchResults(query, type, size, offset).then(results => {
|
const params: Search3Params = { query }
|
||||||
|
if (type === 'album') {
|
||||||
|
params.albumCount = size
|
||||||
|
params.albumOffset = offset
|
||||||
|
} else if (type === 'artist') {
|
||||||
|
params.artistCount = size
|
||||||
|
params.artistOffset = offset
|
||||||
|
} else if (type === 'song') {
|
||||||
|
params.songCount = size
|
||||||
|
params.songOffset = offset
|
||||||
|
} else {
|
||||||
|
params.albumCount = 5
|
||||||
|
params.artistCount = 5
|
||||||
|
params.songCount = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = await fetchSearchResults(params)
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'album':
|
case 'album':
|
||||||
return results.albums
|
return results.albums
|
||||||
@@ -55,12 +72,30 @@ const SearchResultsView: React.FC<{
|
|||||||
default:
|
default:
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}),
|
},
|
||||||
[fetchSearchResults, query, type],
|
[fetchSearchResults, query, type],
|
||||||
),
|
),
|
||||||
100,
|
100,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const items: SearchListItemType[] = useStoreDeep(
|
||||||
|
useCallback(
|
||||||
|
store => {
|
||||||
|
switch (type) {
|
||||||
|
case 'album':
|
||||||
|
return mapById(store.entities.albums, list)
|
||||||
|
case 'artist':
|
||||||
|
return mapById(store.entities.artists, list)
|
||||||
|
case 'song':
|
||||||
|
return mapById(store.entities.songs, list)
|
||||||
|
default:
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[list, type],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
navigation.setOptions({
|
navigation.setOptions({
|
||||||
title: `Search: "${query}"`,
|
title: `Search: "${query}"`,
|
||||||
@@ -70,7 +105,7 @@ const SearchResultsView: React.FC<{
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<GradientFlatList
|
<GradientFlatList
|
||||||
data={list}
|
data={items}
|
||||||
renderItem={SearchResultsRenderItem}
|
renderItem={SearchResultsRenderItem}
|
||||||
keyExtractor={(item, i) => i.toString()}
|
keyExtractor={(item, i) => i.toString()}
|
||||||
onRefresh={refresh}
|
onRefresh={refresh}
|
||||||
|
|||||||
@@ -1,23 +1,10 @@
|
|||||||
import { AlbumListItem, Artist, PlaylistListItem, SearchResults, StarrableItemType } from '@app/models/music'
|
import { StarrableItemType } from '@app/models/music'
|
||||||
import { Store } from '@app/state/store'
|
import { Store } from '@app/state/store'
|
||||||
import { GetAlbumList2Params, Search3Params, StarParams } from '@app/subsonic/params'
|
import { StarParams } from '@app/subsonic/params'
|
||||||
import produce from 'immer'
|
import produce from 'immer'
|
||||||
import { GetState, SetState } from 'zustand'
|
import { GetState, SetState } from 'zustand'
|
||||||
|
|
||||||
export type MusicSlice = {
|
export type MusicSlice = {
|
||||||
//
|
|
||||||
// lists-style state
|
|
||||||
//
|
|
||||||
fetchArtists: (size?: number, offset?: number) => Promise<Artist[]>
|
|
||||||
fetchPlaylists: () => Promise<PlaylistListItem[]>
|
|
||||||
fetchAlbums: () => Promise<AlbumListItem[]>
|
|
||||||
fetchSearchResults: (
|
|
||||||
query: string,
|
|
||||||
type?: 'album' | 'song' | 'artist',
|
|
||||||
size?: number,
|
|
||||||
offset?: number,
|
|
||||||
) => Promise<SearchResults>
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// actions, etc.
|
// actions, etc.
|
||||||
//
|
//
|
||||||
@@ -33,11 +20,6 @@ export type MusicSlice = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const selectMusic = {
|
export const selectMusic = {
|
||||||
fetchArtists: (store: MusicSlice) => store.fetchArtists,
|
|
||||||
fetchPlaylists: (store: MusicSlice) => store.fetchPlaylists,
|
|
||||||
fetchAlbums: (store: MusicSlice) => store.fetchAlbums,
|
|
||||||
fetchSearchResults: (store: MusicSlice) => store.fetchSearchResults,
|
|
||||||
|
|
||||||
starItem: (store: MusicSlice) => store.starItem,
|
starItem: (store: MusicSlice) => store.starItem,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,141 +37,6 @@ function reduceStarred(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const createMusicSlice = (set: SetState<Store>, get: GetState<Store>): MusicSlice => ({
|
export const createMusicSlice = (set: SetState<Store>, get: GetState<Store>): MusicSlice => ({
|
||||||
fetchArtists: async () => {
|
|
||||||
const client = get().client
|
|
||||||
if (!client) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await client.getArtists()
|
|
||||||
const artists = response.data.artists.map(get().mapArtistID3toArtist)
|
|
||||||
|
|
||||||
set(
|
|
||||||
produce<MusicSlice>(state => {
|
|
||||||
state.starredArtists = reduceStarred(state.starredArtists, artists)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
return artists
|
|
||||||
} catch {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
fetchPlaylists: async () => {
|
|
||||||
const client = get().client
|
|
||||||
if (!client) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await client.getPlaylists()
|
|
||||||
return response.data.playlists.map(get().mapPlaylistListItem)
|
|
||||||
} catch {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
fetchAlbums: async (size = 500, offset = 0) => {
|
|
||||||
const client = get().client
|
|
||||||
if (!client) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const filter = get().settings.screens.library.albums
|
|
||||||
|
|
||||||
let params: GetAlbumList2Params
|
|
||||||
switch (filter.type) {
|
|
||||||
case 'byYear':
|
|
||||||
params = {
|
|
||||||
size,
|
|
||||||
offset,
|
|
||||||
type: filter.type,
|
|
||||||
fromYear: filter.fromYear,
|
|
||||||
toYear: filter.toYear,
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'byGenre':
|
|
||||||
params = {
|
|
||||||
size,
|
|
||||||
offset,
|
|
||||||
type: filter.type,
|
|
||||||
genre: filter.genre,
|
|
||||||
}
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
params = {
|
|
||||||
size,
|
|
||||||
offset,
|
|
||||||
type: filter.type,
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await client.getAlbumList2(params)
|
|
||||||
const albums = response.data.albums.map(get().mapAlbumID3toAlbumListItem)
|
|
||||||
|
|
||||||
set(
|
|
||||||
produce<MusicSlice>(state => {
|
|
||||||
state.starredAlbums = reduceStarred(state.starredAlbums, albums)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
return albums
|
|
||||||
} catch {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
fetchSearchResults: async (query, type, size, offset) => {
|
|
||||||
if (query.length < 2) {
|
|
||||||
return { artists: [], albums: [], songs: [] }
|
|
||||||
}
|
|
||||||
|
|
||||||
const client = get().client
|
|
||||||
if (!client) {
|
|
||||||
return { artists: [], albums: [], songs: [] }
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const params: Search3Params = { query }
|
|
||||||
if (type === 'album') {
|
|
||||||
params.albumCount = size
|
|
||||||
params.albumOffset = offset
|
|
||||||
} else if (type === 'artist') {
|
|
||||||
params.artistCount = size
|
|
||||||
params.artistOffset = offset
|
|
||||||
} else if (type === 'song') {
|
|
||||||
params.songCount = size
|
|
||||||
params.songOffset = offset
|
|
||||||
} else {
|
|
||||||
params.albumCount = 5
|
|
||||||
params.artistCount = 5
|
|
||||||
params.songCount = 5
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await client.search3(params)
|
|
||||||
|
|
||||||
const artists = response.data.artists.map(get().mapArtistID3toArtist)
|
|
||||||
const albums = response.data.albums.map(get().mapAlbumID3toAlbumListItem)
|
|
||||||
const songs = await get().mapChildrenToSongs(response.data.songs)
|
|
||||||
|
|
||||||
set(
|
|
||||||
produce<MusicSlice>(state => {
|
|
||||||
state.starredSongs = reduceStarred(state.starredSongs, songs)
|
|
||||||
state.starredArtists = reduceStarred(state.starredArtists, artists)
|
|
||||||
state.starredAlbums = reduceStarred(state.starredAlbums, albums)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
return { artists, albums, songs }
|
|
||||||
} catch {
|
|
||||||
return { artists: [], albums: [], songs: [] }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
starredSongs: {},
|
starredSongs: {},
|
||||||
starredAlbums: {},
|
starredAlbums: {},
|
||||||
starredArtists: {},
|
starredArtists: {},
|
||||||
|
|||||||
6
index.js
6
index.js
@@ -6,6 +6,12 @@ enableScreens()
|
|||||||
|
|
||||||
import { LogBox } from 'react-native'
|
import { LogBox } from 'react-native'
|
||||||
LogBox.ignoreLogs(["The action 'POP_TO_TOP'"])
|
LogBox.ignoreLogs(["The action 'POP_TO_TOP'"])
|
||||||
|
LogBox.ignoreLogs([
|
||||||
|
'`new NativeEventEmitter()` was called with a non-null argument without the required `addListener` method.',
|
||||||
|
])
|
||||||
|
LogBox.ignoreLogs([
|
||||||
|
'`new NativeEventEmitter()` was called with a non-null argument without the required `removeListeners` method.',
|
||||||
|
])
|
||||||
|
|
||||||
import { AppRegistry } from 'react-native'
|
import { AppRegistry } from 'react-native'
|
||||||
import App from '@app/App'
|
import App from '@app/App'
|
||||||
|
|||||||
Reference in New Issue
Block a user