index.jsx 5.4 KB
Newer Older
1 2 3
import React from 'react';
import { connect } from 'react-redux';
import InputRange from 'react-input-range';
J
Jason Park 已提交
4
import axios from 'axios';
5 6 7 8 9
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';
J
Jason Park 已提交
10
import { classes, extension } from '/common/util';
11 12
import { TracerApi } from '/apis';
import { actions } from '/reducers';
J
Jason Park 已提交
13
import { BaseComponent, Button, ProgressBar } from '/components';
14 15 16
import styles from './stylesheet.scss';

@connect(({ player }) => ({ player }), actions)
J
Jason Park 已提交
17
class Player extends BaseComponent {
18 19 20 21
  constructor(props) {
    super(props);

    this.state = {
J
Jason Park 已提交
22
      speed: 2,
23 24 25 26
      playing: false,
      building: false,
    };

J
Jason Park 已提交
27 28
    this.tracerApiSource = null;

29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
    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) {
J
Jason Park 已提交
70
    this.reset();
71
    if (!file) return;
J
Jason Park 已提交
72 73 74

    if (this.tracerApiSource) this.tracerApiSource.cancel();
    this.tracerApiSource = axios.CancelToken.source();
75
    this.setState({ building: true });
J
Jason Park 已提交
76

77
    const ext = extension(file.name);
J
Jason Park 已提交
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
    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'));
    }
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
  }

  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)) {
J
Jason Park 已提交
113 114
      const interval = 4000 / Math.pow(Math.E, this.state.speed);
      this.timer = window.setTimeout(() => this.resume(), interval);
115 116 117 118 119 120
      this.setState({ playing: true });
    }
  }

  pause() {
    if (this.timer) {
J
Jason Park 已提交
121 122
      window.clearTimeout(this.timer);
      this.timer = undefined;
123 124 125 126 127 128 129 130 131 132 133 134
      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;
  }

J
Jason Park 已提交
135 136
  handleChangeSpeed(speed) {
    this.setState({ speed });
137 138 139 140 141 142 143 144 145 146 147 148
  }

  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;
J
Jason Park 已提交
149
    const { speed, playing, building } = this.state;
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167

    return (
      <div className={classes(styles.player, className)}>
        <Button icon={faWrench} primary disabled={building} inProgress={building} onClick={() => this.build(file)}>
          {building ? 'Building' : 'Build'}
        </Button>
        {
          playing ? (
            <Button icon={faPause} primary active onClick={() => this.pause()}>Pause</Button>
          ) : (
            <Button icon={faPlay} primary onClick={() => this.resume(true)}>Play</Button>
          )
        }
        <Button icon={faChevronLeft} primary disabled={!this.isValidCursor(cursor - 1)} onClick={() => this.prev()} />
        <ProgressBar className={styles.progress_bar} current={cursor} total={chunks.length}
                     onChangeProgress={progress => this.handleChangeProgress(progress)} />
        <Button icon={faChevronRight} reverse primary disabled={!this.isValidCursor(cursor + 1)}
                onClick={() => this.next()} />
J
Jason Park 已提交
168
        <div className={styles.speed}>
169 170 171 172 173 174 175
          Speed
          <InputRange
            classNames={{
              inputRange: styles.range,
              labelContainer: styles.range_label_container,
              slider: styles.range_slider,
              track: styles.range_track,
J
Jason Park 已提交
176 177
            }} minValue={0} maxValue={4} step={.5} value={speed}
            onChange={speed => this.handleChangeSpeed(speed)} />
178 179 180 181 182 183 184
        </div>
      </div>
    );
  }
}

export default Player;