krasnoukhov/gospotify

View on GitHub
app/jsx/application.jsx

Summary

Maintainability
F
3 days
Test Coverage
/** @jsx React.DOM */
var App = React.createClass({
  render: function() {
    return (
      <div className="providers">
        <Provider name="SoundCloud" provider="soundcloud" icon="soundcloud" />
        <Provider name="VK" provider="vkontakte" icon="vk" />
        <Provider name="Last.fm" provider="lastfm" icon="lastfm" />
      </div>
    );
  }
});

var Provider = React.createClass({
  propTypes: {
    name: React.PropTypes.string.isRequired,
    provider: React.PropTypes.string.isRequired,
    icon: React.PropTypes.string.isRequired,
  },

  getInitialState: function() {
    return {
      loaded: false,
      authorized: true,
      playlists: []
    }
  },

  componentWillMount: function() {
    this.requestPlaylists();
  },

  requestPlaylists: function() {
    $.ajax({
      url: "/provider/" + this.props.provider + "/playlists",
      dataType: "json",
      success: function(data) {
        this.setState({ loaded: true, authorized: true, playlists: data })
      }.bind(this),
      error: function(xhr, status, err) {
        this.setState({ loaded: true })

        if (xhr.status == 401) {
          this.setState({ authorized: false })
        }
      }.bind(this)
    })
  },

  render: function() {
    var icon = 'fa fa-' + this.props.icon;
    var content;

    if (!this.state.loaded) {
      content = <p className="text-muted">Loading...</p>;
    } else if (!this.state.authorized) {
      var href = "/auth/"+this.props.provider;
      content = (
        <a href={href} className="btn btn-primary btn-sm">
          Sign in with {this.props.name}
        </a>
      );
    } else if (this.state.playlists.length > 0) {
      content = (
        <table className="table table-hover">
          {this.state.playlists.map(function(playlist) {
            return <Playlist key={playlist.external_id} provider={this.props.provider} playlist={playlist} />
          }.bind(this))}
        </table>
      )
    } else {
      content = <p className="text-danger">Request error</p>
    }

    return (
      <div className="provider">
        <h2>{this.props.name} <i className={icon}></i></h2>
        {content}
      </div>
    )
  }
});

var Playlist = React.createClass({
  propTypes: {
    provider: React.PropTypes.string.isRequired,
    playlist: React.PropTypes.object.isRequired
  },

  getInitialState: function() {
    return this.props.playlist.status;
  },

  componentWillMount: function() {
    setInterval(this.requestGet, (3+Math.floor(Math.random()*5))*1000);
    setInterval(this.forceUpdate.bind(this), 60*1000);
  },

  status: function() {
    return this.state.status || "sync";
  },

  patchDisabled: function() {
    return this.status() == "sync" || this.status() == "error" ? false : true
  },

  requestPatch: function() {
    this.setState({ status: "loading..." })
    $.ajax({
      url: "/provider/" + this.props.provider + "/playlists/" + this.props.playlist.external_id,
      dataType: "json",
      method: "PATCH",
      success: function(data) {
        this.props.playlist = data;
        this.setState(data.status)
      }.bind(this),
      error: function(xhr, status, err) {
        this.setState({ status: "error" })
      }.bind(this)
    })
  },

  requestGet: function() {
    if (this.status() == "sync" || $.active == 1) {
      return;
    }

    $.ajax({
      url: "/provider/" + this.props.provider + "/playlists/" + this.props.playlist.external_id,
      dataType: "json",
      success: function(data) {
        this.props.playlist = data;
        this.setState(data.status);

        // if (!$.cookie("donate") && this.status() == "sync") {
        if (this.status() == "sync") {
          $("#donate").modal();
          $.cookie("donate", 1, { expires: 30, path: "/" });
        }
      }.bind(this),
      error: function(xhr, status, err) {
        this.setState({ status: "error" })
      }.bind(this)
    });
  },

  render: function() {
    var status;
    if (!this.patchDisabled() && this.props.playlist.synced_at) {
      status = (
        <span className="text-muted">Last synced {moment(this.props.playlist.synced_at).fromNow()}</span>
      )
    } else if (this.state.total && this.state.at) {
      status = (
        <span className="text-success">Processing {this.state.at}/{this.state.total}</span>
      )
    }

    var icon = "fa fa-" + this.props.playlist.icon;

    return (
      <tr>
        <td>
          <h4>
            <small><i className={icon}></i></small> {this.props.playlist.title}
          </h4>
        </td>
        <td className="controls">
          {status}
          <button className="btn btn-success btn-xs" disabled={this.patchDisabled()} onClick={this.requestPatch}>{this.status()}</button>
        </td>
      </tr>
    )
  }
})

var root = document.getElementById('app');
if (root) {
  React.renderComponent(<App />, root);
}