clean up artist art/placeholders

This commit is contained in:
austinried 2021-07-14 12:28:21 +09:00
parent d1598d53f8
commit e7f9b1db86
3 changed files with 92 additions and 75 deletions

View File

@ -1,11 +1,12 @@
import { useAtomValue } from 'jotai/utils' import { useAtomValue } from 'jotai/utils'
import React from 'react' import React, { useState } from 'react'
import { ActivityIndicator, View } from 'react-native' import { ActivityIndicator, LayoutChangeEvent, StyleSheet, View } from 'react-native'
import FastImage from 'react-native-fast-image' import FastImage from 'react-native-fast-image'
import LinearGradient from 'react-native-linear-gradient' import LinearGradient from 'react-native-linear-gradient'
import { artistArtAtomFamily } from '@app/state/music' import { artistArtAtomFamily } from '@app/state/music'
import colors from '@app/styles/colors' import colors from '@app/styles/colors'
import CoverArt from '@app/components/CoverArt' import CoverArt from '@app/components/CoverArt'
import IconFA5 from 'react-native-vector-icons/FontAwesome5'
interface ArtistArtSizeProps { interface ArtistArtSizeProps {
height: number height: number
@ -20,26 +21,31 @@ interface ArtistArtProps extends ArtistArtSizeProps {
id: string id: string
} }
const PlaceholderContainer: React.FC<ArtistArtSizeProps> = ({ height, width, children }) => ( const PlaceholderContainer: React.FC<ArtistArtSizeProps> = ({ height, width, children }) => {
const [layout, setLayout] = useState({ x: 0, y: 0, width: 0, height: 0 })
const onLayout = (event: LayoutChangeEvent) => {
setLayout(event.nativeEvent.layout)
}
return (
<LinearGradient <LinearGradient
onLayout={onLayout}
colors={[colors.accent, colors.accentLow]} colors={[colors.accent, colors.accentLow]}
style={{ style={[styles.placeholderContainer, { height, width }]}>
height, <IconFA5 name="microphone" color="black" size={layout.width / 1.8} style={styles.placeholderIcon} />
width,
alignItems: 'center',
justifyContent: 'center',
}}>
{children} {children}
</LinearGradient> </LinearGradient>
) )
}
const FourUp: React.FC<ArtistArtXUpProps> = ({ height, width, coverArtUris }) => { const FourUp = React.memo<ArtistArtXUpProps>(({ height, width, coverArtUris }) => {
const halfHeight = height / 2 const halfHeight = height / 2
const halfWidth = width / 2 const halfWidth = width / 2
return ( return (
<PlaceholderContainer height={height} width={width}> <PlaceholderContainer height={height} width={width}>
<View style={{ width, height: halfHeight, flexDirection: 'row' }}> <View style={[styles.artRow, { width, height: halfHeight }]}>
<FastImage <FastImage
source={{ uri: coverArtUris[0] }} source={{ uri: coverArtUris[0] }}
style={{ height: halfHeight, width: halfWidth }} style={{ height: halfHeight, width: halfWidth }}
@ -51,7 +57,7 @@ const FourUp: React.FC<ArtistArtXUpProps> = ({ height, width, coverArtUris }) =>
resizeMode={FastImage.resizeMode.cover} resizeMode={FastImage.resizeMode.cover}
/> />
</View> </View>
<View style={{ width, height: halfHeight, flexDirection: 'row' }}> <View style={[styles.artRow, { width, height: halfHeight }]}>
<FastImage <FastImage
source={{ uri: coverArtUris[2] }} source={{ uri: coverArtUris[2] }}
style={{ height: halfHeight, width: halfWidth }} style={{ height: halfHeight, width: halfWidth }}
@ -65,22 +71,22 @@ const FourUp: React.FC<ArtistArtXUpProps> = ({ height, width, coverArtUris }) =>
</View> </View>
</PlaceholderContainer> </PlaceholderContainer>
) )
} })
const ThreeUp: React.FC<ArtistArtXUpProps> = ({ height, width, coverArtUris }) => { const ThreeUp = React.memo<ArtistArtXUpProps>(({ height, width, coverArtUris }) => {
const halfHeight = height / 2 const halfHeight = height / 2
const halfWidth = width / 2 const halfWidth = width / 2
return ( return (
<PlaceholderContainer height={height} width={width}> <PlaceholderContainer height={height} width={width}>
<View style={{ width, height: halfHeight, flexDirection: 'row' }}> <View style={[styles.artRow, { width, height: halfHeight }]}>
<FastImage <FastImage
source={{ uri: coverArtUris[0] }} source={{ uri: coverArtUris[0] }}
style={{ height: halfHeight, width }} style={{ height: halfHeight, width }}
resizeMode={FastImage.resizeMode.cover} resizeMode={FastImage.resizeMode.cover}
/> />
</View> </View>
<View style={{ width, height: halfHeight, flexDirection: 'row' }}> <View style={[styles.artRow, { width, height: halfHeight }]}>
<FastImage <FastImage
source={{ uri: coverArtUris[1] }} source={{ uri: coverArtUris[1] }}
style={{ height: halfHeight, width: halfWidth }} style={{ height: halfHeight, width: halfWidth }}
@ -94,21 +100,21 @@ const ThreeUp: React.FC<ArtistArtXUpProps> = ({ height, width, coverArtUris }) =
</View> </View>
</PlaceholderContainer> </PlaceholderContainer>
) )
} })
const TwoUp: React.FC<ArtistArtXUpProps> = ({ height, width, coverArtUris }) => { const TwoUp = React.memo<ArtistArtXUpProps>(({ height, width, coverArtUris }) => {
const halfHeight = height / 2 const halfHeight = height / 2
return ( return (
<PlaceholderContainer height={height} width={width}> <PlaceholderContainer height={height} width={width}>
<View style={{ width, height: halfHeight, flexDirection: 'row' }}> <View style={[styles.artRow, { width, height: halfHeight }]}>
<FastImage <FastImage
source={{ uri: coverArtUris[0] }} source={{ uri: coverArtUris[0] }}
style={{ height: halfHeight, width }} style={{ height: halfHeight, width }}
resizeMode={FastImage.resizeMode.cover} resizeMode={FastImage.resizeMode.cover}
/> />
</View> </View>
<View style={{ width, height: halfHeight, flexDirection: 'row' }}> <View style={[styles.artRow, { width, height: halfHeight }]}>
<FastImage <FastImage
source={{ uri: coverArtUris[1] }} source={{ uri: coverArtUris[1] }}
style={{ height: halfHeight, width }} style={{ height: halfHeight, width }}
@ -117,32 +123,19 @@ const TwoUp: React.FC<ArtistArtXUpProps> = ({ height, width, coverArtUris }) =>
</View> </View>
</PlaceholderContainer> </PlaceholderContainer>
) )
} })
const OneUp: React.FC<ArtistArtXUpProps> = ({ height, width, coverArtUris }) => { const OneUp = React.memo<ArtistArtXUpProps>(({ height, width, coverArtUris }) => (
return (
<PlaceholderContainer height={height} width={width}> <PlaceholderContainer height={height} width={width}>
<FastImage source={{ uri: coverArtUris[0] }} style={{ height, width }} resizeMode={FastImage.resizeMode.cover} /> <FastImage source={{ uri: coverArtUris[0] }} style={{ height, width }} resizeMode={FastImage.resizeMode.cover} />
</PlaceholderContainer> </PlaceholderContainer>
) ))
}
const NoneUp: React.FC<ArtistArtSizeProps> = ({ height, width }) => { const NoneUp = React.memo<ArtistArtSizeProps>(({ height, width }) => (
return ( <PlaceholderContainer height={height} width={width} />
<PlaceholderContainer height={height} width={width}> ))
<FastImage
source={require('@res/icons/mic_on-fill.png')}
style={{
height: height - height / 4,
width: width - width / 4,
}}
resizeMode={FastImage.resizeMode.cover}
/>
</PlaceholderContainer>
)
}
const ArtistArt: React.FC<ArtistArtProps> = ({ id, height, width }) => { const ArtistArt = React.memo<ArtistArtProps>(({ id, height, width }) => {
const artistArt = useAtomValue(artistArtAtomFamily(id)) const artistArt = useAtomValue(artistArtAtomFamily(id))
const Placeholder = () => { const Placeholder = () => {
@ -170,27 +163,17 @@ const ArtistArt: React.FC<ArtistArtProps> = ({ id, height, width }) => {
} }
return ( return (
<View <View style={[styles.container, { borderRadius: height / 2 }]}>
style={{
borderRadius: height / 2,
overflow: 'hidden',
}}>
<CoverArt PlaceholderComponent={Placeholder} height={height} width={width} coverArtUri={artistArt?.uri} /> <CoverArt PlaceholderComponent={Placeholder} height={height} width={width} coverArtUri={artistArt?.uri} />
</View> </View>
) )
} })
const ArtistArtFallback: React.FC<ArtistArtProps> = ({ height, width }) => ( const ArtistArtFallback = React.memo<ArtistArtProps>(({ height, width }) => (
<View <View style={[styles.fallback, { height, width }]}>
style={{ <ActivityIndicator size="large" color={colors.accent} />
height,
width,
alignItems: 'center',
justifyContent: 'center',
}}>
<ActivityIndicator size="small" color={colors.accent} />
</View> </View>
) ))
const ArtistArtLoader: React.FC<ArtistArtProps> = props => ( const ArtistArtLoader: React.FC<ArtistArtProps> = props => (
<React.Suspense fallback={<ArtistArtFallback {...props} />}> <React.Suspense fallback={<ArtistArtFallback {...props} />}>
@ -198,4 +181,24 @@ const ArtistArtLoader: React.FC<ArtistArtProps> = props => (
</React.Suspense> </React.Suspense>
) )
const styles = StyleSheet.create({
placeholderContainer: {
alignItems: 'center',
justifyContent: 'center',
},
placeholderIcon: {
position: 'absolute',
},
artRow: {
flexDirection: 'row',
},
container: {
overflow: 'hidden',
},
fallback: {
alignItems: 'center',
justifyContent: 'center',
},
})
export default React.memo(ArtistArtLoader) export default React.memo(ArtistArtLoader)

View File

@ -1,16 +1,20 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { ActivityIndicator, StyleSheet, View } from 'react-native' import { ActivityIndicator, LayoutChangeEvent, StyleSheet, View } from 'react-native'
import FastImage from 'react-native-fast-image' import FastImage from 'react-native-fast-image'
import colors from '@app/styles/colors' import colors from '@app/styles/colors'
import IconFA5 from 'react-native-vector-icons/FontAwesome5'
import LinearGradient from 'react-native-linear-gradient'
const CoverArt: React.FC<{ const CoverArt: React.FC<{
PlaceholderComponent?: () => JSX.Element PlaceholderComponent?: () => JSX.Element
placeholderIcon?: string
height?: string | number height?: string | number
width?: string | number width?: string | number
coverArtUri?: string coverArtUri?: string
}> = ({ PlaceholderComponent, height, width, coverArtUri }) => { }> = ({ PlaceholderComponent, placeholderIcon, height, width, coverArtUri }) => {
const [placeholderVisible, setPlaceholderVisible] = useState(false) const [placeholderVisible, setPlaceholderVisible] = useState(false)
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
const [layout, setLayout] = useState({ x: 0, y: 0, width: 0, height: 0 })
useEffect(() => { useEffect(() => {
if (!coverArtUri) { if (!coverArtUri) {
@ -25,18 +29,27 @@ const CoverArt: React.FC<{
resizeMode={FastImage.resizeMode.contain} resizeMode={FastImage.resizeMode.contain}
onError={() => { onError={() => {
setLoading(false) setLoading(false)
console.log('asdfdsaf')
setPlaceholderVisible(true) setPlaceholderVisible(true)
}} }}
onLoadEnd={() => setLoading(false)} onLoadEnd={() => setLoading(false)}
/> />
) )
const Placeholder = () => (
<LinearGradient colors={[colors.accent, colors.accentLow]} style={styles.placeholder}>
<IconFA5 name={placeholderIcon || 'record-vinyl'} color="black" size={layout.width / 1.5} />
</LinearGradient>
)
const onLayout = (event: LayoutChangeEvent) => {
setLayout(event.nativeEvent.layout)
}
return ( return (
<View style={{ ...styles.container, height, width }}> <View style={{ ...styles.container, height, width }} onLayout={onLayout}>
{coverArtUri ? <Image /> : <></>} {coverArtUri ? <Image /> : <></>}
<View style={{ ...styles.placeholderContainer, opacity: placeholderVisible ? 1 : 0 }}> <View style={{ ...styles.placeholderContainer, opacity: placeholderVisible ? 1 : 0 }}>
{PlaceholderComponent ? <PlaceholderComponent /> : <></>} {PlaceholderComponent ? <PlaceholderComponent /> : <Placeholder />}
</View> </View>
<ActivityIndicator style={styles.indicator} animating={loading} size={'large'} color={colors.accent} /> <ActivityIndicator style={styles.indicator} animating={loading} size={'large'} color={colors.accent} />
</View> </View>
@ -54,6 +67,12 @@ const styles = StyleSheet.create({
width: '100%', width: '100%',
position: 'absolute', position: 'absolute',
}, },
placeholder: {
height: '100%',
width: '100%',
justifyContent: 'center',
alignItems: 'center',
},
indicator: { indicator: {
height: '100%', height: '100%',
width: '100%', width: '100%',

View File

@ -21,12 +21,7 @@ const AlbumItem = React.memo<{
onPress={() => navigation.navigate('AlbumView', { id: album.id, title: album.name })} onPress={() => navigation.navigate('AlbumView', { id: album.id, title: album.name })}
key={album.id} key={album.id}
style={styles.item}> style={styles.item}>
<CoverArt <CoverArt coverArtUri={album.coverArtThumbUri} height={styles.item.width} width={styles.item.width} />
PlaceholderComponent={() => <></>}
coverArtUri={album.coverArtThumbUri}
height={styles.item.width}
width={styles.item.width}
/>
<Text style={styles.title} numberOfLines={1}> <Text style={styles.title} numberOfLines={1}>
{album.name} {album.name}
</Text> </Text>