diff --git a/src/public/js/actions/shows.js b/src/public/js/actions/shows.js
index fc84fae..20c5c28 100644
--- a/src/public/js/actions/shows.js
+++ b/src/public/js/actions/shows.js
@@ -24,18 +24,13 @@ export function getEpisodeDetails(imdbId, season, episode) {
return request(
'EPISODE_GET_DETAILS',
configureAxios().post(`/shows/${imdbId}/seasons/${season}/episodes/${episode}`),
- )
-}
-
-export function updateEpisodeDetailsStore(imdbId, season, episode) {
- return {
- type: 'EPISODE_GET_DETAILS',
- payload: {
+ null,
+ {
imdbId,
season,
episode,
- },
- }
+ }
+ )
}
export function fetchShowDetails(imdbId) {
diff --git a/src/public/js/actions/subtitles.js b/src/public/js/actions/subtitles.js
index 843ed43..0f950f9 100644
--- a/src/public/js/actions/subtitles.js
+++ b/src/public/js/actions/subtitles.js
@@ -9,21 +9,15 @@ export function refreshSubtitles(type, id, season, episode) {
return request(
'MOVIE_SUBTITLES_UPDATE',
configureAxios().post(`${resourceURL}/subtitles/refresh`),
- [
- addAlertOk("Subtitles refreshed"),
- ],
- {
- imdb_id: id,
- },
+ null,
+ { imdb_id: id },
)
case 'episode':
var resourceURL = `/shows/${id}/seasons/${season}/episodes/${episode}`
return request(
'EPISODE_SUBTITLES_UPDATE',
configureAxios().post(`${resourceURL}/subtitles/refresh`),
- [
- addAlertOk("Subtitles refreshed"),
- ],
+ null,
{
imdb_id: id,
season: season,
diff --git a/src/public/js/components/buttons/subtitles.js b/src/public/js/components/buttons/subtitles.js
index 6a96c63..083ac23 100644
--- a/src/public/js/components/buttons/subtitles.js
+++ b/src/public/js/components/buttons/subtitles.js
@@ -13,7 +13,7 @@ export default class SubtitlesButton extends React.Component {
return
}
// Refresh the subtitles
- this.props.refreshSubtitles(this.props.type, this.props.resourceID, this.props.data.season, this.props.data.episode);
+ this.props.refreshSubtitles(this.props.type, this.props.resourceID, this.props.season, this.props.episode);
}
render() {
// If there is no URL, the resource is not in polochon, we won't be able to download subtitles
diff --git a/src/public/js/components/shows/details.js b/src/public/js/components/shows/details.js
index a5593ec..46931a3 100644
--- a/src/public/js/components/shows/details.js
+++ b/src/public/js/components/shows/details.js
@@ -1,26 +1,27 @@
import React from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
+import { toJS } from 'immutable'
import { addTorrent } from '../../actions/torrents'
import { refreshSubtitles } from '../../actions/subtitles'
-import { addShowToWishlist, deleteFromWishlist, getEpisodeDetails,
- updateEpisodeDetailsStore, updateShowDetails } from '../../actions/shows'
+import { addShowToWishlist, deleteShowFromWishlist, getEpisodeDetails, updateShowDetails } from '../../actions/shows'
import Loader from '../loader/loader'
import DownloadButton from '../buttons/download'
import SubtitlesButton from '../buttons/subtitles'
+
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
function mapStateToProps(state) {
return {
loading: state.showStore.loading,
- show: state.showStore.show,
+ show: state.showStore.get('show'),
};
}
const mapDispatchToProps = (dispatch) =>
- bindActionCreators({addTorrent, addShowToWishlist, deleteFromWishlist,
- updateShowDetails, updateEpisodeDetailsStore, getEpisodeDetails,
+ bindActionCreators({addTorrent, addShowToWishlist, deleteShowFromWishlist,
+ updateShowDetails, getEpisodeDetails,
refreshSubtitles }, dispatch)
class ShowDetails extends React.Component {
@@ -41,7 +42,6 @@ class ShowDetails extends React.Component {
addTorrent={this.props.addTorrent}
addToWishlist={this.props.addShowToWishlist}
getEpisodeDetails={this.props.getEpisodeDetails}
- updateEpisodeDetailsStore={this.props.updateEpisodeDetailsStore}
refreshSubtitles={this.props.refreshSubtitles}
/>
@@ -70,21 +70,21 @@ function Header(props){
function HeaderThumbnail(props){
return (
-
);
}
function HeaderDetails(props){
- const imdbLink = `http://www.imdb.com/title/${props.data.imdb_id}`;
+ const imdbLink = `http://www.imdb.com/title/${props.data.get('imdb_id')}`;
return (
- Title
- - {props.data.title}
+ - {props.data.get('title')}
- Plot
- - {props.data.plot}
+ - {props.data.get('plot')}
- IMDB
-
@@ -92,9 +92,9 @@ function HeaderDetails(props){
- Year
- - {props.data.year}
+ - {props.data.get('year')}
- Rating
- - {props.data.rating}
+ - {props.data.get('rating')}
- {props.data.seasons.length > 0 && props.data.seasons.map(function(season, index) {
+ {props.data.get('seasons').entrySeq().map(function([season, data]) {
return (
-
)
@@ -140,8 +141,8 @@ class Season extends React.Component {
return (
this.handleClick(e)}>
- Season {this.props.data.season}
-
— ({this.props.data.episodes.length} episodes)
+ Season {this.props.season}
+
— ({this.props.data.toList().size} episodes)
{this.state.colapsed ||
@@ -154,8 +155,8 @@ class Season extends React.Component {
{this.state.colapsed ||
- {this.props.data.episodes.map(function(episode, index) {
- let key = `${episode.season}-${episode.episode}`;
+ {this.props.data.toList().map(function(episode) {
+ let key = `${episode.get('season')}-${episode.get('episode')}`;
return (
)
@@ -177,6 +177,12 @@ class Season extends React.Component {
}
function Episode(props) {
+ // TODO: remove this when everything uses immutable
+ let subtitles;
+ if (props.data.has('subtitles') && props.data.get('subtitles')) {
+ subtitles = props.data.get('subtitles').toJS();
+ }
+
return (
@@ -184,23 +190,24 @@ function Episode(props) {
data={props.data}
addToWishlist={props.addToWishlist}
/>
- {props.data.episode}
+ {props.data.get('episode')}
|
- {props.data.title}
+ {props.data.get('title')}
- {props.data.torrents && props.data.torrents.map(function(torrent, index) {
- let key = `${props.data.season}-${props.data.episode}-${torrent.source}-${torrent.quality}`;
+ {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 (
|
@@ -239,9 +245,9 @@ class Torrent extends React.Component {
this.handleClick(e, this.props.data.url)}
+ onClick={(e) => this.handleClick(e, this.props.data.get('url'))}
href={this.props.data.url} >
- {this.props.data.quality}
+ {this.props.data.get('quality')}
)
@@ -255,28 +261,30 @@ class TrackHeader extends React.Component {
}
handleClick(e, url) {
e.preventDefault();
- let wishlisted = (this.props.data.tracked_season !== null && this.props.data.tracked_episode !== null);
+ 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(this.props.data.imdb_id);
+ this.props.deleteFromWishlist(imdbId);
} else {
- this.props.addToWishlist(this.props.data.imdb_id);
+ this.props.addToWishlist(imdbId);
}
}
render() {
- let wishlisted = (this.props.data.tracked_season !== null && this.props.data.tracked_episode !== null);
+ 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);
+ let msg;
if (wishlisted) {
- let msg;
- if (this.props.data.tracked_season !== 0 && this.props.data.tracked_episode !== 0) {
+ if (trackedSeason !== 0 && trackedEpisode !== 0) {
msg = (
-
- Show tracked from season {this.props.data.tracked_season} episode {this.props.data.tracked_episode}
-
+ Show tracked from season {trackedSeason} episode {trackedEpisode}
);
} else {
msg = (
-
- Whole show tracked
-
+ Whole show tracked
);
}
return (
@@ -313,7 +321,10 @@ class TrackButton extends React.Component {
}
handleClick(e, url) {
e.preventDefault();
- this.props.addToWishlist(this.props.data.show_imdb_id, this.props.data.season, this.props.data.episode);
+ 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}`;
@@ -337,22 +348,23 @@ class GetDetailsButton extends React.Component {
}
handleClick(e, url) {
e.preventDefault();
- if (this.props.data.fetching) {
+ if (this.props.data.get('fetching')) {
return
}
- this.props.updateEpisodeDetailsStore(this.props.data.show_imdb_id, this.props.data.season, this.props.data.episode);
- console.log(this.props.data);
- this.props.getEpisodeDetails(this.props.data.show_imdb_id, this.props.data.season, this.props.data.episode);
+ 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.getEpisodeDetails(imdbId, season, episode);
}
render() {
return (
this.handleClick(e)}>
- {this.props.data.fetching ||
+ {this.props.data.get('fetching') ||
Refresh
}
- {this.props.data.fetching &&
+ {this.props.data.get('fetching') &&
Refreshing
diff --git a/src/public/js/components/shows/list.js b/src/public/js/components/shows/list.js
index fb7452f..38c705a 100644
--- a/src/public/js/components/shows/list.js
+++ b/src/public/js/components/shows/list.js
@@ -2,23 +2,23 @@ import React from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { selectShow, addShowToWishlist,
- deleteFromWishlist, getShowDetails } from '../../actions/shows'
+ deleteShowFromWishlist, getShowDetails } from '../../actions/shows'
import ListDetails from '../list/details'
import ListPosters from '../list/posters'
import ShowButtons from './listButtons'
function mapStateToProps(state) {
- return { showStore: state.showStore };
+ return { showsStore: state.showsStore };
}
const mapDispatchToProps = (dispatch) =>
bindActionCreators({ selectShow, addShowToWishlist,
- deleteFromWishlist, getShowDetails }, dispatch)
+ deleteShowFromWishlist, getShowDetails }, dispatch)
class ShowList extends React.Component {
render() {
- const shows = this.props.showStore.shows;
- const selectedShowId = this.props.showStore.selectedImdbId;
+ const shows = this.props.showsStore.shows;
+ const selectedShowId = this.props.showsStore.selectedImdbId;
let index = shows.map((el) => el.imdb_id).indexOf(selectedShowId);
if (index === -1) {
index = 0;
@@ -30,17 +30,17 @@ class ShowList extends React.Component {
{selectedShow &&
@@ -49,7 +49,7 @@ class ShowList extends React.Component {
deleteFromWishlist={this.props.deleteShowFromWishlist}
addToWishlist={this.props.addShowToWishlist}
getDetails={this.props.getShowDetails}
- fetching={this.props.showStore.getDetails}
+ fetching={this.props.showsStore.getDetails}
/>
}
diff --git a/src/public/js/reducers/index.js b/src/public/js/reducers/index.js
index 2079637..5874a94 100644
--- a/src/public/js/reducers/index.js
+++ b/src/public/js/reducers/index.js
@@ -2,7 +2,8 @@ import { combineForms } from 'react-redux-form'
import { routerReducer } from 'react-router-redux'
import movieStore from './movies'
-import showStore from './shows'
+import showsStore from './shows'
+import showStore from './show'
import userStore from './users'
import alerts from './alerts'
import torrentStore from './torrents'
@@ -13,6 +14,7 @@ import torrentStore from './torrents'
const rootReducer = combineForms({
routing: routerReducer,
movieStore,
+ showsStore,
showStore,
userStore,
alerts,
diff --git a/src/public/js/reducers/show.js b/src/public/js/reducers/show.js
new file mode 100644
index 0000000..178c5ef
--- /dev/null
+++ b/src/public/js/reducers/show.js
@@ -0,0 +1,94 @@
+import { OrderedMap, Map, List, fromJS } from 'immutable'
+
+const defaultState = Map({
+ loading: false,
+ show: Map({
+ seasons: OrderedMap(),
+ }),
+});
+
+export default function showStore(state = defaultState, action) {
+ switch (action.type) {
+ case 'SHOW_FETCH_DETAILS_PENDING':
+ return state.set('loading', true)
+ case 'SHOW_FETCH_DETAILS_FULFILLED':
+ return sortEpisodes(state, action.payload.response.data);
+ case 'SHOW_UPDATE_STORE_WISHLIST':
+ let season = action.payload.season;
+ let episode = action.payload.episode;
+ if (action.payload.wishlisted && season === null) {
+ season = 0;
+ episode = 0;
+ }
+ return state.mergeDeep(fromJS({
+ 'show': {
+ 'tracked_season': season,
+ 'tracked_episode': episode,
+ }
+ }));
+ case 'EPISODE_GET_DETAILS_PENDING':
+ return state.setIn(['show', 'seasons', action.payload.main.season, action.payload.main.episode, 'fetching'], true);
+ case 'EPISODE_GET_DETAILS_FULFILLED':
+ let data = action.payload.response.data;
+ if (!data) { return state }
+ data.fetching = false;
+ return state.setIn(['show', 'seasons', data.season, data.episode], fromJS(data));
+ case 'EPISODE_SUBTITLES_UPDATE_PENDING':
+ return state.setIn(['show', 'seasons', action.payload.main.season, action.payload.main.episode, 'fetchingSubtitles'], true);
+ case 'EPISODE_SUBTITLES_UPDATE_FULFILLED':
+ let epId = ['show', 'seasons', action.payload.main.season, action.payload.main.episode];
+ let ep = state.getIn(epId);
+ ep = ep.set('subtitles', fromJS(action.payload.response.data)).set('fetchingSubtitles', false);
+ return state.setIn(epId, ep);
+ default:
+ return state
+ }
+}
+
+function updateEpisode(state, fetching, data = null) {
+ if (data === null) {
+ return state;
+ }
+
+ if (!state.hasIn(['show', 'season', data.season, data.episode])) {
+ return show;
+ }
+
+ data.fetching = fetching
+ return show.updateIn(['show', 'seasons', data.season, data.episode], fromJS(data));
+}
+
+function sortEpisodes(state, show) {
+ let episodes = show.episodes;
+ delete show["episodes"];
+
+ let ret = state.set('loading', false);
+ if (episodes.length == 0) {
+ return ret;
+ }
+
+ // Set the show data
+ ret = ret.set('show', fromJS(show));
+
+ // Set the show episodes
+ for (let ep of episodes) {
+ ep.fetching = false;
+ ret = ret.setIn(['show', 'seasons', ep.season, ep.episode], fromJS(ep));
+ }
+
+ // Sort the episodes
+ ret = ret.updateIn(['show', 'seasons'], function(seasons) {
+ return seasons.map(function(episodes) {
+ return episodes.sort((a,b) => a.get('episode') - b.get('episode'));
+ });
+ });
+
+ // Sort the seasons
+ ret = ret.updateIn(['show', 'seasons'], function(seasons) {
+ return seasons.sort(function(a,b) {
+ return a.first().get('season') - b.first().get('season');
+ });
+ });
+
+ return ret
+}
diff --git a/src/public/js/reducers/shows.js b/src/public/js/reducers/shows.js
index 1f5773e..83430da 100644
--- a/src/public/js/reducers/shows.js
+++ b/src/public/js/reducers/shows.js
@@ -4,15 +4,12 @@ const defaultState = {
filter: "",
perPage: 30,
selectedImdbId: "",
- show: {
- seasons: [],
- },
getDetails: false,
lastShowsFetchUrl: "",
exploreOptions: {},
};
-export default function showStore(state = defaultState, action) {
+export default function showsStore(state = defaultState, action) {
switch (action.type) {
case 'SHOW_LIST_FETCH_PENDING':
return Object.assign({}, state, {
@@ -29,7 +26,6 @@ export default function showStore(state = defaultState, action) {
shows: action.payload.response.data,
selectedImdbId: selectedImdbId,
filter: defaultState.filter,
- perPage: defaultState.perPage,
loading: false,
})
case 'SHOW_GET_DETAILS_PENDING':
@@ -41,23 +37,6 @@ export default function showStore(state = defaultState, action) {
shows: updateShowDetails(state.shows.slice(), action.payload.response.data),
getDetails: false,
})
- case 'SHOW_FETCH_DETAILS_PENDING':
- return Object.assign({}, state, {
- loading: true,
- })
- case 'SHOW_FETCH_DETAILS_FULFILLED':
- return Object.assign({}, state, {
- show: sortEpisodes(action.payload.response.data),
- loading: false,
- })
- case 'EPISODE_GET_DETAILS':
- return Object.assign({}, state, {
- show: updateEpisode(Object.assign({}, state.show), true, action.payload),
- })
- case 'EPISODE_GET_DETAILS_FULFILLED':
- return Object.assign({}, state, {
- show: updateEpisode(Object.assign({}, state.show), false, action.payload.response.data),
- })
case 'EXPLORE_SHOWS_PENDING':
return Object.assign({}, state, {
loading: true,
@@ -74,21 +53,11 @@ export default function showStore(state = defaultState, action) {
case 'SHOW_UPDATE_STORE_WISHLIST':
return Object.assign({}, state, {
shows: updateShowsStoreWishlist(state.shows.slice(), action.payload),
- show: updateShowStoreWishlist(Object.assign({}, state.show), action.payload),
})
case 'UPDATE_LAST_SHOWS_FETCH_URL':
return Object.assign({}, state, {
lastShowsFetchUrl: action.payload.url,
})
- case 'EPISODE_SUBTITLES_UPDATE_PENDING':
- return Object.assign({}, state, {
- show: updateEpisodeSubtitles(Object.assign({}, state.show), action.payload.main.season, action.payload.main.episode, true),
- })
- case 'EPISODE_SUBTITLES_UPDATE_FULFILLED':
- console.log("payload :", action.payload);
- return Object.assign({}, state, {
- show: updateEpisodeSubtitles(Object.assign({}, state.show), action.payload.main.season, action.payload.main.episode, false, action.payload.response.data),
- })
case 'SELECT_SHOW':
// Don't select the show if we're fetching another show's details
if (state.fetchingDetails) {
@@ -103,82 +72,6 @@ export default function showStore(state = defaultState, action) {
}
}
-function updateEpisode(show, fetching, data = null) {
- // Error handling for PouuleT
- if (data === null) {
- for (let seasonIndex of Object.keys(show.seasons)) {
- for (let episodeIndex of Object.keys(show.seasons[seasonIndex].episodes)) {
- show.seasons[seasonIndex].episodes[episodeIndex].fetching = false;
- }
- }
- return show
- }
-
- let seasonIndex = show.seasons.map((el) => el.season).indexOf(data.season.toString());
- let episodeIndex = show.seasons[seasonIndex].episodes.map((el) => el.episode).indexOf(data.episode);
- if ('imdb_id' in data) {
- show.seasons[seasonIndex].episodes[episodeIndex] = data;
- }
- show.seasons[seasonIndex].episodes[episodeIndex].fetching = fetching;
- return show
-}
-
-function updateEpisodeSubtitles(show, season, episode, fetching, data = null) {
- let seasonIndex = show.seasons.map((el) => el.season).indexOf(season.toString());
- let episodeIndex = show.seasons[seasonIndex].episodes.map((el) => el.episode).indexOf(episode);
-
- if (data) {
- show.seasons[seasonIndex].episodes[episodeIndex].subtitles = data;
- }
- show.seasons[seasonIndex].episodes[episodeIndex].fetchingSubtitles = fetching;
- return show
-}
-
-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) {
- ep.fetching = false;
- 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;
-}
-
// Update the store containing all the shows
function updateShowsStoreWishlist(shows, payload) {
if (shows.length === 0) {
@@ -201,26 +94,6 @@ function updateShowsStoreWishlist(shows, payload) {
return shows
}
-// Update the store containing the current detailed show
-function updateShowStoreWishlist(show, payload) {
- if (show.seasons.length === 0) {
- return show;
- }
- let season = payload.season;
- let episode = payload.episode;
- if (payload.wishlisted) {
- if (season === null) {
- season = 0;
- }
- if (episode === null) {
- episode = 0;
- }
- }
- show.tracked_season = season;
- show.tracked_episode = episode;
- return show
-}
-
function updateShowDetails(shows, data) {
let index = shows.map((el) => el.imdb_id).indexOf(data.imdb_id);
shows[index] = data;
diff --git a/src/public/js/routes.js b/src/public/js/routes.js
index 9355f9a..33e3d03 100644
--- a/src/public/js/routes.js
+++ b/src/public/js/routes.js
@@ -15,13 +15,6 @@ import { fetchShows, fetchShowDetails, getShowExploreOptions } from './actions/s
import store from './store'
-function startPollingTorrents() {
- return request(
- 'TORRENTS_FETCH',
- configureAxios().get('/torrents')
- )
-}
-
// This function returns true if the user is logged in, false otherwise
function isLoggedIn() {
const state = store.getState();
@@ -198,7 +191,7 @@ export default function getRoutes(App) {
loginCheck(nextState, replace, next, function() {
var state = store.getState();
// Fetch the explore options
- if (Object.keys(state.showStore.exploreOptions).length === 0) {
+ if (Object.keys(state.showsStore.exploreOptions).length === 0) {
store.dispatch(getShowExploreOptions());
}
store.dispatch(fetchShows(