reworked fetchAlbumList to remove ui state

refactored home screen to use new method
i broke playing songs somehow, JS thread goes into a loop
This commit is contained in:
austinried
2022-03-18 13:04:20 +09:00
parent 76306f1558
commit 98ef0d1d44
8 changed files with 381 additions and 160 deletions

View File

@@ -3,17 +3,21 @@ import CoverArt from '@app/components/CoverArt'
import GradientScrollView from '@app/components/GradientScrollView'
import Header from '@app/components/Header'
import NothingHere from '@app/components/NothingHere'
import { useFetchPaginatedList } from '@app/hooks/list'
import { useActiveServerRefresh } from '@app/hooks/server'
import { AlbumListItem } from '@app/models/music'
import { Album } from '@app/state/library'
import { selectMusic } from '@app/state/music'
import { selectSettings } from '@app/state/settings'
import { useStore } from '@app/state/store'
import { Store, useStore } from '@app/state/store'
import colors from '@app/styles/colors'
import font from '@app/styles/font'
import { GetAlbumListType } from '@app/subsonic/params'
import { GetAlbumList2Params, GetAlbumList2TypeBase, GetAlbumListType } from '@app/subsonic/params'
import { useNavigation } from '@react-navigation/native'
import React, { useCallback } from 'react'
import produce from 'immer'
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'
import { RefreshControl, ScrollView, StatusBar, StyleSheet, Text, View } from 'react-native'
import create from 'zustand'
const titles: { [key in GetAlbumListType]?: string } = {
recent: 'Recently Played',
@@ -49,9 +53,11 @@ const AlbumItem = React.memo<{
})
const Category = React.memo<{
name?: string
data: AlbumListItem[]
}>(({ name, data }) => {
type: string
}>(({ type }) => {
const list = useHomeStore(useCallback(store => store.lists[type] || [], [type]))
const albums = useStore(useCallback(store => list.map(id => store.entities.albums[id]), [list]))
const Albums = () => (
<ScrollView
horizontal={true}
@@ -59,7 +65,7 @@ const Category = React.memo<{
overScrollMode={'never'}
style={styles.artScroll}
contentContainerStyle={styles.artScrollContent}>
{data.map(album => (
{albums.map(album => (
<AlbumItem key={album.id} album={album} />
))}
</ScrollView>
@@ -73,24 +79,55 @@ const Category = React.memo<{
return (
<View style={styles.category}>
<Header style={styles.header}>{name}</Header>
{data.length > 0 ? <Albums /> : <Nothing />}
<Header style={styles.header}>{titles[type as GetAlbumListType] || ''}</Header>
{albums.length > 0 ? <Albums /> : <Nothing />}
</View>
)
})
interface HomeState {
lists: { [type: string]: string[] }
setList: (type: string, list: string[]) => void
}
const useHomeStore = create<HomeState>((set, get) => ({
lists: {},
setList: (type, list) => {
set(
produce<HomeState>(state => {
state.lists[type] = list
}),
)
},
}))
const Home = () => {
const [refreshing, setRefreshing] = useState(false)
const types = useStore(selectSettings.homeLists)
const lists = useStore(selectMusic.homeLists)
const updating = useStore(selectMusic.homeListsUpdating)
const update = useStore(selectMusic.fetchHomeLists)
const clear = useStore(selectMusic.clearHomeLists)
const fetchAlbumList = useStore(store => store.fetchLibraryAlbumList)
const setList = useHomeStore(store => store.setList)
const refresh = useCallback(async () => {
setRefreshing(true)
await Promise.all(
types.map(async type => {
console.log('fetch', type)
const ids = await fetchAlbumList({ type: type as GetAlbumList2TypeBase, size: 20, offset: 0 })
console.log('set', type)
setList(type, ids)
}),
)
setRefreshing(false)
}, [fetchAlbumList, setList, types])
useActiveServerRefresh(
useCallback(() => {
clear()
update()
}, [clear, update]),
types.forEach(type => setList(type, []))
refresh()
}, [refresh, setList, types]),
)
return (
@@ -99,15 +136,15 @@ const Home = () => {
contentContainerStyle={styles.scrollContentContainer}
refreshControl={
<RefreshControl
refreshing={updating}
onRefresh={update}
refreshing={refreshing}
onRefresh={refresh}
colors={[colors.accent, colors.accentLow]}
progressViewOffset={StatusBar.currentHeight}
/>
}>
<View style={styles.content}>
{types.map(type => (
<Category key={type} name={titles[type as GetAlbumListType]} data={type in lists ? lists[type] : []} />
<Category key={type} type={type} />
))}
</View>
</GradientScrollView>

View File

@@ -2,15 +2,16 @@ import { AlbumContextPressable } from '@app/components/ContextMenu'
import CoverArt from '@app/components/CoverArt'
import FilterButton, { OptionData } from '@app/components/FilterButton'
import GradientFlatList from '@app/components/GradientFlatList'
import { useFetchPaginatedList2 } from '@app/hooks/list'
import { useFetchPaginatedList } from '@app/hooks/list'
import { Album, AlbumListItem } from '@app/models/music'
import { selectSettings } from '@app/state/settings'
import { Store, useStore } from '@app/state/store'
import colors from '@app/styles/colors'
import font from '@app/styles/font'
import { GetAlbumList2Type } from '@app/subsonic/params'
import { GetAlbumList2Params, GetAlbumList2Type } from '@app/subsonic/params'
import { useNavigation } from '@react-navigation/native'
import React, { useEffect } from 'react'
import pick from 'lodash.pick'
import React, { useCallback, useEffect } from 'react'
import { StyleSheet, Text, useWindowDimensions, View } from 'react-native'
const AlbumItem = React.memo<{
@@ -55,35 +56,57 @@ const filterOptions: OptionData[] = [
// { text: 'By Genre...', value: 'byGenre' },
]
const selectAlbumList = (store: Store) => {
return Object.values(store.entities.albumsList)
.flat()
.map(id => store.entities.albums[id])
}
const AlbumsList = () => {
const list = useStore(selectAlbumList)
const fetchAlbumsNextPage = useStore(store => store.fetchLibraryAlbumsNextPage)
const resetAlbumsList = useStore(store => store.resetLibraryAlbumsList)
const { refreshing, refresh, fetchNextPage } = useFetchPaginatedList2(fetchAlbumsNextPage, resetAlbumsList)
const filter = useStore(selectSettings.libraryAlbumFilter)
const setFilter = useStore(selectSettings.setLibraryAlbumFilter)
const fetchAlbumList = useStore(store => store.fetchLibraryAlbumList)
const fetchPage = useCallback(
(size: number, offset: number) => {
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
}
return fetchAlbumList(params)
},
[fetchAlbumList, filter.fromYear, filter.genre, filter.toYear, filter.type],
)
const { list, refreshing, refresh, fetchNextPage } = useFetchPaginatedList(fetchPage, 300)
const albums = useStore(useCallback(store => list.map(id => store.entities.albums[id]), [list]))
const layout = useWindowDimensions()
const size = layout.width / 3 - styles.itemWrapper.marginHorizontal * 2
const height = size + 36
useEffect(() => {
refresh()
}, [refresh, filter])
return (
<View style={styles.container}>
<GradientFlatList
data={list.map(album => ({ album, size, height }))}
data={albums.map(album => ({ album, size, height }))}
renderItem={AlbumListRenderItem}
keyExtractor={item => item.album.id}
numColumns={3}

View File

@@ -6,12 +6,13 @@ import ListItem from '@app/components/ListItem'
import ListPlayerControls from '@app/components/ListPlayerControls'
import { useCoverArtFile } from '@app/hooks/cache'
import { useAlbumWithSongs, usePlaylistWithSongs } from '@app/hooks/music'
import { AlbumWithSongs, PlaylistWithSongs, Song } from '@app/models/music'
import { Album, AlbumWithSongs, PlaylistListItem, PlaylistWithSongs, Song } from '@app/models/music'
import { useStore } from '@app/state/store'
import { selectTrackPlayer } from '@app/state/trackplayer'
import colors from '@app/styles/colors'
import font from '@app/styles/font'
import React, { useState } from 'react'
import pick from 'lodash.pick'
import React, { useCallback, useEffect, useState } from 'react'
import { ActivityIndicator, StyleSheet, Text, View } from 'react-native'
type SongListType = 'album' | 'playlist'
@@ -46,18 +47,19 @@ const SongRenderItem: React.FC<{
const SongListDetails = React.memo<{
title: string
type: SongListType
songList?: AlbumWithSongs | PlaylistWithSongs
songList?: Album | PlaylistListItem
songs?: Song[]
subtitle?: string
}>(({ title, songList, subtitle, type }) => {
}>(({ title, songList, songs, subtitle, type }) => {
const coverArtFile = useCoverArtFile(songList?.coverArt, 'thumbnail')
const [headerColor, setHeaderColor] = useState<string | undefined>(undefined)
const setQueue = useStore(selectTrackPlayer.setQueue)
if (!songList) {
if (!songList || !songs) {
return <SongListDetailsFallback />
}
const _songs = [...songList.songs]
const _songs = [...songs]
let typeName = ''
if (type === 'album') {
@@ -106,7 +108,7 @@ const SongListDetails = React.memo<{
<CoverArt type="cover" size="original" coverArt={songList.coverArt} style={styles.cover} />
<Text style={styles.title}>{songList.name}</Text>
{subtitle ? <Text style={styles.subtitle}>{subtitle}</Text> : <></>}
{songList.songs.length > 0 && (
{songs.length > 0 && (
<ListPlayerControls
style={styles.controls}
songs={_songs}
@@ -135,11 +137,32 @@ const AlbumView = React.memo<{
id: string
title: string
}>(({ id, title }) => {
const album = useAlbumWithSongs(id)
// const album = useAlbumWithSongs(id)
const album = useStore(useCallback(store => store.entities.albums[id], [id]))
const songs = useStore(
useCallback(
store => {
const ids = store.entities.albumSongs[id]
return ids ? ids.map(i => store.entities.songs[i]) : undefined
},
[id],
),
)
const fetchAlbum = useStore(store => store.fetchLibraryAlbum)
useEffect(() => {
if (!album || !songs) {
fetchAlbum(id)
}
}, [album, fetchAlbum, id, songs])
return (
<SongListDetails
title={title}
songList={album}
songs={songs}
subtitle={(album?.artist || '') + (album?.year ? ' • ' + album?.year : '')}
type="album"
/>