UTD-CRSS/app.exploreapollo.org

View on GitHub
src/containers/DJ/index.js

Summary

Maintainability
D
2 days
Test Coverage
import React, { Component } from "react";
import { AppFooter, AppHeader } from "../App";
import "./index.scss";

import Spinner from "react-spinner";

import Wavesurfer from "react-wavesurfer";
import { PlayButton, AudioPlayer } from "../../components/MomentPlayer";
import config from "../../../config";
import { fromJS } from "immutable";

// import { Timeline } from "../../components"; // Certain Timeline/Transcript components are commented out because they are non-functional currently

import getActiveIndex from "../MomentViewer/getActiveIndex";

export class DJ extends Component {
  constructor(props) {
    super(props);
    this.state = {
      loading: true,
      audio: { playing: false, time: 0, momentId: 0 },
      media: [],
      transcript: [],
      metric: [],
      metStart: 0,
      metEnd: 0,
      audioUrl: "",
      title: "",
      currentMission: null,
      scPlaying: false,
      scTime: 0,
      scVolume: 1,
      apolloVolume: 1,
      crossFade: 0,
      autoplay: false,
      onEnd: null,
    };
  }

  async componentDidMount() {
    const moments = await fetch(`${config.apiEntry}/api/moments/random`);
    const momentJson = await moments.json();
    let momentId = fromJS(momentJson.id);
    const momentMedia = fromJS(momentJson.media);
    const startmet = fromJS(momentJson.metStart);
    const endmet = fromJS(momentJson.metEnd);
    const url = fromJS(momentJson.audioUrl);
    const t = fromJS(momentJson.title);
    const mission = fromJS(momentJson.mission);
    const transcripts = await fetch(
      `${config.apiEntry}/api/moments/${momentId}/transcripts`
    );
    const transcriptJson = await transcripts.json();
    const orgMetrics = await fetch(
      `${config.apiEntry}/api/moments/${momentId}/metrics`
    );
    const metricsJson = await orgMetrics.json();

    this.setState({
      loading: false,
      audio: { playing: false, time: 0, momentId: momentId },
      media: momentMedia,
      transcript: transcriptJson,
      metric: metricsJson,
      metStart: startmet,
      metEnd: endmet,
      audioUrl: url,
      title: t,
      currentMission: mission,
      scPlaying: false,
      scTime: 0,
      scVolume: 1,
      apolloVolume: 1,
      crossFade: 0,
      autoplay: false,
      onEnd: null,
    });
  }

  playaudio() {
    this.setState({
      audio: {
        playing: true,
        time: this.state.audio.time,
        momentId: this.state.audio.momentId,
      },
    });
  }

  pauseaudio() {
    this.setState({
      audio: {
        playing: false,
        time: this.state.audio.time,
        momentId: this.state.audio.momentId,
      },
    });
  }

  seek() {
    this.setState({
      audio: {
        playing: this.state.audio.playing,
        time: this.seekTime,
        momentId: this.state.audio.momentId,
      },
    });
  }

  scPlay() {
    this.setState({ scPlaying: true });
  }

  scPause() {
    this.setState({ scPlaying: false });
  }

  scPosChange(e) {
    const scTime = e.originalArgs[0];
    this.setState({ scTime });
  }

  masterPlay() {
    this.scPlay();
    this.setState({
      audio: {
        playing: true,
        time: this.state.audio.time,
        momentId: this.state.audio.momentId,
      },
    });
  }

  masterPause() {
    this.scPause();
    this.setState({
      audio: {
        playing: false,
        time: this.state.audio.time,
        momentId: this.state.audio.momentId,
      },
    });
  }

  setVolumeFactory(playerKey) {
    return (e) => {
      this.setState({
        [playerKey]: +e.target.value,
      });
    };
  }

  crossFadeFactory() {
    return (e) => {
      this.setState({
        crossFade: +e.target.value,
      });
    };
  }

  calculateVolume(lr, crossFade, volume) {
    if (crossFade > 0 && lr === "left") {
      return (1 - crossFade) * volume;
    }
    if (crossFade < 0 && lr === "right") {
      return (1 + crossFade) * volume;
    }
    return volume;
  }

  render() {
    const { autoplay, onEnd } = this.state;
    if (this.state.loading) {
      return (
        <div className="text-center lead">
          <p>Loading DJ Apollo...</p>
          <Spinner />
        </div>
      );
    }

    if (!this.state.audio.momentId) {
      return <div>Error fetching moment.</div>;
    }

    const { time, playing } = this.state.audio;
    let transcripts = this.state.transcript;

    const momentMetStart = this.state.metStart;
    const currentMissionTime = momentMetStart + time * 1000;

    const activeIndex = getActiveIndex(transcripts, currentMissionTime);

    if (activeIndex >= 0) {
      transcripts[activeIndex].active = true;
    }

    const { audioUrl } = this.state;

    const { scPlaying, scTime, scVolume, apolloVolume, crossFade } = this.state;

    const surferOptions = {
      normalize: true,
    };

    return (
      <div>
        <AppHeader />
        <h1 className="text-center">Apollo 11 DJ</h1>
        <div className="dj-viewer-container">
          <div className="dj-viewer">
            <h2>Moe Shop - Crosstalk</h2>
            <AudioPlayer>
              <PlayButton
                isPlaying={scPlaying}
                play={this.scPlay.bind(this)}
                pause={this.scPause.bind(this)}
              />
              <Wavesurfer
                audioFile={
                  "https://api.soundcloud.com/tracks/255043058/stream?client_id=7535f0fce0d2cb28070cdb86b8746f77"
                }
                pos={scTime}
                playing={scPlaying}
                volume={this.calculateVolume("left", crossFade, scVolume)}
                onPosChange={this.scPosChange.bind(this)}
              />
            </AudioPlayer>
            <div>
              <small>
                Streamed from{" "}
                <a href="https://soundcloud.com">soundcloud.com</a>. Song
                Courtesy of <a href="https://moeshop.bandcamp.com/">Moe Shop</a>
                .
              </small>
            </div>
          </div>
          <div className="dj-volume-control panel panel-info">
            <div className="panel-body">
              <div className="dj-play-button-container">
                <PlayButton
                  isPlaying={scPlaying || playing}
                  pause={this.masterPause.bind(this)}
                  play={this.masterPlay.bind(this)}
                />
              </div>
              <div className="dj-volume-control-sliders">
                <input
                  type="range"
                  min={0}
                  max={1}
                  step={0.01}
                  onChange={this.setVolumeFactory("scVolume")}
                  value={scVolume}
                  className="dj-vertical"
                />
                <input
                  type="range"
                  min={-1}
                  max={1}
                  step={0.01}
                  onChange={this.crossFadeFactory()}
                  className="dj-horizontal"
                />
                <input
                  type="range"
                  min={0}
                  max={1}
                  step={0.01}
                  onChange={this.setVolumeFactory("apolloVolume")}
                  value={apolloVolume}
                  className="dj-vertical"
                />
              </div>
            </div>
          </div>
          <div className="dj-viewer">
            <h2 className="text-center">{this.state.title}</h2>

            <AudioPlayer>
              <PlayButton
                isPlaying={this.state.audio.playing}
                play={this.playaudio.bind(this)}
                pause={this.pauseaudio.bind(this)}
              />
              <Wavesurfer
                audioFile={audioUrl}
                volume={this.calculateVolume("right", crossFade, apolloVolume)}
                pos={time}
                onPosChange={this.seek.bind(this)}
                playing={playing}
                options={surferOptions}
                onFinish={onEnd}
                onReady={function () {
                  if (autoplay) {
                    this.playaudio.bind(this);
                  }
                }}
              />
            </AudioPlayer>
            {/* <div style={{ marginTop: "0.5em" }} className="timeline-panel row">
              <Timeline
                timeline={transcripts}
                clickEvent={timelineClickEvent}
              />
            </div> */}
          </div>
        </div>
        <AppFooter />
      </div>
    );
  }
}