import { useState , useCallback , useEffect , useRef , MutableRefObject , } from "react" ; import { useSelector } from "@xstate/react" ; import raf from "raf" ; import useAudio from "../useAudio/useAudio" ; import { ReturnArgs } from "./usePlayer.types" ; import { Episode } from "src/machines/Player/PlayerMachine.types" ; param { string } src param { string } preload param { boolean } autoplay param { number } volume param { number } rate param { boolean } mute param { boolean } loop return { boolean } return { boolean } return { boolean } return { boolean } return { boolean } return { boolean } return { boolean } return { number } return { number } return { number } return { number } return { boolean } return { boolean } return { function } return { function } return { function } return { function } return { function } return { function } return { function } return { function } return { function } return { function } const usePlayer = ( ) : ReturnArgs => { const { state , send , service , onLoadAudio } = useAudio ( ) ; const [ audio , setAudio ] = useState < HTMLAudioElement | undefined > ( undefined ) ; const playerRef = useRef < HTMLAudioElement | undefined > ( undefined ) ; const [ seek , setSeek ] = useState < number > ( 0 ) ; const seekRef = useRef < number > ( 0 ) ; const ready = useSelector ( service , ( state ) => state . matches ( "ready" ) ) ; const playing = useSelector ( service , ( state ) => state . matches ( "ready.playing" ) ) ; const paused = useSelector ( service , ( state ) => state . matches ( "ready.paused" ) ) ; useEffect ( ( ) => { const animate = ( ) => { const seek = audio ?. currentTime ; setSeek ( seek as number ) ; seekRef . current = raf ( animate ) ; } ; if ( audio && playing ) { seekRef . current = raf ( animate ) ; } return ( ) => { if ( seekRef . current ) { raf . cancel ( seekRef . current ) ; } } ; } , [ audio , playing ] ) ; returns const onToggle = useCallback ( ( episode ) => { if ( ! audio ) { const newAudio = onLoadAudio ( audio , episode . enclosureUrl , episode ) ; setAudio ( newAudio ) ; playerRef . current = newAudio ; } else { if ( ready || paused ) { audio . play ( ) ; send ( "PLAY" ) ; } if ( playing ) { audio . pause ( ) ; send ( "PAUSE" ) ; } } } , [ audio , onLoadAudio , paused , playing , ready , send ] ) ; returns const onPlay = useCallback ( ( ) => { if ( ! audio ) return ; send ( "PLAY" ) ; audio . play ( ) ; } , [ audio , send ] ) ; returns const onPause = useCallback ( ( ) => { if ( ! audio ) return ; send ( "PAUSE" ) ; audio . pause ( ) ; } , [ audio , send ] ) ; returns const onMute = ( ) : void => { if ( ! audio ) return ; send ( "MUTE" ) ; audio . muted = ! state . context . mute ; } ; returns const onLoop = ( ) : void => { if ( ! audio ) return ; send ( "LOOP" ) ; audio . loop = ! state . context . loop ; } ; param { number } value returns const onVolume = ( value ) : void => { if ( ! audio ) return ; send ( { type : "VOLUME" , volume : value } ) ; audio . volume = value ; } ; param { string } value returns const onRate = ( value ) : void => { if ( ! audio ) return ; const rate = parseFloat ( value ) ; send ( { type : "RATE" , rate } ) ; audio . playbackRate = rate ; } ; param { number } value returns const onSeek = ( value ) : void => { if ( ! audio ) return ; setSeek ( value ) ; audio . currentTime = value ; } ; param { number } value returns const onForward = ( value ) : void => { if ( ! audio || audio . ended ) return ; const newSeek = seek + value ; setSeek ( newSeek ) ; audio . currentTime = newSeek ; } ; param { number } value returns const onBackward = ( value ) : void => { if ( ! audio || audio . ended ) return ; const newSeek = seek - value ; setSeek ( newSeek ) ; audio . currentTime = newSeek ; } ; return { state , send , service , seek , onToggle , onPlay , onPause , onVolume , onRate , onMute , onLoop , onSeek , onForward , onBackward , } ; } ; export default usePlayer ;