import React from 'react'; import { connect } from 'react-redux'; import InputRange from 'react-input-range'; import axios from 'axios'; import faPlay from '@fortawesome/fontawesome-free-solid/faPlay'; import faChevronLeft from '@fortawesome/fontawesome-free-solid/faChevronLeft'; import faChevronRight from '@fortawesome/fontawesome-free-solid/faChevronRight'; import faPause from '@fortawesome/fontawesome-free-solid/faPause'; import faWrench from '@fortawesome/fontawesome-free-solid/faWrench'; import { classes, extension } from '/common/util'; import { TracerApi } from '/apis'; import { actions } from '/reducers'; import { BaseComponent, Button, ProgressBar } from '/components'; import styles from './stylesheet.scss'; @connect(({ player }) => ({ player }), actions) class Player extends BaseComponent { constructor(props) { super(props); this.state = { speed: 2, playing: false, building: false, }; this.tracerApiSource = null; this.reset(); } componentDidMount() { const { file } = this.props; this.build(file); } componentWillReceiveProps(nextProps) { const { file } = nextProps; const { buildAt } = nextProps.player; if (buildAt !== this.props.player.buildAt) { this.build(file); } } reset(traces = []) { const chunks = [{ traces: [], lineNumber: undefined, }]; while (traces.length) { const trace = traces.shift(); if (trace.method === 'delay') { const [lineNumber] = trace.args; chunks[chunks.length - 1].lineNumber = lineNumber; chunks.push({ traces: [], lineNumber: undefined, }); } else { chunks[chunks.length - 1].traces.push(trace); } } this.props.setChunks(chunks); this.props.setCursor(0); this.pause(); this.props.setLineIndicator(undefined); } build(file) { this.reset(); if (!file) return; if (this.tracerApiSource) this.tracerApiSource.cancel(); this.tracerApiSource = axios.CancelToken.source(); this.setState({ building: true }); const ext = extension(file.name); if (ext in TracerApi) { TracerApi[ext]({ code: file.content }, undefined, this.tracerApiSource.token) .then(traces => { this.tracerApiSource = null; this.setState({ building: false }); this.reset(traces); this.next(); }) .catch(error => { if (axios.isCancel(error)) return; this.tracerApiSource = null; this.setState({ building: false }); this.handleError(error); }); } else { this.handleError(new Error('Language Not Supported')); } } isValidCursor(cursor) { const { chunks } = this.props.player; return 1 <= cursor && cursor <= chunks.length; } prev() { this.pause(); const cursor = this.props.player.cursor - 1; if (!this.isValidCursor(cursor)) return false; this.props.setCursor(cursor); return true; } resume(wrap = false) { this.pause(); if (this.next() || wrap && this.props.setCursor(1)) { const interval = 4000 / Math.pow(Math.E, this.state.speed); this.timer = window.setTimeout(() => this.resume(), interval); this.setState({ playing: true }); } } pause() { if (this.timer) { window.clearTimeout(this.timer); this.timer = undefined; this.setState({ playing: false }); } } next() { this.pause(); const cursor = this.props.player.cursor + 1; if (!this.isValidCursor(cursor)) return false; this.props.setCursor(cursor); return true; } handleChangeSpeed(speed) { this.setState({ speed }); } handleChangeProgress(progress) { const { chunks } = this.props.player; const cursor = Math.max(1, Math.min(chunks.length, Math.round(progress * chunks.length))); this.pause(); this.props.setCursor(cursor); } render() { const { className, file } = this.props; const { chunks, cursor } = this.props.player; const { speed, playing, building } = this.state; return (
{ playing ? ( ) : ( ) }
); } } export default Player;