import { useState , useEffect , useRef , MutableRefObject } from 'react' ; import { useSelector } from '@xstate/react' ; import raf from 'raf' ; import useAudio from '../useAudio/useAudio' ; import { Args , ReturnArgs } from './useRoover.types' ; import { EVENTS } from '../../utils/constants' ; 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 useRoover = ( { src = '' , preload = 'auto' , autoplay = false , volume = 1.0 , rate = 1.0 , mute = false , loop = false , } ) : ReturnArgs => { const { 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 initial = useSelector ( service , state => state . matches ( 'initial' ) ) ; const loading = useSelector ( service , state => state . matches ( 'loading' ) ) ; const ready = useSelector ( service , state => state . matches ( 'ready' ) ) ; const idle = useSelector ( service , state => state . matches ( 'ready.idle' ) ) ; const playing = useSelector ( service , state => state . matches ( 'ready.playing' ) ) ; const paused = useSelector ( service , state => state . matches ( 'ready.paused' ) ) ; const end = useSelector ( service , state => state . matches ( 'end' ) ) ; const playerContextVolume = useSelector ( service , state => state . context . volume ) ; const playerContextRate = useSelector ( service , state => state . context . rate ) ; const playerContextDuration = useSelector ( service , state => state . context . duration ) ; const playerContextMute = useSelector ( service , state => state . context . mute ) ; const playerContextLoop = useSelector ( service , state => state . context . loop ) ; const playerContextError = useSelector ( service , state => state . context . error ) ; 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 = ( ) : void => { if ( ! audio ) { const newAudio = onLoadAudio ( audio , { src , preload , autoplay , volume , rate , mute , loop } ) ; setAudio ( newAudio ) ; playerRef . current = newAudio ; } else { if ( ready || paused ) { audio . play ( ) ; service . send ( EVENTS . PLAY ) ; } if ( playing ) { audio . pause ( ) ; service . send ( EVENTS . PAUSE ) ; } } } ; returns const onPlay = ( ) : void => { if ( ! audio ) { const newAudio = onLoadAudio ( audio , { src , preload , autoplay , volume , rate , mute , loop } ) ; setAudio ( newAudio ) ; playerRef . current = newAudio ; } else { if ( ready || paused ) { audio . play ( ) ; service . send ( EVENTS . PLAY ) ; } } } ; returns const onPause = ( ) : void => { if ( ! audio ) return ; service . send ( EVENTS . PAUSE ) ; audio . pause ( ) ; } ; returns const onMute = ( ) : void => { if ( ! audio ) return ; service . send ( EVENTS . MUTE ) ; audio . muted = ! playerContextMute ; } ; returns const onLoop = ( ) : void => { if ( ! audio ) return ; service . send ( EVENTS . LOOP ) ; audio . loop = ! playerContextLoop ; } ; param { number } value returns const onVolume = ( value ) : void => { if ( ! audio ) return ; service . send ( { type : EVENTS . VOLUME , volume : value } ) ; audio . volume = value ; } ; param { string } value returns const onRate = ( value ) : void => { if ( ! audio ) return ; const rate = parseFloat ( value ) ; service . send ( { type : EVENTS . 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 { initial , loading , ready , idle , playing , paused , end , seek , volume : playerContextVolume , rate : playerContextRate , duration : playerContextDuration , mute : playerContextMute , loop : playerContextLoop , error : playerContextError , onToggle , onPlay , onPause , onVolume , onRate , onMute , onLoop , onSeek , onForward , onBackward } ; } ; export default useRoover ;