diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..f017f3e --- /dev/null +++ b/.eslintrc @@ -0,0 +1,23 @@ +{ + "parser": "babel-eslint", + "env": { + "browser": true, + "node": true + }, + "settings": { + "ecmascript": 6, + "jsx": true + }, + "plugins": [ + "react" + ], + "rules": { + "strict": 1, + "quotes": 1, + "no-unused-vars": 1, + "camelcase": 1, + "no-underscore-dangle": 1, + "react/jsx-uses-react": 1, + "react/jsx-uses-vars": 1 + } +} diff --git a/package.json b/package.json index 867633a..ab243ef 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,6 @@ "react-infinite-scroller": "^1.0.4", "react-loading": "^0.0.9", "react-redux": "^4.4.6", - "react-redux-form": "^1.2.4", "react-router": "^3.0.0", "react-router-bootstrap": "^0.23.1", "react-router-redux": "^4.0.7", @@ -34,12 +33,15 @@ "axios": "^0.15.2", "babel": "^6.5.2", "babel-core": "^6.18.2", + "babel-eslint": "^7.2.3", "babel-loader": "^6.2.7", "babel-preset-es2015": "^6.18.0", "babel-preset-latest": "^6.16.0", "babel-preset-react": "^6.16.0", "css-loader": "^0.26.0", "del": "^2.2.2", + "eslint": "^3.19.0", + "eslint-plugin-react": "^7.0.1", "file-loader": "^0.9.0", "gulp": "^3.9.1", "gulp-babel": "^6.1.2", diff --git a/src/public/js/actions/alerts.js b/src/public/js/actions/alerts.js index 83c1198..d4ae8b0 100644 --- a/src/public/js/actions/alerts.js +++ b/src/public/js/actions/alerts.js @@ -1,6 +1,6 @@ export function addAlertError(message) { return { - type: 'ADD_ALERT_ERROR', + type: "ADD_ALERT_ERROR", payload: { message, } @@ -9,7 +9,7 @@ export function addAlertError(message) { export function addAlertOk(message) { return { - type: 'ADD_ALERT_OK', + type: "ADD_ALERT_OK", payload: { message, } @@ -18,6 +18,6 @@ export function addAlertOk(message) { export function dismissAlert() { return { - type: 'DISMISS_ALERT', + type: "DISMISS_ALERT", } } diff --git a/src/public/js/actions/movies.js b/src/public/js/actions/movies.js index e5e0d36..128234e 100644 --- a/src/public/js/actions/movies.js +++ b/src/public/js/actions/movies.js @@ -1,10 +1,10 @@ -import { configureAxios, request } from '../requests' +import { configureAxios, request } from "../requests" -import { addAlertOk } from './alerts' +import { addAlertOk } from "./alerts" export function updateLastMovieFetchUrl(url) { return { - type: 'UPDATE_LAST_MOVIE_FETCH_URL', + type: "UPDATE_LAST_MOVIE_FETCH_URL", payload: { url: url, }, @@ -13,28 +13,43 @@ export function updateLastMovieFetchUrl(url) { export function selectMovie(imdbId) { return { - type: 'SELECT_MOVIE', - imdbId + type: "SELECT_MOVIE", + payload: { + imdbId, + }, + } +} + +export function updateFilter(filter) { + return { + type: "MOVIE_UPDATE_FILTER", + payload: { + filter, + }, } } export function getMovieExploreOptions() { return request( - 'MOVIE_GET_EXPLORE_OPTIONS', - configureAxios().get('/movies/explore/options') + "MOVIE_GET_EXPLORE_OPTIONS", + configureAxios().get("/movies/explore/options") ) } export function getMovieDetails(imdbId) { return request( - 'MOVIE_GET_DETAILS', - configureAxios().post(`/movies/${imdbId}/refresh`) + "MOVIE_GET_DETAILS", + configureAxios().post(`/movies/${imdbId}/refresh`), + null, + { + imdbId, + } ) } export function deleteMovie(imdbId, lastFetchUrl) { return request( - 'MOVIE_DELETE', + "MOVIE_DELETE", configureAxios().delete(`/movies/${imdbId}`), [ fetchMovies(lastFetchUrl), @@ -45,10 +60,9 @@ export function deleteMovie(imdbId, lastFetchUrl) { export function addMovieToWishlist(imdbId) { return request( - 'MOVIE_ADD_TO_WISHLIST', + "MOVIE_ADD_TO_WISHLIST", configureAxios().post(`/wishlist/movies/${imdbId}`), [ - addAlertOk("Movie added to the wishlist"), updateMovieWishlistStore(imdbId, true), ], ) @@ -56,10 +70,9 @@ export function addMovieToWishlist(imdbId) { export function deleteMovieFromWishlist(imdbId) { return request( - 'MOVIE_DELETE_FROM_WISHLIST', + "MOVIE_DELETE_FROM_WISHLIST", configureAxios().delete(`/wishlist/movies/${imdbId}`), [ - addAlertOk("Movie deleted from the wishlist"), updateMovieWishlistStore(imdbId, false), ], ) @@ -67,7 +80,7 @@ export function deleteMovieFromWishlist(imdbId) { export function updateMovieWishlistStore(imdbId, wishlisted) { return { - type: 'MOVIE_UPDATE_STORE_WISHLIST', + type: "MOVIE_UPDATE_STORE_WISHLIST", payload: { imdbId, wishlisted, @@ -77,7 +90,7 @@ export function updateMovieWishlistStore(imdbId, wishlisted) { export function fetchMovies(url) { return request( - 'MOVIE_LIST_FETCH', + "MOVIE_LIST_FETCH", configureAxios().get(url), [ updateLastMovieFetchUrl(url), diff --git a/src/public/js/actions/shows.js b/src/public/js/actions/shows.js index 20c5c28..6d097ef 100644 --- a/src/public/js/actions/shows.js +++ b/src/public/js/actions/shows.js @@ -1,10 +1,8 @@ -import { configureAxios, request } from '../requests' - -import { addAlertOk } from './alerts' +import { configureAxios, request } from "../requests" export function fetchShows(url) { return request( - 'SHOW_LIST_FETCH', + "SHOW_LIST_FETCH", configureAxios().get(url), [ updateLastShowsFetchUrl(url), @@ -14,15 +12,17 @@ export function fetchShows(url) { export function getShowDetails(imdbId) { return request( - 'SHOW_GET_DETAILS', - configureAxios().post(`/shows/${imdbId}/refresh`) + "SHOW_GET_DETAILS", + configureAxios().post(`/shows/${imdbId}/refresh`), + null, + { imdbId } ) } export function getEpisodeDetails(imdbId, season, episode) { return request( - 'EPISODE_GET_DETAILS', + "EPISODE_GET_DETAILS", configureAxios().post(`/shows/${imdbId}/seasons/${season}/episodes/${episode}`), null, { @@ -35,20 +35,21 @@ export function getEpisodeDetails(imdbId, season, episode) { export function fetchShowDetails(imdbId) { return request( - 'SHOW_FETCH_DETAILS', - configureAxios().get(`/shows/${imdbId}`) + "SHOW_FETCH_DETAILS", + configureAxios().get(`/shows/${imdbId}`), + null, + { imdbId } ) } export function addShowToWishlist(imdbId, season = null, episode = null) { return request( - 'SHOW_ADD_TO_WISHLIST', + "SHOW_ADD_TO_WISHLIST", configureAxios().post(`/wishlist/shows/${imdbId}`, { season: season, episode: episode, }), [ - addAlertOk("Show added to the wishlist"), updateShowWishlistStore(imdbId, true, season, episode), ], ) @@ -56,10 +57,9 @@ export function addShowToWishlist(imdbId, season = null, episode = null) { export function deleteShowFromWishlist(imdbId) { return request( - 'SHOW_DELETE_FROM_WISHLIST', + "SHOW_DELETE_FROM_WISHLIST", configureAxios().delete(`/wishlist/shows/${imdbId}`), [ - addAlertOk("Show deleted from the wishlist"), updateShowWishlistStore(imdbId, false), ], ) @@ -67,7 +67,7 @@ export function deleteShowFromWishlist(imdbId) { export function updateShowWishlistStore(imdbId, wishlisted, season = null, episode = null) { return { - type: 'SHOW_UPDATE_STORE_WISHLIST', + type: "SHOW_UPDATE_STORE_WISHLIST", payload: { wishlisted: wishlisted, imdbId, @@ -79,21 +79,33 @@ export function updateShowWishlistStore(imdbId, wishlisted, season = null, episo export function getShowExploreOptions() { return request( - 'SHOW_GET_EXPLORE_OPTIONS', - configureAxios().get('/shows/explore/options') + "SHOW_GET_EXPLORE_OPTIONS", + configureAxios().get("/shows/explore/options") ) } export function selectShow(imdbId) { return { - type: 'SELECT_SHOW', - imdbId + type: "SELECT_SHOW", + payload: { + imdbId, + } } } +export function updateFilter(filter) { + return { + type: "SHOWS_UPDATE_FILTER", + payload: { + filter, + }, + } +} + + export function updateLastShowsFetchUrl(url) { return { - type: 'UPDATE_LAST_SHOWS_FETCH_URL', + type: "UPDATE_LAST_SHOWS_FETCH_URL", payload: { url: url, }, diff --git a/src/public/js/actions/subtitles.js b/src/public/js/actions/subtitles.js index 0f950f9..b5aa249 100644 --- a/src/public/js/actions/subtitles.js +++ b/src/public/js/actions/subtitles.js @@ -1,30 +1,28 @@ -import { configureAxios, request } from '../requests' - -import { addAlertOk } from './alerts' +import { configureAxios, request } from "../requests" export function refreshSubtitles(type, id, season, episode) { switch (type) { - case 'movie': + case "movie": var resourceURL = `/movies/${id}` return request( - 'MOVIE_SUBTITLES_UPDATE', + "MOVIE_SUBTITLES_UPDATE", configureAxios().post(`${resourceURL}/subtitles/refresh`), null, - { imdb_id: id }, + { imdbId: id }, ) - case 'episode': + case "episode": var resourceURL = `/shows/${id}/seasons/${season}/episodes/${episode}` return request( - 'EPISODE_SUBTITLES_UPDATE', + "EPISODE_SUBTITLES_UPDATE", 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) } } diff --git a/src/public/js/actions/torrents.js b/src/public/js/actions/torrents.js index e92880c..ccf8ace 100644 --- a/src/public/js/actions/torrents.js +++ b/src/public/js/actions/torrents.js @@ -1,11 +1,11 @@ -import { configureAxios, request } from '../requests' +import { configureAxios, request } from "../requests" -import { addAlertOk } from './alerts' +import { addAlertOk } from "./alerts" export function addTorrent(url) { return request( - 'ADD_TORRENT', - configureAxios().post('/torrents', { + "ADD_TORRENT", + configureAxios().post("/torrents", { url: url, }), [ @@ -16,7 +16,7 @@ export function addTorrent(url) { export function fetchTorrents() { return request( - 'TORRENTS_FETCH', - configureAxios().get('/torrents') + "TORRENTS_FETCH", + configureAxios().get("/torrents") ) } diff --git a/src/public/js/actions/users.js b/src/public/js/actions/users.js index 76734e4..61e9552 100644 --- a/src/public/js/actions/users.js +++ b/src/public/js/actions/users.js @@ -1,18 +1,18 @@ -import { configureAxios, request } from '../requests' +import { configureAxios, request } from "../requests" -import { addAlertOk } from './alerts' +import { addAlertOk } from "./alerts" export function userLogout() { return { - type: 'USER_LOGOUT', + type: "USER_LOGOUT", } } export function loginUser(username, password) { return request( - 'USER_LOGIN', + "USER_LOGIN", configureAxios().post( - '/users/login', + "/users/login", { username: username, password: password, @@ -23,8 +23,8 @@ export function loginUser(username, password) { export function updateUser(config) { return request( - 'USER_UPDATE', - configureAxios().post('/users/edit', config), + "USER_UPDATE", + configureAxios().post("/users/edit", config), [ addAlertOk("User updated"), ], @@ -33,14 +33,14 @@ export function updateUser(config) { export function userSignUp(config) { return request( - 'USER_SIGNUP', - configureAxios().post('/users/signup', config) + "USER_SIGNUP", + configureAxios().post("/users/signup", config) ) } export function getUserInfos() { return request( - 'GET_USER', - configureAxios().get('/users/details') + "GET_USER", + configureAxios().get("/users/details") ) } diff --git a/src/public/js/app.js b/src/public/js/app.js index 8db0d3b..4d914e0 100644 --- a/src/public/js/app.js +++ b/src/public/js/app.js @@ -1,52 +1,51 @@ // Html page -import 'file-loader?name=[name].[ext]!../index.html' +import "file-loader?name=[name].[ext]!../index.html" // Import default image -import 'file-loader?name=img/[name].png!../img/noimage.png' +import "file-loader?name=img/[name].png!../img/noimage.png" // Import favicon settings -import 'file-loader?name=[name].png!../img/android-chrome-192x192.png' -import 'file-loader?name=[name].png!../img/android-chrome-512x512.png' -import 'file-loader?name=[name].png!../img/apple-touch-icon.png' -import 'file-loader?name=[name].png!../img/favicon-16x16.png' -import 'file-loader?name=[name].png!../img/favicon-32x32.png' -import 'file-loader?name=[name].png!../img/favicon.ico' -import 'file-loader?name=[name].png!../img/safari-pinned-tab.svg' +import "file-loader?name=[name].png!../img/android-chrome-192x192.png" +import "file-loader?name=[name].png!../img/android-chrome-512x512.png" +import "file-loader?name=[name].png!../img/apple-touch-icon.png" +import "file-loader?name=[name].png!../img/favicon-16x16.png" +import "file-loader?name=[name].png!../img/favicon-32x32.png" +import "file-loader?name=[name].png!../img/favicon.ico" +import "file-loader?name=[name].png!../img/safari-pinned-tab.svg" // Import manifest -import 'file-loader?name=[name].json!../manifest.json' +import "file-loader?name=[name].json!../manifest.json" // Styles -import '../less/app.less' +import "../less/app.less" // React -import React from 'react' -import ReactDOM from 'react-dom' -import { bindActionCreators } from 'redux' -import { Provider, connect } from 'react-redux' -import { Router } from 'react-router' -import { routerActions } from 'react-router-redux' +import React from "react" +import ReactDOM from "react-dom" +import { bindActionCreators } from "redux" +import { Provider, connect } from "react-redux" +import { Router } from "react-router" // Action creators -import { dismissAlert } from './actions/alerts' +import { dismissAlert } from "./actions/alerts" // Store -import store, { history } from './store' +import store, { history } from "./store" // Components -import NavBar from './components/navbar' -import Alert from './components/alerts/alert' +import NavBar from "./components/navbar" +import Alert from "./components/alerts/alert" // Routes -import getRoutes from './routes' +import getRoutes from "./routes" function mapStateToProps(state) { let torrentCount = 0; - if (state.torrentStore.has('torrents')) { - torrentCount = state.torrentStore.get('torrents').size; + if (state.torrentStore.has("torrents") && state.torrentStore.get("torrents") !== undefined) { + torrentCount = state.torrentStore.get("torrents").size; } return { - username: state.userStore.username, + username: state.userStore.get("username"), torrentCount: torrentCount, alerts: state.alerts, } @@ -80,4 +79,4 @@ ReactDOM.render(( -),document.getElementById('app')); +),document.getElementById("app")); diff --git a/src/public/js/components/alerts/alert.js b/src/public/js/components/alerts/alert.js index bcb46ae..5de2c8d 100644 --- a/src/public/js/components/alerts/alert.js +++ b/src/public/js/components/alerts/alert.js @@ -1,17 +1,16 @@ -import React from 'react' - -import SweetAlert from 'react-bootstrap-sweetalert'; +import React from "react" +import SweetAlert from "react-bootstrap-sweetalert"; export default function Alert(props) { - if (!props.alerts.show) { + if (!props.alerts.get("show")) { return null } return ( ) } diff --git a/src/public/js/components/buttons/actions.js b/src/public/js/components/buttons/actions.js index 04a9338..41b42e1 100644 --- a/src/public/js/components/buttons/actions.js +++ b/src/public/js/components/buttons/actions.js @@ -1,8 +1,10 @@ -import React from 'react' +import React from "react" -import { MenuItem } from 'react-bootstrap' +import { MenuItem } from "react-bootstrap" -export class WishlistButton extends React.Component { +import RefreshIndicator from "./refresh" + +export class WishlistButton extends React.PureComponent { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); @@ -36,7 +38,7 @@ export class WishlistButton extends React.Component { } } -export class DeleteButton extends React.Component { +export class DeleteButton extends React.PureComponent { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); @@ -56,7 +58,7 @@ export class DeleteButton extends React.Component { } } -export class RefreshButton extends React.Component { +export class RefreshButton extends React.PureComponent { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); @@ -71,16 +73,7 @@ export class RefreshButton extends React.Component { render() { return ( - {this.props.fetching || - - Refresh - - } - {this.props.fetching && - - Refreshing - - } + ); } diff --git a/src/public/js/components/buttons/download.js b/src/public/js/components/buttons/download.js index 8c18ece..12f0bac 100644 --- a/src/public/js/components/buttons/download.js +++ b/src/public/js/components/buttons/download.js @@ -1,8 +1,8 @@ -import React from 'react' +import React from "react" -import { Button, Dropdown, MenuItem, Modal } from 'react-bootstrap' +import { Button, Dropdown, MenuItem, Modal } from "react-bootstrap" -export default class DownloadButton extends React.Component { +export default class DownloadButton extends React.PureComponent { constructor(props) { super(props); this.showModal = this.showModal.bind(this); @@ -66,30 +66,25 @@ export default class DownloadButton extends React.Component { } } -class Player extends React.Component { +class Player extends React.PureComponent { constructor(props) { super(props); - var subtitles = []; - if (props.subtitles && props.subtitles.length) { - subtitles = props.subtitles; - } - this.state = { - subtitles: subtitles, - }; } render() { + const subtitles = this.props.subtitles; + const hasSubtitles = !(subtitles === undefined || subtitles === null || subtitles.size === 0); return (