Files
pawol.nu/components/teks/lekte.js
T
Cédric FAMIBELLE-PRONZOLA db6edcfec4 Fix image in Lekte
2022-05-22 00:17:11 +04:00

268 lines
7.9 KiB
JavaScript

import {useState, useEffect, useRef} from 'react'
import PropTypes from 'prop-types'
import {styled, useTheme} from '@mui/material/styles'
import Box from '@mui/material/Box'
import Typography from '@mui/material/Typography'
import Slider from '@mui/material/Slider'
import IconButton from '@mui/material/IconButton'
import Stack from '@mui/material/Stack'
import PauseRounded from '@mui/icons-material/PauseRounded'
import PlayArrowRounded from '@mui/icons-material/PlayArrowRounded'
import VolumeUpRounded from '@mui/icons-material/VolumeUpRounded'
import VolumeDownRounded from '@mui/icons-material/VolumeDownRounded'
import Image from 'next/image'
import {grey} from '@mui/material/colors'
import {Link} from '@mui/material'
const IMAGE_URL = process.env.NEXT_PUBLIC_API_URL_ROOT || 'http://localhost:1337'
const Widget = styled('div')(({theme}) => ({
padding: 16,
borderRadius: 16,
width: 343,
maxWidth: '100%',
margin: 'auto',
position: 'relative',
zIndex: 1,
backgroundColor:
theme.palette.mode === 'dark' ? 'rgba(0,0,0,0.6)' : grey[200],
backdropFilter: 'blur(40px)',
}))
const CoverImage = styled('div')({
width: 100,
height: 100,
objectFit: 'cover',
overflow: 'hidden',
flexShrink: 0,
borderRadius: 8,
backgroundColor: 'rgba(0,0,0,0.08)',
'& > img': {
width: '100%',
},
})
const TinyText = styled(Typography)({
fontSize: '0.75rem',
opacity: 0.38,
fontWeight: 500,
letterSpacing: 0.2,
})
export default function Lekte({audio, url, parole}) {
const audioRef = useRef(new Audio(audio))
const intervalRef = useRef()
const isReady = useRef(false)
const {duration} = audioRef.current
const theme = useTheme()
const [position, setPosition] = useState(0)
const [volume, setVolume] = useState(100)
const [isPlaying, setIsPlaying] = useState(false)
const awtis = parole.artistes.data.map(({attributes}) => attributes.alias)
function formatDuration(value) {
const minute = Math.floor(value / 60)
const secondLeft = value - (minute * 60)
if (Number.isNaN(minute) || Number.isNaN(secondLeft)) {
return 'Information indisponible'
}
return `${minute}:${secondLeft <= 9 ? `0${secondLeft}` : secondLeft}`
}
const mainIconColor = theme.palette.mode === 'dark' ? '#fff' : '#000'
const lightIconColor = theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.4)' : grey[900]
const startTimer = () => {
clearInterval(intervalRef.current)
intervalRef.current = setInterval(() => {
setPosition(Math.round(audioRef.current.currentTime))
}, [1000])
}
useEffect(() => {
if (isReady.current) {
audioRef.current.play()
setIsPlaying(true)
startTimer()
} else {
isReady.current = true
}
}, [])
useEffect(() => {
if (isPlaying) {
audioRef.current.play()
} else {
audioRef.current.pause()
}
}, [isPlaying])
useEffect(() =>
() => {
audioRef.current.pause()
clearInterval(intervalRef.current)
}
, [])
useEffect(() => {
if (isPlaying) {
audioRef.current.play()
startTimer()
} else {
clearInterval(intervalRef.current)
audioRef.current.pause()
}
}, [isPlaying])
useEffect(() => {
audioRef.current.pause()
audioRef.current = new Audio(audio)
setIsPlaying(false)
setVolume(100)
setPosition(0)
}, [audio])
const handleChangePosition = value => {
setPosition(value)
audioRef.current.currentTime = value
}
const handleChangeVolume = value => {
setVolume(value)
audioRef.current.volume = value / 100
}
return (
<Box sx={{width: '80%', position: 'relative', zIndex: 0}}>
<Widget>
<Box sx={{display: 'flex', alignItems: 'center'}}>
<CoverImage>
<Image
alt={parole.titre}
src={parole?.couverture?.data?.attributes?.formats?.thumbnail ? `${IMAGE_URL}${parole.couverture.data.attributes.formats.thumbnail.url}` : '/oki-logo-192x192.png'}
width={parole?.couverture?.data?.attributes?.formats?.thumbnail ? parole.couverture.data.attributes.formats.thumbnail.width : 192}
height={parole?.couverture?.data?.attributes?.formats?.thumbnail ? parole.couverture.data.attributes.formats.thumbnail.height : 192}
/>
</CoverImage>
<Box sx={{ml: 1.5, minWidth: 0}}>
<Typography fontWeight={500}>
{new Intl.ListFormat('fr').format(awtis)}
</Typography>
<Typography>
<Link underline='hover' sx={{cursor: 'pointer'}} target='_blank' rel='noreferrer' href={url}>
<strong>{parole.titre}</strong>
</Link>
</Typography>
<Typography variant='caption'>
<i>{parole.annee}</i>
</Typography>
</Box>
</Box>
<Slider
aria-label='time-indicator'
size='small'
defaultValue={position}
value={position || 0}
min={0}
step={1}
max={duration || 100}
sx={{
color: theme.palette.mode === 'dark' ? '#fff' : 'rgba(0,0,0,0.87)',
height: 4,
'& .MuiSlider-thumb': {
width: 8,
height: 8,
transition: '0.3s cubic-bezier(.47,1.64,.41,.8)',
'&:before': {
boxShadow: '0 2px 12px 0 rgba(0,0,0,0.4)',
},
'&:hover, &.Mui-focusVisible': {
boxShadow: `0px 0px 0px 8px ${
theme.palette.mode === 'dark'
? 'rgb(255 255 255 / 16%)'
: 'rgb(0 0 0 / 16%)'
}`,
},
'&.Mui-active': {
width: 20,
height: 20,
},
},
'& .MuiSlider-rail': {
opacity: 0.28,
},
}}
onChange={(_, value) => handleChangePosition(value)}
/>
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
mt: -2,
}}
>
<TinyText>{formatDuration(position)}</TinyText>
<TinyText>-{formatDuration(Math.round(duration) - position)}</TinyText>
</Box>
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
mt: -1,
}}
>
<IconButton
aria-label={isPlaying ? 'pause' : 'play'}
onClick={() => setIsPlaying(!isPlaying)}
>
{isPlaying ? (
<PauseRounded sx={{fontSize: '3rem'}} htmlColor={mainIconColor} />
) : (
<PlayArrowRounded
sx={{fontSize: '3rem'}}
htmlColor={mainIconColor}
/>
)}
</IconButton>
</Box>
<Stack spacing={2} direction='row' sx={{mb: 1, px: 1}} alignItems='center'>
<VolumeDownRounded htmlColor={lightIconColor} />
<Slider
aria-label='Volume'
defaultValue={volume}
value={volume}
sx={{
color: theme.palette.mode === 'dark' ? '#fff' : 'rgba(0,0,0,0.87)',
'& .MuiSlider-track': {
border: 'none',
},
'& .MuiSlider-thumb': {
width: 24,
height: 24,
backgroundColor: '#fff',
'&:before': {
boxShadow: '0 4px 8px rgba(0,0,0,0.4)',
},
'&:hover, &.Mui-focusVisible, &.Mui-active': {
boxShadow: 'none',
},
},
}}
onChange={(_, value) => handleChangeVolume(value)}
/>
<VolumeUpRounded htmlColor={lightIconColor} />
</Stack>
</Widget>
</Box>
)
}
Lekte.propTypes = {
audio: PropTypes.string.isRequired,
url: PropTypes.string.isRequired,
parole: PropTypes.object.isRequired
}