import { AlbumContextPressable } from '@app/components/ContextMenu' import CoverArt from '@app/components/CoverArt' import GradientBackground from '@app/components/GradientBackground' import GradientScrollView from '@app/components/GradientScrollView' import Header from '@app/components/Header' import HeaderBar from '@app/components/HeaderBar' import ListItem from '@app/components/ListItem' import { Album, Song } from '@app/models/library' import { useStore, useStoreDeep } from '@app/state/store' import colors from '@app/styles/colors' import dimensions from '@app/styles/dimensions' import font from '@app/styles/font' import { mapById } from '@app/util/state' import { useLayout } from '@react-native-community/hooks' import { useNavigation } from '@react-navigation/native' import React, { useCallback, useEffect } from 'react' import { ActivityIndicator, StyleSheet, Text, View } from 'react-native' import { useAnimatedScrollHandler, useAnimatedStyle, useSharedValue } from 'react-native-reanimated' const AlbumItem = React.memo<{ album: Album height: number width: number }>(({ album, height, width }) => { const navigation = useNavigation() if (height <= 0 || width <= 0) { return <> } return ( navigation.navigate('album', { id: album.id, title: album.name })} menuStyle={[styles.albumItem, { width }]} triggerOuterWrapperStyle={{ width }}> {album.name} {album.year ? album.year : ''} ) }) const TopSongs = React.memo<{ songs: Song[] name: string artistId: string }>(({ songs, name, artistId }) => { const setQueue = useStore(store => store.setQueue) return ( <>
Top Songs
{songs.slice(0, 5).map((s, i) => ( setQueue(songs, name, 'artist', artistId, i)} /> ))} ) }) const ArtistAlbums = React.memo<{ albums: Album[] }>(({ albums }) => { const albumsLayout = useLayout() const sortedAlbums = [...albums] .sort((a, b) => a.name.localeCompare(b.name)) .sort((a, b) => (b.year || 0) - (a.year || 0)) const albumSize = albumsLayout.width / 2 - styles.contentContainer.paddingHorizontal / 2 return ( <>
Albums
{sortedAlbums.map(a => ( ))} ) }) const ArtistViewFallback = React.memo(() => ( )) const ArtistView = React.memo<{ id: string; title: string }>(({ id, title }) => { const artist = useStoreDeep(useCallback(store => store.library.artists[id], [id])) const topSongIds = useStoreDeep(useCallback(store => store.library.artistNameTopSongs[artist?.name], [artist?.name])) const topSongs = useStoreDeep( useCallback(store => (topSongIds ? mapById(store.library.songs, topSongIds) : undefined), [topSongIds]), ) const albumIds = useStoreDeep(useCallback(store => store.library.artistAlbums[id], [id])) const albums = useStoreDeep( useCallback(store => (albumIds ? mapById(store.library.albums, albumIds) : undefined), [albumIds]), ) const fetchArtist = useStore(store => store.fetchArtist) const fetchTopSongs = useStore(store => store.fetchArtistTopSongs) const coverLayout = useLayout() const headerOpacity = useSharedValue(0) const onScroll = useAnimatedScrollHandler({ onScroll: event => { headerOpacity.value = Math.max(0, event.contentOffset.y - 70) / (artistCoverHeight - (70 + dimensions.header)) }, }) const animatedOpacity = useAnimatedStyle(() => { return { opacity: headerOpacity.value, } }) useEffect(() => { if (!artist || !albumIds) { fetchArtist(id) } }, [artist, albumIds, fetchArtist, id]) useEffect(() => { if (artist && !topSongIds) { fetchTopSongs(artist.name) } }, [artist, fetchTopSongs, topSongIds]) if (!artist) { return } return ( {artist.name} {topSongs && albums ? ( topSongs.length > 0 ? ( <> ) : ( ) ) : ( )} ) }) const artistCoverHeight = 350 const styles = StyleSheet.create({ container: { flex: 1, }, header: { position: 'absolute', zIndex: 1, }, scroll: { flex: 1, }, fallback: { alignItems: 'center', paddingTop: 100, }, scrollContent: { alignItems: 'center', }, contentContainer: { minHeight: artistCoverHeight * 2, width: '100%', paddingHorizontal: 20, }, titleContainer: { width: '100%', height: artistCoverHeight, justifyContent: 'flex-end', }, title: { fontFamily: font.bold, fontSize: 44, color: colors.text.primary, textAlign: 'center', textShadowColor: 'black', textShadowOffset: { width: 0, height: 0 }, textShadowRadius: 16, paddingHorizontal: 10, marginBottom: 10, }, artistCover: { position: 'absolute', height: artistCoverHeight, width: '100%', }, albums: { width: '100%', flexDirection: 'row', flexWrap: 'wrap', alignItems: 'flex-start', justifyContent: 'space-between', }, albumItem: { marginBottom: 20, }, albumTitle: { fontFamily: font.semiBold, fontSize: 14, color: colors.text.primary, marginTop: 4, textAlign: 'center', }, albumYear: { color: colors.text.secondary, fontFamily: font.regular, textAlign: 'center', }, loading: { marginTop: 30, }, }) export default ArtistView