Update the movie store to be immutable
This commit is contained in:
parent
697997c0ae
commit
80db4383a3
@ -14,7 +14,18 @@ export function updateLastMovieFetchUrl(url) {
|
||||
export function selectMovie(imdbId) {
|
||||
return {
|
||||
type: 'SELECT_MOVIE',
|
||||
imdbId
|
||||
payload: {
|
||||
imdbId,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function updateFilter(filter) {
|
||||
return {
|
||||
type: 'MOVIE_UPDATE_FILTER',
|
||||
payload: {
|
||||
filter,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +39,11 @@ export function getMovieExploreOptions() {
|
||||
export function getMovieDetails(imdbId) {
|
||||
return request(
|
||||
'MOVIE_GET_DETAILS',
|
||||
configureAxios().post(`/movies/${imdbId}/refresh`)
|
||||
configureAxios().post(`/movies/${imdbId}/refresh`),
|
||||
null,
|
||||
{
|
||||
imdbId,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@ -48,7 +63,6 @@ export function addMovieToWishlist(imdbId) {
|
||||
'MOVIE_ADD_TO_WISHLIST',
|
||||
configureAxios().post(`/wishlist/movies/${imdbId}`),
|
||||
[
|
||||
addAlertOk("Movie added to the wishlist"),
|
||||
updateMovieWishlistStore(imdbId, true),
|
||||
],
|
||||
)
|
||||
@ -59,7 +73,6 @@ export function deleteMovieFromWishlist(imdbId) {
|
||||
'MOVIE_DELETE_FROM_WISHLIST',
|
||||
configureAxios().delete(`/wishlist/movies/${imdbId}`),
|
||||
[
|
||||
addAlertOk("Movie deleted from the wishlist"),
|
||||
updateMovieWishlistStore(imdbId, false),
|
||||
],
|
||||
)
|
||||
|
@ -10,7 +10,7 @@ export function refreshSubtitles(type, id, season, episode) {
|
||||
'MOVIE_SUBTITLES_UPDATE',
|
||||
configureAxios().post(`${resourceURL}/subtitles/refresh`),
|
||||
null,
|
||||
{ imdb_id: id },
|
||||
{ imdbId: id },
|
||||
)
|
||||
case 'episode':
|
||||
var resourceURL = `/shows/${id}/seasons/${season}/episodes/${episode}`
|
||||
@ -19,12 +19,12 @@ export function refreshSubtitles(type, id, season, episode) {
|
||||
configureAxios().post(`${resourceURL}/subtitles/refresh`),
|
||||
null,
|
||||
{
|
||||
imdb_id: id,
|
||||
imdbId: id,
|
||||
season: season,
|
||||
episode: episode,
|
||||
},
|
||||
)
|
||||
default:
|
||||
console.log("refreshSubtitles - Unknown type " + type)
|
||||
console.warn("refreshSubtitles - Unknown type " + type)
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ import getRoutes from './routes'
|
||||
|
||||
function mapStateToProps(state) {
|
||||
let torrentCount = 0;
|
||||
if (state.torrentStore.has('torrents')) {
|
||||
if (state.torrentStore.has('torrents') && state.torrentStore.get('torrents') !== undefined) {
|
||||
torrentCount = state.torrentStore.get('torrents').size;
|
||||
}
|
||||
return {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import { toJS } from 'immutable'
|
||||
|
||||
import { Button, Dropdown, MenuItem, Modal } from 'react-bootstrap'
|
||||
|
||||
@ -69,20 +70,13 @@ export default class DownloadButton extends React.Component {
|
||||
class Player extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
var subtitles = [];
|
||||
if (props.subtitles && props.subtitles.length) {
|
||||
subtitles = props.subtitles;
|
||||
}
|
||||
this.state = {
|
||||
subtitles: subtitles,
|
||||
};
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div className="embed-responsive embed-responsive-16by9">
|
||||
<video controls>
|
||||
<source src={this.props.url} type="video/mp4"/>
|
||||
{this.props.subtitles.map(function(el, index) {
|
||||
{this.props.subtitles !== undefined && subtitles.toKeyedSeq().map(function(el, index) {
|
||||
return (
|
||||
<track
|
||||
key={index}
|
||||
|
@ -9,9 +9,8 @@ export default class SubtitlesButton extends React.Component {
|
||||
}
|
||||
handleClick(e, url) {
|
||||
e.preventDefault();
|
||||
if (this.props.fetching) {
|
||||
return
|
||||
}
|
||||
if (this.props.fetching) { return }
|
||||
|
||||
// Refresh the subtitles
|
||||
this.props.refreshSubtitles(this.props.type, this.props.resourceID, this.props.season, this.props.episode);
|
||||
}
|
||||
@ -35,16 +34,16 @@ export default class SubtitlesButton extends React.Component {
|
||||
case 'action':
|
||||
return (
|
||||
<MenuItem key={index} onClick={(event) => this.handleClick(event, e.url)}>
|
||||
{this.props.fetchingSubtitles ||
|
||||
{this.props.fetching ||
|
||||
<span>
|
||||
<i className="fa fa-refresh">
|
||||
</i> Refresh
|
||||
<i className="fa fa-refresh">
|
||||
</i> Refresh
|
||||
</span>
|
||||
}
|
||||
{this.props.fetchingSubtitles &&
|
||||
{this.props.fetching &&
|
||||
<span>
|
||||
<i className="fa fa-spin fa-refresh">
|
||||
</i> Refreshing
|
||||
<i className="fa fa-spin fa-refresh">
|
||||
</i> Refreshing
|
||||
</span>
|
||||
}
|
||||
</MenuItem>
|
||||
@ -77,7 +76,7 @@ function buildMenuItems(subtitles) {
|
||||
});
|
||||
|
||||
// If there is no subtitles, stop here
|
||||
if (!subtitles) {
|
||||
if (subtitles == undefined) {
|
||||
return entries;
|
||||
}
|
||||
|
||||
@ -88,8 +87,8 @@ function buildMenuItems(subtitles) {
|
||||
entries.push({
|
||||
type: "entry",
|
||||
// Take only the last part of fr_FR
|
||||
lang: sub.language.split("_")[1],
|
||||
url: sub.url,
|
||||
lang: sub.get('language').split("_")[1],
|
||||
url: sub.get('url'),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,46 +1,47 @@
|
||||
import React from 'react'
|
||||
|
||||
export default function ListDetails(props) {
|
||||
let genres;
|
||||
if (props.data.genres) {
|
||||
let genres = props.data.get('genres');
|
||||
if (genres !== undefined) {
|
||||
// Uppercase first genres
|
||||
genres = props.data.genres.map(
|
||||
genres = genres.toJS().map(
|
||||
(word) => word[0].toUpperCase() + word.substr(1)
|
||||
).join(', ');
|
||||
}
|
||||
|
||||
let wishlistStr = "";
|
||||
if (props.data.wishlisted === true) {
|
||||
if (props.data.get('wishlisted') === true) {
|
||||
wishlistStr = "Wishlisted";
|
||||
}
|
||||
if (props.data.tracked_episode !== null && props.data.tracked_season != null) {
|
||||
let season = props.data.tracked_season;
|
||||
let episode = props.data.tracked_episode;
|
||||
if ((season === 0) && (episode === 0)) {
|
||||
|
||||
const trackedSeason = props.data.get('tracked_season');
|
||||
const trackedEpisode = props.data.get('tracked_episode');
|
||||
if (trackedEpisode !== undefined && trackedSeason !== undefined) {
|
||||
if ((trackedSeason === 0) && (trackedEpisode === 0)) {
|
||||
wishlistStr = "Whole show tracked";
|
||||
} else {
|
||||
wishlistStr = `Tracked from season ${season} episode ${episode}`;
|
||||
wishlistStr = `Tracked from season ${trackedSeason} episode ${trackedEpisode}`;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="col-xs-7 col-md-4">
|
||||
<div className="affix">
|
||||
<h1 className="hidden-xs">{props.data.title}</h1>
|
||||
<h3 className="visible-xs">{props.data.title}</h3>
|
||||
<h1 className="hidden-xs">{props.data.get('title')}</h1>
|
||||
<h3 className="visible-xs">{props.data.get('title')}</h3>
|
||||
{wishlistStr !== "" &&
|
||||
<span className="label label-default">
|
||||
<i className="fa fa-bookmark"></i> {wishlistStr}
|
||||
</span>
|
||||
}
|
||||
<h4>{props.data.year}</h4>
|
||||
{props.data.runtime &&
|
||||
<h4>{props.data.get('year')}</h4>
|
||||
{props.data.get('runtime') !== undefined &&
|
||||
<p>
|
||||
<i className="fa fa-clock-o"></i>
|
||||
{props.data.runtime} min
|
||||
{props.data.get('runtime')} min
|
||||
</p>
|
||||
}
|
||||
{props.data.genres &&
|
||||
{genres !== undefined &&
|
||||
<p>
|
||||
<i className="fa fa-tags"></i>
|
||||
{genres}
|
||||
@ -48,12 +49,12 @@ export default function ListDetails(props) {
|
||||
}
|
||||
<p>
|
||||
<i className="fa fa-star-o"></i>
|
||||
{Number(props.data.rating).toFixed(1)}
|
||||
{props.data.votes &&
|
||||
<small>({props.data.votes} counts)</small>
|
||||
{Number(props.data.get('rating')).toFixed(1)}
|
||||
{props.data.get('votes') !== undefined &&
|
||||
<small>({props.data.get('votes')} counts)</small>
|
||||
}
|
||||
</p>
|
||||
<p className="plot">{props.data.plot}</p>
|
||||
<p className="plot">{props.data.get('plot')}</p>
|
||||
</div>
|
||||
{props.children}
|
||||
</div>
|
||||
|
@ -9,7 +9,7 @@ export default class ExplorerOptions extends React.Component {
|
||||
}
|
||||
handleSourceChange(event) {
|
||||
let source = event.target.value;
|
||||
let category = this.props.options[event.target.value][0];
|
||||
let category = this.props.options.get(event.target.value).first();
|
||||
this.props.router.push(`/${this.props.type}/explore/${source}/${category}`);
|
||||
}
|
||||
handleCategoryChange(event) {
|
||||
@ -40,7 +40,7 @@ export default class ExplorerOptions extends React.Component {
|
||||
}
|
||||
|
||||
// Options are not yet fetched
|
||||
if (Object.keys(this.props.options).length === 0) {
|
||||
if (this.props.options.size === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ export default class ExplorerOptions extends React.Component {
|
||||
|
||||
let source = this.props.params.source;
|
||||
let category = this.props.params.category;
|
||||
let categories = this.props.options[this.props.params.source];
|
||||
let categories = this.props.options.get(this.props.params.source);
|
||||
|
||||
return (
|
||||
<div className="row">
|
||||
@ -67,8 +67,9 @@ export default class ExplorerOptions extends React.Component {
|
||||
onChange={this.handleSourceChange}
|
||||
value={source}
|
||||
>
|
||||
{Object.keys(this.props.options).map(function(source) {
|
||||
return (<option key={source} value={source}>{this.prettyName(source)}</option>)
|
||||
{this.props.options.entrySeq().map(function([source, categories]) {
|
||||
return (
|
||||
<option key={source} value={source}>{this.prettyName(source)}</option>)
|
||||
}, this)}
|
||||
</FormControl>
|
||||
</FormGroup>
|
||||
|
@ -1,30 +1,42 @@
|
||||
import React from 'react'
|
||||
import { Control, Form } from 'react-redux-form';
|
||||
|
||||
export default function ListFilter(props) {
|
||||
if (!props.display) {
|
||||
return null;
|
||||
export default class ListFilter extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { filter: '' };
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
handleChange(ev) {
|
||||
if (ev) { ev.preventDefault(); }
|
||||
const value = this.input.value;
|
||||
if (this.state.filter === value) { return }
|
||||
this.setState({ filter: value });
|
||||
|
||||
if (props.listSize === 0) {
|
||||
return null;
|
||||
// Start filtering at 3 chars
|
||||
if (value.length >= 3) {
|
||||
this.props.updateFilter(value);
|
||||
} else {
|
||||
this.props.updateFilter('');
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="col-xs-12 col-md-12 list-filter">
|
||||
<Form model={props.formModel} className="input-group hidebtn-xs" >
|
||||
<Control.text
|
||||
model={props.controlModel}
|
||||
className="form-control input-sm"
|
||||
placeholder={props.controlPlaceHolder}
|
||||
updateOn="change"
|
||||
/>
|
||||
<span className="input-group-btn hidden-xs">
|
||||
<button className="btn btn-default btn-sm" type="button">Filter</button>
|
||||
</span>
|
||||
</Form>
|
||||
render() {
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="col-xs-12 col-md-12 list-filter">
|
||||
<form className="input-group" onSubmit={(ev) => this.handleChange(ev)}>
|
||||
<input
|
||||
className="form-control input-sm"
|
||||
placeholder={this.props.placeHolder}
|
||||
onChange={this.handleChange}
|
||||
ref={(input) => this.input = input}
|
||||
value={this.state.filter}
|
||||
/>
|
||||
<span className="input-group-btn hidden-xs">
|
||||
<button className="btn btn-default btn-sm" type="button">Filter</button>
|
||||
</span>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +1,35 @@
|
||||
import React from 'react'
|
||||
|
||||
export default function ListPoster(props) {
|
||||
const selected = props.selected ? ' thumbnail-selected' : '';
|
||||
const imgClass = 'thumbnail' + selected;
|
||||
const displayClearFixLg = (props.index % 6) === 0;
|
||||
const displayClearFixMd = (props.index % 4) === 0;
|
||||
const displayClearFixSm = (props.index % 2) === 0;
|
||||
return (
|
||||
<div>
|
||||
{displayClearFixLg &&
|
||||
<div className="clearfix visible-lg"></div>
|
||||
}
|
||||
{displayClearFixMd &&
|
||||
<div className="clearfix visible-md"></div>
|
||||
}
|
||||
{displayClearFixSm &&
|
||||
<div className="clearfix visible-sm"></div>
|
||||
}
|
||||
<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>
|
||||
export default class ListPoster extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
const selected = this.props.selected ? ' thumbnail-selected' : '';
|
||||
const imgClass = 'thumbnail' + selected;
|
||||
const displayClearFixLg = (this.props.index % 6) === 0;
|
||||
const displayClearFixMd = (this.props.index % 4) === 0;
|
||||
const displayClearFixSm = (this.props.index % 2) === 0;
|
||||
return (
|
||||
<div>
|
||||
{displayClearFixLg &&
|
||||
<div className="clearfix visible-lg"></div>
|
||||
}
|
||||
{displayClearFixMd &&
|
||||
<div className="clearfix visible-md"></div>
|
||||
}
|
||||
{displayClearFixSm &&
|
||||
<div className="clearfix visible-sm"></div>
|
||||
}
|
||||
<div className="col-xs-12 col-sm-6 col-md-3 col-lg-2">
|
||||
<a className={imgClass}>
|
||||
<img
|
||||
src={this.props.data.get('poster_url')}
|
||||
onClick={this.props.onClick}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import { Map, List, fromJS } from 'immutable'
|
||||
|
||||
import fuzzy from 'fuzzy';
|
||||
import InfiniteScroll from 'react-infinite-scroller';
|
||||
@ -24,14 +25,14 @@ export default class ListPosters extends React.Component {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.props.data.length) {
|
||||
if (this.props.data === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState(this.getNextState(this.props));
|
||||
}
|
||||
getNextState(props) {
|
||||
let totalListSize = props.data.length;
|
||||
let totalListSize = props.data !== undefined ? props.data.size : 0;
|
||||
let currentListSize = (this.state && this.state.items) ? this.state.items : 0;
|
||||
let nextListSize = currentListSize + DEFAULT_ADD_EXTRA_ITEMS;
|
||||
let hasMore = true;
|
||||
@ -47,44 +48,44 @@ export default class ListPosters extends React.Component {
|
||||
};
|
||||
}
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (this.props.data.length !== nextProps.data.length) {
|
||||
if (this.props.data === undefined) { return }
|
||||
if (nextProps.data === undefined) { return }
|
||||
|
||||
if (this.props.data.size !== nextProps.data.size) {
|
||||
this.setState(this.getNextState(nextProps));
|
||||
}
|
||||
}
|
||||
render() {
|
||||
let elmts = this.props.data.slice();
|
||||
const listSize = elmts.length;
|
||||
let elmts = this.props.data;
|
||||
const listSize = elmts !== undefined ? elmts.size : 0;
|
||||
const colSize = (listSize !== 0) ? "col-xs-5 col-md-8" : "col-xs-12";
|
||||
|
||||
// Filter the list of elements
|
||||
if (this.props.filter !== "") {
|
||||
const filtered = fuzzy.filter(this.props.filter, elmts, {
|
||||
extract: (el) => el.title
|
||||
});
|
||||
elmts = filtered.map((el) => el.original);
|
||||
} else {
|
||||
elmts = elmts.slice(0, this.state.items);
|
||||
}
|
||||
// Filter the list of elements
|
||||
if (this.props.filter !== "") {
|
||||
elmts = elmts.filter((v) => fuzzy.test(this.props.filter, v.get('title')), this);
|
||||
} else {
|
||||
elmts = elmts.slice(0, this.state.items);
|
||||
}
|
||||
|
||||
// Chose when to display filter / explore options
|
||||
let displayFilter = true;
|
||||
if (this.props.params
|
||||
if ((this.props.params
|
||||
&& this.props.params.category
|
||||
&& this.props.params.category !== ""
|
||||
&& this.props.params.source
|
||||
&& this.props.params.source !== "") {
|
||||
&& this.props.params.source !== "")
|
||||
|| (listSize === 0)) {
|
||||
displayFilter = false;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={colSize}>
|
||||
<ListFilter
|
||||
listSize={listSize}
|
||||
display={displayFilter}
|
||||
formModel={this.props.formModel}
|
||||
controlModel={this.props.filterControlModel}
|
||||
controlPlaceHolder={this.props.filterControlPlaceHolder}
|
||||
/>
|
||||
{displayFilter &&
|
||||
<ListFilter
|
||||
updateFilter={this.props.updateFilter}
|
||||
placeHolder={this.props.placeHolder}
|
||||
/>
|
||||
}
|
||||
<ExplorerOptions
|
||||
type={this.props.type}
|
||||
display={!displayFilter}
|
||||
@ -105,39 +106,45 @@ export default class ListPosters extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
function Posters(props) {
|
||||
if (props.loading) {
|
||||
return (<Loader />);
|
||||
class Posters extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
if (this.props.loading) {
|
||||
return (<Loader />);
|
||||
}
|
||||
|
||||
if (this.props.elmts.size === 0) {
|
||||
return (
|
||||
<div className="jumbotron">
|
||||
<h2>No result</h2>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (props.elmts.length === 0) {
|
||||
return (
|
||||
<div className="jumbotron">
|
||||
<h2>No result</h2>
|
||||
<div>
|
||||
<InfiniteScroll
|
||||
hasMore={this.props.hasMore}
|
||||
loadMore={this.props.loadMore}
|
||||
className="row"
|
||||
>
|
||||
{this.props.elmts.toIndexedSeq().map(function(movie, index) {
|
||||
const imdbId = movie.get('imdb_id');
|
||||
const selected = (imdbId === this.props.selectedImdbId) ? true : false;
|
||||
return (
|
||||
<ListPoster
|
||||
index={index}
|
||||
data={movie}
|
||||
key={imdbId}
|
||||
selected={selected}
|
||||
onClick={() => this.props.onClick(imdbId)}
|
||||
/>
|
||||
)
|
||||
} ,this)}
|
||||
</InfiniteScroll>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<InfiniteScroll
|
||||
hasMore={props.hasMore}
|
||||
loadMore={props.loadMore}
|
||||
className="row"
|
||||
>
|
||||
{props.elmts.map(function(el, index) {
|
||||
const selected = (el.imdb_id === props.selectedImdbId) ? true : false;
|
||||
return (
|
||||
<ListPoster
|
||||
index={index}
|
||||
data={el}
|
||||
key={el.imdb_id}
|
||||
selected={selected}
|
||||
onClick={() => props.onClick(el.imdb_id)}
|
||||
/>
|
||||
)}
|
||||
)}
|
||||
</InfiniteScroll>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ import { connect } from 'react-redux'
|
||||
import { bindActionCreators } from 'redux'
|
||||
import { addTorrent } from '../../actions/torrents'
|
||||
import { refreshSubtitles } from '../../actions/subtitles'
|
||||
import { addMovieToWishlist, deleteMovieFromWishlist,
|
||||
getMovieDetails, selectMovie } from '../../actions/movies'
|
||||
import { addMovieToWishlist, deleteMovie, deleteMovieFromWishlist,
|
||||
getMovieDetails, selectMovie, updateFilter } from '../../actions/movies'
|
||||
|
||||
import DownloadButton from '../buttons/download'
|
||||
import SubtitlesButton from '../buttons/subtitles'
|
||||
@ -14,48 +14,55 @@ import ListPosters from '../list/posters'
|
||||
import ListDetails from '../list/details'
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return { movieStore: state.movieStore };
|
||||
return {
|
||||
loading : state.movieStore.get('loading'),
|
||||
movies : state.movieStore.get('movies'),
|
||||
filter : state.movieStore.get('filter'),
|
||||
selectedImdbId : state.movieStore.get('selectedImdbId'),
|
||||
lastFetchUrl : state.movieStore.get('lastFetchUrl'),
|
||||
exploreOptions : state.movieStore.get('exploreOptions'),
|
||||
};
|
||||
}
|
||||
const mapDispatchToProps = (dipatch) =>
|
||||
bindActionCreators({ selectMovie, getMovieDetails, addTorrent,
|
||||
addMovieToWishlist, deleteMovieFromWishlist, refreshSubtitles }, dipatch)
|
||||
addMovieToWishlist, deleteMovie, deleteMovieFromWishlist,
|
||||
refreshSubtitles, updateFilter }, dipatch)
|
||||
|
||||
function MovieButtons(props) {
|
||||
const imdb_link = `http://www.imdb.com/title/${props.movie.imdb_id}`;
|
||||
const hasMovie = (props.movie.polochon_url !== "");
|
||||
const imdb_link = `http://www.imdb.com/title/${props.movie.get('imdb_id')}`;
|
||||
const hasMovie = (props.movie.get('polochon_url') !== "");
|
||||
return (
|
||||
<div className="list-details-buttons btn-toolbar">
|
||||
<ActionsButton
|
||||
fetching={props.fetching}
|
||||
movieId={props.movie.imdb_id}
|
||||
fetching={props.movie.get('fetchingDetails')}
|
||||
movieId={props.movie.get('imdb_id')}
|
||||
getDetails={props.getMovieDetails}
|
||||
deleteMovie={props.deleteMovie}
|
||||
hasMovie={hasMovie}
|
||||
wishlisted={props.movie.wishlisted}
|
||||
wishlisted={props.movie.get('wishlisted')}
|
||||
addToWishlist={props.addToWishlist}
|
||||
deleteFromWishlist={props.deleteFromWishlist}
|
||||
lastFetchUrl={props.lastFetchUrl}
|
||||
fetchMovies={props.fetchMovies}
|
||||
/>
|
||||
|
||||
{props.movie.torrents &&
|
||||
{props.movie.get('torrents') !== null &&
|
||||
<TorrentsButton
|
||||
torrents={props.movie.torrents}
|
||||
torrents={props.movie.get('torrents')}
|
||||
addTorrent={props.addTorrent}
|
||||
/>
|
||||
}
|
||||
|
||||
<DownloadButton
|
||||
url={props.movie.polochon_url}
|
||||
subtitles={props.movie.subtitles}
|
||||
url={props.movie.get('polochon_url')}
|
||||
subtitles={props.movie.get('subtitles')}
|
||||
/>
|
||||
|
||||
<SubtitlesButton
|
||||
url={props.movie.polochon_url}
|
||||
subtitles={props.movie.subtitles}
|
||||
fetching={props.movie.get('fetchingSubtitles')}
|
||||
url={props.movie.get('polochon_url')}
|
||||
subtitles={props.movie.get('subtitles')}
|
||||
refreshSubtitles={props.refreshSubtitles}
|
||||
resourceID={props.movie.imdb_id}
|
||||
data={props.movie}
|
||||
resourceID={props.movie.get('imdb_id')}
|
||||
type="movie"
|
||||
/>
|
||||
|
||||
@ -71,42 +78,36 @@ class MovieList extends React.Component {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
const movies = this.props.movieStore.movies;
|
||||
const selectedMovieId = this.props.movieStore.selectedImdbId;
|
||||
let index = movies.map((el) => el.imdb_id).indexOf(selectedMovieId);
|
||||
if (index === -1) {
|
||||
index = 0;
|
||||
let selectedMovie = undefined;
|
||||
if (this.props.movies !== undefined && this.props.movies.has(this.props.selectedImdbId)) {
|
||||
selectedMovie = this.props.movies.get(this.props.selectedImdbId);
|
||||
}
|
||||
const selectedMovie = movies[index];
|
||||
|
||||
return (
|
||||
<div className="row" id="container">
|
||||
<ListPosters
|
||||
data={movies}
|
||||
data={this.props.movies}
|
||||
type="movies"
|
||||
formModel="movieStore"
|
||||
filterControlModel="movieStore.filter"
|
||||
filterControlPlaceHolder="Filter movies..."
|
||||
exploreOptions={this.props.movieStore.exploreOptions}
|
||||
selectedImdbId={selectedMovieId}
|
||||
filter={this.props.movieStore.filter}
|
||||
perPage={this.props.movieStore.perPage}
|
||||
placeHolder="Filter movies..."
|
||||
exploreOptions={this.props.exploreOptions}
|
||||
selectedImdbId={this.props.selectedImdbId}
|
||||
updateFilter={this.props.updateFilter}
|
||||
filter={this.props.filter}
|
||||
onClick={this.props.selectMovie}
|
||||
params={this.props.params}
|
||||
router={this.props.router}
|
||||
loading={this.props.movieStore.loading}
|
||||
loading={this.props.loading}
|
||||
/>
|
||||
{selectedMovie &&
|
||||
{selectedMovie !== undefined &&
|
||||
<ListDetails data={selectedMovie}>
|
||||
<MovieButtons
|
||||
movie={selectedMovie}
|
||||
fetching={this.props.movieStore.fetchingDetails}
|
||||
getMovieDetails={this.props.getMovieDetails}
|
||||
addTorrent={this.props.addTorrent}
|
||||
deleteMovie={this.props.deleteMovie}
|
||||
addToWishlist={this.props.addMovieToWishlist}
|
||||
deleteFromWishlist={this.props.deleteMovieFromWishlist}
|
||||
lastFetchUrl={this.props.movieStore.lastFetchUrl}
|
||||
lastFetchUrl={this.props.lastFetchUrl}
|
||||
refreshSubtitles={this.props.refreshSubtitles}
|
||||
/>
|
||||
</ListDetails>
|
||||
|
@ -41,20 +41,12 @@ export default class TorrentsButton extends React.Component {
|
||||
}
|
||||
|
||||
function buildMenuItems(torrents) {
|
||||
// Organise by source
|
||||
let sources = {}
|
||||
for (let torrent of torrents) {
|
||||
if (!sources[torrent.source]) {
|
||||
sources[torrent.source] = [];
|
||||
}
|
||||
sources[torrent.source].push(torrent);
|
||||
}
|
||||
const t = torrents.groupBy((el) => el.get('source'));
|
||||
|
||||
// Build the array of entries
|
||||
let entries = [];
|
||||
let sourceNames = Object.keys(sources);
|
||||
let dividerCount = sourceNames.length - 1;
|
||||
for (let source of sourceNames) {
|
||||
let dividerCount = t.size - 1;
|
||||
for (let [source, torrentList] of t.entrySeq()) {
|
||||
// Push the title
|
||||
entries.push({
|
||||
type: "header",
|
||||
@ -62,11 +54,11 @@ function buildMenuItems(torrents) {
|
||||
});
|
||||
|
||||
// Push the torrents
|
||||
for (let torrent of sources[source]) {
|
||||
for (let torrent of torrentList) {
|
||||
entries.push({
|
||||
type: "entry",
|
||||
quality: torrent.quality,
|
||||
url: torrent.url,
|
||||
quality: torrent.get('quality'),
|
||||
url: torrent.get('url'),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,96 +1,63 @@
|
||||
const defaultState = {
|
||||
import { OrderedMap, Map, fromJS } from 'immutable'
|
||||
|
||||
const defaultState = Map({
|
||||
loading: false,
|
||||
movies: [],
|
||||
movies: OrderedMap(),
|
||||
filter: "",
|
||||
perPage: 30,
|
||||
selectedImdbId: "",
|
||||
fetchingDetails: false,
|
||||
lastFetchUrl: "",
|
||||
exploreOptions: {},
|
||||
};
|
||||
exploreOptions: Map(),
|
||||
});
|
||||
|
||||
export default function movieStore(state = defaultState, action) {
|
||||
switch (action.type) {
|
||||
case 'MOVIE_LIST_FETCH_PENDING':
|
||||
return Object.assign({}, state, {
|
||||
loading: true,
|
||||
})
|
||||
return state.set('loading', true);
|
||||
case 'MOVIE_LIST_FETCH_FULFILLED':
|
||||
|
||||
let movies = Map();
|
||||
action.payload.response.data.map(function (movie) {
|
||||
movie.fetchingDetails = false;
|
||||
movie.fetchingSubtitles = false;
|
||||
movies = movies.set(movie.imdb_id, fromJS(movie));
|
||||
})
|
||||
|
||||
// Select the first movie if the list is not empty
|
||||
let selectedImdbId = "";
|
||||
// Select the first movie
|
||||
if (action.payload.response.data.length > 0) {
|
||||
if (movies.size > 0) {
|
||||
// Sort by year
|
||||
action.payload.response.data.sort((a,b) => b.year - a.year);
|
||||
selectedImdbId = action.payload.response.data[0].imdb_id;
|
||||
}
|
||||
return Object.assign({}, state, {
|
||||
movies: action.payload.response.data,
|
||||
selectedImdbId: selectedImdbId,
|
||||
filter: defaultState.filter,
|
||||
perPage: defaultState.perPage,
|
||||
loading: false,
|
||||
})
|
||||
case 'MOVIE_GET_DETAILS_PENDING':
|
||||
return Object.assign({}, state, {
|
||||
fetchingDetails: true,
|
||||
})
|
||||
case 'MOVIE_GET_DETAILS_FULFILLED':
|
||||
return Object.assign({}, state, {
|
||||
movies: updateMovieDetails(state.movies.slice(), action.payload.response.data.imdb_id, action.payload.response.data),
|
||||
fetchingDetails: false,
|
||||
})
|
||||
case 'MOVIE_UPDATE_STORE_WISHLIST':
|
||||
return Object.assign({}, state, {
|
||||
movies: updateStoreWishlist(state.movies.slice(), action.payload.imdbId, action.payload.wishlisted),
|
||||
})
|
||||
case 'MOVIE_GET_EXPLORE_OPTIONS_FULFILLED':
|
||||
return Object.assign({}, state, {
|
||||
exploreOptions: action.payload.response.data,
|
||||
})
|
||||
case 'UPDATE_LAST_MOVIE_FETCH_URL':
|
||||
return Object.assign({}, state, {
|
||||
lastFetchUrl: action.payload.url,
|
||||
})
|
||||
case 'MOVIE_SUBTITLES_UPDATE_PENDING':
|
||||
return Object.assign({}, state, {
|
||||
movies: updateMovieSubtitles(state.movies.slice(), state.selectedImdbId, true),
|
||||
})
|
||||
case 'MOVIE_SUBTITLES_UPDATE_FULFILLED':
|
||||
console.log("payload :", action.payload);
|
||||
return Object.assign({}, state, {
|
||||
movies: updateMovieSubtitles(state.movies.slice(), state.selectedImdbId, false, action.payload.response.data),
|
||||
})
|
||||
case 'SELECT_MOVIE':
|
||||
// Don't select the movie if we're fetching another movie's details
|
||||
if (state.fetchingDetails) {
|
||||
return state
|
||||
movies = movies.sort((a,b) => b.get('year') - a.get('year'));
|
||||
selectedImdbId = movies.first().get('imdb_id');
|
||||
}
|
||||
|
||||
return Object.assign({}, state, {
|
||||
selectedImdbId: action.imdbId,
|
||||
})
|
||||
return state.delete('movies').merge(Map({
|
||||
movies: movies,
|
||||
filter: "",
|
||||
loading: false,
|
||||
selectedImdbId: selectedImdbId,
|
||||
}))
|
||||
case 'MOVIE_GET_DETAILS_PENDING':
|
||||
return state.setIn(['movies', action.payload.main.imdbId, 'fetchingDetails'], true);
|
||||
case 'MOVIE_GET_DETAILS_FULFILLED':
|
||||
let movie = action.payload.response.data;
|
||||
movie.fetchingDetails = false;
|
||||
movie.fetchingSubtitles = false;
|
||||
return state.setIn(['movies', movie.imdb_id], fromJS(movie));
|
||||
case 'MOVIE_UPDATE_STORE_WISHLIST':
|
||||
return state.setIn(['movies', action.payload.imdbId, 'wishlisted'], action.payload.wishlisted);
|
||||
case 'MOVIE_GET_EXPLORE_OPTIONS_FULFILLED':
|
||||
return state.set('exploreOptions', fromJS(action.payload.response.data));
|
||||
case 'UPDATE_LAST_MOVIE_FETCH_URL':
|
||||
return state.set('lastFetchUrl', action.payload.url);
|
||||
case 'MOVIE_SUBTITLES_UPDATE_PENDING':
|
||||
return state.setIn(['movies', action.payload.main.imdbId, 'fetchingSubtitles'], true);
|
||||
case 'MOVIE_SUBTITLES_UPDATE_FULFILLED':
|
||||
return state.setIn(['movies', action.payload.main.imdbId, 'fetchingSubtitles'], false).setIn(['movies', action.payload.main.imdbId, 'subtitles'], fromJS(action.payload.data.response));
|
||||
case 'SELECT_MOVIE':
|
||||
return state.set('selectedImdbId', action.payload.imdbId);
|
||||
case 'MOVIE_UPDATE_FILTER':
|
||||
return state.set('filter', action.payload.filter);
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
function updateMovieDetails(movies, imdbId, data) {
|
||||
let index = movies.map((el) => el.imdb_id).indexOf(imdbId);
|
||||
movies[index] = data;
|
||||
return movies
|
||||
}
|
||||
|
||||
function updateStoreWishlist(movies, imdbId, wishlisted) {
|
||||
let index = movies.map((el) => el.imdb_id).indexOf(imdbId);
|
||||
movies[index].wishlisted = wishlisted;
|
||||
return movies
|
||||
}
|
||||
|
||||
function updateMovieSubtitles(movies, imdbId, fetching, data = null) {
|
||||
let index = movies.map((el) => el.imdb_id).indexOf(imdbId);
|
||||
if (data) {
|
||||
movies[index].subtitles = data;
|
||||
}
|
||||
movies[index].fetchingSubtitles = fetching;
|
||||
return movies
|
||||
}
|
||||
|
@ -38,7 +38,8 @@ export function request(eventPrefix, promise, callbackEvents = null, mainPayload
|
||||
message: response.data.message,
|
||||
main: mainPayload,
|
||||
}
|
||||
})
|
||||
});
|
||||
return;
|
||||
}
|
||||
dispatch({
|
||||
type: fulfilled,
|
||||
|
@ -130,7 +130,7 @@ export default function getRoutes(App) {
|
||||
loginCheck(nextState, replace, next, function() {
|
||||
var state = store.getState();
|
||||
// Fetch the explore options
|
||||
if (Object.keys(state.movieStore.exploreOptions).length === 0) {
|
||||
if (state.movieStore.get('exploreOptions').size === 0) {
|
||||
store.dispatch(getMovieExploreOptions());
|
||||
}
|
||||
store.dispatch(fetchMovies(
|
||||
|
Loading…
x
Reference in New Issue
Block a user