Add show list
This commit is contained in:
parent
aaf3ca4213
commit
c98117c4f6
@ -1,18 +1,8 @@
|
||||
import { configureAxios, request } from '../requests'
|
||||
|
||||
// Select Movie
|
||||
export function selectMovie(imdbId) {
|
||||
return {
|
||||
type: 'SELECT_MOVIE',
|
||||
imdbId
|
||||
}
|
||||
}
|
||||
|
||||
export function isUserLoggedIn() {
|
||||
return {
|
||||
type: 'IS_USER_LOGGED_IN',
|
||||
}
|
||||
}
|
||||
// ======================
|
||||
// Errors
|
||||
// ======================
|
||||
|
||||
export function addError(message) {
|
||||
return {
|
||||
@ -29,12 +19,22 @@ export function dismissError() {
|
||||
}
|
||||
}
|
||||
|
||||
// ======================
|
||||
// Users
|
||||
// ======================
|
||||
|
||||
export function userLogout() {
|
||||
return {
|
||||
type: 'USER_LOGOUT',
|
||||
}
|
||||
}
|
||||
|
||||
export function isUserLoggedIn() {
|
||||
return {
|
||||
type: 'IS_USER_LOGGED_IN',
|
||||
}
|
||||
}
|
||||
|
||||
export function loginUser(username, password) {
|
||||
return request(
|
||||
'USER_LOGIN',
|
||||
@ -69,6 +69,17 @@ export function getUserInfos() {
|
||||
)
|
||||
}
|
||||
|
||||
// ======================
|
||||
// Movies
|
||||
// ======================
|
||||
|
||||
export function selectMovie(imdbId) {
|
||||
return {
|
||||
type: 'SELECT_MOVIE',
|
||||
imdbId
|
||||
}
|
||||
}
|
||||
|
||||
export function getMovieDetails(imdbId) {
|
||||
return request(
|
||||
'MOVIE_GET_DETAILS',
|
||||
@ -82,3 +93,21 @@ export function fetchMovies(url) {
|
||||
configureAxios().get(url)
|
||||
)
|
||||
}
|
||||
|
||||
// ======================
|
||||
// Shows
|
||||
// ======================
|
||||
|
||||
export function fetchShows(url) {
|
||||
return request(
|
||||
'SHOW_LIST_FETCH',
|
||||
configureAxios().get(url)
|
||||
)
|
||||
}
|
||||
|
||||
export function selectShow(imdbId) {
|
||||
return {
|
||||
type: 'SELECT_SHOW',
|
||||
imdbId
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ import store, { history } from './store'
|
||||
import NavBar from './components/navbar'
|
||||
import Error from './components/errors'
|
||||
import MovieList from './components/movies/list'
|
||||
import ShowList from './components/shows/list'
|
||||
import UserLoginForm from './components/users/login'
|
||||
import UserEdit from './components/users/edit'
|
||||
import UserSignUp from './components/users/signup'
|
||||
@ -53,6 +54,7 @@ class Main extends React.Component {
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
movieStore: state.movieStore,
|
||||
showStore: state.showStore,
|
||||
userStore: state.userStore,
|
||||
errors: state.errors,
|
||||
}
|
||||
@ -81,6 +83,10 @@ const MovieListPolochon = (props) => (
|
||||
<MovieList {...props} moviesUrl='/movies/polochon'/>
|
||||
)
|
||||
|
||||
const ShowListPopular = (props) => (
|
||||
<ShowList {...props} showsUrl='/shows/explore'/>
|
||||
)
|
||||
|
||||
ReactDOM.render((
|
||||
<Provider store={store}>
|
||||
<Router history={history}>
|
||||
@ -91,6 +97,7 @@ ReactDOM.render((
|
||||
<Route path="/users/edit" component={UserIsAuthenticated(UserEdit)} />
|
||||
<Route path="/movies/popular" component={UserIsAuthenticated(MovieListPopular)} />
|
||||
<Route path="/movies/polochon(/:page)" component={UserIsAuthenticated(MovieListPolochon)} />
|
||||
<Route path="/shows/popular" component={UserIsAuthenticated(ShowListPopular)} />
|
||||
</Route>
|
||||
</Router>
|
||||
</Provider>
|
||||
|
24
src/public/js/components/list/details.js
Normal file
24
src/public/js/components/list/details.js
Normal file
@ -0,0 +1,24 @@
|
||||
import React from 'react'
|
||||
|
||||
export default function ListDetails(props) {
|
||||
return (
|
||||
<div className="col-xs-7 col-md-4">
|
||||
<div className="show-detail affix">
|
||||
<h1 className="hidden-xs">{props.data.title}</h1>
|
||||
<h3 className="visible-xs">{props.data.title}</h3>
|
||||
{props.data.runtime &&
|
||||
<p>
|
||||
<i className="fa fa-clock-o"></i>
|
||||
{props.data.runtime} min
|
||||
</p>
|
||||
}
|
||||
<p>
|
||||
<i className="fa fa-star-o"></i>
|
||||
{props.data.rating} <small>({props.data.votes} counts)</small>
|
||||
</p>
|
||||
<p className="list-plot">{props.data.plot}</p>
|
||||
</div>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
22
src/public/js/components/list/filter.js
Normal file
22
src/public/js/components/list/filter.js
Normal file
@ -0,0 +1,22 @@
|
||||
import React from 'react'
|
||||
import { Control, Form } from 'react-redux-form';
|
||||
|
||||
export default function ListFilter(props) {
|
||||
return (
|
||||
<div className="col-xs-12 col-md-12 list-filter">
|
||||
<div className="row">
|
||||
<Form model={props.formModel} className="input-group" >
|
||||
<Control.text
|
||||
model={props.controlModel}
|
||||
className="form-control input-sm"
|
||||
placeholder={props.controlPlaceHolder}
|
||||
updateOn="change"
|
||||
/>
|
||||
<span className="input-group-btn">
|
||||
<button className="btn btn-default btn-sm" type="button">Filter</button>
|
||||
</span>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
16
src/public/js/components/list/poster.js
Normal file
16
src/public/js/components/list/poster.js
Normal file
@ -0,0 +1,16 @@
|
||||
import React from 'react'
|
||||
|
||||
export default function ListPoster(props) {
|
||||
const selected = props.selected ? ' thumbnail-selected' : '';
|
||||
const imgClass = 'thumbnail' + selected;
|
||||
return (
|
||||
<div className="col-xs-12 col-sm-6 col-md-3 col-lg-2">
|
||||
<a className={imgClass}>
|
||||
<img
|
||||
src={props.data.poster_url}
|
||||
onClick={props.onClick}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
45
src/public/js/components/list/posters.js
Normal file
45
src/public/js/components/list/posters.js
Normal file
@ -0,0 +1,45 @@
|
||||
import React from 'react'
|
||||
import fuzzy from 'fuzzy';
|
||||
|
||||
import ListFilter from './filter'
|
||||
import ListPoster from './poster'
|
||||
|
||||
export default function ListPosters(props) {
|
||||
let elmts = props.data.slice();
|
||||
|
||||
// Filter the list of elements
|
||||
if (props.filter !== "") {
|
||||
const filtered = fuzzy.filter(props.filter, elmts, {
|
||||
extract: (el) => el.title
|
||||
});
|
||||
elmts = filtered.map((el) => el.original);
|
||||
}
|
||||
|
||||
// Limit the number of results
|
||||
if (elmts.length > props.perPage) {
|
||||
elmts = elmts.slice(0, props.perPage);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="col-xs-5 col-md-8">
|
||||
<ListFilter
|
||||
formModel={props.formModel}
|
||||
controlModel={props.controlModel}
|
||||
controlPlaceHolder={props.controlPlaceHolder}
|
||||
/>
|
||||
<div className="row">
|
||||
{elmts.map(function(el, index) {
|
||||
const selected = (el.imdb_id === props.selectedImdbId) ? true : false;
|
||||
return (
|
||||
<ListPoster
|
||||
data={el}
|
||||
key={el.imdb_id}
|
||||
selected={selected}
|
||||
onClick={() => props.onClick(el.imdb_id)}
|
||||
/>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,101 +1,7 @@
|
||||
import React from 'react'
|
||||
import axios from 'axios'
|
||||
import { Control, Form } from 'react-redux-form';
|
||||
import fuzzy from 'fuzzy';
|
||||
|
||||
function MoviePosters(props) {
|
||||
let movies = props.movies.slice();
|
||||
|
||||
// Filter the movies
|
||||
if (props.filter !== "") {
|
||||
const filtered = fuzzy.filter(props.filter, movies, {
|
||||
extract: (el) => el.title
|
||||
});
|
||||
movies = filtered.map((el) => el.original);
|
||||
}
|
||||
|
||||
// Limit the number of results
|
||||
if (movies.length > props.perPage) {
|
||||
movies = movies.slice(0, props.perPage);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="col-xs-5 col-md-8">
|
||||
<MovieListFilter />
|
||||
<div className="row">
|
||||
{movies.map(function(movie, index) {
|
||||
const selected = (movie.imdb_id === props.selectedMovieId) ? true : false;
|
||||
return (
|
||||
<MoviePoster
|
||||
data={movie}
|
||||
key={movie.imdb_id}
|
||||
selected={selected}
|
||||
onClick={() => props.onClick(movie.imdb_id)}
|
||||
/>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
class MovieListFilter extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="col-xs-12 col-md-12 movie-list-filter">
|
||||
<div className="row">
|
||||
<Form model="movieStore" className="input-group" >
|
||||
<Control.text
|
||||
model="movieStore.filter"
|
||||
className="form-control input-sm"
|
||||
placeholder="Filter movies..."
|
||||
updateOn="change"
|
||||
/>
|
||||
<span className="input-group-btn">
|
||||
<button className="btn btn-default btn-sm" type="button">Filter</button>
|
||||
</span>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function MoviePoster(props) {
|
||||
const selected = props.selected ? ' thumbnail-selected' : '';
|
||||
const imgClass = 'thumbnail' + selected;
|
||||
return (
|
||||
<div className="col-xs-12 col-sm-6 col-md-3 col-lg-2">
|
||||
<a className={imgClass}>
|
||||
<img
|
||||
src={props.data.poster_url}
|
||||
onClick={props.onClick}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function MovieDetails(props) {
|
||||
return (
|
||||
<div className="col-xs-7 col-md-4">
|
||||
<div className="movie-detail affix">
|
||||
<h1 className="hidden-xs">{props.movie.title}</h1>
|
||||
<h3 className="visible-xs">{props.movie.title}</h3>
|
||||
<p>
|
||||
<i className="fa fa-clock-o"></i>
|
||||
{props.movie.runtime} min
|
||||
</p>
|
||||
<p>
|
||||
<i className="fa fa-star-o"></i>
|
||||
{props.movie.rating} <small>({props.movie.votes} counts)</small>
|
||||
</p>
|
||||
<p className="movie-plot">{props.movie.plot}</p>
|
||||
</div>
|
||||
<MovieButtons {...props} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
import ListPosters from '../list/posters'
|
||||
import ListDetails from '../list/details'
|
||||
|
||||
class MovieButtons extends React.Component {
|
||||
constructor(props) {
|
||||
@ -112,7 +18,7 @@ class MovieButtons extends React.Component {
|
||||
render() {
|
||||
const imdb_link = `http://www.imdb.com/title/${this.props.movie.imdb_id}`;
|
||||
return (
|
||||
<div className="movie-details-buttons btn-toolbar">
|
||||
<div className="list-details-buttons btn-toolbar">
|
||||
<a type="button" className="btn btn-default btn-sm" onClick={this.handleClick}>
|
||||
{this.props.fetching ||
|
||||
<span>
|
||||
@ -159,19 +65,24 @@ export default class MovieList extends React.Component {
|
||||
const selectedMovie = movies[index];
|
||||
return (
|
||||
<div className="row" id="container">
|
||||
<MoviePosters
|
||||
movies={movies}
|
||||
selectedMovieId={selectedMovieId}
|
||||
<ListPosters
|
||||
data={movies}
|
||||
formModel="movieStore"
|
||||
controlModel="movieStore.filter"
|
||||
controlPlaceHolder="Filter movies..."
|
||||
selectedImdbId={selectedMovieId}
|
||||
filter={this.props.movieStore.filter}
|
||||
perPage={this.props.movieStore.perPage}
|
||||
onClick={this.props.selectMovie}
|
||||
/>
|
||||
{selectedMovie &&
|
||||
<MovieDetails
|
||||
<ListDetails data={selectedMovie}>
|
||||
<MovieButtons
|
||||
movie={selectedMovie}
|
||||
fetching={this.props.movieStore.fetchingDetails}
|
||||
getMovieDetails={this.props.getMovieDetails}
|
||||
/>
|
||||
</ListDetails>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
|
@ -27,6 +27,9 @@ export default class NavBar extends React.Component {
|
||||
<LinkContainer to="/movies/polochon">
|
||||
<NavItem>Polochon movies</NavItem>
|
||||
</LinkContainer>
|
||||
<LinkContainer to="/shows/popular">
|
||||
<NavItem>Polochon shows</NavItem>
|
||||
</LinkContainer>
|
||||
</Nav>
|
||||
<Nav pullRight>
|
||||
{isLoggedIn ||
|
||||
|
49
src/public/js/components/shows/list.js
Normal file
49
src/public/js/components/shows/list.js
Normal file
@ -0,0 +1,49 @@
|
||||
import React from 'react'
|
||||
|
||||
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}`;
|
||||
return (
|
||||
<div className="list-details-buttons btn-toolbar">
|
||||
<a type="button" className="btn btn-warning btn-sm" href={imdb_link}>
|
||||
<i className="fa fa-external-link"></i> IMDB
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default class ShowList extends React.Component {
|
||||
componentWillMount() {
|
||||
this.props.fetchShows(this.props.showsUrl);
|
||||
}
|
||||
render() {
|
||||
const shows = this.props.showStore.shows;
|
||||
const selectedShowId = this.props.showStore.selectedImdbId;
|
||||
let index = shows.map((el) => el.imdb_id).indexOf(selectedShowId);
|
||||
if (index === -1) {
|
||||
index = 0;
|
||||
}
|
||||
const selectedShow = shows[index];
|
||||
return (
|
||||
<div className="row" id="container">
|
||||
<ListPosters
|
||||
data={shows}
|
||||
formModel="showStore"
|
||||
controlModel="showStore.filter"
|
||||
controlPlaceHolder="Filter shows..."
|
||||
selectedImdbId={selectedShowId}
|
||||
filter={this.props.showStore.filter}
|
||||
perPage={this.props.showStore.perPage}
|
||||
onClick={this.props.selectShow}
|
||||
/>
|
||||
{selectedShow &&
|
||||
<ListDetails data={selectedShow} >
|
||||
<ShowButtons show={selectedShow} />
|
||||
</ListDetails>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ import { combineForms } from 'react-redux-form'
|
||||
import { routerReducer } from 'react-router-redux'
|
||||
|
||||
import movieStore from './movies'
|
||||
import showStore from './shows'
|
||||
import userStore from './users'
|
||||
import errors from './errors'
|
||||
|
||||
@ -11,6 +12,7 @@ import errors from './errors'
|
||||
const rootReducer = combineForms({
|
||||
routing: routerReducer,
|
||||
movieStore,
|
||||
showStore,
|
||||
userStore,
|
||||
errors,
|
||||
})
|
||||
|
32
src/public/js/reducers/shows.js
Normal file
32
src/public/js/reducers/shows.js
Normal file
@ -0,0 +1,32 @@
|
||||
const defaultState = {
|
||||
shows: [],
|
||||
filter: "",
|
||||
perPage: 30,
|
||||
selectedImdbId: "",
|
||||
};
|
||||
|
||||
export default function showStore(state = defaultState, action) {
|
||||
switch (action.type) {
|
||||
case 'SHOW_LIST_FETCH_FULFILLED':
|
||||
let selectedImdbId = "";
|
||||
// Select the first show
|
||||
if (action.payload.data.length > 0) {
|
||||
selectedImdbId = action.payload.data[0].imdb_id;
|
||||
}
|
||||
return Object.assign({}, state, {
|
||||
shows: action.payload.data,
|
||||
selectedImdbId: selectedImdbId,
|
||||
})
|
||||
case 'SELECT_SHOW':
|
||||
// Don't select the show if we're fetching another show's details
|
||||
if (state.fetchingDetails) {
|
||||
return state
|
||||
}
|
||||
|
||||
return Object.assign({}, state, {
|
||||
selectedImdbId: action.imdbId,
|
||||
})
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
@ -12,18 +12,18 @@ body {
|
||||
background-color: @brand-primary;
|
||||
}
|
||||
|
||||
.movie-plot {
|
||||
.list-plot {
|
||||
.text-justify;
|
||||
margin-right: 5%;
|
||||
}
|
||||
|
||||
.movie-details-buttons {
|
||||
.list-details-buttons {
|
||||
position: fixed;
|
||||
bottom: 1%;
|
||||
right: 1%;
|
||||
}
|
||||
|
||||
.movie-list-filter {
|
||||
.list-filter {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user