just-paja/radio-drama-queen

View on GitHub
src/soundCategories/components/SoundPlaybackInfo.jsx

Summary

Maintainability
A
2 hrs
Test Coverage
import formatDuration from 'format-duration'
import PropTypes from 'prop-types'
import React from 'react'
import AudioManager from '../../sounds/AudioManager'

import { Classes } from '../../proptypes'
import { withStyles } from '@material-ui/core/styles'

const styles = theme => ({
  duration: {
    alignItems: 'center',
    display: 'flex',
    flexDirection: 'row',
    justifyContent: 'space-between',
    position: 'relative',
    width: '100%'
  },
  data: {
    flexShrink: 0
  },
  progress: {
    background: theme.palette.listSeparator,
    display: 'flex',
    flexGrow: 1,
    flexShrink: 1,
    height: 3,
    marginLeft: theme.spacing(1),
    marginRight: theme.spacing(1)
  },
  indicator: {
    background: theme.palette.interactionOverlay,
    transformOrigin: 'center left',
    width: '100%'
  }
})

class SoundPlaybackInfoComponent extends React.Component {
  animationFrameId = null

  audio = null

  state = { position: 0 }

  constructor (props) {
    super(props)
    this.pipePosition = this.pipePosition.bind(this)
    this.reset = this.reset.bind(this)
    this.updatePosition = this.updatePosition.bind(this)
  }

  componentDidMount () {
    this.readAudio(this.props.uuiid)
  }

  componentDidUpdate (prevProps) {
    if (prevProps.uuid !== this.props.uuid || !this.audio) {
      this.readAudio()
    }
    if (!prevProps.playing && this.props.playing) {
      this.queueFrame(this.pipePosition)
    }
    if (prevProps.playing && !this.props.playing) {
      this.cancelNextFrame()
      this.queueFrame(this.reset)
    }
  }

  componentWillUnmount () {
    this.cancelNextFrame()
  }

  cancelNextFrame () {
    global.cancelAnimationFrame(this.animationFrameId)
  }

  queueFrame (lambda) {
    this.animationFrameId = global.requestAnimationFrame(lambda)
  }

  readAudio () {
    this.audio = AudioManager.findByUuid(this.props.uuid)
  }

  pipePosition () {
    if (this.audio) {
      this.updatePosition()
      if (this.props.playing) {
        this.queueFrame(this.pipePosition)
      }
    }
  }

  reset () {
    this.setState({ position: 0 })
  }

  updatePosition () {
    this.setState({
      position: this.audio ? this.audio.sound.seek() : 0
    })
  }

  render () {
    const { classes, duration } = this.props
    return !duration ? null : (
      <span className={classes.duration}>
        <span className={classes.data}>
          {formatDuration(this.state.position * 1000)}
        </span>
        <span className={classes.progress}>
          <span
            className={classes.indicator}
            style={{
              transform: `scaleX(${this.state.position / this.props.duration})`
            }}
          />
        </span>
        <span className={classes.data}>
          {formatDuration(duration * 1000)}
        </span>
      </span>
    )
  }
}

SoundPlaybackInfoComponent.displayName = 'SoundPlaybackInfo'
SoundPlaybackInfoComponent.propTypes = {
  classes: Classes.isRequired,
  duration: PropTypes.number,
  playing: PropTypes.bool,
  uuid: PropTypes.string.isRequired
}

SoundPlaybackInfoComponent.defaultProps = {
  duration: null,
  playing: false
}

export const SoundPlaybackInfo = withStyles(styles)(SoundPlaybackInfoComponent)