Spaces:
Paused
Paused
import type { OdFileObject } from '../../types' | |
import { FC, useEffect, useRef, useState } from 'react' | |
import ReactAudioPlayer from 'react-audio-player' | |
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' | |
import { useTranslation } from 'next-i18next' | |
import { useRouter } from 'next/router' | |
import DownloadButtonGroup from '../DownloadBtnGtoup' | |
import { DownloadBtnContainer, PreviewContainer } from './Containers' | |
import { LoadingIcon } from '../Loading' | |
import { formatModifiedDateTime } from '../../utils/fileDetails' | |
import { getStoredToken } from '../../utils/protectedRouteHandler' | |
enum PlayerState { | |
Loading, | |
Ready, | |
Playing, | |
Paused, | |
} | |
const AudioPreview: FC<{ file: OdFileObject }> = ({ file }) => { | |
const { t } = useTranslation() | |
const { asPath } = useRouter() | |
const hashedToken = getStoredToken(asPath) | |
const rapRef = useRef<ReactAudioPlayer>(null) | |
const [playerStatus, setPlayerStatus] = useState(PlayerState.Loading) | |
const [playerVolume, setPlayerVolume] = useState(1) | |
// Render audio thumbnail, and also check for broken thumbnails | |
const thumbnail = `/api/thumbnail/?path=${asPath}&size=medium${hashedToken ? `&odpt=${hashedToken}` : ''}` | |
const [brokenThumbnail, setBrokenThumbnail] = useState(false) | |
useEffect(() => { | |
// Manually get the HTML audio element and set onplaying event. | |
// - As the default event callbacks provided by the React component does not guarantee playing state to be set | |
// - properly when the user seeks through the timeline or the audio is buffered. | |
const rap = rapRef.current?.audioEl.current | |
if (rap) { | |
rap.oncanplay = () => setPlayerStatus(PlayerState.Ready) | |
rap.onended = () => setPlayerStatus(PlayerState.Paused) | |
rap.onpause = () => setPlayerStatus(PlayerState.Paused) | |
rap.onplay = () => setPlayerStatus(PlayerState.Playing) | |
rap.onplaying = () => setPlayerStatus(PlayerState.Playing) | |
rap.onseeking = () => setPlayerStatus(PlayerState.Loading) | |
rap.onwaiting = () => setPlayerStatus(PlayerState.Loading) | |
rap.onerror = () => setPlayerStatus(PlayerState.Paused) | |
rap.onvolumechange = () => setPlayerVolume(rap.volume) | |
} | |
}, []) | |
return ( | |
<> | |
<PreviewContainer> | |
<div className="flex flex-col space-y-4 md:flex-row md:space-x-4"> | |
<div className="relative flex aspect-square w-full items-center justify-center rounded bg-gray-100 transition-all duration-75 dark:bg-gray-700 md:w-48"> | |
<div | |
className={`absolute z-20 flex h-full w-full items-center justify-center transition-all duration-300 ${ | |
playerStatus === PlayerState.Loading | |
? 'bg-white opacity-80 dark:bg-gray-800' | |
: 'bg-transparent opacity-0' | |
}`} | |
> | |
<LoadingIcon className="z-10 inline-block h-5 w-5 animate-spin" /> | |
</div> | |
{!brokenThumbnail ? ( | |
<div className="absolute m-4 aspect-square rounded-full shadow-lg"> | |
{/* eslint-disable-next-line @next/next/no-img-element */} | |
<img | |
className={`h-full w-full rounded-full object-cover object-top ${ | |
playerStatus === PlayerState.Playing ? 'animate-spin-slow' : '' | |
}`} | |
src={thumbnail} | |
alt={file.name} | |
onError={() => setBrokenThumbnail(true)} | |
/> | |
</div> | |
) : ( | |
<FontAwesomeIcon | |
className={`z-10 h-5 w-5 ${playerStatus === PlayerState.Playing ? 'animate-spin' : ''}`} | |
icon="music" | |
size="2x" | |
/> | |
)} | |
</div> | |
<div className="flex w-full flex-col justify-between"> | |
<div> | |
<div className="mb-2 font-medium">{file.name}</div> | |
<div className="mb-4 text-sm text-gray-500"> | |
{t('Last modified:') + ' ' + formatModifiedDateTime(file.lastModifiedDateTime)} | |
</div> | |
</div> | |
<ReactAudioPlayer | |
className="h-11 w-full" | |
src={`/api/raw/?path=${asPath}${hashedToken ? `&odpt=${hashedToken}` : ''}`} | |
ref={rapRef} | |
controls | |
preload="auto" | |
volume={playerVolume} | |
/> | |
</div> | |
</div> | |
</PreviewContainer> | |
<DownloadBtnContainer> | |
<DownloadButtonGroup /> | |
</DownloadBtnContainer> | |
</> | |
) | |
} | |
export default AudioPreview | |