diff --git a/app/components/BackgroundHeaderFlatList.tsx b/app/components/BackgroundHeaderFlatList.tsx index 87c07f8..e419216 100644 --- a/app/components/BackgroundHeaderFlatList.tsx +++ b/app/components/BackgroundHeaderFlatList.tsx @@ -37,7 +37,7 @@ function BackgroundHeaderFlatList(props: BackgroundHeaderFlatListProp } ListHeaderComponentStyle={[headerStyle]} - ListEmptyComponent={} + ListEmptyComponent={props.ListEmptyComponent || } /> ) } diff --git a/app/components/CoverArt.tsx b/app/components/CoverArt.tsx index 5260de4..ab5dcc0 100644 --- a/app/components/CoverArt.tsx +++ b/app/components/CoverArt.tsx @@ -1,5 +1,5 @@ import { useArtistArtFile, useCoverArtFile } from '@app/hooks/cache' -import { CacheFile, CacheImageSize, CacheRequest } from '@app/models/cache' +import { CacheFile, CacheRequest } from '@app/models/cache' import colors from '@app/styles/colors' import React, { useState } from 'react' import { @@ -18,7 +18,6 @@ type BaseProps = { imageStyle?: ImageStyle resizeMode?: ImageResizeMode round?: boolean - size?: CacheImageSize } type ArtistCoverArtProps = BaseProps & { @@ -63,13 +62,13 @@ const ImageSource = React.memo<{ cache?: { file?: CacheFile; request?: CacheRequ ) const ArtistImage = React.memo(props => { - const cache = useArtistArtFile(props.artistId, props.size) + const cache = useArtistArtFile(props.artistId) return }) const CoverArtImage = React.memo(props => { - const cache = useCoverArtFile(props.coverArt, props.size) + const cache = useCoverArtFile(props.coverArt) return }) diff --git a/app/components/ListPlayerControls.tsx b/app/components/ListPlayerControls.tsx index 3a94983..3e0e6a3 100644 --- a/app/components/ListPlayerControls.tsx +++ b/app/components/ListPlayerControls.tsx @@ -36,11 +36,14 @@ const ListPlayerControls = React.memo<{ diff --git a/app/hooks/cache.ts b/app/hooks/cache.ts index 3fbd6af..a7f47d8 100644 --- a/app/hooks/cache.ts +++ b/app/hooks/cache.ts @@ -1,4 +1,4 @@ -import { CacheImageSize, CacheItemTypeKey } from '@app/models/cache' +import { CacheItemTypeKey } from '@app/models/cache' import { selectCache } from '@app/state/cache' import { selectSettings } from '@app/state/settings' import { Store, useStore, useStoreDeep } from '@app/state/store' @@ -35,20 +35,15 @@ const useFileRequest = (key: CacheItemTypeKey, id: string) => { return { file, request } } -export const useCoverArtFile = (coverArt = '-1', size: CacheImageSize = 'thumbnail') => { - const type: CacheItemTypeKey = size === 'original' ? 'coverArt' : 'coverArtThumb' +export const useCoverArtFile = (coverArt = '-1') => { + const type: CacheItemTypeKey = 'coverArt' const { file, request } = useFileRequest(type, coverArt) const client = useStore(selectSettings.client) const cacheItem = useStore(selectCache.cacheItem) useEffect(() => { if (!file && client) { - cacheItem(type, coverArt, () => - client.getCoverArtUri({ - id: coverArt, - size: type === 'coverArtThumb' ? '256' : undefined, - }), - ) + cacheItem(type, coverArt, () => client.getCoverArtUri({ id: coverArt })) } // intentionally leaving file out so it doesn't re-render if the request fails // eslint-disable-next-line react-hooks/exhaustive-deps @@ -57,8 +52,8 @@ export const useCoverArtFile = (coverArt = '-1', size: CacheImageSize = 'thumbna return { file, request } } -export const useArtistArtFile = (artistId: string, size: CacheImageSize = 'thumbnail') => { - const type: CacheItemTypeKey = size === 'original' ? 'artistArt' : 'artistArtThumb' +export const useArtistArtFile = (artistId: string) => { + const type: CacheItemTypeKey = 'artistArt' const fetchArtistInfo = useStore(store => store.fetchLibraryArtistInfo) const artistInfo = useStoreDeep(store => store.entities.artistInfo[artistId]) const { file, request } = useFileRequest(type, artistId) @@ -70,10 +65,8 @@ export const useArtistArtFile = (artistId: string, size: CacheImageSize = 'thumb return } - if (!file) { - cacheItem(type, artistId, async () => { - return type === 'artistArtThumb' ? artistInfo?.smallImageUrl : artistInfo?.largeImageUrl - }) + if (!file && artistInfo.largeImageUrl) { + cacheItem(type, artistId, artistInfo.largeImageUrl) } // intentionally leaving file out so it doesn't re-render if the request fails // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/app/screens/ArtistView.tsx b/app/screens/ArtistView.tsx index 325961b..7b3d537 100644 --- a/app/screens/ArtistView.tsx +++ b/app/screens/ArtistView.tsx @@ -6,6 +6,7 @@ import Header from '@app/components/Header' import HeaderBar from '@app/components/HeaderBar' import ListItem from '@app/components/ListItem' import { Album, Song } from '@app/models/music' +import { mapById } from '@app/state/library' import { useStore, useStoreDeep } from '@app/state/store' import { selectTrackPlayer } from '@app/state/trackplayer' import colors from '@app/styles/colors' @@ -13,7 +14,6 @@ import dimensions from '@app/styles/dimensions' import font from '@app/styles/font' import { useLayout } from '@react-native-community/hooks' import { useNavigation } from '@react-navigation/native' -import pick from 'lodash.pick' import React, { useCallback, useEffect } from 'react' import { ActivityIndicator, StyleSheet, Text, View } from 'react-native' import { useAnimatedScrollHandler, useAnimatedStyle, useSharedValue } from 'react-native-reanimated' @@ -52,7 +52,7 @@ const TopSongs = React.memo<{ return ( <>
Top Songs
- {songs.map((s, i) => ( + {songs.slice(0, 5).map((s, i) => ( (({ id }) => { - const albums = useStoreDeep( - useCallback( - store => { - const ids = store.entities.artistAlbums[id] - return ids ? pick(store.entities.albums, ids) : undefined - }, - [id], - ), - ) - - const fetchArtist = useStore(store => store.fetchLibraryArtist) + albums: Album[] +}>(({ albums }) => { const albumsLayout = useLayout() - useEffect(() => { - if (!albums) { - fetchArtist(id) - } - }, [albums, fetchArtist, id]) - - const sortedAlbums = (albums ? Object.values(albums) : []) + const sortedAlbums = [...albums] .sort((a, b) => a.name.localeCompare(b.name)) .sort((a, b) => (b.year || 0) - (a.year || 0)) @@ -115,10 +98,17 @@ const ArtistViewFallback = React.memo(() => ( const ArtistView = React.memo<{ id: string; title: string }>(({ id, title }) => { const artist = useStoreDeep(useCallback(store => store.entities.artists[id], [id])) - const artistInfo = useStoreDeep(useCallback(store => store.entities.artistInfo[id], [id])) + const topSongIds = useStoreDeep(useCallback(store => store.entities.artistNameTopSongs[artist?.name], [artist?.name])) + const topSongs = useStoreDeep( + useCallback(store => (topSongIds ? mapById(store.entities.songs, topSongIds) : undefined), [topSongIds]), + ) + const albumIds = useStoreDeep(useCallback(store => store.entities.artistAlbums[id], [id])) + const albums = useStoreDeep( + useCallback(store => (albumIds ? mapById(store.entities.albums, albumIds) : undefined), [albumIds]), + ) const fetchArtist = useStore(store => store.fetchLibraryArtist) - const fetchArtistInfo = useStore(store => store.fetchLibraryArtistInfo) + const fetchTopSongs = useStore(store => store.fetchLibraryArtistTopSongs) const coverLayout = useLayout() const headerOpacity = useSharedValue(0) @@ -136,16 +126,16 @@ const ArtistView = React.memo<{ id: string; title: string }>(({ id, title }) => }) useEffect(() => { - if (!artist) { + if (!artist || !albumIds) { fetchArtist(id) } - }, [artist, fetchArtist, id]) + }, [artist, albumIds, fetchArtist, id]) useEffect(() => { - if (!artistInfo) { - fetchArtistInfo(id) + if (artist && !topSongIds) { + fetchTopSongs(artist.name) } - }, [artistInfo, fetchArtistInfo, id]) + }, [artist, fetchTopSongs, topSongIds]) if (!artist) { return @@ -160,17 +150,23 @@ const ArtistView = React.memo<{ id: string; title: string }>(({ id, title }) => style={styles.scroll} contentContainerStyle={styles.scrollContent} onScroll={onScroll}> - + {artist.name} - {/* {artist.topSongs.length > 0 ? ( - + {topSongs && albums ? ( + topSongs.length > 0 ? ( + <> + + + + ) : ( + + ) ) : ( - <> - )} */} - + + )} @@ -245,6 +241,9 @@ const styles = StyleSheet.create({ fontFamily: font.regular, textAlign: 'center', }, + loading: { + marginTop: 30, + }, }) export default ArtistView diff --git a/app/screens/NowPlayingView.tsx b/app/screens/NowPlayingView.tsx index 0fe6288..1f5a02c 100644 --- a/app/screens/NowPlayingView.tsx +++ b/app/screens/NowPlayingView.tsx @@ -96,7 +96,7 @@ const SongCoverArt = () => { return ( - + ) } diff --git a/app/screens/SongListView.tsx b/app/screens/SongListView.tsx index 3ea6882..38de9da 100644 --- a/app/screens/SongListView.tsx +++ b/app/screens/SongListView.tsx @@ -4,6 +4,7 @@ import HeaderBar from '@app/components/HeaderBar' import ImageGradientFlatList from '@app/components/ImageGradientFlatList' import ListItem from '@app/components/ListItem' import ListPlayerControls from '@app/components/ListPlayerControls' +import NothingHere from '@app/components/NothingHere' import { useCoverArtFile } from '@app/hooks/cache' import { Album, PlaylistListItem, Song } from '@app/models/music' import { useStore, useStoreDeep } from '@app/state/store' @@ -49,15 +50,15 @@ const SongListDetails = React.memo<{ songs?: Song[] subtitle?: string }>(({ title, songList, songs, subtitle, type }) => { - const coverArtFile = useCoverArtFile(songList?.coverArt, 'thumbnail') + const coverArtFile = useCoverArtFile(songList?.coverArt) const [headerColor, setHeaderColor] = useState(undefined) const setQueue = useStore(selectTrackPlayer.setQueue) - if (!songList || !songs) { + if (!songList) { return } - const _songs = [...songs] + const _songs = [...(songs || [])] let typeName = '' if (type === 'album') { @@ -101,21 +102,26 @@ const SongListDetails = React.memo<{ overScrollMode="never" windowSize={7} contentMarginTop={26} + ListEmptyComponent={ + songs ? ( + + ) : ( + + ) + } ListHeaderComponent={ - + {songList.name} {subtitle ? {subtitle} : <>} - {songs.length > 0 && ( - - )} + } /> @@ -235,6 +241,12 @@ const styles = StyleSheet.create({ listItem: { paddingHorizontal: 20, }, + nothing: { + width: '100%', + }, + listLoading: { + marginTop: 10, + }, }) export default SongListView diff --git a/app/state/library.ts b/app/state/library.ts index 4021bee..c63403a 100644 --- a/app/state/library.ts +++ b/app/state/library.ts @@ -51,7 +51,6 @@ export interface Artist { export interface ArtistInfo { id: string - smallImageUrl?: string largeImageUrl?: string } @@ -108,7 +107,6 @@ function mapArtist(artist: ArtistID3Element): Artist { function mapArtistInfo(id: string, info: ArtistInfo2Element): ArtistInfo { return { id, - smallImageUrl: info.smallImageUrl, largeImageUrl: info.largeImageUrl, } } @@ -119,7 +117,7 @@ function mapAlbum(album: AlbumID3Element): Album { id: album.id, name: album.name, artist: album.artist, - artistId: album.artist, + artistId: album.artistId, starred: album.starred, coverArt: album.coverArt, year: album.year,