Compare commits
16 Commits
937b12bb67
...
817da07a2d
Author | SHA1 | Date | |
---|---|---|---|
817da07a2d | |||
af2641c317 | |||
d9fae3e23a | |||
e7f96a1bd7 | |||
2e83c3169f | |||
6ac382b659 | |||
81f497170f | |||
6cfee5ea74 | |||
ea62b1c6ec | |||
d998d2838d | |||
27f5c5d558 | |||
27f0b742a4 | |||
ac0d746cc9 | |||
c5336c477a | |||
834ee8bcfc | |||
5f7d402614 |
@ -12,6 +12,7 @@ steps:
|
||||
commands:
|
||||
- cd frontend
|
||||
- npm install
|
||||
- npm run-script lint
|
||||
- npm run-script build
|
||||
|
||||
- name: backend
|
||||
|
@ -28,12 +28,10 @@ import { AdminPanel } from "./components/admins/panel";
|
||||
import { Notifications } from "./components/notifications/notifications";
|
||||
import Alert from "./components/alerts/alert";
|
||||
import MovieList from "./components/movies/list";
|
||||
import MoviesRoute from "./components/movies/route";
|
||||
import NavBar from "./components/navbar";
|
||||
import WsHandler from "./components/websocket";
|
||||
import { ShowDetails } from "./components/shows/details";
|
||||
import ShowList from "./components/shows/list";
|
||||
import ShowsRoute from "./components/shows/route";
|
||||
import TorrentList from "./components/torrents/list";
|
||||
import TorrentSearch from "./components/torrents/search";
|
||||
import UserActivation from "./components/users/activation";
|
||||
@ -61,31 +59,23 @@ const App = () => (
|
||||
exact
|
||||
component={TorrentSearch}
|
||||
/>
|
||||
<MoviesRoute path="/movies/polochon" exact component={MovieList} />
|
||||
<MoviesRoute path="/movies/wishlist" exact component={MovieList} />
|
||||
<MoviesRoute
|
||||
path="/movies/search/:search"
|
||||
exact
|
||||
component={MovieList}
|
||||
/>
|
||||
<MoviesRoute
|
||||
<Route path="/movies/polochon" exact component={MovieList} />
|
||||
<Route path="/movies/wishlist" exact component={MovieList} />
|
||||
<Route path="/movies/search/:search" exact component={MovieList} />
|
||||
<Route
|
||||
path="/movies/explore/:source/:category"
|
||||
exact
|
||||
component={MovieList}
|
||||
/>
|
||||
<ShowsRoute path="/shows/polochon" exact component={ShowList} />
|
||||
<ShowsRoute path="/shows/wishlist" exact component={ShowList} />
|
||||
<ShowsRoute path="/shows/search/:search" exact component={ShowList} />
|
||||
<ShowsRoute
|
||||
<Route path="/shows/polochon" exact component={ShowList} />
|
||||
<Route path="/shows/wishlist" exact component={ShowList} />
|
||||
<Route path="/shows/search/:search" exact component={ShowList} />
|
||||
<Route
|
||||
path="/shows/explore/:source/:category"
|
||||
exact
|
||||
component={ShowList}
|
||||
/>
|
||||
<ShowsRoute
|
||||
path="/shows/details/:imdbId"
|
||||
exact
|
||||
component={ShowDetails}
|
||||
/>
|
||||
<Route path="/shows/details/:imdbId" exact component={ShowDetails} />
|
||||
<Route render={() => <Redirect to="/movies/explore/yts/seeds" />} />
|
||||
</Switch>
|
||||
</Container>
|
||||
|
@ -1,17 +1,19 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { Route, Redirect } from "react-router-dom";
|
||||
|
||||
import { setUserToken } from "./actions/users";
|
||||
|
||||
const protectedRoute = ({
|
||||
component: Component,
|
||||
isLogged,
|
||||
isActivated,
|
||||
isTokenSet,
|
||||
setUserToken,
|
||||
...otherProps
|
||||
}) => {
|
||||
export const ProtectedRoute = ({ component: Component, ...otherProps }) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const isLogged = useSelector((state) => state.userStore.get("isLogged"));
|
||||
const isActivated = useSelector((state) =>
|
||||
state.userStore.get("isActivated")
|
||||
);
|
||||
const isTokenSet = useSelector((state) => state.userStore.get("isTokenSet"));
|
||||
|
||||
const isAuthenticated = () => {
|
||||
if (isTokenSet) {
|
||||
return true;
|
||||
@ -20,7 +22,7 @@ const protectedRoute = ({
|
||||
const token = localStorage.getItem("token");
|
||||
if (isLogged || (token && token !== "")) {
|
||||
if (!isTokenSet) {
|
||||
setUserToken(token);
|
||||
dispatch(setUserToken(token));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -45,24 +47,12 @@ const protectedRoute = ({
|
||||
/>
|
||||
);
|
||||
};
|
||||
protectedRoute.propTypes = {
|
||||
ProtectedRoute.propTypes = {
|
||||
component: PropTypes.func,
|
||||
isLogged: PropTypes.bool.isRequired,
|
||||
isActivated: PropTypes.bool.isRequired,
|
||||
isTokenSet: PropTypes.bool.isRequired,
|
||||
setUserToken: PropTypes.func.isRequired,
|
||||
};
|
||||
export const ProtectedRoute = connect(
|
||||
(state) => ({
|
||||
isLogged: state.userStore.get("isLogged"),
|
||||
isAdmin: state.userStore.get("isLogged"),
|
||||
isActivated: state.userStore.get("isActivated"),
|
||||
isTokenSet: state.userStore.get("isTokenSet"),
|
||||
}),
|
||||
{ setUserToken }
|
||||
)(protectedRoute);
|
||||
|
||||
const adminRoute = ({ component: Component, isAdmin, ...otherProps }) => {
|
||||
export const AdminRoute = ({ component: Component, ...otherProps }) => {
|
||||
const isAdmin = useSelector((state) => state.userStore.get("isAdmin"));
|
||||
return (
|
||||
<Route
|
||||
{...otherProps}
|
||||
@ -76,10 +66,6 @@ const adminRoute = ({ component: Component, isAdmin, ...otherProps }) => {
|
||||
/>
|
||||
);
|
||||
};
|
||||
adminRoute.propTypes = {
|
||||
AdminRoute.propTypes = {
|
||||
component: PropTypes.func,
|
||||
isAdmin: PropTypes.bool.isRequired,
|
||||
};
|
||||
export const AdminRoute = connect((state) => ({
|
||||
isAdmin: state.userStore.get("isLogged"),
|
||||
}))(adminRoute);
|
||||
|
@ -1,28 +1,20 @@
|
||||
import React, { useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { getAdminModules } from "../../actions/admins";
|
||||
|
||||
import Modules from "../modules/modules";
|
||||
|
||||
const AdminModulesConnected = ({ modules, loading, getAdminModules }) => {
|
||||
export const AdminModules = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const loading = useSelector((state) =>
|
||||
state.adminStore.get("fetchingModules")
|
||||
);
|
||||
const modules = useSelector((state) => state.adminStore.get("modules"));
|
||||
|
||||
useEffect(() => {
|
||||
getAdminModules();
|
||||
}, [getAdminModules]);
|
||||
dispatch(getAdminModules());
|
||||
}, [dispatch]);
|
||||
|
||||
return <Modules modules={modules} isLoading={loading} />;
|
||||
};
|
||||
AdminModulesConnected.propTypes = {
|
||||
modules: PropTypes.object,
|
||||
loading: PropTypes.bool,
|
||||
getAdminModules: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
loading: state.adminStore.get("fetchingModules"),
|
||||
modules: state.adminStore.get("modules"),
|
||||
});
|
||||
|
||||
export const AdminModules = connect(mapStateToProps, { getAdminModules })(
|
||||
AdminModulesConnected
|
||||
);
|
||||
|
@ -1,15 +1,17 @@
|
||||
import React, { useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
import { Stat } from "./stat";
|
||||
|
||||
import { getStats } from "../../actions/admins";
|
||||
|
||||
const StatsConnected = ({ stats, getStats }) => {
|
||||
export const Stats = () => {
|
||||
const dispatch = useDispatch();
|
||||
const stats = useSelector((state) => state.adminStore.get("stats"));
|
||||
|
||||
useEffect(() => {
|
||||
getStats();
|
||||
}, [getStats]);
|
||||
dispatch(getStats());
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<div className="row d-flex flex-wrap">
|
||||
@ -34,13 +36,3 @@ const StatsConnected = ({ stats, getStats }) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
StatsConnected.propTypes = {
|
||||
stats: PropTypes.object,
|
||||
getStats: PropTypes.func,
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
stats: state.adminStore.get("stats"),
|
||||
});
|
||||
|
||||
export const Stats = connect(mapStateToProps, { getStats })(StatsConnected);
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { List } from "immutable";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
|
||||
import { updateUser, deleteUser } from "../../actions/admins";
|
||||
|
||||
@ -11,7 +10,7 @@ import { PolochonSelect } from "../polochons/select";
|
||||
import { FormModal } from "../forms/modal";
|
||||
import { FormInput } from "../forms/input";
|
||||
|
||||
const UserEditConnect = ({
|
||||
export const UserEdit = ({
|
||||
id,
|
||||
name,
|
||||
admin: initAdmin,
|
||||
@ -19,10 +18,10 @@ const UserEditConnect = ({
|
||||
polochonToken,
|
||||
polochonId: initPolochonId,
|
||||
polochonActivated: initPolochonActivated,
|
||||
updateUser,
|
||||
deleteUser,
|
||||
publicPolochons,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const publicPolochons = useSelector((state) => state.polochon.get("public"));
|
||||
|
||||
const [modal, setModal] = useState(false);
|
||||
const [admin, setAdmin] = useState(initAdmin);
|
||||
const [activated, setActivated] = useState(initActivated);
|
||||
@ -38,15 +37,17 @@ const UserEditConnect = ({
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
updateUser({
|
||||
userId: id,
|
||||
polochonToken: token,
|
||||
admin,
|
||||
activated,
|
||||
polochonId,
|
||||
polochonActivated,
|
||||
password,
|
||||
});
|
||||
dispatch(
|
||||
updateUser({
|
||||
userId: id,
|
||||
polochonToken: token,
|
||||
admin,
|
||||
activated,
|
||||
polochonId,
|
||||
polochonActivated,
|
||||
password,
|
||||
})
|
||||
);
|
||||
setModal(false);
|
||||
};
|
||||
|
||||
@ -55,7 +56,7 @@ const UserEditConnect = ({
|
||||
e.preventDefault();
|
||||
}
|
||||
if (confirmDelete) {
|
||||
deleteUser(name);
|
||||
dispatch(deleteUser(name));
|
||||
setModal(false);
|
||||
} else {
|
||||
setConfirmDelete(true);
|
||||
@ -137,23 +138,12 @@ const UserEditConnect = ({
|
||||
</span>
|
||||
);
|
||||
};
|
||||
UserEditConnect.propTypes = {
|
||||
UserEdit.propTypes = {
|
||||
id: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
activated: PropTypes.bool,
|
||||
admin: PropTypes.bool,
|
||||
updateUser: PropTypes.func,
|
||||
deleteUser: PropTypes.func,
|
||||
polochonToken: PropTypes.string,
|
||||
polochonId: PropTypes.string,
|
||||
polochonActivated: PropTypes.bool,
|
||||
publicPolochons: PropTypes.instanceOf(List),
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
publicPolochons: state.polochon.get("public"),
|
||||
});
|
||||
|
||||
export const UserEdit = connect(mapStateToProps, { updateUser, deleteUser })(
|
||||
UserEditConnect
|
||||
);
|
||||
|
@ -1,23 +1,19 @@
|
||||
import React, { useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
import { User } from "./user";
|
||||
|
||||
import { getUsers } from "../../actions/admins";
|
||||
import { getPolochons } from "../../actions/polochon";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
users: state.adminStore.get("users"),
|
||||
});
|
||||
const mapDispatchToProps = { getUsers, getPolochons };
|
||||
export const UserList = () => {
|
||||
const dispatch = useDispatch();
|
||||
const users = useSelector((state) => state.adminStore.get("users"));
|
||||
|
||||
const UserListConnect = ({ users, getUsers, getPolochons }) => {
|
||||
useEffect(() => {
|
||||
getUsers();
|
||||
getPolochons();
|
||||
}, [getUsers, getPolochons]);
|
||||
dispatch(getUsers());
|
||||
dispatch(getPolochons());
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<div className="table-responsive my-2">
|
||||
@ -54,13 +50,3 @@ const UserListConnect = ({ users, getUsers, getPolochons }) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
UserListConnect.propTypes = {
|
||||
getUsers: PropTypes.func,
|
||||
getPolochons: PropTypes.func,
|
||||
users: PropTypes.PropTypes.instanceOf(List),
|
||||
};
|
||||
|
||||
export const UserList = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(UserListConnect);
|
||||
|
@ -1,35 +1,27 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import SweetAlert from "react-bootstrap-sweetalert";
|
||||
import { connect } from "react-redux";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
|
||||
import { dismissAlert } from "../../actions/alerts";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
show: state.alerts.get("show"),
|
||||
title: state.alerts.get("message"),
|
||||
type: state.alerts.get("type"),
|
||||
});
|
||||
const mapDispatchToProps = { dismissAlert };
|
||||
const Alert = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const Alert = (props) => {
|
||||
if (!props.show) {
|
||||
const show = useSelector((state) => state.alerts.get("show"));
|
||||
const title = useSelector((state) => state.alerts.get("message"));
|
||||
const type = useSelector((state) => state.alerts.get("type"));
|
||||
|
||||
if (!show) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<SweetAlert
|
||||
type={props.type}
|
||||
title={props.title}
|
||||
onConfirm={props.dismissAlert}
|
||||
type={type}
|
||||
title={title}
|
||||
onConfirm={() => dispatch(dismissAlert())}
|
||||
/>
|
||||
);
|
||||
};
|
||||
Alert.propTypes = {
|
||||
show: PropTypes.bool.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
dismissAlert: PropTypes.func.isRequired,
|
||||
type: PropTypes.string,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Alert);
|
||||
export default Alert;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
import { prettySize } from "../../utils";
|
||||
import { addTorrent } from "../../actions/torrents";
|
||||
@ -45,10 +45,10 @@ function buildMenuItems(torrents) {
|
||||
return entries;
|
||||
}
|
||||
|
||||
const torrentsButton = ({ torrents, search, searching, addTorrent, url }) => {
|
||||
/* eslint-disable */
|
||||
export const TorrentsButton = ({ torrents, search, searching, url }) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [show, setShow] = useState(false);
|
||||
/* eslint-enable */
|
||||
const entries = buildMenuItems(torrents);
|
||||
const count = torrents && torrents.size !== 0 ? torrents.size : 0;
|
||||
|
||||
@ -97,7 +97,10 @@ const torrentsButton = ({ torrents, search, searching, addTorrent, url }) => {
|
||||
return <Dropdown.Divider key={index} />;
|
||||
case "entry":
|
||||
return (
|
||||
<Dropdown.Item key={index} onClick={() => addTorrent(e.url)}>
|
||||
<Dropdown.Item
|
||||
key={index}
|
||||
onClick={() => dispatch(addTorrent(e.url))}
|
||||
>
|
||||
{e.quality}
|
||||
{e.size !== 0 && (
|
||||
<small className="ml-1">({prettySize(e.size)})</small>
|
||||
@ -111,15 +114,12 @@ const torrentsButton = ({ torrents, search, searching, addTorrent, url }) => {
|
||||
</span>
|
||||
);
|
||||
};
|
||||
torrentsButton.propTypes = {
|
||||
TorrentsButton.propTypes = {
|
||||
torrents: PropTypes.instanceOf(List),
|
||||
searching: PropTypes.bool,
|
||||
search: PropTypes.func.isRequired,
|
||||
addTorrent: PropTypes.func.isRequired,
|
||||
url: PropTypes.string,
|
||||
};
|
||||
torrentsButton.defaultProps = {
|
||||
TorrentsButton.defaultProps = {
|
||||
torrents: List(),
|
||||
};
|
||||
|
||||
export const TorrentsButton = connect(null, { addTorrent })(torrentsButton);
|
||||
|
@ -1,10 +1,20 @@
|
||||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import { Form, FormGroup, FormControl, FormLabel } from "react-bootstrap";
|
||||
|
||||
const ExplorerOptions = ({ display, params, options, type, history }) => {
|
||||
// Should this componennt be displayed
|
||||
const ExplorerOptions = ({
|
||||
display,
|
||||
params,
|
||||
options,
|
||||
type,
|
||||
history,
|
||||
fetch,
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
if (!display) {
|
||||
return null;
|
||||
}
|
||||
@ -103,6 +113,7 @@ ExplorerOptions.propTypes = {
|
||||
type: PropTypes.string,
|
||||
options: PropTypes.object,
|
||||
display: PropTypes.bool,
|
||||
fetch: PropTypes.func,
|
||||
};
|
||||
|
||||
export default withRouter(ExplorerOptions);
|
||||
|
@ -55,6 +55,7 @@ const ListPosters = (props) => {
|
||||
type={props.type}
|
||||
display={displayExplorerOptions}
|
||||
params={props.params}
|
||||
fetch={props.exploreFetchOptions}
|
||||
options={props.exploreOptions}
|
||||
/>
|
||||
<Posters
|
||||
@ -81,6 +82,7 @@ ListPosters.propTypes = {
|
||||
placeHolder: PropTypes.string.isRequired,
|
||||
updateFilter: PropTypes.func.isRequired,
|
||||
filter: PropTypes.string,
|
||||
exploreFetchOptions: PropTypes.func,
|
||||
};
|
||||
|
||||
export default ListPosters;
|
||||
|
@ -1,11 +1,14 @@
|
||||
import React from "react";
|
||||
import React, { useEffect, useCallback } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { OrderedMap, Map } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { Map } from "immutable";
|
||||
|
||||
import {
|
||||
selectMovie,
|
||||
updateFilter,
|
||||
movieWishlistToggle,
|
||||
fetchMovies,
|
||||
getMovieExploreOptions,
|
||||
} from "../../actions/movies";
|
||||
|
||||
import ListDetails from "../list/details";
|
||||
@ -18,54 +21,86 @@ import { ShowMore } from "../buttons/showMore";
|
||||
import { MovieSubtitlesButton } from "./subtitlesButton";
|
||||
import { MovieTorrentsButton } from "./torrentsButton";
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
loading: state.movieStore.get("loading"),
|
||||
movies: state.movieStore.get("movies"),
|
||||
filter: state.movieStore.get("filter"),
|
||||
selectedImdbId: state.movieStore.get("selectedImdbId"),
|
||||
exploreOptions: state.movieStore.get("exploreOptions"),
|
||||
};
|
||||
}
|
||||
const mapDispatchToProps = {
|
||||
selectMovie,
|
||||
updateFilter,
|
||||
movieWishlistToggle,
|
||||
const fetchUrl = (match) => {
|
||||
switch (match.path) {
|
||||
case "/movies/polochon":
|
||||
return "/movies/polochon";
|
||||
case "/movies/wishlist":
|
||||
return "/wishlist/movies";
|
||||
case "/movies/search/:search":
|
||||
return "/movies/search/" + match.params.search;
|
||||
case "/movies/explore/:source/:category":
|
||||
return (
|
||||
"/movies/explore?source=" +
|
||||
encodeURI(match.params.source) +
|
||||
"&category=" +
|
||||
encodeURI(match.params.category)
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const MovieList = (props) => {
|
||||
const MovieList = ({ match }) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
const url = fetchUrl(match);
|
||||
if (url !== null) {
|
||||
dispatch(fetchMovies(url));
|
||||
}
|
||||
}, [dispatch, match]);
|
||||
|
||||
const loading = useSelector((state) => state.movieStore.get("loading"));
|
||||
const movies = useSelector((state) => state.movieStore.get("movies"));
|
||||
const filter = useSelector((state) => state.movieStore.get("filter"));
|
||||
const selectedImdbId = useSelector((state) =>
|
||||
state.movieStore.get("selectedImdbId")
|
||||
);
|
||||
const exploreOptions = useSelector((state) =>
|
||||
state.movieStore.get("exploreOptions")
|
||||
);
|
||||
|
||||
let selectedMovie = Map();
|
||||
if (props.movies !== undefined && props.movies.has(props.selectedImdbId)) {
|
||||
selectedMovie = props.movies.get(props.selectedImdbId);
|
||||
if (movies !== undefined && movies.has(selectedImdbId)) {
|
||||
selectedMovie = movies.get(selectedImdbId);
|
||||
}
|
||||
|
||||
const selectFunc = useCallback((id) => dispatch(selectMovie(id)), [dispatch]);
|
||||
const filterFunc = useCallback((filter) => dispatch(updateFilter(filter)), [
|
||||
dispatch,
|
||||
]);
|
||||
const exploreFetchOptions = useCallback(
|
||||
() => dispatch(getMovieExploreOptions()),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="row">
|
||||
<ListPosters
|
||||
data={props.movies}
|
||||
data={movies}
|
||||
type="movies"
|
||||
placeHolder="Filter movies..."
|
||||
exploreOptions={props.exploreOptions}
|
||||
selectedImdbId={props.selectedImdbId}
|
||||
updateFilter={props.updateFilter}
|
||||
filter={props.filter}
|
||||
onClick={props.selectMovie}
|
||||
onDoubleClick={function () {
|
||||
return;
|
||||
}}
|
||||
onKeyEnter={function () {
|
||||
return;
|
||||
}}
|
||||
params={props.match.params}
|
||||
loading={props.loading}
|
||||
exploreFetchOptions={exploreFetchOptions}
|
||||
exploreOptions={exploreOptions}
|
||||
selectedImdbId={selectedImdbId}
|
||||
updateFilter={filterFunc}
|
||||
filter={filter}
|
||||
onClick={selectFunc}
|
||||
onDoubleClick={() => {}}
|
||||
onKeyEnter={() => {}}
|
||||
params={match.params}
|
||||
loading={loading}
|
||||
/>
|
||||
<ListDetails
|
||||
data={selectedMovie}
|
||||
loading={props.loading}
|
||||
loading={loading}
|
||||
wishlist={() =>
|
||||
props.movieWishlistToggle(
|
||||
selectedMovie.get("imdb_id"),
|
||||
isWishlisted(selectedMovie)
|
||||
dispatch(
|
||||
movieWishlistToggle(
|
||||
selectedMovie.get("imdb_id"),
|
||||
isWishlisted(selectedMovie)
|
||||
)
|
||||
)
|
||||
}
|
||||
>
|
||||
@ -91,15 +126,12 @@ const MovieList = (props) => {
|
||||
);
|
||||
};
|
||||
MovieList.propTypes = {
|
||||
movies: PropTypes.instanceOf(OrderedMap),
|
||||
exploreOptions: PropTypes.instanceOf(Map),
|
||||
selectedImdbId: PropTypes.string,
|
||||
filter: PropTypes.string,
|
||||
loading: PropTypes.bool,
|
||||
fetchMovies: PropTypes.func,
|
||||
getMovieExploreOptions: PropTypes.func,
|
||||
updateFilter: PropTypes.func,
|
||||
movieWishlistToggle: PropTypes.func,
|
||||
selectMovie: PropTypes.func,
|
||||
match: PropTypes.object,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(MovieList);
|
||||
export default MovieList;
|
||||
|
@ -1,65 +0,0 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Route } from "react-router-dom";
|
||||
import { connect } from "react-redux";
|
||||
import { fetchMovies, getMovieExploreOptions } from "../../actions/movies";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
isExplorerFetched: state.movieStore.get("exploreOptions").size !== 0,
|
||||
});
|
||||
const mapDispatchToProps = { fetchMovies, getMovieExploreOptions };
|
||||
|
||||
const MoviesRoute = ({
|
||||
component: Component,
|
||||
isExplorerFetched,
|
||||
fetchMovies,
|
||||
getMovieExploreOptions,
|
||||
...otherProps
|
||||
}) => {
|
||||
return (
|
||||
<Route
|
||||
{...otherProps}
|
||||
render={(props) => {
|
||||
let fetchUrl = "";
|
||||
switch (props.match.path) {
|
||||
case "/movies/polochon":
|
||||
fetchUrl = "/movies/polochon";
|
||||
break;
|
||||
case "/movies/wishlist":
|
||||
fetchUrl = "/wishlist/movies";
|
||||
break;
|
||||
case "/movies/search/:search":
|
||||
fetchUrl = "/movies/search/" + props.match.params.search;
|
||||
break;
|
||||
case "/movies/explore/:source/:category":
|
||||
if (!isExplorerFetched) {
|
||||
getMovieExploreOptions();
|
||||
}
|
||||
fetchUrl =
|
||||
"/movies/explore?source=" +
|
||||
encodeURI(props.match.params.source) +
|
||||
"&category=" +
|
||||
encodeURI(props.match.params.category);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (fetchUrl != "") {
|
||||
fetchMovies(fetchUrl);
|
||||
}
|
||||
|
||||
return <Component {...props} />;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
MoviesRoute.propTypes = {
|
||||
component: PropTypes.object,
|
||||
match: PropTypes.object,
|
||||
isExplorerFetched: PropTypes.bool.isRequired,
|
||||
fetchMovies: PropTypes.func.isRequired,
|
||||
getMovieExploreOptions: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(MoviesRoute);
|
@ -1,35 +1,32 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
import { searchMovieSubtitles } from "../../actions/subtitles";
|
||||
|
||||
import { SubtitlesButton } from "../buttons/subtitles";
|
||||
|
||||
const movieSubtitlesButton = ({
|
||||
export const MovieSubtitlesButton = ({
|
||||
inLibrary,
|
||||
imdbId,
|
||||
searching,
|
||||
searchMovieSubtitles,
|
||||
subtitles,
|
||||
}) => (
|
||||
<SubtitlesButton
|
||||
inLibrary={inLibrary}
|
||||
searching={searching}
|
||||
subtitles={subtitles}
|
||||
search={() => searchMovieSubtitles(imdbId)}
|
||||
/>
|
||||
);
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
movieSubtitlesButton.propTypes = {
|
||||
return (
|
||||
<SubtitlesButton
|
||||
inLibrary={inLibrary}
|
||||
searching={searching}
|
||||
subtitles={subtitles}
|
||||
search={() => dispatch(searchMovieSubtitles(imdbId))}
|
||||
/>
|
||||
);
|
||||
};
|
||||
MovieSubtitlesButton.propTypes = {
|
||||
searching: PropTypes.bool,
|
||||
inLibrary: PropTypes.bool,
|
||||
imdbId: PropTypes.string,
|
||||
searchMovieSubtitles: PropTypes.func,
|
||||
subtitles: PropTypes.instanceOf(List),
|
||||
};
|
||||
|
||||
export const MovieSubtitlesButton = connect(null, { searchMovieSubtitles })(
|
||||
movieSubtitlesButton
|
||||
);
|
||||
|
@ -1,33 +1,26 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
import { getMovieDetails } from "../../actions/movies";
|
||||
import { TorrentsButton } from "../buttons/torrents";
|
||||
|
||||
const movieTorrentsButton = ({
|
||||
torrents,
|
||||
imdbId,
|
||||
title,
|
||||
searching,
|
||||
getMovieDetails,
|
||||
}) => (
|
||||
<TorrentsButton
|
||||
torrents={torrents}
|
||||
searching={searching}
|
||||
search={() => getMovieDetails(imdbId)}
|
||||
url={`#/torrents/search/movies/${encodeURI(title)}`}
|
||||
/>
|
||||
);
|
||||
movieTorrentsButton.propTypes = {
|
||||
export const MovieTorrentsButton = ({ torrents, imdbId, title, searching }) => {
|
||||
const dispatch = useDispatch();
|
||||
return (
|
||||
<TorrentsButton
|
||||
torrents={torrents}
|
||||
searching={searching}
|
||||
search={() => dispatch(getMovieDetails(imdbId))}
|
||||
url={`#/torrents/search/movies/${encodeURI(title)}`}
|
||||
/>
|
||||
);
|
||||
};
|
||||
MovieTorrentsButton.propTypes = {
|
||||
torrents: PropTypes.instanceOf(List),
|
||||
imdbId: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
searching: PropTypes.bool,
|
||||
getMovieDetails: PropTypes.func,
|
||||
};
|
||||
|
||||
export const MovieTorrentsButton = connect(null, { getMovieDetails })(
|
||||
movieTorrentsButton
|
||||
);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import { Route } from "react-router-dom";
|
||||
import { connect } from "react-redux";
|
||||
import { useSelector } from "react-redux";
|
||||
import { LinkContainer } from "react-router-bootstrap";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
@ -8,24 +8,15 @@ import Nav from "react-bootstrap/Nav";
|
||||
import Navbar from "react-bootstrap/Navbar";
|
||||
import NavDropdown from "react-bootstrap/NavDropdown";
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
let torrentCount = 0;
|
||||
if (
|
||||
state.torrentStore.has("torrents") &&
|
||||
state.torrentStore.get("torrents") !== undefined
|
||||
) {
|
||||
torrentCount = state.torrentStore.get("torrents").size;
|
||||
}
|
||||
return {
|
||||
username: state.userStore.get("username"),
|
||||
isAdmin: state.userStore.get("isAdmin"),
|
||||
torrentCount: torrentCount,
|
||||
};
|
||||
};
|
||||
|
||||
const AppNavBar = (props) => {
|
||||
const AppNavBar = () => {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
const username = useSelector((state) => state.userStore.get("username"));
|
||||
const isAdmin = useSelector((state) => state.userStore.get("isAdmin"));
|
||||
const torrentCount = useSelector(
|
||||
(state) => state.torrentStore.get("torrents").size
|
||||
);
|
||||
|
||||
return (
|
||||
<Navbar
|
||||
fixed="top"
|
||||
@ -45,7 +36,7 @@ const AppNavBar = (props) => {
|
||||
<MoviesDropdown />
|
||||
<ShowsDropdown />
|
||||
<WishlistDropdown />
|
||||
<TorrentsDropdown torrentsCount={props.torrentCount} />
|
||||
<TorrentsDropdown torrentsCount={torrentCount} />
|
||||
</Nav>
|
||||
<Nav>
|
||||
<Route
|
||||
@ -70,20 +61,16 @@ const AppNavBar = (props) => {
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<UserDropdown username={props.username} isAdmin={props.isAdmin} />
|
||||
<UserDropdown username={username} isAdmin={isAdmin} />
|
||||
</Nav>
|
||||
</Navbar.Collapse>
|
||||
</Navbar>
|
||||
);
|
||||
};
|
||||
AppNavBar.propTypes = {
|
||||
torrentCount: PropTypes.number.isRequired,
|
||||
username: PropTypes.string.isRequired,
|
||||
isAdmin: PropTypes.bool.isRequired,
|
||||
history: PropTypes.object,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(AppNavBar);
|
||||
export default AppNavBar;
|
||||
|
||||
const Search = ({ path, placeholder, setExpanded, history }) => {
|
||||
const [search, setSearch] = useState("");
|
||||
|
@ -1,12 +1,12 @@
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
import { Toast } from "react-bootstrap";
|
||||
|
||||
import { removeNotification } from "../../actions/notifications";
|
||||
|
||||
const NotificationConnected = ({
|
||||
export const Notification = ({
|
||||
id,
|
||||
icon,
|
||||
title,
|
||||
@ -14,13 +14,13 @@ const NotificationConnected = ({
|
||||
imageUrl,
|
||||
autohide,
|
||||
delay,
|
||||
removeNotification,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const [show, setShow] = useState(true);
|
||||
|
||||
const hide = () => {
|
||||
setShow(false);
|
||||
setTimeout(() => removeNotification(id), 200);
|
||||
setTimeout(() => dispatch(removeNotification(id)), 200);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -38,7 +38,7 @@ const NotificationConnected = ({
|
||||
</Toast>
|
||||
);
|
||||
};
|
||||
NotificationConnected.propTypes = {
|
||||
Notification.propTypes = {
|
||||
id: PropTypes.string,
|
||||
icon: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
@ -46,10 +46,9 @@ NotificationConnected.propTypes = {
|
||||
imageUrl: PropTypes.string,
|
||||
autohide: PropTypes.bool,
|
||||
delay: PropTypes.number,
|
||||
removeNotification: PropTypes.func,
|
||||
};
|
||||
|
||||
NotificationConnected.defaultProps = {
|
||||
Notification.defaultProps = {
|
||||
autohide: false,
|
||||
delay: 5000,
|
||||
icon: "",
|
||||
@ -57,7 +56,3 @@ NotificationConnected.defaultProps = {
|
||||
title: "Info",
|
||||
message: "",
|
||||
};
|
||||
|
||||
export const Notification = connect(null, { removeNotification })(
|
||||
NotificationConnected
|
||||
);
|
||||
|
@ -1,11 +1,12 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
import { Notification } from "./notification";
|
||||
|
||||
const NotificationsConnected = ({ notifications }) => {
|
||||
export const Notifications = () => {
|
||||
const notifications = useSelector((state) => state.notifications);
|
||||
return (
|
||||
<div className="notifications">
|
||||
{notifications.map((el) => (
|
||||
@ -23,12 +24,6 @@ const NotificationsConnected = ({ notifications }) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
NotificationsConnected.propTypes = {
|
||||
Notifications.propTypes = {
|
||||
notifications: PropTypes.instanceOf(List),
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
notifications: state.notifications,
|
||||
});
|
||||
|
||||
export const Notifications = connect(mapStateToProps)(NotificationsConnected);
|
||||
|
@ -1,12 +1,13 @@
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
import { addPolochon } from "../../actions/polochon";
|
||||
|
||||
import { PolochonEdit } from "./edit";
|
||||
|
||||
export const PolochonAddConnected = ({ addPolochon }) => {
|
||||
export const PolochonAdd = () => {
|
||||
const dispatch = useDispatch();
|
||||
const [modal, setModal] = useState(false);
|
||||
|
||||
return (
|
||||
@ -21,13 +22,11 @@ export const PolochonAddConnected = ({ addPolochon }) => {
|
||||
icon="plus"
|
||||
show={modal}
|
||||
setShow={setModal}
|
||||
update={addPolochon}
|
||||
update={(params) => dispatch(addPolochon(params))}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
PolochonAddConnected.propTypes = {
|
||||
PolochonAdd.propTypes = {
|
||||
addPolochon: PropTypes.func,
|
||||
};
|
||||
|
||||
export const PolochonAdd = connect(null, { addPolochon })(PolochonAddConnected);
|
||||
|
@ -1,25 +1,18 @@
|
||||
import React, { useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { List } from "immutable";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
|
||||
import { getManagedPolochons } from "../../actions/polochon";
|
||||
|
||||
import { Polochon } from "./polochon";
|
||||
import { PolochonAdd } from "./add";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
managedList: state.polochon.get("managed"),
|
||||
});
|
||||
export const PolochonList = () => {
|
||||
const list = useSelector((state) => state.polochon.get("managed"));
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const mapDispatchToProps = {
|
||||
getManagedPolochons,
|
||||
};
|
||||
|
||||
const PolochonListConnected = ({ getManagedPolochons, managedList }) => {
|
||||
useEffect(() => {
|
||||
getManagedPolochons();
|
||||
}, [getManagedPolochons]);
|
||||
dispatch(getManagedPolochons());
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<div className="row mb-3">
|
||||
@ -27,7 +20,7 @@ const PolochonListConnected = ({ getManagedPolochons, managedList }) => {
|
||||
<h2>My polochons</h2>
|
||||
<hr />
|
||||
<span>
|
||||
{managedList.map((el, index) => (
|
||||
{list.map((el, index) => (
|
||||
<Polochon
|
||||
key={index}
|
||||
id={el.get("id")}
|
||||
@ -44,12 +37,3 @@ const PolochonListConnected = ({ getManagedPolochons, managedList }) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
PolochonListConnected.propTypes = {
|
||||
getManagedPolochons: PropTypes.func,
|
||||
managedList: PropTypes.instanceOf(List),
|
||||
};
|
||||
|
||||
export const PolochonList = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(PolochonListConnected);
|
||||
|
@ -1,25 +1,18 @@
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
import { PolochonUsers } from "./users";
|
||||
import { PolochonEdit } from "./edit";
|
||||
|
||||
import { updatePolochon, deletePolochon } from "../../actions/polochon";
|
||||
|
||||
export const PolochonConnected = ({
|
||||
id,
|
||||
name,
|
||||
token,
|
||||
url,
|
||||
authToken,
|
||||
users,
|
||||
updatePolochon,
|
||||
deletePolochon,
|
||||
}) => {
|
||||
export const Polochon = ({ id, name, token, url, authToken, users }) => {
|
||||
const [edit, setEdit] = useState(false);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="card mb-2">
|
||||
@ -33,7 +26,7 @@ export const PolochonConnected = ({
|
||||
/>
|
||||
<i
|
||||
className="fa fa-trash clickable"
|
||||
onClick={() => deletePolochon(id)}
|
||||
onClick={() => dispatch(deletePolochon(id))}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
@ -49,7 +42,7 @@ export const PolochonConnected = ({
|
||||
title="Polochon config"
|
||||
show={edit}
|
||||
setShow={setEdit}
|
||||
update={updatePolochon}
|
||||
update={(params) => dispatch(updatePolochon(params))}
|
||||
id={id}
|
||||
initialName={name}
|
||||
initialUrl={url}
|
||||
@ -58,17 +51,11 @@ export const PolochonConnected = ({
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
PolochonConnected.propTypes = {
|
||||
Polochon.propTypes = {
|
||||
id: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
token: PropTypes.string,
|
||||
url: PropTypes.string,
|
||||
authToken: PropTypes.string,
|
||||
users: PropTypes.instanceOf(List),
|
||||
updatePolochon: PropTypes.func,
|
||||
deletePolochon: PropTypes.func,
|
||||
};
|
||||
|
||||
export const Polochon = connect(null, { updatePolochon, deletePolochon })(
|
||||
PolochonConnected
|
||||
);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
import { editPolochonUser } from "../../actions/polochon";
|
||||
|
||||
@ -9,14 +9,14 @@ import Toggle from "react-bootstrap-toggle";
|
||||
import { FormModal } from "../forms/modal";
|
||||
import { FormInput } from "../forms/input";
|
||||
|
||||
export const PolochonUserConnected = ({
|
||||
export const PolochonUser = ({
|
||||
polochonId,
|
||||
id,
|
||||
name,
|
||||
initialToken,
|
||||
initialActivated,
|
||||
editPolochonUser,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const [edit, setEdit] = useState(false);
|
||||
const [token, setToken] = useState(initialToken);
|
||||
const [activated, setActivated] = useState(initialActivated);
|
||||
@ -27,12 +27,14 @@ export const PolochonUserConnected = ({
|
||||
}, [initialActivated, initialToken]);
|
||||
|
||||
const handleSubmit = () => {
|
||||
editPolochonUser({
|
||||
polochonId,
|
||||
id,
|
||||
token,
|
||||
activated,
|
||||
});
|
||||
dispatch(
|
||||
editPolochonUser({
|
||||
polochonId,
|
||||
id,
|
||||
token,
|
||||
activated,
|
||||
})
|
||||
);
|
||||
setEdit(false);
|
||||
};
|
||||
|
||||
@ -67,7 +69,7 @@ export const PolochonUserConnected = ({
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
PolochonUserConnected.propTypes = {
|
||||
PolochonUser.propTypes = {
|
||||
polochonId: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
@ -75,7 +77,3 @@ PolochonUserConnected.propTypes = {
|
||||
initialActivated: PropTypes.bool,
|
||||
editPolochonUser: PropTypes.func,
|
||||
};
|
||||
|
||||
export const PolochonUser = connect(null, { editPolochonUser })(
|
||||
PolochonUserConnected
|
||||
);
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Map } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
|
||||
import Loader from "../loader/loader";
|
||||
|
||||
@ -9,12 +8,17 @@ import { Fanart } from "./details/fanart";
|
||||
import { Header } from "./details/header";
|
||||
import { SeasonsList } from "./details/seasons";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
loading: state.showStore.get("loading"),
|
||||
show: state.showStore.get("show"),
|
||||
});
|
||||
import { fetchShowDetails } from "../../actions/shows";
|
||||
|
||||
export const ShowDetails = ({ match }) => {
|
||||
const dispatch = useDispatch();
|
||||
const loading = useSelector((state) => state.showStore.get("loading"));
|
||||
const show = useSelector((state) => state.showStore.get("show"));
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchShowDetails(match.params.imdbId));
|
||||
}, [dispatch, match]);
|
||||
|
||||
const showDetails = ({ show, loading }) => {
|
||||
if (loading === true) {
|
||||
return <Loader />;
|
||||
}
|
||||
@ -29,8 +33,6 @@ const showDetails = ({ show, loading }) => {
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
showDetails.propTypes = {
|
||||
loading: PropTypes.bool,
|
||||
show: PropTypes.instanceOf(Map),
|
||||
ShowDetails.propTypes = {
|
||||
match: PropTypes.object.isRequired,
|
||||
};
|
||||
export const ShowDetails = connect(mapStateToProps)(showDetails);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Map } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
import { showWishlistToggle } from "../../../actions/shows";
|
||||
|
||||
@ -24,87 +24,88 @@ import { EpisodeSubtitlesButton } from "./subtitlesButton";
|
||||
import { EpisodeThumb } from "./episodeThumb";
|
||||
import { EpisodeTorrentsButton } from "./torrentsButton";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
trackedSeason: state.showStore.getIn(["show", "tracked_season"], null),
|
||||
trackedEpisode: state.showStore.getIn(["show", "tracked_episode"], null),
|
||||
});
|
||||
export const Episode = (props) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const episode = (props) => (
|
||||
<div className="d-flex flex-column flex-lg-row mb-3 pb-3 border-bottom border-light">
|
||||
<EpisodeThumb url={props.data.get("thumb")} />
|
||||
<div className="d-flex flex-column">
|
||||
<Title
|
||||
title={`${props.data.get("episode")}. ${props.data.get("title")}`}
|
||||
wishlisted={isEpisodeWishlisted(
|
||||
props.data,
|
||||
props.trackedSeason,
|
||||
props.trackedEpisode
|
||||
)}
|
||||
wishlist={() =>
|
||||
props.showWishlistToggle(
|
||||
isEpisodeWishlisted(props.data),
|
||||
props.data.get("show_imdb_id"),
|
||||
const trackedSeason = useSelector((state) =>
|
||||
state.showStore.getIn(["show", "tracked_season"], null)
|
||||
);
|
||||
const trackedEpisode = useSelector((state) =>
|
||||
state.showStore.getIn(["show", "tracked_episode"], null)
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="d-flex flex-column flex-lg-row mb-3 pb-3 border-bottom border-light">
|
||||
<EpisodeThumb url={props.data.get("thumb")} />
|
||||
<div className="d-flex flex-column">
|
||||
<Title
|
||||
title={`${props.data.get("episode")}. ${props.data.get("title")}`}
|
||||
wishlisted={isEpisodeWishlisted(
|
||||
props.data,
|
||||
trackedSeason,
|
||||
trackedEpisode
|
||||
)}
|
||||
wishlist={() =>
|
||||
dispatch(
|
||||
showWishlistToggle(
|
||||
isEpisodeWishlisted(props.data),
|
||||
props.data.get("show_imdb_id"),
|
||||
props.data.get("season"),
|
||||
props.data.get("episode")
|
||||
)
|
||||
)
|
||||
}
|
||||
/>
|
||||
<ReleaseDate date={props.data.get("aired")} />
|
||||
<Runtime runtime={props.data.get("runtime")} />
|
||||
<Plot plot={props.data.get("plot")} />
|
||||
<DownloadAndStream
|
||||
name={prettyEpisodeName(
|
||||
props.showName,
|
||||
props.data.get("season"),
|
||||
props.data.get("episode")
|
||||
)
|
||||
}
|
||||
/>
|
||||
<ReleaseDate date={props.data.get("aired")} />
|
||||
<Runtime runtime={props.data.get("runtime")} />
|
||||
<Plot plot={props.data.get("plot")} />
|
||||
<DownloadAndStream
|
||||
name={prettyEpisodeName(
|
||||
props.showName,
|
||||
props.data.get("season"),
|
||||
props.data.get("episode")
|
||||
)}
|
||||
url={props.data.get("polochon_url")}
|
||||
subtitles={props.data.get("subtitles")}
|
||||
/>
|
||||
<PolochonMetadata
|
||||
quality={props.data.get("quality")}
|
||||
releaseGroup={props.data.get("release_group")}
|
||||
container={props.data.get("container")}
|
||||
audioCodec={props.data.get("audio_codec")}
|
||||
videoCodec={props.data.get("video_codec")}
|
||||
light
|
||||
/>
|
||||
<ShowMore
|
||||
id={prettyEpisodeName(
|
||||
props.showName,
|
||||
props.data.get("season"),
|
||||
props.data.get("episode")
|
||||
)}
|
||||
inLibrary={inLibrary(props.data)}
|
||||
>
|
||||
<EpisodeTorrentsButton
|
||||
torrents={props.data.get("torrents")}
|
||||
showName={props.showName}
|
||||
imdbId={props.data.get("show_imdb_id")}
|
||||
season={props.data.get("season")}
|
||||
episode={props.data.get("episode")}
|
||||
searching={props.data.get("fetching")}
|
||||
/>
|
||||
<EpisodeSubtitlesButton
|
||||
)}
|
||||
url={props.data.get("polochon_url")}
|
||||
subtitles={props.data.get("subtitles")}
|
||||
inLibrary={inLibrary(props.data)}
|
||||
searching={props.data.get("fetchingSubtitles", false)}
|
||||
imdbId={props.data.get("show_imdb_id")}
|
||||
season={props.data.get("season")}
|
||||
episode={props.data.get("episode")}
|
||||
/>
|
||||
</ShowMore>
|
||||
<PolochonMetadata
|
||||
quality={props.data.get("quality")}
|
||||
releaseGroup={props.data.get("release_group")}
|
||||
container={props.data.get("container")}
|
||||
audioCodec={props.data.get("audio_codec")}
|
||||
videoCodec={props.data.get("video_codec")}
|
||||
light
|
||||
/>
|
||||
<ShowMore
|
||||
id={prettyEpisodeName(
|
||||
props.showName,
|
||||
props.data.get("season"),
|
||||
props.data.get("episode")
|
||||
)}
|
||||
inLibrary={inLibrary(props.data)}
|
||||
>
|
||||
<EpisodeTorrentsButton
|
||||
torrents={props.data.get("torrents")}
|
||||
showName={props.showName}
|
||||
imdbId={props.data.get("show_imdb_id")}
|
||||
season={props.data.get("season")}
|
||||
episode={props.data.get("episode")}
|
||||
searching={props.data.get("fetching")}
|
||||
/>
|
||||
<EpisodeSubtitlesButton
|
||||
subtitles={props.data.get("subtitles")}
|
||||
inLibrary={inLibrary(props.data)}
|
||||
searching={props.data.get("fetchingSubtitles", false)}
|
||||
imdbId={props.data.get("show_imdb_id")}
|
||||
season={props.data.get("season")}
|
||||
episode={props.data.get("episode")}
|
||||
/>
|
||||
</ShowMore>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
episode.propTypes = {
|
||||
data: PropTypes.instanceOf(Map).isRequired,
|
||||
trackedSeason: PropTypes.number,
|
||||
trackedEpisode: PropTypes.number,
|
||||
showName: PropTypes.string.isRequired,
|
||||
showWishlistToggle: PropTypes.func,
|
||||
);
|
||||
};
|
||||
Episode.propTypes = {
|
||||
data: PropTypes.instanceOf(Map).isRequired,
|
||||
showName: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export const Episode = connect(mapStateToProps, { showWishlistToggle })(
|
||||
episode
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Map } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
import { isWishlisted } from "../../../utils";
|
||||
|
||||
@ -15,56 +15,58 @@ import { TrackingLabel } from "../../details/tracking";
|
||||
|
||||
import { ImdbBadge } from "../../buttons/imdb";
|
||||
|
||||
export const header = (props) => (
|
||||
<div className="card col-12 col-md-10 offset-md-1 mt-n3 mb-3">
|
||||
<div className="d-flex flex-column flex-md-row">
|
||||
<div className="d-flex justify-content-center">
|
||||
<img
|
||||
className="overflow-hidden object-fit-cover"
|
||||
src={props.data.get("poster_url")}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className="card-body">
|
||||
<p className="card-title">
|
||||
<Title
|
||||
title={props.data.get("title")}
|
||||
wishlisted={isWishlisted(props.data)}
|
||||
wishlist={() =>
|
||||
props.showWishlistToggle(
|
||||
isWishlisted(props.data),
|
||||
props.data.get("imdb_id")
|
||||
)
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
<p className="card-text">
|
||||
<ReleaseDate date={props.data.get("year")} />
|
||||
</p>
|
||||
<p className="card-text">
|
||||
<Rating rating={props.data.get("rating")} />
|
||||
</p>
|
||||
<p className="card-text">
|
||||
<ImdbBadge imdbId={props.data.get("imdb_id")} />
|
||||
</p>
|
||||
<p className="card-text">
|
||||
<TrackingLabel
|
||||
inLibrary={false}
|
||||
trackedSeason={props.data.get("tracked_season")}
|
||||
trackedEpisode={props.data.get("tracked_episode")}
|
||||
/>
|
||||
</p>
|
||||
<p className="card-text">
|
||||
<Plot plot={props.data.get("plot")} />
|
||||
</p>
|
||||
export const Header = (props) => {
|
||||
const dispatch = useDispatch();
|
||||
return (
|
||||
<div className="card col-12 col-md-10 offset-md-1 mt-n3 mb-3">
|
||||
<div className="d-flex flex-column flex-md-row">
|
||||
<div className="d-flex justify-content-center">
|
||||
<img
|
||||
className="overflow-hidden object-fit-cover"
|
||||
src={props.data.get("poster_url")}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className="card-body">
|
||||
<p className="card-title">
|
||||
<Title
|
||||
title={props.data.get("title")}
|
||||
wishlisted={isWishlisted(props.data)}
|
||||
wishlist={() =>
|
||||
dispatch(
|
||||
showWishlistToggle(
|
||||
isWishlisted(props.data),
|
||||
props.data.get("imdb_id")
|
||||
)
|
||||
)
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
<p className="card-text">
|
||||
<ReleaseDate date={props.data.get("year")} />
|
||||
</p>
|
||||
<p className="card-text">
|
||||
<Rating rating={props.data.get("rating")} />
|
||||
</p>
|
||||
<p className="card-text">
|
||||
<ImdbBadge imdbId={props.data.get("imdb_id")} />
|
||||
</p>
|
||||
<p className="card-text">
|
||||
<TrackingLabel
|
||||
inLibrary={false}
|
||||
trackedSeason={props.data.get("tracked_season")}
|
||||
trackedEpisode={props.data.get("tracked_episode")}
|
||||
/>
|
||||
</p>
|
||||
<p className="card-text">
|
||||
<Plot plot={props.data.get("plot")} />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
header.propTypes = {
|
||||
data: PropTypes.instanceOf(Map),
|
||||
showWishlistToggle: PropTypes.func,
|
||||
);
|
||||
};
|
||||
Header.propTypes = {
|
||||
data: PropTypes.instanceOf(Map),
|
||||
};
|
||||
|
||||
export const Header = connect(null, { showWishlistToggle })(header);
|
||||
|
@ -1,39 +1,37 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
import { searchEpisodeSubtitles } from "../../../actions/subtitles";
|
||||
|
||||
import { SubtitlesButton } from "../../buttons/subtitles";
|
||||
|
||||
const episodeSubtitlesButton = ({
|
||||
export const EpisodeSubtitlesButton = ({
|
||||
inLibrary,
|
||||
imdbId,
|
||||
season,
|
||||
episode,
|
||||
searching,
|
||||
searchEpisodeSubtitles,
|
||||
subtitles,
|
||||
}) => (
|
||||
<SubtitlesButton
|
||||
subtitles={subtitles}
|
||||
inLibrary={inLibrary}
|
||||
searching={searching}
|
||||
search={() => searchEpisodeSubtitles(imdbId, season, episode)}
|
||||
/>
|
||||
);
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
episodeSubtitlesButton.propTypes = {
|
||||
return (
|
||||
<SubtitlesButton
|
||||
subtitles={subtitles}
|
||||
inLibrary={inLibrary}
|
||||
searching={searching}
|
||||
search={() => dispatch(searchEpisodeSubtitles(imdbId, season, episode))}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
EpisodeSubtitlesButton.propTypes = {
|
||||
inLibrary: PropTypes.bool,
|
||||
searching: PropTypes.bool,
|
||||
imdbId: PropTypes.string,
|
||||
season: PropTypes.number,
|
||||
episode: PropTypes.number,
|
||||
searchEpisodeSubtitles: PropTypes.func,
|
||||
subtitles: PropTypes.instanceOf(List),
|
||||
};
|
||||
|
||||
export const EpisodeSubtitlesButton = connect(null, { searchEpisodeSubtitles })(
|
||||
episodeSubtitlesButton
|
||||
);
|
||||
|
@ -1,40 +1,39 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { List } from "immutable";
|
||||
|
||||
import { getEpisodeDetails } from "../../../actions/shows";
|
||||
|
||||
import { TorrentsButton } from "../../buttons/torrents";
|
||||
import { prettyEpisodeName } from "../../../utils";
|
||||
|
||||
const episodeTorrentsButton = ({
|
||||
export const EpisodeTorrentsButton = ({
|
||||
torrents,
|
||||
imdbId,
|
||||
season,
|
||||
episode,
|
||||
showName,
|
||||
searching,
|
||||
getEpisodeDetails,
|
||||
}) => (
|
||||
<TorrentsButton
|
||||
torrents={torrents}
|
||||
searching={searching}
|
||||
search={() => getEpisodeDetails(imdbId, season, episode)}
|
||||
url={`#/torrents/search/shows/${encodeURI(
|
||||
prettyEpisodeName(showName, season, episode)
|
||||
)}`}
|
||||
/>
|
||||
);
|
||||
episodeTorrentsButton.propTypes = {
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
return (
|
||||
<TorrentsButton
|
||||
torrents={torrents}
|
||||
searching={searching}
|
||||
search={() => dispatch(getEpisodeDetails(imdbId, season, episode))}
|
||||
url={`#/torrents/search/shows/${encodeURI(
|
||||
prettyEpisodeName(showName, season, episode)
|
||||
)}`}
|
||||
/>
|
||||
);
|
||||
};
|
||||
EpisodeTorrentsButton.propTypes = {
|
||||
torrents: PropTypes.instanceOf(List),
|
||||
showName: PropTypes.string.isRequired,
|
||||
imdbId: PropTypes.string.isRequired,
|
||||
episode: PropTypes.number.isRequired,
|
||||
season: PropTypes.number.isRequired,
|
||||
searching: PropTypes.bool.isRequired,
|
||||
getEpisodeDetails: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export const EpisodeTorrentsButton = connect(null, { getEpisodeDetails })(
|
||||
episodeTorrentsButton
|
||||
);
|
||||
|
@ -1,12 +1,14 @@
|
||||
import React from "react";
|
||||
import React, { useEffect, useCallback } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Map } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
|
||||
import {
|
||||
fetchShows,
|
||||
selectShow,
|
||||
showWishlistToggle,
|
||||
getShowDetails,
|
||||
updateFilter,
|
||||
getShowExploreOptions,
|
||||
} from "../../actions/shows";
|
||||
|
||||
import { isWishlisted } from "../../utils";
|
||||
@ -14,55 +16,90 @@ import { isWishlisted } from "../../utils";
|
||||
import ListDetails from "../list/details";
|
||||
import ListPosters from "../list/posters";
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
loading: state.showsStore.get("loading"),
|
||||
shows: state.showsStore.get("shows"),
|
||||
filter: state.showsStore.get("filter"),
|
||||
selectedImdbId: state.showsStore.get("selectedImdbId"),
|
||||
exploreOptions: state.showsStore.get("exploreOptions"),
|
||||
};
|
||||
}
|
||||
const mapDispatchToProps = {
|
||||
selectShow,
|
||||
showWishlistToggle,
|
||||
getShowDetails,
|
||||
updateFilter,
|
||||
const fetchUrl = (match) => {
|
||||
switch (match.path) {
|
||||
case "/shows/polochon":
|
||||
return "/shows/polochon";
|
||||
case "/shows/wishlist":
|
||||
return "/wishlist/shows";
|
||||
case "/shows/search/:search":
|
||||
return "/shows/search/" + match.params.search;
|
||||
case "/shows/explore/:source/:category":
|
||||
return (
|
||||
"/shows/explore?source=" +
|
||||
encodeURI(match.params.source) +
|
||||
"&category=" +
|
||||
encodeURI(match.params.category)
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const ShowList = (props) => {
|
||||
const ShowList = ({ match, history }) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
const url = fetchUrl(match);
|
||||
if (url !== null) {
|
||||
dispatch(fetchShows(url));
|
||||
}
|
||||
}, [dispatch, match]);
|
||||
|
||||
const loading = useSelector((state) => state.showsStore.get("loading"));
|
||||
const shows = useSelector((state) => state.showsStore.get("shows"));
|
||||
const filter = useSelector((state) => state.showsStore.get("filter"));
|
||||
const selectedImdbId = useSelector((state) =>
|
||||
state.showsStore.get("selectedImdbId")
|
||||
);
|
||||
const exploreOptions = useSelector((state) =>
|
||||
state.showsStore.get("exploreOptions")
|
||||
);
|
||||
|
||||
const showDetails = (imdbId) => {
|
||||
props.history.push("/shows/details/" + imdbId);
|
||||
history.push("/shows/details/" + imdbId);
|
||||
};
|
||||
|
||||
let selectedShow = Map();
|
||||
if (props.selectedImdbId !== "") {
|
||||
selectedShow = props.shows.get(props.selectedImdbId);
|
||||
if (selectedImdbId !== "") {
|
||||
selectedShow = shows.get(selectedImdbId);
|
||||
}
|
||||
|
||||
const selectFunc = useCallback((id) => dispatch(selectShow(id)), [dispatch]);
|
||||
const filterFunc = useCallback((filter) => dispatch(updateFilter(filter)), [
|
||||
dispatch,
|
||||
]);
|
||||
const exploreFetchOptions = useCallback(
|
||||
() => dispatch(getShowExploreOptions()),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="row" id="container">
|
||||
<ListPosters
|
||||
data={props.shows}
|
||||
data={shows}
|
||||
type="shows"
|
||||
placeHolder="Filter shows..."
|
||||
exploreOptions={props.exploreOptions}
|
||||
updateFilter={props.updateFilter}
|
||||
selectedImdbId={props.selectedImdbId}
|
||||
filter={props.filter}
|
||||
onClick={props.selectShow}
|
||||
exploreOptions={exploreOptions}
|
||||
exploreFetchOptions={exploreFetchOptions}
|
||||
updateFilter={filterFunc}
|
||||
selectedImdbId={selectedImdbId}
|
||||
filter={filter}
|
||||
onClick={selectFunc}
|
||||
onDoubleClick={showDetails}
|
||||
onKeyEnter={showDetails}
|
||||
params={props.match.params}
|
||||
loading={props.loading}
|
||||
params={match.params}
|
||||
loading={loading}
|
||||
/>
|
||||
<ListDetails
|
||||
data={selectedShow}
|
||||
loading={props.loading}
|
||||
loading={loading}
|
||||
wishlist={() =>
|
||||
props.showWishlistToggle(
|
||||
isWishlisted(selectedShow),
|
||||
selectedShow.get("imdb_id")
|
||||
dispatch(
|
||||
showWishlistToggle(
|
||||
isWishlisted(selectedShow),
|
||||
selectedShow.get("imdb_id")
|
||||
)
|
||||
)
|
||||
}
|
||||
>
|
||||
@ -82,14 +119,5 @@ const ShowList = (props) => {
|
||||
ShowList.propTypes = {
|
||||
match: PropTypes.object,
|
||||
history: PropTypes.object,
|
||||
shows: PropTypes.instanceOf(Map),
|
||||
exploreOptions: PropTypes.instanceOf(Map),
|
||||
selectedImdbId: PropTypes.string,
|
||||
filter: PropTypes.string,
|
||||
loading: PropTypes.bool,
|
||||
showWishlistToggle: PropTypes.func,
|
||||
selectShow: PropTypes.func,
|
||||
getShowDetails: PropTypes.func,
|
||||
updateFilter: PropTypes.func,
|
||||
};
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ShowList);
|
||||
export default ShowList;
|
||||
|
@ -1,76 +0,0 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Route } from "react-router-dom";
|
||||
import { connect } from "react-redux";
|
||||
import {
|
||||
fetchShows,
|
||||
fetchShowDetails,
|
||||
getShowExploreOptions,
|
||||
} from "../../actions/shows";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
isExplorerFetched: state.showsStore.get("exploreOptions").size !== 0,
|
||||
});
|
||||
const mapDispatchToProps = {
|
||||
fetchShows,
|
||||
fetchShowDetails,
|
||||
getShowExploreOptions,
|
||||
};
|
||||
|
||||
const ShowsRoute = ({
|
||||
component: Component,
|
||||
isExplorerFetched,
|
||||
fetchShows,
|
||||
fetchShowDetails,
|
||||
getShowExploreOptions,
|
||||
...otherProps
|
||||
}) => {
|
||||
return (
|
||||
<Route
|
||||
{...otherProps}
|
||||
render={(props) => {
|
||||
let fetchUrl = "";
|
||||
switch (props.match.path) {
|
||||
case "/shows/polochon":
|
||||
fetchUrl = "/shows/polochon";
|
||||
break;
|
||||
case "/shows/wishlist":
|
||||
fetchUrl = "/wishlist/shows";
|
||||
break;
|
||||
case "/shows/search/:search":
|
||||
fetchUrl = "/shows/search/" + props.match.params.search;
|
||||
break;
|
||||
case "/shows/explore/:source/:category":
|
||||
if (!isExplorerFetched) {
|
||||
getShowExploreOptions();
|
||||
}
|
||||
fetchUrl =
|
||||
"/shows/explore?source=" +
|
||||
encodeURI(props.match.params.source) +
|
||||
"&category=" +
|
||||
encodeURI(props.match.params.category);
|
||||
break;
|
||||
case "/shows/details/:imdbId":
|
||||
fetchShowDetails(props.match.params.imdbId);
|
||||
fetchUrl = "";
|
||||
}
|
||||
|
||||
if (fetchUrl != "") {
|
||||
fetchShows(fetchUrl);
|
||||
}
|
||||
|
||||
return <Component {...props} />;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
ShowsRoute.propTypes = {
|
||||
component: PropTypes.object,
|
||||
match: PropTypes.object,
|
||||
isExplorerFetched: PropTypes.bool.isRequired,
|
||||
fetchShows: PropTypes.func.isRequired,
|
||||
fetchShowDetails: PropTypes.func.isRequired,
|
||||
getShowExploreOptions: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ShowsRoute);
|
@ -1,39 +1,28 @@
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Map, List } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
import { prettySize } from "../../utils";
|
||||
import {
|
||||
fetchTorrents,
|
||||
addTorrent,
|
||||
removeTorrent,
|
||||
} from "../../actions/torrents";
|
||||
import { addTorrent, removeTorrent } from "../../actions/torrents";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
torrents: state.torrentStore.get("torrents"),
|
||||
});
|
||||
const mapDispatchToProps = {
|
||||
fetchTorrents,
|
||||
addTorrent,
|
||||
removeTorrent,
|
||||
};
|
||||
const TorrentList = () => {
|
||||
const torrents = useSelector((state) => state.torrentStore.get("torrents"));
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const TorrentList = (props) => (
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<AddTorrent addTorrent={props.addTorrent} />
|
||||
<Torrents torrents={props.torrents} removeTorrent={props.removeTorrent} />
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<AddTorrent addTorrent={(url) => dispatch(addTorrent(url))} />
|
||||
<Torrents
|
||||
torrents={torrents}
|
||||
removeTorrent={(id) => dispatch(removeTorrent(id))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
TorrentList.propTypes = {
|
||||
fetchTorrents: PropTypes.func.isRequired,
|
||||
addTorrent: PropTypes.func.isRequired,
|
||||
removeTorrent: PropTypes.func.isRequired,
|
||||
torrents: PropTypes.instanceOf(List),
|
||||
);
|
||||
};
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(TorrentList);
|
||||
export default TorrentList;
|
||||
|
||||
const AddTorrent = (props) => {
|
||||
const [url, setUrl] = useState("");
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { addTorrent, searchTorrents } from "../../actions/torrents";
|
||||
import { Map, List } from "immutable";
|
||||
import Loader from "../loader/loader";
|
||||
@ -9,40 +9,38 @@ import { OverlayTrigger, Tooltip } from "react-bootstrap";
|
||||
|
||||
import { prettySize } from "../../utils";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
searching: state.torrentStore.get("searching"),
|
||||
results: state.torrentStore.get("searchResults"),
|
||||
});
|
||||
const mapDispatchToProps = { addTorrent, searchTorrents };
|
||||
const TorrentSearch = ({ history, match }) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const searching = useSelector((state) => state.torrentStore.get("searching"));
|
||||
const results = useSelector((state) =>
|
||||
state.torrentStore.get("searchResults")
|
||||
);
|
||||
|
||||
const TorrentSearch = ({
|
||||
searching,
|
||||
searchTorrents,
|
||||
results,
|
||||
addTorrent,
|
||||
history,
|
||||
match,
|
||||
}) => {
|
||||
const [search, setSearch] = useState(match.params.search || "");
|
||||
const [type, setType] = useState(match.params.type || "");
|
||||
const [url, setUrl] = useState("");
|
||||
|
||||
const getUrl = useCallback(() => {
|
||||
const url = useCallback(() => {
|
||||
if (search === "" || type === "") {
|
||||
return "";
|
||||
}
|
||||
|
||||
return `/torrents/search/${type}/${encodeURI(search)}`;
|
||||
}, [type, search]);
|
||||
|
||||
useEffect(() => {
|
||||
if (search === "") {
|
||||
return;
|
||||
}
|
||||
if (type === "") {
|
||||
const searchFunc = useCallback(() => {
|
||||
const searchURL = url();
|
||||
if (searchURL === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = getUrl();
|
||||
searchTorrents(url);
|
||||
history.push(url);
|
||||
}, [url, getUrl, searchTorrents, history, search, type]);
|
||||
dispatch(searchTorrents(searchURL));
|
||||
history.push(searchURL);
|
||||
}, [dispatch, history, url]);
|
||||
|
||||
useEffect(() => {
|
||||
searchFunc();
|
||||
}, [searchFunc]);
|
||||
|
||||
return (
|
||||
<div className="row">
|
||||
@ -61,7 +59,7 @@ const TorrentSearch = ({
|
||||
typeFromURL={type}
|
||||
handleClick={() => {
|
||||
setType("movies");
|
||||
setUrl(getUrl());
|
||||
searchFunc();
|
||||
}}
|
||||
/>
|
||||
<SearchButton
|
||||
@ -70,7 +68,7 @@ const TorrentSearch = ({
|
||||
typeFromURL={type}
|
||||
handleClick={() => {
|
||||
setType("shows");
|
||||
setUrl(getUrl());
|
||||
searchFunc();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@ -79,7 +77,7 @@ const TorrentSearch = ({
|
||||
<TorrentList
|
||||
searching={searching}
|
||||
results={results}
|
||||
addTorrent={addTorrent}
|
||||
addTorrent={(url) => dispatch(addTorrent(url))}
|
||||
searchFromURL={search}
|
||||
/>
|
||||
</div>
|
||||
@ -87,24 +85,20 @@ const TorrentSearch = ({
|
||||
);
|
||||
};
|
||||
TorrentSearch.propTypes = {
|
||||
searching: PropTypes.bool.isRequired,
|
||||
results: PropTypes.instanceOf(List),
|
||||
searchFromURL: PropTypes.string,
|
||||
match: PropTypes.object,
|
||||
history: PropTypes.object,
|
||||
addTorrent: PropTypes.func.isRequired,
|
||||
searchTorrents: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const SearchButton = (props) => {
|
||||
const variant = props.type === props.typeFromURL ? "primary" : "secondary";
|
||||
const SearchButton = ({ type, typeFromURL, text, handleClick }) => {
|
||||
const variant = type === typeFromURL ? "primary" : "secondary";
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={`w-50 btn m-1 btn-lg btn-${variant}`}
|
||||
onClick={props.handleClick}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<i className="fa fa-search" aria-hidden="true"></i> {props.text}
|
||||
<i className="fa fa-search" aria-hidden="true"></i> {text}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@ -233,4 +227,4 @@ TorrentHealth.propTypes = {
|
||||
leechers: PropTypes.number,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(TorrentSearch);
|
||||
export default TorrentSearch;
|
||||
|
@ -1,19 +1,18 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { useSelector } from "react-redux";
|
||||
import { Redirect, Link } from "react-router-dom";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
isActivated: state.userStore.get("isActivated"),
|
||||
isLogged: state.userStore.get("isLogged"),
|
||||
});
|
||||
const UserActivation = () => {
|
||||
const isLogged = useSelector((state) => state.userStore.get("isLogged"));
|
||||
const isActivated = useSelector((state) =>
|
||||
state.userStore.get("isActivated")
|
||||
);
|
||||
|
||||
const UserActivation = (props) => {
|
||||
if (!props.isLogged) {
|
||||
if (!isLogged) {
|
||||
return <Redirect to="/users/login" />;
|
||||
}
|
||||
|
||||
if (props.isActivated) {
|
||||
if (isActivated) {
|
||||
return <Redirect to="/" />;
|
||||
}
|
||||
|
||||
@ -31,9 +30,4 @@ const UserActivation = (props) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
UserActivation.propTypes = {
|
||||
isActivated: PropTypes.bool.isRequired,
|
||||
isLogged: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(UserActivation);
|
||||
export default UserActivation;
|
||||
|
@ -1,44 +1,30 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import Loader from "../loader/loader";
|
||||
import { List } from "immutable";
|
||||
|
||||
import { getUserInfos, updateUser } from "../../actions/users";
|
||||
import { getPolochons } from "../../actions/polochon";
|
||||
|
||||
import { PolochonSelect } from "../polochons/select";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
loading: state.userStore.get("loading"),
|
||||
publicPolochons: state.polochon.get("public"),
|
||||
polochonId: state.userStore.get("polochonId"),
|
||||
polochonActivated: state.userStore.get("polochonActivated"),
|
||||
});
|
||||
export const UserEdit = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const mapDispatchToProps = {
|
||||
updateUser,
|
||||
getPolochons,
|
||||
getUserInfos,
|
||||
};
|
||||
|
||||
const UserEditConnect = ({
|
||||
loading,
|
||||
polochonId,
|
||||
polochonActivated,
|
||||
updateUser,
|
||||
getPolochons,
|
||||
getUserInfos,
|
||||
publicPolochons,
|
||||
}) => {
|
||||
const [id, setId] = useState(polochonId);
|
||||
const [password, setPassword] = useState("");
|
||||
const [passwordConfirm, setPasswordConfirm] = useState("");
|
||||
|
||||
const loading = useSelector((state) => state.userStore.get("loading"));
|
||||
const publicPolochons = useSelector((state) => state.polochon.get("public"));
|
||||
const polochonId = useSelector((state) => state.userStore.get("polochonId"));
|
||||
const polochonActivated = useSelector((state) =>
|
||||
state.userStore.get("polochonActivated")
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
getPolochons();
|
||||
getUserInfos();
|
||||
}, [getPolochons, getUserInfos]);
|
||||
dispatch(getPolochons());
|
||||
dispatch(getUserInfos());
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
setId(polochonId);
|
||||
@ -46,11 +32,13 @@ const UserEditConnect = ({
|
||||
|
||||
const handleSubmit = (ev) => {
|
||||
ev.preventDefault();
|
||||
updateUser({
|
||||
password: password,
|
||||
password_confirm: passwordConfirm, // eslint-disable-line camelcase
|
||||
polochon_id: id, // eslint-disable-line camelcase
|
||||
});
|
||||
dispatch(
|
||||
updateUser({
|
||||
password: password,
|
||||
password_confirm: passwordConfirm, // eslint-disable-line camelcase
|
||||
polochon_id: id, // eslint-disable-line camelcase
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
@ -115,17 +103,3 @@ const UserEditConnect = ({
|
||||
</div>
|
||||
);
|
||||
};
|
||||
UserEditConnect.propTypes = {
|
||||
loading: PropTypes.bool.isRequired,
|
||||
polochonId: PropTypes.string,
|
||||
polochonActivated: PropTypes.bool,
|
||||
updateUser: PropTypes.func,
|
||||
getPolochons: PropTypes.func,
|
||||
getUserInfos: PropTypes.func,
|
||||
publicPolochons: PropTypes.instanceOf(List),
|
||||
};
|
||||
|
||||
export const UserEdit = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(UserEditConnect);
|
||||
|
@ -1,29 +1,28 @@
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { Redirect, Link } from "react-router-dom";
|
||||
|
||||
import { loginUser } from "../../actions/users";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
isLogged: state.userStore.get("isLogged"),
|
||||
isLoading: state.userStore.get("loading"),
|
||||
error: state.userStore.get("error"),
|
||||
});
|
||||
const mapDispatchToProps = { loginUser };
|
||||
const UserLoginForm = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const isLogged = useSelector((state) => state.userStore.get("isLogged"));
|
||||
const isLoading = useSelector((state) => state.userStore.get("loading"));
|
||||
const error = useSelector((state) => state.userStore.get("error"));
|
||||
|
||||
const UserLoginForm = (props) => {
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
if (!props.isLoading) {
|
||||
props.loginUser(username, password);
|
||||
if (!isLoading) {
|
||||
dispatch(loginUser(username, password));
|
||||
}
|
||||
};
|
||||
|
||||
if (props.isLogged) {
|
||||
if (isLogged) {
|
||||
return <Redirect to="/" />;
|
||||
}
|
||||
|
||||
@ -32,8 +31,8 @@ const UserLoginForm = (props) => {
|
||||
<div className="col-10 offset-1 col-md-6 offset-md-3">
|
||||
<h2>Log in</h2>
|
||||
<hr />
|
||||
{props.error && props.error !== "" && (
|
||||
<div className="alert alert-danger">{props.error}</div>
|
||||
{error && error !== "" && (
|
||||
<div className="alert alert-danger">{error}</div>
|
||||
)}
|
||||
<form className="form-horizontal" onSubmit={(e) => handleSubmit(e)}>
|
||||
<div>
|
||||
@ -66,12 +65,12 @@ const UserLoginForm = (props) => {
|
||||
No account yet ? <Link to="/users/signup">Create one</Link>
|
||||
</small>
|
||||
</span>
|
||||
{props.isLoading && (
|
||||
{isLoading && (
|
||||
<button className="btn btn-primary pull-right">
|
||||
<i className="fa fa-spinner fa-spin"></i>
|
||||
</button>
|
||||
)}
|
||||
{props.isLoading || (
|
||||
{isLoading || (
|
||||
<span className="spaced-icons">
|
||||
<input
|
||||
className="btn btn-primary pull-right"
|
||||
@ -87,11 +86,5 @@ const UserLoginForm = (props) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
UserLoginForm.propTypes = {
|
||||
loginUser: PropTypes.func.isRequired,
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
isLogged: PropTypes.bool.isRequired,
|
||||
error: PropTypes.string,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(UserLoginForm);
|
||||
export default UserLoginForm;
|
||||
|
@ -1,26 +1,18 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { Redirect } from "react-router-dom";
|
||||
|
||||
import { userLogout } from "../../actions/users";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
isLogged: state.userStore.get("isLogged"),
|
||||
});
|
||||
const mapDispatchToProps = { userLogout };
|
||||
const UserLogout = () => {
|
||||
const dispatch = useDispatch();
|
||||
const isLogged = useSelector((state) => state.userStore.get("isLogged"));
|
||||
|
||||
const UserLogout = (props) => {
|
||||
if (props.isLogged) {
|
||||
props.userLogout();
|
||||
if (isLogged) {
|
||||
dispatch(userLogout());
|
||||
}
|
||||
|
||||
return <Redirect to="/users/login" />;
|
||||
};
|
||||
UserLogout.propTypes = {
|
||||
isLogged: PropTypes.bool.isRequired,
|
||||
userLogout: PropTypes.func.isRequired,
|
||||
history: PropTypes.object,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(UserLogout);
|
||||
export default UserLogout;
|
||||
|
@ -1,7 +1,5 @@
|
||||
import React, { useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { Map } from "immutable";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
import { PolochonList } from "../polochons/list";
|
||||
import { UserEdit } from "./edit";
|
||||
@ -9,17 +7,16 @@ import { UserEdit } from "./edit";
|
||||
import { getUserModules } from "../../actions/users";
|
||||
import Modules from "../modules/modules";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
modules: state.userStore.get("modules"),
|
||||
modulesLoading: state.userStore.get("modulesLoading"),
|
||||
});
|
||||
const UserProfile = () => {
|
||||
const dispatch = useDispatch();
|
||||
const modules = useSelector((state) => state.userStore.get("modules"));
|
||||
const modulesLoading = useSelector((state) =>
|
||||
state.userStore.get("modulesLoading")
|
||||
);
|
||||
|
||||
const mapDispatchToProps = { getUserModules };
|
||||
|
||||
const UserProfile = ({ modules, modulesLoading, getUserModules }) => {
|
||||
useEffect(() => {
|
||||
getUserModules();
|
||||
}, [getUserModules]);
|
||||
dispatch(getUserModules());
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -29,10 +26,5 @@ const UserProfile = ({ modules, modulesLoading, getUserModules }) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
UserProfile.propTypes = {
|
||||
getUserModules: PropTypes.func.isRequired,
|
||||
modules: PropTypes.instanceOf(Map),
|
||||
modulesLoading: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(UserProfile);
|
||||
export default UserProfile;
|
||||
|
@ -1,33 +1,33 @@
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { Redirect } from "react-router-dom";
|
||||
|
||||
import { userSignUp } from "../../actions/users";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
isLogged: state.userStore.get("isLogged"),
|
||||
isLoading: state.userStore.get("loading"),
|
||||
error: state.userStore.get("error"),
|
||||
});
|
||||
const mapDispatchToProps = { userSignUp };
|
||||
const UserSignUp = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const UserSignUp = (props) => {
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [passwordConfirm, setPasswordConfirm] = useState("");
|
||||
|
||||
if (props.isLogged) {
|
||||
const isLogged = useSelector((state) => state.userStore.get("isLogged"));
|
||||
const isLoading = useSelector((state) => state.userStore.get("loading"));
|
||||
const error = useSelector((state) => state.userStore.get("error"));
|
||||
|
||||
if (isLogged) {
|
||||
return <Redirect to="/" />;
|
||||
}
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
props.userSignUp({
|
||||
username: username,
|
||||
password: password,
|
||||
password_confirm: passwordConfirm, // eslint-disable-line camelcase
|
||||
});
|
||||
dispatch(
|
||||
userSignUp({
|
||||
username: username,
|
||||
password: password,
|
||||
password_confirm: passwordConfirm, // eslint-disable-line camelcase
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -35,8 +35,8 @@ const UserSignUp = (props) => {
|
||||
<div className="col-10 offset-1 col-md-6 offset-md-3">
|
||||
<h2>Sign up</h2>
|
||||
<hr />
|
||||
{props.error && props.error !== "" && (
|
||||
<div className="alert alert-danger">{props.error}</div>
|
||||
{error && error !== "" && (
|
||||
<div className="alert alert-danger">{error}</div>
|
||||
)}
|
||||
|
||||
<form className="form-horizontal" onSubmit={(e) => handleSubmit(e)}>
|
||||
@ -70,12 +70,12 @@ const UserSignUp = (props) => {
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
{props.isLoading && (
|
||||
{isLoading && (
|
||||
<button className="btn btn-primary pull-right">
|
||||
<i className="fa fa-spinner fa-spin"></i>
|
||||
</button>
|
||||
)}
|
||||
{props.isLoading || (
|
||||
{isLoading || (
|
||||
<span>
|
||||
<input
|
||||
className="btn btn-primary pull-right"
|
||||
@ -90,11 +90,5 @@ const UserSignUp = (props) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
UserSignUp.propTypes = {
|
||||
isLogged: PropTypes.bool.isRequired,
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
userSignUp: PropTypes.func.isRequired,
|
||||
error: PropTypes.string,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(UserSignUp);
|
||||
export default UserSignUp;
|
||||
|
@ -1,40 +1,35 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { UAParser } from "ua-parser-js";
|
||||
import moment from "moment";
|
||||
import { Map, List } from "immutable";
|
||||
import { Map } from "immutable";
|
||||
|
||||
import { getUserTokens, deleteUserToken } from "../../actions/users";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
tokens: state.userStore.get("tokens"),
|
||||
});
|
||||
const mapDispatchToProps = { getUserTokens, deleteUserToken };
|
||||
const UserTokens = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const UserTokens = (props) => {
|
||||
const [fetched, setIsFetched] = useState(false);
|
||||
if (!fetched) {
|
||||
props.getUserTokens();
|
||||
setIsFetched(true);
|
||||
}
|
||||
const tokens = useSelector((state) => state.userStore.get("tokens"));
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getUserTokens());
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
{props.tokens.map((el, index) => (
|
||||
<Token key={index} data={el} deleteToken={props.deleteUserToken} />
|
||||
{tokens.map((el, index) => (
|
||||
<Token
|
||||
key={index}
|
||||
data={el}
|
||||
deleteToken={(token) => dispatch(deleteUserToken(token))}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
UserTokens.propTypes = {
|
||||
tokens: PropTypes.instanceOf(List),
|
||||
isLoading: PropTypes.bool,
|
||||
getUserTokens: PropTypes.func.isRequired,
|
||||
deleteUserToken: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const Token = (props) => {
|
||||
const ua = UAParser(props.data.get("user_agent"));
|
||||
@ -171,4 +166,4 @@ Browser.propTypes = {
|
||||
version: PropTypes.string,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(UserTokens);
|
||||
export default UserTokens;
|
||||
|
@ -1,25 +1,14 @@
|
||||
import { useEffect, useState, useCallback } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { setFetchedTorrents } from "../actions/torrents";
|
||||
import { newMovieEvent } from "../actions/movies";
|
||||
import { newEpisodeEvent } from "../actions/shows";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
isLogged: state.userStore.get("isLogged"),
|
||||
});
|
||||
const mapDispatchToProps = {
|
||||
setFetchedTorrents,
|
||||
newMovieEvent,
|
||||
newEpisodeEvent,
|
||||
};
|
||||
const WsHandler = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const isLogged = useSelector((state) => state.userStore.get("isLogged"));
|
||||
|
||||
const WsHandler = ({
|
||||
isLogged,
|
||||
setFetchedTorrents,
|
||||
newMovieEvent,
|
||||
newEpisodeEvent,
|
||||
}) => {
|
||||
const [ws, setWs] = useState(null);
|
||||
|
||||
const stop = useCallback(() => {
|
||||
@ -73,13 +62,13 @@ const WsHandler = ({
|
||||
|
||||
switch (data.type) {
|
||||
case "torrents":
|
||||
setFetchedTorrents(data.message);
|
||||
dispatch(setFetchedTorrents(data.message));
|
||||
break;
|
||||
case "newVideo":
|
||||
if (data.message.type === "movie") {
|
||||
newMovieEvent(data.message.data);
|
||||
dispatch(newMovieEvent(data.message.data));
|
||||
} else if (data.message.type === "episode") {
|
||||
newEpisodeEvent(data.message.data);
|
||||
dispatch(newEpisodeEvent(data.message.data));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -90,7 +79,7 @@ const WsHandler = ({
|
||||
};
|
||||
|
||||
setWs(socket);
|
||||
}, [ws, isLogged, newMovieEvent, setFetchedTorrents, newEpisodeEvent, stop]);
|
||||
}, [ws, isLogged, dispatch, stop]);
|
||||
|
||||
useEffect(() => {
|
||||
const intervalID = setInterval(() => {
|
||||
@ -118,11 +107,5 @@ const WsHandler = ({
|
||||
|
||||
return null;
|
||||
};
|
||||
WsHandler.propTypes = {
|
||||
isLogged: PropTypes.bool.isRequired,
|
||||
setFetchedTorrents: PropTypes.func,
|
||||
newMovieEvent: PropTypes.func,
|
||||
newEpisodeEvent: PropTypes.func,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(WsHandler);
|
||||
export default WsHandler;
|
||||
|
@ -3,7 +3,7 @@
|
||||
"scripts": {
|
||||
"start": "NODE_ENV=development ./node_modules/webpack/bin/webpack.js -d --progress --colors --watch",
|
||||
"build": "NODE_ENV=production ./node_modules/webpack/bin/webpack.js -p --progress --colors",
|
||||
"lint": "./node_modules/eslint/bin/eslint.js ."
|
||||
"lint": "./node_modules/eslint/bin/eslint.js js"
|
||||
},
|
||||
"dependencies": {
|
||||
"bootstrap": "^4.4.1",
|
||||
|
Loading…
x
Reference in New Issue
Block a user