diff --git a/src/public/js/actions/actionCreators.js b/src/public/js/actions/actionCreators.js index 3603633..e0c1665 100644 --- a/src/public/js/actions/actionCreators.js +++ b/src/public/js/actions/actionCreators.js @@ -105,6 +105,13 @@ export function fetchShows(url) { ) } +export function fetchShowDetails(imdbId) { + return request( + 'SHOW_FETCH_DETAILS', + configureAxios().get(`/shows/${imdbId}`) + ) +} + export function selectShow(imdbId) { return { type: 'SELECT_SHOW', diff --git a/src/public/js/app.js b/src/public/js/app.js index c82d250..b75f21e 100644 --- a/src/public/js/app.js +++ b/src/public/js/app.js @@ -30,6 +30,7 @@ import NavBar from './components/navbar' import Error from './components/errors' import MovieList from './components/movies/list' import ShowList from './components/shows/list' +import ShowDetails from './components/shows/details' import UserLoginForm from './components/users/login' import UserEdit from './components/users/edit' import UserSignUp from './components/users/signup' @@ -87,6 +88,10 @@ const ShowListPopular = (props) => ( ) +const ShowDetailsView = (props) => ( + +) + ReactDOM.render(( @@ -98,6 +103,7 @@ ReactDOM.render(( + diff --git a/src/public/js/components/list/details.js b/src/public/js/components/list/details.js index ef5945a..12ca5e8 100644 --- a/src/public/js/components/list/details.js +++ b/src/public/js/components/list/details.js @@ -16,7 +16,7 @@ export default function ListDetails(props) {  {props.data.rating} ({props.data.votes} counts)

-

{props.data.plot}

+

{props.data.plot}

{props.children} diff --git a/src/public/js/components/shows/details.js b/src/public/js/components/shows/details.js new file mode 100644 index 0000000..9f33991 --- /dev/null +++ b/src/public/js/components/shows/details.js @@ -0,0 +1,145 @@ +import React from 'react' + +export default class ShowDetails extends React.Component { + componentWillMount() { + this.props.fetchShowDetails(this.props.params.imdbId); + } + render() { + return ( +
+
+ +
+ ); + } +} + +function Header(props){ + return ( +
+
+
+ + +
+
+
+ ); +} + +function HeaderThumbnail(props){ + return ( +
+ +
+ ); +} + +function HeaderDetails(props){ + const imdbLink = `http://www.imdb.com/title/${props.data.imdb_id}`; + return ( +
+
+
Title
+
{props.data.title}
+
Plot
+
{props.data.plot}
+
IMDB
+
+ + Open in IMDB + +
+
Year
+
{props.data.year}
+
Rating
+
{props.data.rating}
+
+
+ ); +} + +function SeasonsList(props){ + return ( +
+ {props.data.seasons.length > 0 && props.data.seasons.map(function(season, index) { + return ( +
+ +
+ ) + })} +
+ ) +} + +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 ( +
+
this.handleClick(e)}> + Season {this.props.data.season} + — ({this.props.data.episodes.length} episodes) + + {this.state.colapsed || + + } + {this.state.colapsed && + + } + +
+ {this.state.colapsed || + + + {this.props.data.episodes.map(function(episode, index) { + let key = `${episode.season}-${episode.episode}`; + return ( + + ) + })} + +
+ } +
+ ) + } +} + +function Episode(props) { + return ( + + {props.data.episode} + {props.data.title} + + + {props.data.torrents && props.data.torrents.map(function(torrent, index) { + let key = `${props.data.season}-${props.data.episode}-${torrent.source}-${torrent.quality}`; + return ( + + ) + })} + + + + ) +} + +function Torrent(props) { + return ( + + + {props.data.quality} + + + ) +} diff --git a/src/public/js/components/shows/list.js b/src/public/js/components/shows/list.js index fb1ef4d..1d83f0c 100644 --- a/src/public/js/components/shows/list.js +++ b/src/public/js/components/shows/list.js @@ -1,15 +1,19 @@ import React from 'react' +import { Link } from 'react-router' import ListDetails from '../list/details' import ListPosters from '../list/posters' function ShowButtons(props) { - const imdb_link = `http://www.imdb.com/title/${props.show.imdb_id}`; + const imdbLink = `http://www.imdb.com/title/${props.show.imdb_id}`; return (
- + IMDB + + Details +
); } diff --git a/src/public/js/reducers/shows.js b/src/public/js/reducers/shows.js index de3f99d..43ed2af 100644 --- a/src/public/js/reducers/shows.js +++ b/src/public/js/reducers/shows.js @@ -3,6 +3,9 @@ const defaultState = { filter: "", perPage: 30, selectedImdbId: "", + show: { + seasons: [], + }, }; export default function showStore(state = defaultState, action) { @@ -17,6 +20,11 @@ export default function showStore(state = defaultState, action) { shows: action.payload.data, selectedImdbId: selectedImdbId, }) + case 'SHOW_FETCH_DETAILS_FULFILLED': + return Object.assign({}, state, { + show: sortEpisodes(action.payload.data), + }) + return state; case 'SELECT_SHOW': // Don't select the show if we're fetching another show's details if (state.fetchingDetails) { @@ -30,3 +38,47 @@ export default function showStore(state = defaultState, action) { return state } } + +function sortEpisodes(show) { + let episodes = show.episodes; + delete show["episodes"]; + + if (episodes.length == 0) { + return show; + } + + // Extract the seasons + let seasons = {}; + for (let ep of episodes) { + if (!seasons[ep.season]) { + seasons[ep.season] = { episodes: [] }; + } + seasons[ep.season].episodes.push(ep); + } + + if (seasons.length === 0) { + return show; + } + + // Put all the season in an array + let sortedSeasons = []; + for (let season of Object.keys(seasons)) { + let seasonEpisodes = seasons[season].episodes; + // Order the episodes in each season + seasonEpisodes.sort((a,b) => (a.episode - b.episode)) + // Add the season in the list + sortedSeasons.push({ + season: season, + episodes: seasonEpisodes, + }) + } + + // Order the seasons + for (let i=0; i (a.season - b.season)) + } + + show.seasons = sortedSeasons; + + return show; +} diff --git a/src/public/less/app.less b/src/public/less/app.less index 638753c..121e568 100644 --- a/src/public/less/app.less +++ b/src/public/less/app.less @@ -12,7 +12,11 @@ body { background-color: @brand-primary; } -.list-plot { +.clickable { + cursor: pointer; +} + +.plot { .text-justify; margin-right: 5%; } @@ -27,6 +31,14 @@ body { padding-bottom: 10px; } +.show-thumbnail { + max-height: 300px; +} + +.episode-button { + padding-right: 5px; +} + .navbar { opacity: 0.95; }