384 lines
12 KiB
JavaScript
384 lines
12 KiB
JavaScript
import React from "react"
|
|
import { connect } from "react-redux"
|
|
import { bindActionCreators } from "redux"
|
|
import { addTorrent } from "../../actions/torrents"
|
|
import { refreshSubtitles } from "../../actions/subtitles"
|
|
import { addShowToWishlist, deleteShowFromWishlist, getEpisodeDetails, updateShowDetails } from "../../actions/shows"
|
|
|
|
import Loader from "../loader/loader"
|
|
import DownloadButton from "../buttons/download"
|
|
import SubtitlesButton from "../buttons/subtitles"
|
|
import ImdbButton from "../buttons/imdb"
|
|
import RefreshIndicator from "../buttons/refresh"
|
|
|
|
import { OverlayTrigger, Tooltip } from "react-bootstrap"
|
|
import { Button, Dropdown, MenuItem } from "react-bootstrap"
|
|
|
|
function mapStateToProps(state) {
|
|
return {
|
|
loading: state.showStore.loading,
|
|
show: state.showStore.get("show"),
|
|
};
|
|
}
|
|
const mapDispatchToProps = (dispatch) =>
|
|
bindActionCreators({addTorrent, addShowToWishlist, deleteShowFromWishlist,
|
|
updateShowDetails, getEpisodeDetails,
|
|
refreshSubtitles }, dispatch)
|
|
|
|
class ShowDetails extends React.Component {
|
|
render() {
|
|
// Loading
|
|
if (this.props.loading) {
|
|
return (<Loader />);
|
|
}
|
|
return (
|
|
<div className="row" id="container">
|
|
<Header
|
|
data={this.props.show}
|
|
addToWishlist={this.props.addShowToWishlist}
|
|
deleteFromWishlist={this.props.deleteShowFromWishlist}
|
|
/>
|
|
<SeasonsList
|
|
data={this.props.show}
|
|
router={this.props.router}
|
|
addTorrent={this.props.addTorrent}
|
|
addToWishlist={this.props.addShowToWishlist}
|
|
getEpisodeDetails={this.props.getEpisodeDetails}
|
|
refreshSubtitles={this.props.refreshSubtitles}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
export default connect(mapStateToProps, mapDispatchToProps)(ShowDetails);
|
|
|
|
function Header(props){
|
|
return (
|
|
<div className="col-xs-12 col-sm-10 col-sm-offset-1 col-md-10 col-md-offset-1">
|
|
<div className="panel panel-default">
|
|
<div className="panel-body">
|
|
<HeaderThumbnail data={props.data} />
|
|
<HeaderDetails
|
|
data={props.data}
|
|
addToWishlist={props.addToWishlist}
|
|
deleteFromWishlist={props.deleteFromWishlist}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function HeaderThumbnail(props){
|
|
return (
|
|
<div className="col-xs-12 col-sm-2 text-center">
|
|
<img src={props.data.get("poster_url")}
|
|
className="show-thumbnail thumbnail-selected img-thumbnail img-responsive"/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function HeaderDetails(props){
|
|
return (
|
|
<div className="col-xs-12 col-sm-10">
|
|
<dl className="dl-horizontal">
|
|
<dt>Title</dt>
|
|
<dd>{props.data.get("title")}</dd>
|
|
<dt>Plot</dt>
|
|
<dd className="plot">{props.data.get("plot")}</dd>
|
|
<dt>IMDB</dt>
|
|
<dd>
|
|
<ImdbButton imdbId={props.data.get("imdb_id")} size="xs"/>
|
|
</dd>
|
|
<dt>Year</dt>
|
|
<dd>{props.data.get("year")}</dd>
|
|
<dt>Rating</dt>
|
|
<dd>{props.data.get("rating")}</dd>
|
|
</dl>
|
|
<TrackHeader
|
|
data={props.data}
|
|
addToWishlist={props.addToWishlist}
|
|
deleteFromWishlist={props.deleteFromWishlist}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function SeasonsList(props){
|
|
return (
|
|
<div>
|
|
{props.data.get("seasons").entrySeq().map(function([season, data]) {
|
|
return (
|
|
|
|
<div className="col-xs-12 col-sm-10 col-sm-offset-1 col-md-10 col-md-offset-1" key={season.toString()}>
|
|
<Season
|
|
data={data}
|
|
season={season}
|
|
showName={props.data.get("title")}
|
|
router={props.router}
|
|
addTorrent={props.addTorrent}
|
|
addToWishlist={props.addToWishlist}
|
|
getEpisodeDetails={props.getEpisodeDetails}
|
|
refreshSubtitles={props.refreshSubtitles}
|
|
/>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
class Season extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.handleClick = this.handleClick.bind(this);
|
|
this.state = { colapsed: true };
|
|
}
|
|
handleClick(e) {
|
|
e.preventDefault();
|
|
this.setState({ colapsed: !this.state.colapsed });
|
|
}
|
|
render() {
|
|
return (
|
|
<div className="panel panel-default">
|
|
<div className="panel-heading clickable" onClick={(e) => this.handleClick(e)}>
|
|
Season {this.props.season}
|
|
<small className="text-primary"> — ({this.props.data.toList().size} episodes)</small>
|
|
<span className="pull-right">
|
|
{this.state.colapsed ||
|
|
<i className="fa fa-chevron-down"></i>
|
|
}
|
|
{this.state.colapsed &&
|
|
<i className="fa fa-chevron-left"></i>
|
|
}
|
|
</span>
|
|
</div>
|
|
{this.state.colapsed ||
|
|
<table className="table table-striped table-hover">
|
|
<tbody>
|
|
{this.props.data.toList().map(function(episode) {
|
|
let key = `${episode.get("season")}-${episode.get("episode")}`;
|
|
return (
|
|
<Episode
|
|
key={key}
|
|
data={episode}
|
|
showName={this.props.showName}
|
|
router={this.props.router}
|
|
addTorrent={this.props.addTorrent}
|
|
addToWishlist={this.props.addToWishlist}
|
|
getEpisodeDetails={this.props.getEpisodeDetails}
|
|
refreshSubtitles={this.props.refreshSubtitles}
|
|
/>
|
|
)
|
|
}, this)}
|
|
</tbody>
|
|
</table>
|
|
}
|
|
</div>
|
|
)
|
|
}
|
|
}
|
|
|
|
function Episode(props) {
|
|
return (
|
|
<tr>
|
|
<th scope="row" className="col-xs-2">
|
|
<TrackButton
|
|
data={props.data}
|
|
addToWishlist={props.addToWishlist}
|
|
/>
|
|
{props.data.get("episode")}
|
|
</th>
|
|
<td className="col-xs-12">
|
|
{props.data.get("title")}
|
|
|
|
<span className="pull-right episode-buttons">
|
|
{props.data.get("polochon_url") !== "" &&
|
|
<SubtitlesButton
|
|
fetching={props.data.get("fetchingSubtitles")}
|
|
subtitles={props.data.get("subtitles")}
|
|
refreshSubtitles={props.refreshSubtitles}
|
|
resourceID={props.data.get("show_imdb_id")}
|
|
season={props.data.get("season")}
|
|
episode={props.data.get("episode")}
|
|
type="episode"
|
|
xs
|
|
/>
|
|
}
|
|
{props.data.get("torrents") && props.data.get("torrents").toList().map(function(torrent) {
|
|
let key = `${props.data.get("season")}-${props.data.get("episode")}-${torrent.get("source")}-${torrent.get("quality")}`;
|
|
return (
|
|
<Torrent
|
|
data={torrent}
|
|
key={key}
|
|
addTorrent={props.addTorrent}
|
|
/>
|
|
)
|
|
})}
|
|
<DownloadButton
|
|
url={props.data.get("polochon_url")}
|
|
subtitles={props.data.get("subtitles")}
|
|
xs
|
|
/>
|
|
<GetDetailsButton
|
|
showName={props.showName}
|
|
router={props.router}
|
|
data={props.data}
|
|
getEpisodeDetails={props.getEpisodeDetails}
|
|
/>
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
)
|
|
}
|
|
|
|
class Torrent extends React.PureComponent {
|
|
constructor(props) {
|
|
super(props);
|
|
this.handleClick = this.handleClick.bind(this);
|
|
}
|
|
handleClick(e, url) {
|
|
e.preventDefault();
|
|
this.props.addTorrent(url);
|
|
}
|
|
render() {
|
|
return (
|
|
<span>
|
|
<a type="button"
|
|
className="btn btn-primary btn-xs"
|
|
onClick={(e) => this.handleClick(e, this.props.data.get("url"))}
|
|
href={this.props.data.url} >
|
|
<i className="fa fa-download"></i> {this.props.data.get("quality")}
|
|
</a>
|
|
</span>
|
|
)
|
|
}
|
|
}
|
|
|
|
class TrackHeader extends React.PureComponent {
|
|
constructor(props) {
|
|
super(props);
|
|
this.handleClick = this.handleClick.bind(this);
|
|
}
|
|
handleClick(e) {
|
|
e.preventDefault();
|
|
const trackedSeason = this.props.data.get("tracked_season");
|
|
const trackedEpisode = this.props.data.get("tracked_episode");
|
|
const imdbId = this.props.data.get("imdb_id");
|
|
const wishlisted = (trackedSeason !== null && trackedEpisode !== null);
|
|
if (wishlisted) {
|
|
this.props.deleteFromWishlist(imdbId);
|
|
} else {
|
|
this.props.addToWishlist(imdbId);
|
|
}
|
|
}
|
|
render() {
|
|
const trackedSeason = this.props.data.get("tracked_season");
|
|
const trackedEpisode = this.props.data.get("tracked_episode");
|
|
const wishlisted = (trackedSeason !== null && trackedEpisode !== null);
|
|
let msg;
|
|
if (wishlisted) {
|
|
if (trackedSeason !== 0 && trackedEpisode !== 0) {
|
|
msg = (
|
|
<dd>Show tracked from <strong>season {trackedSeason} episode {trackedEpisode}</strong></dd>
|
|
);
|
|
} else {
|
|
msg = (
|
|
<dd>Whole show tracked</dd>
|
|
);
|
|
}
|
|
return (
|
|
<dl className="dl-horizontal">
|
|
<dt>Tracking active</dt>
|
|
{msg}
|
|
<dt></dt>
|
|
<dd>
|
|
<a className="btn btn-xs btn-danger" onClick={(e) => this.handleClick(e)}>
|
|
<i className="fa fa-bookmark"></i> Untrack the show
|
|
</a>
|
|
</dd>
|
|
</dl>
|
|
);
|
|
} else {
|
|
return (
|
|
<dl className="dl-horizontal">
|
|
<dt>Tracking inactive</dt>
|
|
<dd>
|
|
<a className="btn btn-xs btn-info" onClick={(e) => this.handleClick(e)}>
|
|
<i className="fa fa-bookmark-o"></i> Track the whole show
|
|
</a>
|
|
</dd>
|
|
</dl>
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
class TrackButton extends React.PureComponent {
|
|
constructor(props) {
|
|
super(props);
|
|
this.handleClick = this.handleClick.bind(this);
|
|
}
|
|
handleClick(e) {
|
|
e.preventDefault();
|
|
const imdbId = this.props.data.get("show_imdb_id");
|
|
const season = this.props.data.get("season");
|
|
const episode = this.props.data.get("episode");
|
|
this.props.addToWishlist(imdbId, season, episode);
|
|
}
|
|
render() {
|
|
const tooltipId = `tooltip-${this.props.data.season}-${this.props.data.episode}`;
|
|
const tooltip = (
|
|
<Tooltip id={tooltipId}>Track show from here</Tooltip>
|
|
);
|
|
return (
|
|
<OverlayTrigger placement="top" overlay={tooltip}>
|
|
<a type="button" className="btn btn-default btn-xs" onClick={(e) => this.handleClick(e)}>
|
|
<i className="fa fa-bookmark"></i>
|
|
</a>
|
|
</OverlayTrigger>
|
|
);
|
|
}
|
|
}
|
|
|
|
class GetDetailsButton extends React.PureComponent {
|
|
constructor(props) {
|
|
super(props);
|
|
this.handleFetchClick = this.handleFetchClick.bind(this);
|
|
this.handleAdvanceTorrentSearchClick = this.handleAdvanceTorrentSearchClick.bind(this);
|
|
this.state = {
|
|
imdbId: this.props.data.get("show_imdb_id"),
|
|
season: this.props.data.get("season"),
|
|
episode: this.props.data.get("episode"),
|
|
};
|
|
}
|
|
handleFetchClick() {
|
|
if (this.props.data.get("fetching")) { return }
|
|
this.props.getEpisodeDetails(this.state.imdbId, this.state.season, this.state.episode);
|
|
}
|
|
handleAdvanceTorrentSearchClick() {
|
|
const pad = (d) => (d < 10) ? "0" + d.toString() : d.toString();
|
|
const search = `${this.props.showName} S${pad(this.state.season)}E${pad(this.state.episode)}`;
|
|
const url = `/torrents/search/shows/${encodeURI(search)}`;
|
|
this.props.router.push(url);
|
|
}
|
|
render() {
|
|
const id = `${this.state.imdbId}-${this.state.season}-${this.state.episode}-refresh-dropdown`;
|
|
return (
|
|
<Dropdown id={id} dropup>
|
|
<Button className="btn-xs" bsStyle="info" onClick={this.handleFetchClick}>
|
|
<RefreshIndicator refresh={this.props.data.get("fetching")} />
|
|
</Button>
|
|
<Dropdown.Toggle className="btn-xs" bsStyle="info"/>
|
|
<Dropdown.Menu>
|
|
<MenuItem onClick={this.handleAdvanceTorrentSearchClick}>
|
|
<span>
|
|
<i className="fa fa-magnet"></i> Advanced torrent search
|
|
</span>
|
|
</MenuItem>
|
|
</Dropdown.Menu>
|
|
</Dropdown>
|
|
);
|
|
}
|
|
}
|