Use prettier on all the js files
This commit is contained in:
parent
051c973d88
commit
a1922b2c75
@ -1,42 +1,29 @@
|
||||
import { configureAxios, request } from "../requests"
|
||||
import { configureAxios, request } from "../requests";
|
||||
|
||||
export function getUsers() {
|
||||
return request(
|
||||
"ADMIN_LIST_USERS",
|
||||
configureAxios().get("/admins/users")
|
||||
)
|
||||
return request("ADMIN_LIST_USERS", configureAxios().get("/admins/users"));
|
||||
}
|
||||
|
||||
export function getStats() {
|
||||
return request(
|
||||
"ADMIN_GET_STATS",
|
||||
configureAxios().get("/admins/stats")
|
||||
)
|
||||
return request("ADMIN_GET_STATS", configureAxios().get("/admins/stats"));
|
||||
}
|
||||
|
||||
export function getAdminModules() {
|
||||
return request(
|
||||
"ADMIN_GET_MODULES",
|
||||
configureAxios().get("/admins/modules")
|
||||
)
|
||||
return request("ADMIN_GET_MODULES", configureAxios().get("/admins/modules"));
|
||||
}
|
||||
|
||||
export function updateUser(data) {
|
||||
return request(
|
||||
"ADMIN_UPDATE_USER",
|
||||
configureAxios().post("/admins/users", data),
|
||||
[
|
||||
() => getUsers(),
|
||||
]
|
||||
)
|
||||
[() => getUsers()]
|
||||
);
|
||||
}
|
||||
|
||||
export function deleteUser(username) {
|
||||
return request(
|
||||
"ADMIN_DELETE_USER",
|
||||
configureAxios().delete("/admins/users/"+ username),
|
||||
[
|
||||
() => getUsers(),
|
||||
]
|
||||
)
|
||||
configureAxios().delete("/admins/users/" + username),
|
||||
[() => getUsers()]
|
||||
);
|
||||
}
|
||||
|
@ -2,22 +2,22 @@ export function addAlertError(message) {
|
||||
return {
|
||||
type: "ADD_ALERT_ERROR",
|
||||
payload: {
|
||||
message,
|
||||
}
|
||||
message
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function addAlertOk(message) {
|
||||
return {
|
||||
type: "ADD_ALERT_OK",
|
||||
payload: {
|
||||
message,
|
||||
}
|
||||
message
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function dismissAlert() {
|
||||
return {
|
||||
type: "DISMISS_ALERT",
|
||||
}
|
||||
type: "DISMISS_ALERT"
|
||||
};
|
||||
}
|
||||
|
@ -1,40 +1,40 @@
|
||||
import { configureAxios, request } from "../requests"
|
||||
import { configureAxios, request } from "../requests";
|
||||
|
||||
import { addAlertOk } from "./alerts"
|
||||
import { sendNotification } from "./notifications"
|
||||
import { addAlertOk } from "./alerts";
|
||||
import { sendNotification } from "./notifications";
|
||||
|
||||
export function updateLastMovieFetchUrl(url) {
|
||||
return {
|
||||
type: "UPDATE_LAST_MOVIE_FETCH_URL",
|
||||
payload: {
|
||||
url: url,
|
||||
},
|
||||
url: url
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function selectMovie(imdbId) {
|
||||
return {
|
||||
type: "SELECT_MOVIE",
|
||||
payload: {
|
||||
imdbId,
|
||||
},
|
||||
imdbId
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function updateFilter(filter) {
|
||||
return {
|
||||
type: "MOVIE_UPDATE_FILTER",
|
||||
payload: {
|
||||
filter,
|
||||
},
|
||||
filter
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function getMovieExploreOptions() {
|
||||
return request(
|
||||
"MOVIE_GET_EXPLORE_OPTIONS",
|
||||
configureAxios().get("/movies/explore/options")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function getMovieDetails(imdbId) {
|
||||
@ -43,27 +43,23 @@ export function getMovieDetails(imdbId) {
|
||||
configureAxios().post(`/movies/${imdbId}/refresh`),
|
||||
null,
|
||||
{
|
||||
imdbId,
|
||||
imdbId
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function deleteMovie(imdbId, lastFetchUrl) {
|
||||
return request(
|
||||
"MOVIE_DELETE",
|
||||
configureAxios().delete(`/movies/${imdbId}`),
|
||||
[
|
||||
return request("MOVIE_DELETE", configureAxios().delete(`/movies/${imdbId}`), [
|
||||
fetchMovies(lastFetchUrl),
|
||||
addAlertOk("Movie deleted"),
|
||||
],
|
||||
)
|
||||
addAlertOk("Movie deleted")
|
||||
]);
|
||||
}
|
||||
|
||||
export function movieWishlistToggle(imdbId, currentState) {
|
||||
if (currentState == true) {
|
||||
return deleteMovieFromWishlist(imdbId)
|
||||
return deleteMovieFromWishlist(imdbId);
|
||||
} else {
|
||||
return addMovieToWishlist(imdbId)
|
||||
return addMovieToWishlist(imdbId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,20 +67,16 @@ export function addMovieToWishlist(imdbId) {
|
||||
return request(
|
||||
"MOVIE_ADD_TO_WISHLIST",
|
||||
configureAxios().post(`/wishlist/movies/${imdbId}`),
|
||||
[
|
||||
updateMovieWishlistStore(imdbId, true),
|
||||
],
|
||||
)
|
||||
[updateMovieWishlistStore(imdbId, true)]
|
||||
);
|
||||
}
|
||||
|
||||
export function deleteMovieFromWishlist(imdbId) {
|
||||
return request(
|
||||
"MOVIE_DELETE_FROM_WISHLIST",
|
||||
configureAxios().delete(`/wishlist/movies/${imdbId}`),
|
||||
[
|
||||
updateMovieWishlistStore(imdbId, false),
|
||||
],
|
||||
)
|
||||
[updateMovieWishlistStore(imdbId, false)]
|
||||
);
|
||||
}
|
||||
|
||||
export function updateMovieWishlistStore(imdbId, wishlisted) {
|
||||
@ -92,29 +84,27 @@ export function updateMovieWishlistStore(imdbId, wishlisted) {
|
||||
type: "MOVIE_UPDATE_STORE_WISHLIST",
|
||||
payload: {
|
||||
imdbId,
|
||||
wishlisted,
|
||||
}
|
||||
wishlisted
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchMovies(url) {
|
||||
return request(
|
||||
"MOVIE_LIST_FETCH",
|
||||
configureAxios().get(url),
|
||||
[
|
||||
updateLastMovieFetchUrl(url),
|
||||
]
|
||||
)
|
||||
return request("MOVIE_LIST_FETCH", configureAxios().get(url), [
|
||||
updateLastMovieFetchUrl(url)
|
||||
]);
|
||||
}
|
||||
|
||||
export const newMovieEvent = (data) => {
|
||||
return (dispatch) => {
|
||||
dispatch(sendNotification({
|
||||
export const newMovieEvent = data => {
|
||||
return dispatch => {
|
||||
dispatch(
|
||||
sendNotification({
|
||||
icon: "film",
|
||||
autohide: true,
|
||||
delay: 10000,
|
||||
title: `${data.title} added to the library`,
|
||||
imageUrl: data.thumb,
|
||||
}));
|
||||
}
|
||||
}
|
||||
imageUrl: data.thumb
|
||||
})
|
||||
);
|
||||
};
|
||||
};
|
||||
|
@ -1,9 +1,9 @@
|
||||
export const sendNotification = (data) => ({
|
||||
export const sendNotification = data => ({
|
||||
type: "ADD_NOTIFICATION",
|
||||
payload: data,
|
||||
})
|
||||
payload: data
|
||||
});
|
||||
|
||||
export const removeNotification = (id) => ({
|
||||
export const removeNotification = id => ({
|
||||
type: "REMOVE_NOTIFICATION",
|
||||
payload: { id },
|
||||
})
|
||||
payload: { id }
|
||||
});
|
||||
|
@ -1,50 +1,38 @@
|
||||
import { configureAxios, request } from "../requests"
|
||||
import { configureAxios, request } from "../requests";
|
||||
|
||||
import { getUserInfos } from "./users"
|
||||
import { getUserInfos } from "./users";
|
||||
|
||||
export const getPolochons = () => request(
|
||||
"PUBLIC_POLOCHON_LIST_FETCH",
|
||||
configureAxios().get("/polochons"),
|
||||
)
|
||||
export const getPolochons = () =>
|
||||
request("PUBLIC_POLOCHON_LIST_FETCH", configureAxios().get("/polochons"));
|
||||
|
||||
export const getManagedPolochons = () => request(
|
||||
export const getManagedPolochons = () =>
|
||||
request(
|
||||
"MANAGED_POLOCHON_LIST_FETCH",
|
||||
configureAxios().get("/users/polochons"),
|
||||
)
|
||||
configureAxios().get("/users/polochons")
|
||||
);
|
||||
|
||||
export const addPolochon = (params) => request(
|
||||
"ADD_POLOCHON",
|
||||
configureAxios().post("/polochons", params),
|
||||
[
|
||||
export const addPolochon = params =>
|
||||
request("ADD_POLOCHON", configureAxios().post("/polochons", params), [
|
||||
() => getPolochons(),
|
||||
() => getManagedPolochons(),
|
||||
],
|
||||
)
|
||||
() => getManagedPolochons()
|
||||
]);
|
||||
|
||||
export const updatePolochon = ({ id, ...params }) => request(
|
||||
export const updatePolochon = ({ id, ...params }) =>
|
||||
request(
|
||||
"UPDATE_POLOCHON",
|
||||
configureAxios().post(`/polochons/${id}`, params),
|
||||
[
|
||||
() => getPolochons(),
|
||||
() => getManagedPolochons(),
|
||||
],
|
||||
)
|
||||
[() => getPolochons(), () => getManagedPolochons()]
|
||||
);
|
||||
|
||||
export const deletePolochon = (id) => request(
|
||||
"DELETE_POLOCHON",
|
||||
configureAxios().delete(`/polochons/${id}`),
|
||||
[
|
||||
export const deletePolochon = id =>
|
||||
request("DELETE_POLOCHON", configureAxios().delete(`/polochons/${id}`), [
|
||||
() => getPolochons(),
|
||||
() => getManagedPolochons(),
|
||||
],
|
||||
)
|
||||
() => getManagedPolochons()
|
||||
]);
|
||||
|
||||
export const editPolochonUser = ({ polochonId, id, ...params }) => request(
|
||||
export const editPolochonUser = ({ polochonId, id, ...params }) =>
|
||||
request(
|
||||
"EDIT_POLOCHON_USER",
|
||||
configureAxios().post(`/polochons/${polochonId}/users/${id}`, params),
|
||||
[
|
||||
() => getPolochons(),
|
||||
() => getManagedPolochons(),
|
||||
() => getUserInfos(),
|
||||
],
|
||||
)
|
||||
[() => getPolochons(), () => getManagedPolochons(), () => getUserInfos()]
|
||||
);
|
||||
|
@ -1,16 +1,12 @@
|
||||
import { configureAxios, request } from "../requests"
|
||||
import { configureAxios, request } from "../requests";
|
||||
|
||||
import { prettyEpisodeName } from "../utils"
|
||||
import { sendNotification } from "./notifications"
|
||||
import { prettyEpisodeName } from "../utils";
|
||||
import { sendNotification } from "./notifications";
|
||||
|
||||
export function fetchShows(url) {
|
||||
return request(
|
||||
"SHOW_LIST_FETCH",
|
||||
configureAxios().get(url),
|
||||
[
|
||||
updateLastShowsFetchUrl(url),
|
||||
]
|
||||
)
|
||||
return request("SHOW_LIST_FETCH", configureAxios().get(url), [
|
||||
updateLastShowsFetchUrl(url)
|
||||
]);
|
||||
}
|
||||
|
||||
export function getShowDetails(imdbId) {
|
||||
@ -19,21 +15,22 @@ export function getShowDetails(imdbId) {
|
||||
configureAxios().post(`/shows/${imdbId}/refresh`),
|
||||
null,
|
||||
{ imdbId }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export function getEpisodeDetails(imdbId, season, episode) {
|
||||
return request(
|
||||
"EPISODE_GET_DETAILS",
|
||||
configureAxios().post(`/shows/${imdbId}/seasons/${season}/episodes/${episode}`),
|
||||
configureAxios().post(
|
||||
`/shows/${imdbId}/seasons/${season}/episodes/${episode}`
|
||||
),
|
||||
null,
|
||||
{
|
||||
imdbId,
|
||||
season,
|
||||
episode,
|
||||
episode
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function fetchShowDetails(imdbId) {
|
||||
@ -42,7 +39,7 @@ export function fetchShowDetails(imdbId) {
|
||||
configureAxios().get(`/shows/${imdbId}`),
|
||||
null,
|
||||
{ imdbId }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function addShowToWishlist(imdbId, season = null, episode = null) {
|
||||
@ -50,25 +47,26 @@ export function addShowToWishlist(imdbId, season = null, episode = null) {
|
||||
"SHOW_ADD_TO_WISHLIST",
|
||||
configureAxios().post(`/wishlist/shows/${imdbId}`, {
|
||||
season: season,
|
||||
episode: episode,
|
||||
episode: episode
|
||||
}),
|
||||
[
|
||||
updateShowWishlistStore(imdbId, true, season, episode),
|
||||
],
|
||||
)
|
||||
[updateShowWishlistStore(imdbId, true, season, episode)]
|
||||
);
|
||||
}
|
||||
|
||||
export function deleteShowFromWishlist(imdbId) {
|
||||
return request(
|
||||
"SHOW_DELETE_FROM_WISHLIST",
|
||||
configureAxios().delete(`/wishlist/shows/${imdbId}`),
|
||||
[
|
||||
updateShowWishlistStore(imdbId, false),
|
||||
],
|
||||
)
|
||||
[updateShowWishlistStore(imdbId, false)]
|
||||
);
|
||||
}
|
||||
|
||||
export function showWishlistToggle(currentState, imdbId, season = 0, episode = 0) {
|
||||
export function showWishlistToggle(
|
||||
currentState,
|
||||
imdbId,
|
||||
season = 0,
|
||||
episode = 0
|
||||
) {
|
||||
if (currentState === true) {
|
||||
return deleteShowFromWishlist(imdbId);
|
||||
} else {
|
||||
@ -76,60 +74,70 @@ export function showWishlistToggle(currentState, imdbId, season = 0, episode = 0
|
||||
}
|
||||
}
|
||||
|
||||
export function updateShowWishlistStore(imdbId, wishlisted, season = null, episode = null) {
|
||||
export function updateShowWishlistStore(
|
||||
imdbId,
|
||||
wishlisted,
|
||||
season = null,
|
||||
episode = null
|
||||
) {
|
||||
return {
|
||||
type: "SHOW_UPDATE_STORE_WISHLIST",
|
||||
payload: {
|
||||
imdbId,
|
||||
season,
|
||||
episode,
|
||||
}
|
||||
episode
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function getShowExploreOptions() {
|
||||
return request(
|
||||
"SHOW_GET_EXPLORE_OPTIONS",
|
||||
configureAxios().get("/shows/explore/options")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function selectShow(imdbId) {
|
||||
return {
|
||||
type: "SELECT_SHOW",
|
||||
payload: {
|
||||
imdbId,
|
||||
}
|
||||
imdbId
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function updateFilter(filter) {
|
||||
return {
|
||||
type: "SHOWS_UPDATE_FILTER",
|
||||
payload: {
|
||||
filter,
|
||||
},
|
||||
filter
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function updateLastShowsFetchUrl(url) {
|
||||
return {
|
||||
type: "UPDATE_LAST_SHOWS_FETCH_URL",
|
||||
payload: {
|
||||
url: url,
|
||||
},
|
||||
url: url
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const newEpisodeEvent = (data) => {
|
||||
return (dispatch) => {
|
||||
dispatch(sendNotification({
|
||||
export const newEpisodeEvent = data => {
|
||||
return dispatch => {
|
||||
dispatch(
|
||||
sendNotification({
|
||||
icon: "video-camera",
|
||||
autohide: true,
|
||||
delay: 10000,
|
||||
title: `${prettyEpisodeName(data.show_title, data.season, data.episode)} added to the library`,
|
||||
imageUrl: `img/shows/${data.show_imdb_id}-poster.jpg`,
|
||||
}));
|
||||
}
|
||||
}
|
||||
title: `${prettyEpisodeName(
|
||||
data.show_title,
|
||||
data.season,
|
||||
data.episode
|
||||
)} added to the library`,
|
||||
imageUrl: `img/shows/${data.show_imdb_id}-poster.jpg`
|
||||
})
|
||||
);
|
||||
};
|
||||
};
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { configureAxios, request } from "../requests"
|
||||
import { configureAxios, request } from "../requests";
|
||||
|
||||
export const searchMovieSubtitles = (imdbId) => {
|
||||
export const searchMovieSubtitles = imdbId => {
|
||||
return request(
|
||||
"MOVIE_SUBTITLES_UPDATE",
|
||||
configureAxios().post(`/movies/${imdbId}/subtitles/refresh`),
|
||||
null,
|
||||
{ imdbId: imdbId },
|
||||
)
|
||||
}
|
||||
{ imdbId: imdbId }
|
||||
);
|
||||
};
|
||||
|
||||
export const searchEpisodeSubtitles = (imdbId, season, episode) => {
|
||||
const url = `/shows/${imdbId}/seasons/${season}/episodes/${episode}`;
|
||||
@ -18,7 +18,7 @@ export const searchEpisodeSubtitles = (imdbId, season, episode) => {
|
||||
{
|
||||
imdbId: imdbId,
|
||||
season: season,
|
||||
episode: episode,
|
||||
},
|
||||
episode: episode
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -1,41 +1,29 @@
|
||||
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", {
|
||||
url: url,
|
||||
url: url
|
||||
}),
|
||||
[
|
||||
addAlertOk("Torrent added"),
|
||||
],
|
||||
)
|
||||
[addAlertOk("Torrent added")]
|
||||
);
|
||||
}
|
||||
|
||||
export function removeTorrent(id) {
|
||||
return request(
|
||||
"REMOVE_TORRENT",
|
||||
configureAxios().delete(`/torrents/${id}`),
|
||||
[
|
||||
() => fetchTorrents(),
|
||||
]
|
||||
)
|
||||
return request("REMOVE_TORRENT", configureAxios().delete(`/torrents/${id}`), [
|
||||
() => fetchTorrents()
|
||||
]);
|
||||
}
|
||||
|
||||
export function fetchTorrents() {
|
||||
return request(
|
||||
"TORRENTS_FETCH",
|
||||
configureAxios().get("/torrents")
|
||||
)
|
||||
return request("TORRENTS_FETCH", configureAxios().get("/torrents"));
|
||||
}
|
||||
|
||||
export function searchTorrents(url) {
|
||||
return request(
|
||||
"TORRENTS_SEARCH",
|
||||
configureAxios().get(url)
|
||||
)
|
||||
return request("TORRENTS_SEARCH", configureAxios().get(url));
|
||||
}
|
||||
|
||||
export function setFetchedTorrents(torrents) {
|
||||
@ -43,8 +31,8 @@ export function setFetchedTorrents(torrents) {
|
||||
type: "TORRENTS_FETCH_FULFILLED",
|
||||
payload: {
|
||||
response: {
|
||||
data : torrents,
|
||||
},
|
||||
},
|
||||
data: torrents
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,40 +1,31 @@
|
||||
import { configureAxios, request } from "../requests"
|
||||
import { configureAxios, request } from "../requests";
|
||||
|
||||
import { addAlertOk, dismissAlert } from "./alerts"
|
||||
import { addAlertOk, dismissAlert } from "./alerts";
|
||||
|
||||
import { getManagedPolochons } from "./polochon"
|
||||
import { getManagedPolochons } from "./polochon";
|
||||
|
||||
export function userLogout() {
|
||||
return {
|
||||
type: "USER_LOGOUT",
|
||||
}
|
||||
type: "USER_LOGOUT"
|
||||
};
|
||||
}
|
||||
|
||||
export function loginUser(username, password) {
|
||||
return request(
|
||||
"USER_LOGIN",
|
||||
configureAxios().post(
|
||||
"/users/login",
|
||||
{
|
||||
configureAxios().post("/users/login", {
|
||||
username: username.trim(),
|
||||
password: password,
|
||||
},
|
||||
),
|
||||
[
|
||||
() => dismissAlert(),
|
||||
]
|
||||
)
|
||||
password: password
|
||||
}),
|
||||
[() => dismissAlert()]
|
||||
);
|
||||
}
|
||||
|
||||
export function updateUser(config) {
|
||||
return request(
|
||||
"USER_UPDATE",
|
||||
configureAxios().post("/users/edit", config),
|
||||
[
|
||||
return request("USER_UPDATE", configureAxios().post("/users/edit", config), [
|
||||
addAlertOk("User updated"),
|
||||
() => getManagedPolochons(),
|
||||
],
|
||||
)
|
||||
() => getManagedPolochons()
|
||||
]);
|
||||
}
|
||||
|
||||
export function userSignUp(config) {
|
||||
@ -44,48 +35,39 @@ export function userSignUp(config) {
|
||||
|
||||
return request(
|
||||
"USER_SIGNUP",
|
||||
configureAxios().post("/users/signup", config), [
|
||||
() => loginUser(config.username, config.password),
|
||||
],
|
||||
)
|
||||
configureAxios().post("/users/signup", config),
|
||||
[() => loginUser(config.username, config.password)]
|
||||
);
|
||||
}
|
||||
|
||||
export function getUserInfos() {
|
||||
return request(
|
||||
"GET_USER",
|
||||
configureAxios().get("/users/details")
|
||||
)
|
||||
return request("GET_USER", configureAxios().get("/users/details"));
|
||||
}
|
||||
|
||||
export function setUserToken(token) {
|
||||
return {
|
||||
type: "USER_SET_TOKEN",
|
||||
payload: {
|
||||
token: token,
|
||||
},
|
||||
token: token
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function getUserTokens() {
|
||||
return request(
|
||||
"GET_USER_TOKENS",
|
||||
configureAxios().get("/users/tokens")
|
||||
)
|
||||
return request("GET_USER_TOKENS", configureAxios().get("/users/tokens"));
|
||||
}
|
||||
|
||||
export function deleteUserToken(token) {
|
||||
return request(
|
||||
"DELETE_USER_TOKEN",
|
||||
configureAxios().delete(`/users/tokens/${token}`),
|
||||
[
|
||||
() => getUserTokens(),
|
||||
]
|
||||
)
|
||||
[() => getUserTokens()]
|
||||
);
|
||||
}
|
||||
|
||||
export function getUserModules() {
|
||||
return request(
|
||||
"GET_USER_MODULES",
|
||||
configureAxios().get("/users/modules/status")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -1,47 +1,47 @@
|
||||
// Import default image
|
||||
import "../img/noimage.png"
|
||||
import "../img/noimage.png";
|
||||
|
||||
// Import favicon settings
|
||||
import "../img/favicon-16x16.png"
|
||||
import "../img/favicon-32x32.png"
|
||||
import "../img/favicon.ico"
|
||||
import "../img/safari-pinned-tab.svg"
|
||||
import "../img/favicon-16x16.png";
|
||||
import "../img/favicon-32x32.png";
|
||||
import "../img/favicon.ico";
|
||||
import "../img/safari-pinned-tab.svg";
|
||||
|
||||
// Styles
|
||||
import "../scss/app.scss"
|
||||
import "../scss/app.scss";
|
||||
|
||||
// React
|
||||
import React from "react"
|
||||
import ReactDOM from "react-dom"
|
||||
import { Provider } from "react-redux"
|
||||
import { Router, Route, Switch, Redirect } from "react-router-dom"
|
||||
import Container from "react-bootstrap/Container"
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { Provider } from "react-redux";
|
||||
import { Router, Route, Switch, Redirect } from "react-router-dom";
|
||||
import Container from "react-bootstrap/Container";
|
||||
|
||||
// Auth
|
||||
import { ProtectedRoute, AdminRoute } from "./auth"
|
||||
import { ProtectedRoute, AdminRoute } from "./auth";
|
||||
|
||||
// Store
|
||||
import store, { history } from "./store"
|
||||
import store, { history } from "./store";
|
||||
|
||||
// Components
|
||||
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"
|
||||
import UserLoginForm from "./components/users/login"
|
||||
import UserLogout from "./components/users/logout"
|
||||
import UserProfile from "./components/users/profile"
|
||||
import UserSignUp from "./components/users/signup"
|
||||
import UserTokens from "./components/users/tokens"
|
||||
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";
|
||||
import UserLoginForm from "./components/users/login";
|
||||
import UserLogout from "./components/users/logout";
|
||||
import UserProfile from "./components/users/profile";
|
||||
import UserSignUp from "./components/users/signup";
|
||||
import UserTokens from "./components/users/tokens";
|
||||
|
||||
const App = () => (
|
||||
<div>
|
||||
@ -56,25 +56,43 @@ const App = () => (
|
||||
<Route path="/users/tokens" exact component={UserTokens} />
|
||||
<Route path="/torrents/list" exact component={TorrentList} />
|
||||
<Route path="/torrents/search" exact component={TorrentSearch} />
|
||||
<Route path="/torrents/search/:type/:search" exact component={TorrentSearch} />
|
||||
<Route
|
||||
path="/torrents/search/:type/:search"
|
||||
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 path="/movies/explore/:source/:category" exact component={MovieList} />
|
||||
<MoviesRoute
|
||||
path="/movies/search/:search"
|
||||
exact
|
||||
component={MovieList}
|
||||
/>
|
||||
<MoviesRoute
|
||||
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 path="/shows/explore/:source/:category" exact component={ShowList} />
|
||||
<ShowsRoute path="/shows/details/:imdbId" exact component={ShowDetails} />
|
||||
<Route render={() =>
|
||||
<Redirect to="/movies/explore/yts/seeds" />
|
||||
}/>
|
||||
<ShowsRoute
|
||||
path="/shows/explore/:source/:category"
|
||||
exact
|
||||
component={ShowList}
|
||||
/>
|
||||
<ShowsRoute
|
||||
path="/shows/details/:imdbId"
|
||||
exact
|
||||
component={ShowDetails}
|
||||
/>
|
||||
<Route render={() => <Redirect to="/movies/explore/yts/seeds" />} />
|
||||
</Switch>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
|
||||
ReactDOM.render((
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
@ -85,5 +103,6 @@ ReactDOM.render((
|
||||
<ProtectedRoute path="*" component={App} />
|
||||
</Switch>
|
||||
</Router>
|
||||
</Provider>
|
||||
),document.getElementById("app"));
|
||||
</Provider>,
|
||||
document.getElementById("app")
|
||||
);
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { connect } from "react-redux"
|
||||
import { Route, Redirect } from "react-router-dom"
|
||||
import { setUserToken } from "./actions/users"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { Route, Redirect } from "react-router-dom";
|
||||
import { setUserToken } from "./actions/users";
|
||||
|
||||
const protectedRoute = ({
|
||||
component: Component,
|
||||
@ -22,59 +22,64 @@ const protectedRoute = ({
|
||||
if (!isTokenSet) {
|
||||
setUserToken(token);
|
||||
}
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
return (
|
||||
<Route {...otherProps} render={(props) => {
|
||||
<Route
|
||||
{...otherProps}
|
||||
render={props => {
|
||||
if (isAuthenticated()) {
|
||||
if (isActivated) {
|
||||
return <Component {...props} />
|
||||
return <Component {...props} />;
|
||||
} else {
|
||||
return <Redirect to="/users/activation" />
|
||||
return <Redirect to="/users/activation" />;
|
||||
}
|
||||
} else {
|
||||
return <Redirect to="/users/login" />
|
||||
return <Redirect to="/users/login" />;
|
||||
}
|
||||
}} />
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
protectedRoute.propTypes = {
|
||||
component: PropTypes.func,
|
||||
isLogged: PropTypes.bool.isRequired,
|
||||
isActivated: PropTypes.bool.isRequired,
|
||||
isTokenSet: PropTypes.bool.isRequired,
|
||||
setUserToken: PropTypes.func.isRequired,
|
||||
setUserToken: PropTypes.func.isRequired
|
||||
};
|
||||
export const ProtectedRoute = connect((state) => ({
|
||||
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);
|
||||
isTokenSet: state.userStore.get("isTokenSet")
|
||||
}),
|
||||
{ setUserToken }
|
||||
)(protectedRoute);
|
||||
|
||||
const adminRoute = ({
|
||||
component: Component,
|
||||
isAdmin,
|
||||
...otherProps
|
||||
}) => {
|
||||
const adminRoute = ({ component: Component, isAdmin, ...otherProps }) => {
|
||||
return (
|
||||
<Route {...otherProps} render={(props) => {
|
||||
<Route
|
||||
{...otherProps}
|
||||
render={props => {
|
||||
if (isAdmin) {
|
||||
return <Component {...props} />
|
||||
return <Component {...props} />;
|
||||
} else {
|
||||
return <Redirect to="/" />
|
||||
return <Redirect to="/" />;
|
||||
}
|
||||
}} />
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
adminRoute.propTypes = {
|
||||
component: PropTypes.func,
|
||||
isAdmin: PropTypes.bool.isRequired,
|
||||
isAdmin: PropTypes.bool.isRequired
|
||||
};
|
||||
export const AdminRoute = connect((state) => ({
|
||||
isAdmin: state.userStore.get("isLogged"),
|
||||
export const AdminRoute = connect(state => ({
|
||||
isAdmin: state.userStore.get("isLogged")
|
||||
}))(adminRoute);
|
||||
|
@ -1,28 +1,28 @@
|
||||
import React, { useEffect } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { connect } from "react-redux"
|
||||
import { getAdminModules} from "../../actions/admins"
|
||||
import React, { useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { getAdminModules } from "../../actions/admins";
|
||||
|
||||
import Modules from "../modules/modules"
|
||||
import Modules from "../modules/modules";
|
||||
|
||||
const AdminModulesConnected = ({ modules, loading, getAdminModules }) => {
|
||||
useEffect(() => {
|
||||
getAdminModules();
|
||||
}, [getAdminModules])
|
||||
}, [getAdminModules]);
|
||||
|
||||
return (
|
||||
<Modules modules={modules} isLoading={loading} />
|
||||
)
|
||||
}
|
||||
return <Modules modules={modules} isLoading={loading} />;
|
||||
};
|
||||
AdminModulesConnected.propTypes = {
|
||||
modules: PropTypes.object,
|
||||
loading: PropTypes.bool,
|
||||
getAdminModules: PropTypes.func.isRequired,
|
||||
getAdminModules: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
loading: state.adminStore.get("fetchingModules"),
|
||||
modules: state.adminStore.get("modules"),
|
||||
modules: state.adminStore.get("modules")
|
||||
});
|
||||
|
||||
export const AdminModules = connect(mapStateToProps, {getAdminModules})(AdminModulesConnected);
|
||||
export const AdminModules = connect(mapStateToProps, { getAdminModules })(
|
||||
AdminModulesConnected
|
||||
);
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React from "react"
|
||||
import React from "react";
|
||||
|
||||
import { UserList } from "./userList"
|
||||
import { Stats } from "./stats"
|
||||
import { AdminModules } from "./modules"
|
||||
import { UserList } from "./userList";
|
||||
import { Stats } from "./stats";
|
||||
import { AdminModules } from "./modules";
|
||||
|
||||
export const AdminPanel = () => (
|
||||
<React.Fragment>
|
||||
@ -10,4 +10,4 @@ export const AdminPanel = () => (
|
||||
<UserList />
|
||||
<AdminModules />
|
||||
</React.Fragment>
|
||||
)
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { TorrentsStat } from "./torrentsStat"
|
||||
import { TorrentsStat } from "./torrentsStat";
|
||||
|
||||
export const Stat = ({ name, count, torrentCount, torrentCountById }) => (
|
||||
<div className="col-12 col-md-4 my-2">
|
||||
@ -9,7 +9,9 @@ export const Stat = ({ name, count, torrentCount, torrentCountById }) => (
|
||||
<div className="card-header">
|
||||
<h3>
|
||||
{name}
|
||||
<span className="badge badge-pill badge-info pull-right">{count}</span>
|
||||
<span className="badge badge-pill badge-info pull-right">
|
||||
{count}
|
||||
</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
@ -21,10 +23,10 @@ export const Stat = ({ name, count, torrentCount, torrentCountById }) => (
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
Stat.propTypes = {
|
||||
name: PropTypes.string,
|
||||
count: PropTypes.number,
|
||||
torrentCount: PropTypes.number,
|
||||
torrentCountById: PropTypes.number,
|
||||
torrentCountById: PropTypes.number
|
||||
};
|
||||
|
@ -1,15 +1,15 @@
|
||||
import React, { useEffect } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { connect } from "react-redux"
|
||||
import React, { useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { Stat } from "./stat"
|
||||
import { Stat } from "./stat";
|
||||
|
||||
import { getStats } from "../../actions/admins"
|
||||
import { getStats } from "../../actions/admins";
|
||||
|
||||
const StatsConnected = ({ stats, getStats }) => {
|
||||
useEffect(() => {
|
||||
getStats();
|
||||
}, [getStats])
|
||||
}, [getStats]);
|
||||
|
||||
return (
|
||||
<div className="row d-flex flex-wrap">
|
||||
@ -32,15 +32,15 @@ const StatsConnected = ({ stats, getStats }) => {
|
||||
torrentCountById={stats.get("episodes_torrents_count_by_id")}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
StatsConnected.propTypes = {
|
||||
stats: PropTypes.object,
|
||||
getStats: PropTypes.func,
|
||||
}
|
||||
getStats: PropTypes.func
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
stats: state.adminStore.get("stats"),
|
||||
stats: state.adminStore.get("stats")
|
||||
});
|
||||
|
||||
export const Stats = connect(mapStateToProps, {getStats})(StatsConnected);
|
||||
export const Stats = connect(mapStateToProps, { getStats })(StatsConnected);
|
||||
|
@ -1,9 +1,9 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
export const TorrentsStat = ({ count, torrentCount, torrentCountById }) => {
|
||||
if (torrentCount === undefined) {
|
||||
return (<span>No torrents</span>);
|
||||
return <span>No torrents</span>;
|
||||
}
|
||||
|
||||
const percentage = Math.floor((torrentCountById * 100) / count);
|
||||
@ -13,9 +13,9 @@ export const TorrentsStat = ({ count, torrentCount, torrentCountById }) => {
|
||||
<small> - {torrentCount} total</small>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
TorrentsStat.propTypes = {
|
||||
count: PropTypes.number,
|
||||
torrentCount: PropTypes.number,
|
||||
torrentCountById: PropTypes.number,
|
||||
torrentCountById: PropTypes.number
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { UserEdit } from "./userEdit"
|
||||
import { UserEdit } from "./userEdit";
|
||||
|
||||
export const User = ({
|
||||
id,
|
||||
@ -12,27 +12,31 @@ export const User = ({
|
||||
polochonUrl,
|
||||
polochonName,
|
||||
polochonId,
|
||||
token,
|
||||
token
|
||||
}) => {
|
||||
return (
|
||||
<tr>
|
||||
<td>{id}</td>
|
||||
<td>{name}</td>
|
||||
<td>
|
||||
<span className={activated ? "fa fa-check" : "fa fa-times text-danger"}></span>
|
||||
<span
|
||||
className={activated ? "fa fa-check" : "fa fa-times text-danger"}
|
||||
></span>
|
||||
</td>
|
||||
<td>
|
||||
<span className={admin ? "fa fa-check" : "fa fa-times"}></span>
|
||||
</td>
|
||||
<td>
|
||||
{polochonName !== "" ? polochonName : "-"}
|
||||
{polochonUrl !== "" &&
|
||||
<small className="ml-1">({polochonUrl})</small>
|
||||
}
|
||||
{polochonUrl !== "" && <small className="ml-1">({polochonUrl})</small>}
|
||||
</td>
|
||||
<td>{token}</td>
|
||||
<td>
|
||||
<span className={polochonActivated ? "fa fa-check" : "fa fa-times text-danger"}></span>
|
||||
<span
|
||||
className={
|
||||
polochonActivated ? "fa fa-check" : "fa fa-times text-danger"
|
||||
}
|
||||
></span>
|
||||
</td>
|
||||
<td>
|
||||
<UserEdit
|
||||
@ -47,7 +51,7 @@ export const User = ({
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
};
|
||||
User.propTypes = {
|
||||
id: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
@ -57,5 +61,5 @@ User.propTypes = {
|
||||
token: PropTypes.string,
|
||||
admin: PropTypes.bool,
|
||||
activated: PropTypes.bool,
|
||||
polochonActivated: PropTypes.bool,
|
||||
polochonActivated: PropTypes.bool
|
||||
};
|
||||
|
@ -1,15 +1,15 @@
|
||||
import React, { useState } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { connect } from "react-redux"
|
||||
import { List } from "immutable"
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { List } from "immutable";
|
||||
|
||||
import { updateUser, deleteUser } from "../../actions/admins"
|
||||
import { updateUser, deleteUser } from "../../actions/admins";
|
||||
|
||||
import Toggle from "react-bootstrap-toggle";
|
||||
|
||||
import { PolochonSelect } from "../polochons/select"
|
||||
import { FormModal } from "../forms/modal"
|
||||
import { FormInput } from "../forms/input"
|
||||
import { PolochonSelect } from "../polochons/select";
|
||||
import { FormModal } from "../forms/modal";
|
||||
import { FormInput } from "../forms/input";
|
||||
|
||||
const UserEditConnect = ({
|
||||
id,
|
||||
@ -21,19 +21,23 @@ const UserEditConnect = ({
|
||||
polochonActivated: initPolochonActivated,
|
||||
updateUser,
|
||||
deleteUser,
|
||||
publicPolochons,
|
||||
publicPolochons
|
||||
}) => {
|
||||
const [modal, setModal] = useState(false);
|
||||
const [admin, setAdmin] = useState(initAdmin);
|
||||
const [activated, setActivated] = useState(initActivated);
|
||||
const [token, setToken] = useState(polochonToken);
|
||||
const [polochonId, setPolochonId] = useState(initPolochonId);
|
||||
const [polochonActivated, setPolochonActivated] = useState(initPolochonActivated);
|
||||
const [polochonActivated, setPolochonActivated] = useState(
|
||||
initPolochonActivated
|
||||
);
|
||||
const [password, setPassword] = useState("");
|
||||
const [confirmDelete, setConfirmDelete] = useState(false);
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
if (e) { e.preventDefault(); }
|
||||
const handleSubmit = e => {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
updateUser({
|
||||
userId: id,
|
||||
polochonToken: token,
|
||||
@ -41,26 +45,26 @@ const UserEditConnect = ({
|
||||
activated,
|
||||
polochonId,
|
||||
polochonActivated,
|
||||
password,
|
||||
password
|
||||
});
|
||||
setModal(false);
|
||||
};
|
||||
|
||||
const handleDeleteUser = (e) => {
|
||||
if (e) { e.preventDefault(); }
|
||||
const handleDeleteUser = e => {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
if (confirmDelete) {
|
||||
deleteUser(name)
|
||||
setModal(false)
|
||||
deleteUser(name);
|
||||
setModal(false);
|
||||
} else {
|
||||
setConfirmDelete(true)
|
||||
}
|
||||
setConfirmDelete(true);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<span>
|
||||
<i
|
||||
className="fa fa-pencil clickable"
|
||||
onClick={() => setModal(true)} />
|
||||
<i className="fa fa-pencil clickable" onClick={() => setModal(true)} />
|
||||
|
||||
<FormModal
|
||||
show={modal}
|
||||
@ -69,28 +73,58 @@ const UserEditConnect = ({
|
||||
icon="edit"
|
||||
handleSubmit={handleSubmit}
|
||||
>
|
||||
|
||||
<div className="form-group">
|
||||
<label>Account status</label>
|
||||
<Toggle className="pull-right" on="Activated" off="Deactivated" active={activated}
|
||||
offstyle="danger" handlestyle="secondary" onClick={() => setActivated(!activated)}
|
||||
<Toggle
|
||||
className="pull-right"
|
||||
on="Activated"
|
||||
off="Deactivated"
|
||||
active={activated}
|
||||
offstyle="danger"
|
||||
handlestyle="secondary"
|
||||
onClick={() => setActivated(!activated)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label>Admin status</label>
|
||||
<Toggle className="pull-right" on="Admin" off="User" active={admin}
|
||||
offstyle="info" handlestyle="secondary" onClick={() => setAdmin(!admin)} />
|
||||
<Toggle
|
||||
className="pull-right"
|
||||
on="Admin"
|
||||
off="User"
|
||||
active={admin}
|
||||
offstyle="info"
|
||||
handlestyle="secondary"
|
||||
onClick={() => setAdmin(!admin)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<FormInput label="Password" value={password} updateValue={setPassword} />
|
||||
<FormInput
|
||||
label="Password"
|
||||
value={password}
|
||||
updateValue={setPassword}
|
||||
/>
|
||||
|
||||
<PolochonSelect value={polochonId} changeValue={setPolochonId} polochonList={publicPolochons} />
|
||||
<FormInput label="Polochon Token" value={token} updateValue={setToken} />
|
||||
<PolochonSelect
|
||||
value={polochonId}
|
||||
changeValue={setPolochonId}
|
||||
polochonList={publicPolochons}
|
||||
/>
|
||||
<FormInput
|
||||
label="Polochon Token"
|
||||
value={token}
|
||||
updateValue={setToken}
|
||||
/>
|
||||
<div className="form-group">
|
||||
<label>Polochon activated</label>
|
||||
<Toggle className="pull-right" on="Activated" off="Deactivated" active={polochonActivated}
|
||||
offstyle="danger" handlestyle="secondary" onClick={() => setPolochonActivated(!polochonActivated)}
|
||||
<Toggle
|
||||
className="pull-right"
|
||||
on="Activated"
|
||||
off="Deactivated"
|
||||
active={polochonActivated}
|
||||
offstyle="danger"
|
||||
handlestyle="secondary"
|
||||
onClick={() => setPolochonActivated(!polochonActivated)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -99,11 +133,10 @@ const UserEditConnect = ({
|
||||
{!confirmDelete ? "Delete user forever" : "Are you sure ?"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</FormModal>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
UserEditConnect.propTypes = {
|
||||
id: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
@ -114,11 +147,13 @@ UserEditConnect.propTypes = {
|
||||
polochonToken: PropTypes.string,
|
||||
polochonId: PropTypes.string,
|
||||
polochonActivated: PropTypes.bool,
|
||||
publicPolochons: PropTypes.instanceOf(List),
|
||||
publicPolochons: PropTypes.instanceOf(List)
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
publicPolochons: state.polochon.get("public"),
|
||||
const mapStateToProps = state => ({
|
||||
publicPolochons: state.polochon.get("public")
|
||||
});
|
||||
|
||||
export const UserEdit = connect(mapStateToProps, {updateUser, deleteUser})(UserEditConnect);
|
||||
export const UserEdit = connect(mapStateToProps, { updateUser, deleteUser })(
|
||||
UserEditConnect
|
||||
);
|
||||
|
@ -1,15 +1,15 @@
|
||||
import React, { useEffect } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { List } from "immutable"
|
||||
import { connect } from "react-redux"
|
||||
import React, { useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { User } from "./user"
|
||||
import { User } from "./user";
|
||||
|
||||
import { getUsers } from "../../actions/admins"
|
||||
import { getPolochons } from "../../actions/polochon"
|
||||
import { getUsers } from "../../actions/admins";
|
||||
import { getPolochons } from "../../actions/polochon";
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
users: state.adminStore.get("users"),
|
||||
users: state.adminStore.get("users")
|
||||
});
|
||||
const mapDispatchToProps = { getUsers, getPolochons };
|
||||
|
||||
@ -17,7 +17,7 @@ const UserListConnect = ({ users, getUsers, getPolochons }) => {
|
||||
useEffect(() => {
|
||||
getUsers();
|
||||
getPolochons();
|
||||
}, [getUsers, getPolochons])
|
||||
}, [getUsers, getPolochons]);
|
||||
|
||||
return (
|
||||
<div className="table-responsive my-2">
|
||||
@ -35,7 +35,7 @@ const UserListConnect = ({ users, getUsers, getPolochons }) => {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{users.map((el, index) =>
|
||||
{users.map((el, index) => (
|
||||
<User
|
||||
key={index}
|
||||
id={el.get("id")}
|
||||
@ -48,7 +48,7 @@ const UserListConnect = ({ users, getUsers, getPolochons }) => {
|
||||
polochonUrl={el.getIn(["polochon", "url"], "")}
|
||||
polochonName={el.getIn(["polochon", "name"], "")}
|
||||
/>
|
||||
)}
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@ -57,7 +57,10 @@ const UserListConnect = ({ users, getUsers, getPolochons }) => {
|
||||
UserListConnect.propTypes = {
|
||||
getUsers: PropTypes.func,
|
||||
getPolochons: PropTypes.func,
|
||||
users: PropTypes.PropTypes.instanceOf(List),
|
||||
users: PropTypes.PropTypes.instanceOf(List)
|
||||
};
|
||||
|
||||
export const UserList = connect(mapStateToProps, mapDispatchToProps)(UserListConnect);
|
||||
export const UserList = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(UserListConnect);
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React, { useState } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { List } from "immutable"
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "immutable";
|
||||
|
||||
import { Button, Modal } from "react-bootstrap"
|
||||
import { Button, Modal } from "react-bootstrap";
|
||||
import Toggle from "react-bootstrap-toggle";
|
||||
|
||||
export const UserList = props => (
|
||||
@ -20,15 +20,16 @@ export const UserList = props => (
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{props.users.map((el, index) =>
|
||||
<User key={index} data={el} updateUser={props.updateUser}/>)}
|
||||
{props.users.map((el, index) => (
|
||||
<User key={index} data={el} updateUser={props.updateUser} />
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
UserList.propTypes = {
|
||||
users: PropTypes.PropTypes.instanceOf(List),
|
||||
updateUser: PropTypes.func,
|
||||
updateUser: PropTypes.func
|
||||
};
|
||||
|
||||
const User = function(props) {
|
||||
@ -39,8 +40,12 @@ const User = function(props) {
|
||||
<tr>
|
||||
<td>{props.data.get("id")}</td>
|
||||
<td>{props.data.get("Name")}</td>
|
||||
<td><UserActivationStatus activated={props.data.get("Activated")}/></td>
|
||||
<td><UserAdminStatus admin={props.data.get("Admin")}/></td>
|
||||
<td>
|
||||
<UserActivationStatus activated={props.data.get("Activated")} />
|
||||
</td>
|
||||
<td>
|
||||
<UserAdminStatus admin={props.data.get("Admin")} />
|
||||
</td>
|
||||
<td>{polochonUrl}</td>
|
||||
<td>{polochonToken}</td>
|
||||
<td>
|
||||
@ -53,10 +58,10 @@ const User = function(props) {
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
};
|
||||
User.propTypes = {
|
||||
data: PropTypes.object,
|
||||
updateUser: PropTypes.func,
|
||||
updateUser: PropTypes.func
|
||||
};
|
||||
|
||||
const UserAdminStatus = props => (
|
||||
@ -65,7 +70,9 @@ const UserAdminStatus = props => (
|
||||
UserAdminStatus.propTypes = { admin: PropTypes.bool.isRequired };
|
||||
|
||||
const UserActivationStatus = props => (
|
||||
<span className={props.activated ? "fa fa-check" : "fa fa-times text-danger"}></span>
|
||||
<span
|
||||
className={props.activated ? "fa fa-check" : "fa fa-times text-danger"}
|
||||
></span>
|
||||
);
|
||||
UserActivationStatus.propTypes = { activated: PropTypes.bool.isRequired };
|
||||
|
||||
@ -77,20 +84,25 @@ function UserEdit(props) {
|
||||
const [token, setToken] = useState(props.polochonToken);
|
||||
|
||||
const handleSubmit = function(e) {
|
||||
if (e) { e.preventDefault(); }
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
props.updateUser({
|
||||
userId: props.data.get("id"),
|
||||
admin: admin,
|
||||
activated: activated,
|
||||
polochonUrl: url,
|
||||
polochonToken: token,
|
||||
polochonToken: token
|
||||
});
|
||||
setModal(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<span>
|
||||
<span className="fa fa-pencil clickable" onClick={() => setModal(true)}></span>
|
||||
<span
|
||||
className="fa fa-pencil clickable"
|
||||
onClick={() => setModal(true)}
|
||||
></span>
|
||||
<Modal show={modal} onHide={() => setModal(false)}>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>
|
||||
@ -99,30 +111,53 @@ function UserEdit(props) {
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body bsPrefix="modal-body admin-edit-user-modal">
|
||||
<form className="form-horizontal" onSubmit={(ev) => handleSubmit(ev)}>
|
||||
<form className="form-horizontal" onSubmit={ev => handleSubmit(ev)}>
|
||||
<div className="form-group">
|
||||
<label>Account status</label>
|
||||
<Toggle className="pull-right" on="Activated" off="Deactivated" active={activated}
|
||||
offstyle="danger" handlestyle="secondary" onClick={() => setActivated(!activated)}
|
||||
<Toggle
|
||||
className="pull-right"
|
||||
on="Activated"
|
||||
off="Deactivated"
|
||||
active={activated}
|
||||
offstyle="danger"
|
||||
handlestyle="secondary"
|
||||
onClick={() => setActivated(!activated)}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Admin status</label>
|
||||
<Toggle className="pull-right" on="Admin" off="User" active={admin}
|
||||
offstyle="info" handlestyle="secondary" onClick={() => setAdmin(!admin)} />
|
||||
<Toggle
|
||||
className="pull-right"
|
||||
on="Admin"
|
||||
off="User"
|
||||
active={admin}
|
||||
offstyle="info"
|
||||
handlestyle="secondary"
|
||||
onClick={() => setAdmin(!admin)}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label className="control-label">Polochon URL</label>
|
||||
<input className="form-control" value={url} onChange={(e) => setUrl(e.target.value)}/>
|
||||
<input
|
||||
className="form-control"
|
||||
value={url}
|
||||
onChange={e => setUrl(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label className="control-label">Polochon token</label>
|
||||
<input className="form-control" value={token} onChange={(e) => setToken(e.target.value)} />
|
||||
<input
|
||||
className="form-control"
|
||||
value={token}
|
||||
onChange={e => setToken(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="success" onClick={handleSubmit}>Apply</Button>
|
||||
<Button variant="success" onClick={handleSubmit}>
|
||||
Apply
|
||||
</Button>
|
||||
<Button onClick={() => setModal(false)}>Close</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
@ -133,5 +168,5 @@ UserEdit.propTypes = {
|
||||
data: PropTypes.object,
|
||||
updateUser: PropTypes.func,
|
||||
polochonUrl: PropTypes.string,
|
||||
polochonToken: PropTypes.string,
|
||||
polochonToken: PropTypes.string
|
||||
};
|
||||
|
@ -1,20 +1,20 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import SweetAlert from "react-bootstrap-sweetalert";
|
||||
import { connect } from "react-redux"
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { dismissAlert } from "../../actions/alerts"
|
||||
import { dismissAlert } from "../../actions/alerts";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
const mapStateToProps = state => ({
|
||||
show: state.alerts.get("show"),
|
||||
title: state.alerts.get("message"),
|
||||
type: state.alerts.get("type"),
|
||||
type: state.alerts.get("type")
|
||||
});
|
||||
const mapDispatchToProps = { dismissAlert };
|
||||
|
||||
const Alert = (props) => {
|
||||
const Alert = props => {
|
||||
if (!props.show) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
@ -23,13 +23,13 @@ const Alert = (props) => {
|
||||
title={props.title}
|
||||
onConfirm={props.dismissAlert}
|
||||
/>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
Alert.propTypes = {
|
||||
show: PropTypes.bool.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
dismissAlert: PropTypes.func.isRequired,
|
||||
type: PropTypes.string,
|
||||
type: PropTypes.string
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Alert);
|
||||
|
@ -1,11 +1,13 @@
|
||||
import React, { useState } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { List } from "immutable"
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "immutable";
|
||||
|
||||
import Modal from "react-bootstrap/Modal"
|
||||
import Modal from "react-bootstrap/Modal";
|
||||
|
||||
export const DownloadAndStream = ({ url, name, subtitles }) => {
|
||||
if (!url || url === "") { return null; }
|
||||
if (!url || url === "") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<span>
|
||||
@ -13,11 +15,11 @@ export const DownloadAndStream = ({ url, name, subtitles }) => {
|
||||
<StreamButton url={url} name={name} subtitles={subtitles} />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
DownloadAndStream.propTypes = {
|
||||
url: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
subtitles: PropTypes.instanceOf(List),
|
||||
subtitles: PropTypes.instanceOf(List)
|
||||
};
|
||||
|
||||
const DownloadButton = ({ url }) => (
|
||||
@ -39,13 +41,22 @@ const StreamButton = ({ name, url, subtitles }) => {
|
||||
<a
|
||||
href="#"
|
||||
className="btn btn-sm btn-primary m-1 clickable"
|
||||
onClick={(e) => { e.preventDefault(); setShowModal(true) }}>
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
setShowModal(true);
|
||||
}}
|
||||
>
|
||||
<i className="fa fa-play-circle mr-1" />
|
||||
Play
|
||||
</a>
|
||||
</h5>
|
||||
|
||||
<Modal show={showModal} onHide={() => setShowModal(false)} size="lg" centered>
|
||||
<Modal
|
||||
show={showModal}
|
||||
onHide={() => setShowModal(false)}
|
||||
size="lg"
|
||||
centered
|
||||
>
|
||||
<Modal.Header closeButton>{name}</Modal.Header>
|
||||
<Modal.Body>
|
||||
<Player url={url} subtitles={subtitles} />
|
||||
@ -53,11 +64,11 @@ const StreamButton = ({ name, url, subtitles }) => {
|
||||
</Modal>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
};
|
||||
StreamButton.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
url: PropTypes.string.isRequired,
|
||||
subtitles: PropTypes.instanceOf(List),
|
||||
subtitles: PropTypes.instanceOf(List)
|
||||
};
|
||||
|
||||
const Player = ({ url, subtitles }) => {
|
||||
@ -65,8 +76,11 @@ const Player = ({ url, subtitles }) => {
|
||||
return (
|
||||
<div className="embed-responsive embed-responsive-16by9">
|
||||
<video className="embed-responsive-item" controls>
|
||||
<source src={url} type="video/mp4"/>
|
||||
{hasSubtitles && subtitles.toIndexedSeq().map((el, index) => (
|
||||
<source src={url} type="video/mp4" />
|
||||
{hasSubtitles &&
|
||||
subtitles
|
||||
.toIndexedSeq()
|
||||
.map((el, index) => (
|
||||
<track
|
||||
key={index}
|
||||
kind="subtitles"
|
||||
@ -78,11 +92,11 @@ const Player = ({ url, subtitles }) => {
|
||||
</video>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
Player.propTypes = {
|
||||
subtitles: PropTypes.instanceOf(List),
|
||||
url: PropTypes.string.isRequired,
|
||||
url: PropTypes.string.isRequired
|
||||
};
|
||||
Player.defaultProps = {
|
||||
subtitles: List(),
|
||||
subtitles: List()
|
||||
};
|
||||
|
@ -1,19 +1,22 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
export const ImdbBadge = ({ imdbId }) => {
|
||||
if (imdbId === undefined) { return null }
|
||||
if (imdbId === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<h5 className="d-inline">
|
||||
<a
|
||||
className="btn btn-sm btn-warning m-1"
|
||||
href={`https://www.imdb.com/title/${imdbId}`}>
|
||||
href={`https://www.imdb.com/title/${imdbId}`}
|
||||
>
|
||||
IMDb
|
||||
<i className="ml-1 fa fa-external-link"></i>
|
||||
</a>
|
||||
</h5>
|
||||
);
|
||||
}
|
||||
};
|
||||
ImdbBadge.propTypes = { imdbId: PropTypes.string };
|
||||
ImdbBadge.defaultProps = { imdbId: undefined };
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React, { useState, useEffect } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import React, { useState, useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
export const ShowMore = ({ children, id, inLibrary }) => {
|
||||
const [ display, setDisplay ] = useState(!inLibrary)
|
||||
const [display, setDisplay] = useState(!inLibrary);
|
||||
|
||||
useEffect(() => {
|
||||
setDisplay(!inLibrary);
|
||||
@ -18,21 +18,18 @@ export const ShowMore = ({ children, id, inLibrary }) => {
|
||||
More options ...
|
||||
</a>
|
||||
</span>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return (<React.Fragment>{children}</React.Fragment>)
|
||||
}
|
||||
return <React.Fragment>{children}</React.Fragment>;
|
||||
};
|
||||
|
||||
ShowMore.propTypes = {
|
||||
id: PropTypes.string,
|
||||
inLibrary: PropTypes.bool.isRequired,
|
||||
children: PropTypes.oneOf(
|
||||
PropTypes.object,
|
||||
PropTypes.array,
|
||||
),
|
||||
}
|
||||
children: PropTypes.oneOf(PropTypes.object, PropTypes.array)
|
||||
};
|
||||
ShowMore.defaultProps = {
|
||||
id: "",
|
||||
inLibrary: false,
|
||||
}
|
||||
inLibrary: false
|
||||
};
|
||||
|
@ -1,61 +1,60 @@
|
||||
import React, { useState } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { List } from "immutable"
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "immutable";
|
||||
|
||||
import Dropdown from "react-bootstrap/Dropdown"
|
||||
import Dropdown from "react-bootstrap/Dropdown";
|
||||
|
||||
export const SubtitlesButton = ({
|
||||
subtitles,
|
||||
inLibrary,
|
||||
searching,
|
||||
search,
|
||||
search
|
||||
}) => {
|
||||
if (inLibrary === false) { return null }
|
||||
if (inLibrary === false) {
|
||||
return null;
|
||||
}
|
||||
/* eslint-disable */
|
||||
const [show, setShow] = useState(false);
|
||||
/* eslint-enable */
|
||||
|
||||
const onSelect = (eventKey) => {
|
||||
const onSelect = eventKey => {
|
||||
if (eventKey === null || eventKey != 1) {
|
||||
setShow(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onToggle = (isOpen, event, metadata) => {
|
||||
// Don't close on select
|
||||
if (metadata && metadata.source !== "select") {
|
||||
setShow(isOpen);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const count = (subtitles && subtitles.size !== 0) ? subtitles.size : 0;
|
||||
const count = subtitles && subtitles.size !== 0 ? subtitles.size : 0;
|
||||
return (
|
||||
<span className="mr-1 mb-1">
|
||||
<Dropdown drop="up" show={show} onToggle={onToggle} onSelect={onSelect}>
|
||||
<Dropdown.Toggle variant="secondary" bsPrefix="btn-sm w-md-100">
|
||||
<i className="fa fa-commenting mr-1" />
|
||||
Subtitles
|
||||
<span className="ml-1 badge badge-pill badge-info">
|
||||
{count}
|
||||
</span>
|
||||
<span className="ml-1 badge badge-pill badge-info">{count}</span>
|
||||
</Dropdown.Toggle>
|
||||
|
||||
<Dropdown.Menu>
|
||||
<Dropdown.Item eventKey={1} onClick={search} >
|
||||
<i className={`fa ${ searching ? "fa-spin" : "" } fa-refresh mr-1`} />
|
||||
<Dropdown.Item eventKey={1} onClick={search}>
|
||||
<i className={`fa ${searching ? "fa-spin" : ""} fa-refresh mr-1`} />
|
||||
Automatic search
|
||||
</Dropdown.Item>
|
||||
{count > 0 &&
|
||||
{count > 0 && (
|
||||
<React.Fragment>
|
||||
<Dropdown.Divider />
|
||||
<Dropdown.Header>
|
||||
<span className="text-warning">
|
||||
Available subtitles
|
||||
</span>
|
||||
<span className="text-warning">Available subtitles</span>
|
||||
</Dropdown.Header>
|
||||
</React.Fragment>
|
||||
}
|
||||
{count > 0 && subtitles.toIndexedSeq().map((subtitle, index) => (
|
||||
)}
|
||||
{count > 0 &&
|
||||
subtitles.toIndexedSeq().map((subtitle, index) => (
|
||||
<Dropdown.Item href={subtitle.get("url")} key={index}>
|
||||
{subtitle.get("language").split("_")[1]}
|
||||
</Dropdown.Item>
|
||||
@ -64,10 +63,10 @@ export const SubtitlesButton = ({
|
||||
</Dropdown>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
SubtitlesButton.propTypes = {
|
||||
subtitles: PropTypes.instanceOf(List),
|
||||
inLibrary: PropTypes.bool.isRequired,
|
||||
searching: PropTypes.bool.isRequired,
|
||||
search: PropTypes.func.isRequired,
|
||||
}
|
||||
search: PropTypes.func.isRequired
|
||||
};
|
||||
|
@ -1,19 +1,19 @@
|
||||
import React, { useState } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { List } from "immutable"
|
||||
import { connect } from "react-redux"
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { prettySize } from "../../utils"
|
||||
import { addTorrent } from "../../actions/torrents"
|
||||
import { prettySize } from "../../utils";
|
||||
import { addTorrent } from "../../actions/torrents";
|
||||
|
||||
import Dropdown from "react-bootstrap/Dropdown"
|
||||
import Dropdown from "react-bootstrap/Dropdown";
|
||||
|
||||
function buildMenuItems(torrents) {
|
||||
if (!torrents || torrents.size === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const t = torrents.groupBy((el) => el.get("source"));
|
||||
const t = torrents.groupBy(el => el.get("source"));
|
||||
|
||||
// Build the array of entries
|
||||
let entries = [];
|
||||
@ -22,7 +22,7 @@ function buildMenuItems(torrents) {
|
||||
// Push the title
|
||||
entries.push({
|
||||
type: "header",
|
||||
value: source,
|
||||
value: source
|
||||
});
|
||||
|
||||
// Push the torrents
|
||||
@ -31,7 +31,7 @@ function buildMenuItems(torrents) {
|
||||
type: "entry",
|
||||
quality: torrent.get("quality"),
|
||||
url: torrent.get("url"),
|
||||
size: torrent.get("size"),
|
||||
size: torrent.get("size")
|
||||
});
|
||||
}
|
||||
|
||||
@ -45,32 +45,26 @@ function buildMenuItems(torrents) {
|
||||
return entries;
|
||||
}
|
||||
|
||||
const torrentsButton = ({
|
||||
torrents,
|
||||
search,
|
||||
searching,
|
||||
addTorrent,
|
||||
url,
|
||||
}) => {
|
||||
const torrentsButton = ({ torrents, search, searching, addTorrent, url }) => {
|
||||
/* eslint-disable */
|
||||
const [show, setShow] = useState(false);
|
||||
/* eslint-enable */
|
||||
const entries = buildMenuItems(torrents);
|
||||
const count = (torrents && torrents.size !== 0) ? torrents.size : 0;
|
||||
const count = torrents && torrents.size !== 0 ? torrents.size : 0;
|
||||
|
||||
const onSelect = (eventKey) => {
|
||||
const onSelect = eventKey => {
|
||||
// Close the dropdown if the eventkey is not specified
|
||||
if (eventKey === null) {
|
||||
setShow(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onToggle = (isOpen, event, metadata) => {
|
||||
// Don't close on select
|
||||
if (metadata && metadata.source !== "select") {
|
||||
setShow(isOpen);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<span className="mr-1 mb-1">
|
||||
@ -78,23 +72,19 @@ const torrentsButton = ({
|
||||
<Dropdown.Toggle variant="secondary" bsPrefix="btn-sm w-md-100">
|
||||
<i className="fa fa-magnet mr-1" />
|
||||
Torrents
|
||||
<span className="ml-1 badge badge-pill badge-info">
|
||||
{count}
|
||||
</span>
|
||||
<span className="ml-1 badge badge-pill badge-info">{count}</span>
|
||||
</Dropdown.Toggle>
|
||||
|
||||
<Dropdown.Menu>
|
||||
<Dropdown.Item eventKey={1} onClick={search} >
|
||||
<i className={`fa ${ searching ? "fa-spin" : "" } fa-refresh mr-1`} />
|
||||
<Dropdown.Item eventKey={1} onClick={search}>
|
||||
<i className={`fa ${searching ? "fa-spin" : ""} fa-refresh mr-1`} />
|
||||
Automatic search
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item href={url} >
|
||||
<Dropdown.Item href={url}>
|
||||
<i className="fa fa-search mr-1" />
|
||||
Manual search
|
||||
</Dropdown.Item>
|
||||
{entries.length > 0 &&
|
||||
<Dropdown.Divider />
|
||||
}
|
||||
{entries.length > 0 && <Dropdown.Divider />}
|
||||
{entries.map((e, index) => {
|
||||
switch (e.type) {
|
||||
case "header":
|
||||
@ -104,16 +94,14 @@ const torrentsButton = ({
|
||||
</Dropdown.Header>
|
||||
);
|
||||
case "divider":
|
||||
return (
|
||||
<Dropdown.Divider key={index}/>
|
||||
);
|
||||
return <Dropdown.Divider key={index} />;
|
||||
case "entry":
|
||||
return (
|
||||
<Dropdown.Item key={index} onClick={() => addTorrent(e.url)}>
|
||||
{e.quality}
|
||||
{e.size !== 0 &&
|
||||
{e.size !== 0 && (
|
||||
<small className="ml-1">({prettySize(e.size)})</small>
|
||||
}
|
||||
)}
|
||||
</Dropdown.Item>
|
||||
);
|
||||
}
|
||||
@ -122,16 +110,16 @@ const torrentsButton = ({
|
||||
</Dropdown>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
torrentsButton.propTypes = {
|
||||
torrents: PropTypes.instanceOf(List),
|
||||
searching: PropTypes.bool,
|
||||
search: PropTypes.func.isRequired,
|
||||
addTorrent: PropTypes.func.isRequired,
|
||||
url: PropTypes.string,
|
||||
}
|
||||
url: PropTypes.string
|
||||
};
|
||||
torrentsButton.defaultProps = {
|
||||
torrents: List(),
|
||||
}
|
||||
torrents: List()
|
||||
};
|
||||
|
||||
export const TorrentsButton = connect(null, {addTorrent})(torrentsButton);
|
||||
export const TorrentsButton = connect(null, { addTorrent })(torrentsButton);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
export const WishlistButton = ({ wishlisted, wishlist }) => {
|
||||
return (
|
||||
@ -10,8 +10,8 @@ export const WishlistButton = ({ wishlisted, wishlist }) => {
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
WishlistButton.propTypes = {
|
||||
wishlisted: PropTypes.bool.isRequired,
|
||||
wishlist: PropTypes.func.isRequired,
|
||||
}
|
||||
wishlist: PropTypes.func.isRequired
|
||||
};
|
||||
|
@ -1,14 +1,17 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { List } from "immutable"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "immutable";
|
||||
|
||||
export const Genres = ({ genres }) => {
|
||||
if (genres.size === 0) { return null }
|
||||
if (genres.size === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Uppercase first genres
|
||||
const prettyGenres = genres.toJS().map(
|
||||
(word) => word[0].toUpperCase() + word.substr(1)
|
||||
).join(", ");
|
||||
const prettyGenres = genres
|
||||
.toJS()
|
||||
.map(word => word[0].toUpperCase() + word.substr(1))
|
||||
.join(", ");
|
||||
|
||||
return (
|
||||
<span>
|
||||
@ -16,6 +19,6 @@ export const Genres = ({ genres }) => {
|
||||
{prettyGenres}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
Genres.propTypes = { genres: PropTypes.instanceOf(List) };
|
||||
Genres.defaultProps = { genres: List() };
|
||||
|
@ -1,12 +1,12 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
export const Plot = ({ plot }) => {
|
||||
if (plot === "") { return null }
|
||||
if (plot === "") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<span className="text text-break plot">{plot}</span>
|
||||
);
|
||||
}
|
||||
return <span className="text text-break plot">{plot}</span>;
|
||||
};
|
||||
Plot.propTypes = { plot: PropTypes.string };
|
||||
Plot.defaultProps = { plot: "" };
|
||||
|
@ -1,26 +1,20 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
export const PolochonMetadata = ({
|
||||
quality,
|
||||
container,
|
||||
videoCodec,
|
||||
audioCodec,
|
||||
releaseGroup,
|
||||
releaseGroup
|
||||
}) => {
|
||||
if (!quality || quality === "") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const metadata = [
|
||||
quality,
|
||||
container,
|
||||
videoCodec,
|
||||
audioCodec,
|
||||
releaseGroup,
|
||||
].
|
||||
filter(m => (m && m !== "")).
|
||||
join(", ")
|
||||
const metadata = [quality, container, videoCodec, audioCodec, releaseGroup]
|
||||
.filter(m => m && m !== "")
|
||||
.join(", ");
|
||||
|
||||
return (
|
||||
<span>
|
||||
@ -28,11 +22,11 @@ export const PolochonMetadata = ({
|
||||
{metadata}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
PolochonMetadata.propTypes = {
|
||||
quality: PropTypes.string,
|
||||
container: PropTypes.string,
|
||||
videoCodec: PropTypes.string,
|
||||
audioCodec: PropTypes.string,
|
||||
releaseGroup: PropTypes.string,
|
||||
releaseGroup: PropTypes.string
|
||||
};
|
||||
|
@ -1,24 +1,24 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
export const Rating = ({ rating, votes }) => {
|
||||
if (rating === 0) { return null; }
|
||||
if (rating === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<span>
|
||||
<i className="fa fa-star mr-1"></i>
|
||||
{Number(rating).toFixed(1)}
|
||||
{votes !== 0 &&
|
||||
<small className="ml-1">({votes} votes)</small>
|
||||
}
|
||||
{votes !== 0 && <small className="ml-1">({votes} votes)</small>}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
Rating.propTypes = {
|
||||
rating: PropTypes.number,
|
||||
votes: PropTypes.number,
|
||||
votes: PropTypes.number
|
||||
};
|
||||
Rating.defaultProps = {
|
||||
rating: 0,
|
||||
votes: 0,
|
||||
votes: 0
|
||||
};
|
||||
|
@ -1,35 +1,42 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import moment from "moment"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import moment from "moment";
|
||||
|
||||
const prettyDate = (input) => {
|
||||
const prettyDate = input => {
|
||||
switch (typeof input) {
|
||||
case "string":
|
||||
if (input === "") { return "" }
|
||||
if (input === "") {
|
||||
return "";
|
||||
}
|
||||
break;
|
||||
case "number":
|
||||
if (input === 0) { return "" }
|
||||
return input
|
||||
if (input === 0) {
|
||||
return "";
|
||||
}
|
||||
return input;
|
||||
default:
|
||||
return input;
|
||||
}
|
||||
|
||||
const date = moment(input);
|
||||
if (!date.isValid()) { return "" }
|
||||
if (!date.isValid()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
let output = date.format("DD/MM/YYYY");
|
||||
|
||||
if ((date > moment().subtract(1, "month"))
|
||||
&& (date < moment().add(1, "month"))) {
|
||||
output += " (" + date.fromNow() + ")"
|
||||
if (date > moment().subtract(1, "month") && date < moment().add(1, "month")) {
|
||||
output += " (" + date.fromNow() + ")";
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
return output;
|
||||
};
|
||||
|
||||
export const ReleaseDate = ({ date }) => {
|
||||
const formattedDate = prettyDate(date);
|
||||
if (formattedDate === "") { return null }
|
||||
if (formattedDate === "") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<span>
|
||||
@ -37,11 +44,8 @@ export const ReleaseDate = ({ date }) => {
|
||||
{formattedDate}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
ReleaseDate.propTypes = {
|
||||
date: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
]),
|
||||
date: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
|
||||
};
|
||||
ReleaseDate.defaultProps = { date: "" };
|
||||
|
@ -1,10 +1,12 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { prettyDurationFromMinutes } from "../../utils"
|
||||
import { prettyDurationFromMinutes } from "../../utils";
|
||||
|
||||
export const Runtime = ({ runtime }) => {
|
||||
if (runtime === 0) { return null; }
|
||||
if (runtime === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<span>
|
||||
@ -12,6 +14,6 @@ export const Runtime = ({ runtime }) => {
|
||||
{prettyDurationFromMinutes(runtime)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
Runtime.propTypes = { runtime: PropTypes.number };
|
||||
Runtime.defaultProps = { runtime: 0 };
|
||||
|
@ -1,10 +1,12 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { WishlistButton } from "../buttons/wishlist"
|
||||
import { WishlistButton } from "../buttons/wishlist";
|
||||
|
||||
export const Title = ({ title, wishlist, wishlisted }) => {
|
||||
if (title === "") { return null; }
|
||||
if (title === "") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<span className="title">
|
||||
@ -12,12 +14,12 @@ export const Title = ({ title, wishlist, wishlisted }) => {
|
||||
{title}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
Title.propTypes = {
|
||||
title: PropTypes.string,
|
||||
wishlist: PropTypes.func,
|
||||
wishlisted: PropTypes.bool,
|
||||
wishlisted: PropTypes.bool
|
||||
};
|
||||
Title.defaultProps = {
|
||||
title: "",
|
||||
title: ""
|
||||
};
|
||||
|
@ -1,7 +1,12 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
export const TrackingLabel = ({ wishlisted, inLibrary, trackedSeason, trackedEpisode }) => {
|
||||
export const TrackingLabel = ({
|
||||
wishlisted,
|
||||
inLibrary,
|
||||
trackedSeason,
|
||||
trackedEpisode
|
||||
}) => {
|
||||
if (wishlisted === false) {
|
||||
return null;
|
||||
}
|
||||
@ -14,14 +19,14 @@ export const TrackingLabel = ({ wishlisted, inLibrary, trackedSeason, trackedEpi
|
||||
return null;
|
||||
}
|
||||
|
||||
let str = ""
|
||||
let str = "";
|
||||
if (trackedSeason === 0 && trackedEpisode === 0) {
|
||||
str = "All the episodes will be downloaded automatically";
|
||||
} else if (trackedSeason > 0 && trackedEpisode > 0) {
|
||||
str = `All the episodes will be downloaded automatically starting from
|
||||
season ${trackedSeason} episode ${trackedEpisode}`;
|
||||
} else {
|
||||
str = "This movie will be downloaded automatically"
|
||||
str = "This movie will be downloaded automatically";
|
||||
}
|
||||
|
||||
return (
|
||||
@ -31,11 +36,11 @@ export const TrackingLabel = ({ wishlisted, inLibrary, trackedSeason, trackedEpi
|
||||
{str}
|
||||
</small>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
TrackingLabel.propTypes = {
|
||||
wishlisted: PropTypes.bool,
|
||||
inLibrary: PropTypes.bool,
|
||||
trackedSeason: PropTypes.number,
|
||||
trackedEpisode: PropTypes.number,
|
||||
trackedEpisode: PropTypes.number
|
||||
};
|
||||
|
@ -1,22 +1,20 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
export const FormInput = ({ label, value, updateValue }) => {
|
||||
return (
|
||||
<div className="form-group">
|
||||
<label className="control-label">
|
||||
{label}
|
||||
</label>
|
||||
<label className="control-label">{label}</label>
|
||||
<input
|
||||
className="form-control"
|
||||
value={value}
|
||||
onChange={(e) => updateValue(e.target.value)}
|
||||
onChange={e => updateValue(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
FormInput.propTypes = {
|
||||
label: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
updateValue: PropTypes.func,
|
||||
updateValue: PropTypes.func
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { Modal } from "react-bootstrap"
|
||||
import { Modal } from "react-bootstrap";
|
||||
|
||||
export const FormModal = ({
|
||||
show,
|
||||
@ -9,10 +9,12 @@ export const FormModal = ({
|
||||
title,
|
||||
icon,
|
||||
handleSubmit,
|
||||
children,
|
||||
children
|
||||
}) => {
|
||||
const submit = function(e) {
|
||||
if (e) { e.preventDefault(); }
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
handleSubmit();
|
||||
};
|
||||
|
||||
@ -25,25 +27,26 @@ export const FormModal = ({
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body bsPrefix="modal-body">
|
||||
<form className="form-horizontal" onSubmit={(ev) => submit(ev)}>
|
||||
<form className="form-horizontal" onSubmit={ev => submit(ev)}>
|
||||
{children}
|
||||
</form>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<div className="btn btn-success" onClick={submit}>Apply</div>
|
||||
<div className="btn btn-danger" onClick={() => setShow(false)}>Close</div>
|
||||
<div className="btn btn-success" onClick={submit}>
|
||||
Apply
|
||||
</div>
|
||||
<div className="btn btn-danger" onClick={() => setShow(false)}>
|
||||
Close
|
||||
</div>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
FormModal.propTypes = {
|
||||
show: PropTypes.bool,
|
||||
setShow: PropTypes.func,
|
||||
icon: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
handleSubmit: PropTypes.func,
|
||||
children: PropTypes.oneOf(
|
||||
PropTypes.object,
|
||||
PropTypes.array,
|
||||
),
|
||||
children: PropTypes.oneOf(PropTypes.object, PropTypes.array)
|
||||
};
|
||||
|
@ -1,24 +1,28 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { Map } from "immutable"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Map } from "immutable";
|
||||
|
||||
import { inLibrary, isWishlisted } from "../../utils"
|
||||
import { inLibrary, isWishlisted } from "../../utils";
|
||||
|
||||
import { DownloadAndStream } from "../buttons/download"
|
||||
import { ImdbBadge } from "../buttons/imdb"
|
||||
import { DownloadAndStream } from "../buttons/download";
|
||||
import { ImdbBadge } from "../buttons/imdb";
|
||||
|
||||
import { TrackingLabel } from "../details/tracking"
|
||||
import { Genres } from "../details/genres"
|
||||
import { Plot } from "../details/plot"
|
||||
import { PolochonMetadata } from "../details/polochon"
|
||||
import { Rating } from "../details/rating"
|
||||
import { ReleaseDate } from "../details/releaseDate"
|
||||
import { Runtime } from "../details/runtime"
|
||||
import { Title } from "../details/title"
|
||||
import { TrackingLabel } from "../details/tracking";
|
||||
import { Genres } from "../details/genres";
|
||||
import { Plot } from "../details/plot";
|
||||
import { PolochonMetadata } from "../details/polochon";
|
||||
import { Rating } from "../details/rating";
|
||||
import { ReleaseDate } from "../details/releaseDate";
|
||||
import { Runtime } from "../details/runtime";
|
||||
import { Title } from "../details/title";
|
||||
|
||||
const ListDetails = (props) => {
|
||||
if (props.data === undefined) { return null }
|
||||
if (props.loading) { return null }
|
||||
const ListDetails = props => {
|
||||
if (props.data === undefined) {
|
||||
return null;
|
||||
}
|
||||
if (props.loading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="col-8 col-md-4 list-details pl-1 d-flex align-items-start flex-column">
|
||||
@ -61,11 +65,11 @@ const ListDetails = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
ListDetails.propTypes = {
|
||||
data: PropTypes.instanceOf(Map),
|
||||
wishlist: PropTypes.func,
|
||||
loading: PropTypes.bool,
|
||||
children: PropTypes.object,
|
||||
children: PropTypes.object
|
||||
};
|
||||
export default ListDetails;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from "react"
|
||||
import { withRouter } from "react-router-dom"
|
||||
import { Form, FormGroup, FormControl, FormLabel } from "react-bootstrap"
|
||||
import React from "react";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import { Form, FormGroup, FormControl, FormLabel } from "react-bootstrap";
|
||||
|
||||
class ExplorerOptions extends React.PureComponent {
|
||||
constructor(props) {
|
||||
@ -11,27 +11,34 @@ class ExplorerOptions extends React.PureComponent {
|
||||
handleSourceChange(event) {
|
||||
let source = event.target.value;
|
||||
let category = this.props.options.get(event.target.value).first();
|
||||
this.props.history.push(`/${this.props.type}/explore/${source}/${category}`);
|
||||
this.props.history.push(
|
||||
`/${this.props.type}/explore/${source}/${category}`
|
||||
);
|
||||
}
|
||||
handleCategoryChange(event) {
|
||||
let source = this.props.params.source;
|
||||
let category = event.target.value;
|
||||
this.props.history.push(`/${this.props.type}/explore/${source}/${category}`);
|
||||
this.props.history.push(
|
||||
`/${this.props.type}/explore/${source}/${category}`
|
||||
);
|
||||
}
|
||||
propsValid(props) {
|
||||
if (!props.params
|
||||
|| !props.params.source
|
||||
|| !props.params.category
|
||||
|| (props.params.source === "")
|
||||
|| (props.params.category === "")) {
|
||||
return false
|
||||
if (
|
||||
!props.params ||
|
||||
!props.params.source ||
|
||||
!props.params.category ||
|
||||
props.params.source === "" ||
|
||||
props.params.category === ""
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
prettyName(name) {
|
||||
return name.replace("_", " ")
|
||||
return name
|
||||
.replace("_", " ")
|
||||
.split(" ")
|
||||
.map((w) => w[0].toUpperCase() + w.substr(1))
|
||||
.map(w => w[0].toUpperCase() + w.substr(1))
|
||||
.join(" ");
|
||||
}
|
||||
render() {
|
||||
@ -47,7 +54,7 @@ class ExplorerOptions extends React.PureComponent {
|
||||
|
||||
// Invalid props
|
||||
if (!this.propsValid(this.props)) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
let source = this.props.params.source;
|
||||
@ -70,7 +77,10 @@ class ExplorerOptions extends React.PureComponent {
|
||||
>
|
||||
{this.props.options.keySeq().map(function(source) {
|
||||
return (
|
||||
<option key={source} value={source}>{this.prettyName(source)}</option>)
|
||||
<option key={source} value={source}>
|
||||
{this.prettyName(source)}
|
||||
</option>
|
||||
);
|
||||
}, this)}
|
||||
</FormControl>
|
||||
</FormGroup>
|
||||
@ -85,7 +95,11 @@ class ExplorerOptions extends React.PureComponent {
|
||||
value={category}
|
||||
>
|
||||
{categories.map(function(category) {
|
||||
return (<option key={category} value={category}>{this.prettyName(category)}</option>)
|
||||
return (
|
||||
<option key={category} value={category}>
|
||||
{this.prettyName(category)}
|
||||
</option>
|
||||
);
|
||||
}, this)}
|
||||
</FormControl>
|
||||
</FormGroup>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import React, { useState, useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const ListFilter = ({ placeHolder, updateFilter }) => {
|
||||
const [filter, setFilter] = useState("");
|
||||
@ -13,18 +13,21 @@ const ListFilter = ({ placeHolder, updateFilter }) => {
|
||||
|
||||
return (
|
||||
<div className="input-group input-group-sm">
|
||||
<input type="text" className="form-control"
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder={placeHolder}
|
||||
onChange={(e) => setFilter(e.target.value)}
|
||||
value={filter} />
|
||||
onChange={e => setFilter(e.target.value)}
|
||||
value={filter}
|
||||
/>
|
||||
<div className="input-group-append d-none d-md-block">
|
||||
<span className="input-group-text">Filter</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
ListFilter.propTypes = {
|
||||
updateFilter: PropTypes.func.isRequired,
|
||||
placeHolder: PropTypes.string.isRequired,
|
||||
placeHolder: PropTypes.string.isRequired
|
||||
};
|
||||
export default ListFilter;
|
||||
|
@ -1,11 +1,13 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import EmptyImg from "../../../img/noimage.png"
|
||||
import EmptyImg from "../../../img/noimage.png";
|
||||
|
||||
const Poster = ({ url, selected, onClick, onDoubleClick }) => {
|
||||
const className = selected ? "border-primary thumbnail-selected" : "border-secondary";
|
||||
const src = (url === "") ? EmptyImg : url;
|
||||
const className = selected
|
||||
? "border-primary thumbnail-selected"
|
||||
: "border-secondary";
|
||||
const src = url === "" ? EmptyImg : url;
|
||||
|
||||
return (
|
||||
<img
|
||||
@ -15,12 +17,12 @@ const Poster = ({ url, selected, onClick, onDoubleClick }) => {
|
||||
className={`my-1 m-md-2 img-thumbnail object-fit-cover ${className}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
Poster.propTypes = {
|
||||
url: PropTypes.string,
|
||||
selected: PropTypes.bool.isRequired,
|
||||
onClick: PropTypes.func,
|
||||
onDoubleClick: PropTypes.func,
|
||||
onDoubleClick: PropTypes.func
|
||||
};
|
||||
|
||||
export default Poster;
|
||||
|
@ -1,18 +1,18 @@
|
||||
import React, { useState, useEffect, useCallback } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { OrderedMap, Map } from "immutable"
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { OrderedMap, Map } from "immutable";
|
||||
import fuzzy from "fuzzy";
|
||||
import InfiniteScroll from "react-infinite-scroll-component";
|
||||
|
||||
import ListFilter from "./filter"
|
||||
import ExplorerOptions from "./explorerOptions"
|
||||
import Poster from "./poster"
|
||||
import ListFilter from "./filter";
|
||||
import ExplorerOptions from "./explorerOptions";
|
||||
import Poster from "./poster";
|
||||
|
||||
import Loader from "../loader/loader"
|
||||
import Loader from "../loader/loader";
|
||||
|
||||
const ListPosters = (props) => {
|
||||
const ListPosters = props => {
|
||||
if (props.loading) {
|
||||
return (<Loader />);
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
let elmts = props.data;
|
||||
@ -20,23 +20,24 @@ const ListPosters = (props) => {
|
||||
|
||||
// Filter the list of elements
|
||||
if (props.filter !== "") {
|
||||
elmts = elmts.filter((v) => fuzzy.test(props.filter, v.get("title")));
|
||||
elmts = elmts.filter(v => fuzzy.test(props.filter, v.get("title")));
|
||||
} else {
|
||||
elmts = elmts.slice(0, listSize.items);
|
||||
}
|
||||
|
||||
// Chose when to display filter / explore options
|
||||
let displayFilter = true;
|
||||
if ((props.params
|
||||
&& props.params.category
|
||||
&& props.params.category !== ""
|
||||
&& props.params.source
|
||||
&& props.params.source !== "")
|
||||
|| (listSize === 0)) {
|
||||
if (
|
||||
(props.params &&
|
||||
props.params.category &&
|
||||
props.params.category !== "" &&
|
||||
props.params.source &&
|
||||
props.params.source !== "") ||
|
||||
listSize === 0
|
||||
) {
|
||||
displayFilter = false;
|
||||
}
|
||||
|
||||
|
||||
let displayExplorerOptions = false;
|
||||
if (listSize !== 0) {
|
||||
displayExplorerOptions = !displayFilter;
|
||||
@ -44,12 +45,12 @@ const ListPosters = (props) => {
|
||||
|
||||
return (
|
||||
<div className="col-4 col-md-8 px-1">
|
||||
{displayFilter &&
|
||||
{displayFilter && (
|
||||
<ListFilter
|
||||
updateFilter={props.updateFilter}
|
||||
placeHolder={props.placeHolder}
|
||||
/>
|
||||
}
|
||||
)}
|
||||
<ExplorerOptions
|
||||
type={props.type}
|
||||
display={displayExplorerOptions}
|
||||
@ -66,7 +67,7 @@ const ListPosters = (props) => {
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
ListPosters.propTypes = {
|
||||
data: PropTypes.instanceOf(OrderedMap),
|
||||
onClick: PropTypes.func,
|
||||
@ -79,34 +80,39 @@ ListPosters.propTypes = {
|
||||
type: PropTypes.string.isRequired,
|
||||
placeHolder: PropTypes.string.isRequired,
|
||||
updateFilter: PropTypes.func.isRequired,
|
||||
filter: PropTypes.string,
|
||||
filter: PropTypes.string
|
||||
};
|
||||
|
||||
export default ListPosters;
|
||||
|
||||
const Posters = (props) => {
|
||||
const Posters = props => {
|
||||
const addMoreCount = 20;
|
||||
const [size, setSize] = useState(0);
|
||||
const [postersPerRow, setPostersPerRow] = useState(0);
|
||||
const [posterHeight, setPosterHeight] = useState(0);
|
||||
|
||||
const loadMore = () => {
|
||||
if ((size === props.elmts.size)) { return }
|
||||
|
||||
const newSize = (((size + addMoreCount) >= props.elmts.size)
|
||||
? props.elmts.size
|
||||
: size + addMoreCount);
|
||||
|
||||
setSize(newSize);
|
||||
if (size === props.elmts.size) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newSize =
|
||||
size + addMoreCount >= props.elmts.size
|
||||
? props.elmts.size
|
||||
: size + addMoreCount;
|
||||
|
||||
setSize(newSize);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadMore()
|
||||
loadMore();
|
||||
}, [props.elmts.size]);
|
||||
|
||||
const move = (event) => {
|
||||
const move = event => {
|
||||
// Only run the function if nothing else if actively focused
|
||||
if (document.activeElement.tagName.toLowerCase() !== "body") { return }
|
||||
if (document.activeElement.tagName.toLowerCase() !== "body") {
|
||||
return;
|
||||
}
|
||||
|
||||
let diff = 0;
|
||||
let moveFocus = 0;
|
||||
@ -121,8 +127,8 @@ const Posters = (props) => {
|
||||
diff = -1;
|
||||
break;
|
||||
case "k":
|
||||
diff = -1*postersPerRow;
|
||||
moveFocus = -1*posterHeight;
|
||||
diff = -1 * postersPerRow;
|
||||
moveFocus = -1 * posterHeight;
|
||||
break;
|
||||
case "j":
|
||||
diff = postersPerRow;
|
||||
@ -138,8 +144,8 @@ const Posters = (props) => {
|
||||
var newIdx = idx + diff;
|
||||
|
||||
// Handle edge cases
|
||||
if (newIdx > props.elmts.size -1) {
|
||||
newIdx = props.elmts.size -1;
|
||||
if (newIdx > props.elmts.size - 1) {
|
||||
newIdx = props.elmts.size - 1;
|
||||
} else if (newIdx < 0) {
|
||||
newIdx = 0;
|
||||
}
|
||||
@ -152,31 +158,36 @@ const Posters = (props) => {
|
||||
if (moveFocus !== 0) {
|
||||
window.scrollBy(0, moveFocus);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const posterCount = useCallback(node => {
|
||||
if (node === null) { return }
|
||||
if (node === null) {
|
||||
return;
|
||||
}
|
||||
const parentWidth = node.getBoundingClientRect().width;
|
||||
const childContainer = node.getElementsByClassName("img-thumbnail");
|
||||
let childWidth = 0;
|
||||
let posterHeight = 0;
|
||||
if ((childContainer !== null) && (childContainer.item(0) !== null)) {
|
||||
if (childContainer !== null && childContainer.item(0) !== null) {
|
||||
const child = childContainer.item(0);
|
||||
childWidth = child.getBoundingClientRect().width + child.getBoundingClientRect().left;
|
||||
childWidth =
|
||||
child.getBoundingClientRect().width +
|
||||
child.getBoundingClientRect().left;
|
||||
posterHeight = child.getBoundingClientRect().height;
|
||||
}
|
||||
|
||||
let numberPerRow = (childWidth >= parentWidth) ? 1 : Math.floor(parentWidth/childWidth);
|
||||
let numberPerRow =
|
||||
childWidth >= parentWidth ? 1 : Math.floor(parentWidth / childWidth);
|
||||
setPostersPerRow(numberPerRow);
|
||||
setPosterHeight(posterHeight);
|
||||
})
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
document.onkeypress = move;
|
||||
return () => {
|
||||
document.onkeypress = null;
|
||||
}
|
||||
}, [move])
|
||||
};
|
||||
}, [move]);
|
||||
|
||||
if (props.elmts.size === 0) {
|
||||
return (
|
||||
@ -192,12 +203,15 @@ const Posters = (props) => {
|
||||
className="poster-list d-flex flex-column flex-sm-row flex-sm-wrap justify-content-around"
|
||||
dataLength={size}
|
||||
next={loadMore}
|
||||
hasMore={(size !== props.elmts.size)}
|
||||
hasMore={size !== props.elmts.size}
|
||||
loader={<Loader />}
|
||||
>
|
||||
{props.elmts.slice(0, size).toIndexedSeq().map(function(el, index) {
|
||||
{props.elmts
|
||||
.slice(0, size)
|
||||
.toIndexedSeq()
|
||||
.map(function(el, index) {
|
||||
const imdbId = el.get("imdb_id");
|
||||
const selected = (imdbId === props.selectedImdbId) ? true : false;
|
||||
const selected = imdbId === props.selectedImdbId ? true : false;
|
||||
|
||||
return (
|
||||
<Poster
|
||||
@ -207,17 +221,17 @@ const Posters = (props) => {
|
||||
onClick={() => props.selectPoster(imdbId)}
|
||||
onDoubleClick={() => props.onDoubleClick(imdbId)}
|
||||
/>
|
||||
)
|
||||
} ,this)}
|
||||
);
|
||||
}, this)}
|
||||
</InfiniteScroll>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
Posters.propTypes = {
|
||||
elmts: PropTypes.instanceOf(OrderedMap),
|
||||
selectedImdbId: PropTypes.string,
|
||||
loading: PropTypes.bool.isRequired,
|
||||
onDoubleClick: PropTypes.func,
|
||||
onKeyEnter: PropTypes.func,
|
||||
selectPoster: PropTypes.func,
|
||||
selectPoster: PropTypes.func
|
||||
};
|
||||
|
@ -1,14 +1,9 @@
|
||||
import React from "react"
|
||||
import Loading from "react-loading"
|
||||
import React from "react";
|
||||
import Loading from "react-loading";
|
||||
|
||||
const Loader = () => (
|
||||
<div className="col-12 col-md-6 offset-md-3">
|
||||
<Loading
|
||||
type="bars"
|
||||
height={"100%"}
|
||||
width={"100%"}
|
||||
color="#EBEBEB"
|
||||
/>
|
||||
<Loading type="bars" height={"100%"} width={"100%"} color="#EBEBEB" />
|
||||
</div>
|
||||
);
|
||||
export default Loader;
|
||||
|
@ -1,19 +1,22 @@
|
||||
import React from "react"
|
||||
import Loader from "../loader/loader"
|
||||
import PropTypes from "prop-types"
|
||||
import { Map, List } from "immutable"
|
||||
import React from "react";
|
||||
import Loader from "../loader/loader";
|
||||
import PropTypes from "prop-types";
|
||||
import { Map, List } from "immutable";
|
||||
|
||||
// TODO: udpate this
|
||||
import { OverlayTrigger, Tooltip } from "react-bootstrap"
|
||||
import { OverlayTrigger, Tooltip } from "react-bootstrap";
|
||||
|
||||
const Modules = (props) => {
|
||||
const Modules = props => {
|
||||
if (props.isLoading) {
|
||||
return <Loader />
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="row">
|
||||
{props.modules && props.modules.keySeq().map((value, key) => (
|
||||
{props.modules &&
|
||||
props.modules
|
||||
.keySeq()
|
||||
.map((value, key) => (
|
||||
<ModulesByVideoType
|
||||
key={key}
|
||||
videoType={value}
|
||||
@ -22,17 +25,16 @@ const Modules = (props) => {
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
Modules.propTypes = {
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
modules: PropTypes.instanceOf(Map),
|
||||
modules: PropTypes.instanceOf(Map)
|
||||
};
|
||||
export default Modules;
|
||||
|
||||
const capitalize = (string) =>
|
||||
string.charAt(0).toUpperCase() + string.slice(1);
|
||||
const capitalize = string => string.charAt(0).toUpperCase() + string.slice(1);
|
||||
|
||||
const ModulesByVideoType = (props) => (
|
||||
const ModulesByVideoType = props => (
|
||||
<div className="col-12 col-md-6">
|
||||
<div className="card mb-3">
|
||||
<div className="card-header">
|
||||
@ -40,11 +42,7 @@ const ModulesByVideoType = (props) => (
|
||||
</div>
|
||||
<div className="card-body">
|
||||
{props.data.keySeq().map((value, key) => (
|
||||
<ModuleByType
|
||||
key={key}
|
||||
type={value}
|
||||
data={props.data.get(value)}
|
||||
/>
|
||||
<ModuleByType key={key} type={value} data={props.data.get(value)} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
@ -52,22 +50,16 @@ const ModulesByVideoType = (props) => (
|
||||
);
|
||||
ModulesByVideoType.propTypes = {
|
||||
videoType: PropTypes.string.isRequired,
|
||||
data: PropTypes.instanceOf(Map),
|
||||
data: PropTypes.instanceOf(Map)
|
||||
};
|
||||
|
||||
const ModuleByType = (props) => (
|
||||
const ModuleByType = props => (
|
||||
<div>
|
||||
<h4>{capitalize(props.type)}</h4>
|
||||
<table className="table">
|
||||
<tbody>
|
||||
{props.data.map(function(value, key) {
|
||||
return (
|
||||
<Module
|
||||
key={key}
|
||||
type={key}
|
||||
data={value}
|
||||
/>
|
||||
);
|
||||
return <Module key={key} type={key} data={value} />;
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
@ -75,41 +67,43 @@ const ModuleByType = (props) => (
|
||||
);
|
||||
ModuleByType.propTypes = {
|
||||
type: PropTypes.string.isRequired,
|
||||
data: PropTypes.instanceOf(List),
|
||||
data: PropTypes.instanceOf(List)
|
||||
};
|
||||
|
||||
const Module = (props) => {
|
||||
const Module = props => {
|
||||
let iconClass, prettyStatus, badgeClass;
|
||||
const name = props.data.get("name");
|
||||
|
||||
switch(props.data.get("status")) {
|
||||
switch (props.data.get("status")) {
|
||||
case "ok":
|
||||
iconClass = "fa fa-check-circle"
|
||||
badgeClass = "badge badge-pill badge-success"
|
||||
prettyStatus = "OK"
|
||||
iconClass = "fa fa-check-circle";
|
||||
badgeClass = "badge badge-pill badge-success";
|
||||
prettyStatus = "OK";
|
||||
break;
|
||||
case "fail":
|
||||
iconClass = "fa fa-times-circle"
|
||||
badgeClass = "badge badge-pill badge-danger"
|
||||
prettyStatus = "Fail"
|
||||
iconClass = "fa fa-times-circle";
|
||||
badgeClass = "badge badge-pill badge-danger";
|
||||
prettyStatus = "Fail";
|
||||
break;
|
||||
case "not_implemented":
|
||||
iconClass = "fa fa-question-circle"
|
||||
badgeClass = "badge badge-pill badge-default"
|
||||
prettyStatus = "Not implemented"
|
||||
iconClass = "fa fa-question-circle";
|
||||
badgeClass = "badge badge-pill badge-default";
|
||||
prettyStatus = "Not implemented";
|
||||
break;
|
||||
default:
|
||||
iconClass = "fa fa-question-circle"
|
||||
badgeClass = "badge badge-pill badge-warning"
|
||||
prettyStatus = "Unknown"
|
||||
iconClass = "fa fa-question-circle";
|
||||
badgeClass = "badge badge-pill badge-warning";
|
||||
prettyStatus = "Unknown";
|
||||
}
|
||||
|
||||
const tooltip = (
|
||||
<Tooltip id={`tooltip-status-${name}`}>
|
||||
<p><span className={badgeClass}>Status: {prettyStatus}</span></p>
|
||||
{props.data.get("error") !== "" &&
|
||||
<p>
|
||||
<span className={badgeClass}>Status: {prettyStatus}</span>
|
||||
</p>
|
||||
{props.data.get("error") !== "" && (
|
||||
<p>Error: {props.data.get("error")}</p>
|
||||
}
|
||||
)}
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@ -125,7 +119,7 @@ const Module = (props) => {
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
Module.propTypes = {
|
||||
data: PropTypes.instanceOf(Map),
|
||||
};
|
||||
Module.propTypes = {
|
||||
data: PropTypes.instanceOf(Map)
|
||||
};
|
||||
|
@ -1,36 +1,41 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { OrderedMap, Map } from "immutable"
|
||||
import { connect } from "react-redux"
|
||||
import { selectMovie, updateFilter, movieWishlistToggle } from "../../actions/movies"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { OrderedMap, Map } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
import {
|
||||
selectMovie,
|
||||
updateFilter,
|
||||
movieWishlistToggle
|
||||
} from "../../actions/movies";
|
||||
|
||||
import ListDetails from "../list/details"
|
||||
import ListPosters from "../list/posters"
|
||||
import ListDetails from "../list/details";
|
||||
import ListPosters from "../list/posters";
|
||||
|
||||
import { inLibrary, isWishlisted } from "../../utils"
|
||||
import { inLibrary, isWishlisted } from "../../utils";
|
||||
|
||||
import { ShowMore } from "../buttons/showMore"
|
||||
import { ShowMore } from "../buttons/showMore";
|
||||
|
||||
import { MovieSubtitlesButton } from "./subtitlesButton"
|
||||
import { MovieTorrentsButton } from "./torrentsButton"
|
||||
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"),
|
||||
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,
|
||||
selectMovie,
|
||||
updateFilter,
|
||||
movieWishlistToggle
|
||||
};
|
||||
|
||||
const MovieList = (props) => {
|
||||
const MovieList = props => {
|
||||
let selectedMovie = Map();
|
||||
if (props.movies !== undefined &&
|
||||
props.movies.has(props.selectedImdbId)) {
|
||||
if (props.movies !== undefined && props.movies.has(props.selectedImdbId)) {
|
||||
selectedMovie = props.movies.get(props.selectedImdbId);
|
||||
}
|
||||
|
||||
@ -45,15 +50,24 @@ const MovieList = (props) => {
|
||||
updateFilter={props.updateFilter}
|
||||
filter={props.filter}
|
||||
onClick={props.selectMovie}
|
||||
onDoubleClick={function() { return; }}
|
||||
onKeyEnter={function() { return; }}
|
||||
onDoubleClick={function() {
|
||||
return;
|
||||
}}
|
||||
onKeyEnter={function() {
|
||||
return;
|
||||
}}
|
||||
params={props.match.params}
|
||||
loading={props.loading}
|
||||
/>
|
||||
<ListDetails
|
||||
data={selectedMovie}
|
||||
loading={props.loading}
|
||||
wishlist={() => props.movieWishlistToggle(selectedMovie.get("imdb_id"), isWishlisted(selectedMovie))}
|
||||
wishlist={() =>
|
||||
props.movieWishlistToggle(
|
||||
selectedMovie.get("imdb_id"),
|
||||
isWishlisted(selectedMovie)
|
||||
)
|
||||
}
|
||||
>
|
||||
<ShowMore
|
||||
id={selectedMovie.get("imdb_id")}
|
||||
@ -75,7 +89,7 @@ const MovieList = (props) => {
|
||||
</ListDetails>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
MovieList.propTypes = {
|
||||
movies: PropTypes.instanceOf(OrderedMap),
|
||||
exploreOptions: PropTypes.instanceOf(Map),
|
||||
@ -85,7 +99,7 @@ MovieList.propTypes = {
|
||||
updateFilter: PropTypes.func,
|
||||
movieWishlistToggle: PropTypes.func,
|
||||
selectMovie: PropTypes.func,
|
||||
match: PropTypes.object,
|
||||
match: PropTypes.object
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(MovieList);
|
||||
|
@ -1,11 +1,11 @@
|
||||
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"
|
||||
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 mapStateToProps = state => ({
|
||||
isExplorerFetched: state.movieStore.get("exploreOptions").size !== 0
|
||||
});
|
||||
const mapDispatchToProps = { fetchMovies, getMovieExploreOptions };
|
||||
|
||||
@ -17,7 +17,9 @@ const MoviesRoute = ({
|
||||
...otherProps
|
||||
}) => {
|
||||
return (
|
||||
<Route {...otherProps} render={(props) => {
|
||||
<Route
|
||||
{...otherProps}
|
||||
render={props => {
|
||||
let fetchUrl = "";
|
||||
switch (props.match.path) {
|
||||
case "/movies/polochon":
|
||||
@ -33,9 +35,11 @@ const MoviesRoute = ({
|
||||
if (!isExplorerFetched) {
|
||||
getMovieExploreOptions();
|
||||
}
|
||||
fetchUrl = "/movies/explore?source=" +
|
||||
fetchUrl =
|
||||
"/movies/explore?source=" +
|
||||
encodeURI(props.match.params.source) +
|
||||
"&category=" + encodeURI(props.match.params.category);
|
||||
"&category=" +
|
||||
encodeURI(props.match.params.category);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -45,16 +49,17 @@ const MoviesRoute = ({
|
||||
fetchMovies(fetchUrl);
|
||||
}
|
||||
|
||||
return <Component {...props} />
|
||||
}} />
|
||||
)
|
||||
}
|
||||
return <Component {...props} />;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
MoviesRoute.propTypes = {
|
||||
component: PropTypes.func,
|
||||
match: PropTypes.object,
|
||||
isExplorerFetched: PropTypes.bool.isRequired,
|
||||
fetchMovies: PropTypes.func.isRequired,
|
||||
getMovieExploreOptions: PropTypes.func.isRequired,
|
||||
getMovieExploreOptions: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(MoviesRoute);
|
||||
|
@ -1,18 +1,18 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { List } from "immutable"
|
||||
import { connect } from "react-redux"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { searchMovieSubtitles } from "../../actions/subtitles"
|
||||
import { searchMovieSubtitles } from "../../actions/subtitles";
|
||||
|
||||
import { SubtitlesButton } from "../buttons/subtitles"
|
||||
import { SubtitlesButton } from "../buttons/subtitles";
|
||||
|
||||
const movieSubtitlesButton = ({
|
||||
inLibrary,
|
||||
imdbId,
|
||||
searching,
|
||||
searchMovieSubtitles,
|
||||
subtitles,
|
||||
subtitles
|
||||
}) => (
|
||||
<SubtitlesButton
|
||||
inLibrary={inLibrary}
|
||||
@ -20,14 +20,16 @@ const movieSubtitlesButton = ({
|
||||
subtitles={subtitles}
|
||||
search={() => searchMovieSubtitles(imdbId)}
|
||||
/>
|
||||
)
|
||||
);
|
||||
|
||||
movieSubtitlesButton.propTypes = {
|
||||
searching: PropTypes.bool,
|
||||
inLibrary: PropTypes.bool,
|
||||
imdbId: PropTypes.string,
|
||||
searchMovieSubtitles: PropTypes.func,
|
||||
subtitles: PropTypes.instanceOf(List),
|
||||
}
|
||||
subtitles: PropTypes.instanceOf(List)
|
||||
};
|
||||
|
||||
export const MovieSubtitlesButton = connect(null, {searchMovieSubtitles})(movieSubtitlesButton);
|
||||
export const MovieSubtitlesButton = connect(null, { searchMovieSubtitles })(
|
||||
movieSubtitlesButton
|
||||
);
|
||||
|
@ -1,17 +1,17 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { List } from "immutable"
|
||||
import { connect } from "react-redux"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { getMovieDetails } from "../../actions/movies"
|
||||
import { TorrentsButton } from "../buttons/torrents"
|
||||
import { getMovieDetails } from "../../actions/movies";
|
||||
import { TorrentsButton } from "../buttons/torrents";
|
||||
|
||||
const movieTorrentsButton = ({
|
||||
torrents,
|
||||
imdbId,
|
||||
title,
|
||||
searching,
|
||||
getMovieDetails,
|
||||
getMovieDetails
|
||||
}) => (
|
||||
<TorrentsButton
|
||||
torrents={torrents}
|
||||
@ -19,13 +19,15 @@ const movieTorrentsButton = ({
|
||||
search={() => 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,
|
||||
getMovieDetails: PropTypes.func
|
||||
};
|
||||
|
||||
export const MovieTorrentsButton = connect(null, {getMovieDetails})(movieTorrentsButton);
|
||||
export const MovieTorrentsButton = connect(null, { getMovieDetails })(
|
||||
movieTorrentsButton
|
||||
);
|
||||
|
@ -1,31 +1,41 @@
|
||||
import React, { useState } from "react"
|
||||
import { Route } from "react-router-dom"
|
||||
import { connect } from "react-redux"
|
||||
import { LinkContainer } from "react-router-bootstrap"
|
||||
import PropTypes from "prop-types"
|
||||
import React, { useState } from "react";
|
||||
import { Route } from "react-router-dom";
|
||||
import { connect } from "react-redux";
|
||||
import { LinkContainer } from "react-router-bootstrap";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import Nav from "react-bootstrap/Nav"
|
||||
import Navbar from "react-bootstrap/Navbar"
|
||||
import NavDropdown from "react-bootstrap/NavDropdown"
|
||||
import Nav from "react-bootstrap/Nav";
|
||||
import Navbar from "react-bootstrap/Navbar";
|
||||
import NavDropdown from "react-bootstrap/NavDropdown";
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const mapStateToProps = state => {
|
||||
let torrentCount = 0;
|
||||
if (state.torrentStore.has("torrents") && state.torrentStore.get("torrents") !== undefined) {
|
||||
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,
|
||||
}
|
||||
torrentCount: torrentCount
|
||||
};
|
||||
};
|
||||
|
||||
const AppNavBar = (props) => {
|
||||
const AppNavBar = props => {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
return (
|
||||
<Navbar fixed="top" collapseOnSelect bg="dark" variant="dark"
|
||||
expanded={expanded} expand="sm" onToggle={() => setExpanded(!expanded)}>
|
||||
<Navbar
|
||||
fixed="top"
|
||||
collapseOnSelect
|
||||
bg="dark"
|
||||
variant="dark"
|
||||
expanded={expanded}
|
||||
expand="sm"
|
||||
onToggle={() => setExpanded(!expanded)}
|
||||
>
|
||||
<LinkContainer to="/">
|
||||
<Navbar.Brand>Canapé</Navbar.Brand>
|
||||
</LinkContainer>
|
||||
@ -38,36 +48,39 @@ const AppNavBar = (props) => {
|
||||
<TorrentsDropdown torrentsCount={props.torrentCount} />
|
||||
</Nav>
|
||||
<Nav>
|
||||
<Route path="/movies" render={(props) =>
|
||||
<Route
|
||||
path="/movies"
|
||||
render={props => (
|
||||
<Search
|
||||
placeholder="Search movies"
|
||||
path='/movies/search'
|
||||
path="/movies/search"
|
||||
history={props.history}
|
||||
setExpanded={setExpanded}
|
||||
/>
|
||||
}/>
|
||||
<Route path="/shows" render={(props) =>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path="/shows"
|
||||
render={props => (
|
||||
<Search
|
||||
placeholder="Search shows"
|
||||
path='/shows/search'
|
||||
path="/shows/search"
|
||||
history={props.history}
|
||||
setExpanded={setExpanded}
|
||||
/>
|
||||
}/>
|
||||
<UserDropdown
|
||||
username={props.username}
|
||||
isAdmin={props.isAdmin}
|
||||
)}
|
||||
/>
|
||||
<UserDropdown username={props.username} isAdmin={props.isAdmin} />
|
||||
</Nav>
|
||||
</Navbar.Collapse>
|
||||
</Navbar>
|
||||
);
|
||||
}
|
||||
};
|
||||
AppNavBar.propTypes = {
|
||||
torrentCount: PropTypes.number.isRequired,
|
||||
username: PropTypes.string.isRequired,
|
||||
isAdmin: PropTypes.bool.isRequired,
|
||||
history: PropTypes.object,
|
||||
history: PropTypes.object
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(AppNavBar);
|
||||
@ -75,30 +88,30 @@ export default connect(mapStateToProps)(AppNavBar);
|
||||
const Search = ({ path, placeholder, setExpanded, history }) => {
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
const handleSearch = (ev) => {
|
||||
const handleSearch = ev => {
|
||||
ev.preventDefault();
|
||||
history.push(`${path}/${encodeURI(search)}`);
|
||||
setExpanded(false);
|
||||
}
|
||||
};
|
||||
|
||||
return(
|
||||
return (
|
||||
<div className="navbar-form navbar-right">
|
||||
<form className="input-group" onSubmit={(ev) => handleSearch(ev)}>
|
||||
<form className="input-group" onSubmit={ev => handleSearch(ev)}>
|
||||
<input
|
||||
className="form-control"
|
||||
placeholder={placeholder}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
onChange={e => setSearch(e.target.value)}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
Search.propTypes = {
|
||||
placeholder: PropTypes.string.isRequired,
|
||||
setExpanded: PropTypes.func.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
history: PropTypes.object,
|
||||
history: PropTypes.object
|
||||
};
|
||||
|
||||
const MoviesDropdown = () => (
|
||||
@ -110,7 +123,7 @@ const MoviesDropdown = () => (
|
||||
<NavDropdown.Item>My movies</NavDropdown.Item>
|
||||
</LinkContainer>
|
||||
</NavDropdown>
|
||||
)
|
||||
);
|
||||
|
||||
const ShowsDropdown = () => (
|
||||
<NavDropdown title="Shows" id="navbar-shows-dropdown">
|
||||
@ -123,14 +136,14 @@ const ShowsDropdown = () => (
|
||||
</NavDropdown>
|
||||
);
|
||||
|
||||
const UserDropdown = (props) => (
|
||||
const UserDropdown = props => (
|
||||
<Nav>
|
||||
<NavDropdown title={props.username} alignRight>
|
||||
{props.isAdmin &&
|
||||
{props.isAdmin && (
|
||||
<LinkContainer to="/admin">
|
||||
<NavDropdown.Item>Admin Panel</NavDropdown.Item>
|
||||
</LinkContainer>
|
||||
}
|
||||
)}
|
||||
<LinkContainer to="/users/profile">
|
||||
<NavDropdown.Item>Profile</NavDropdown.Item>
|
||||
</LinkContainer>
|
||||
@ -142,10 +155,10 @@ const UserDropdown = (props) => (
|
||||
</LinkContainer>
|
||||
</NavDropdown>
|
||||
</Nav>
|
||||
)
|
||||
);
|
||||
UserDropdown.propTypes = {
|
||||
username: PropTypes.string.isRequired,
|
||||
isAdmin: PropTypes.bool.isRequired,
|
||||
isAdmin: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
const WishlistDropdown = () => (
|
||||
@ -159,9 +172,9 @@ const WishlistDropdown = () => (
|
||||
</NavDropdown>
|
||||
);
|
||||
|
||||
const TorrentsDropdown = (props) => {
|
||||
const title = (<TorrentsDropdownTitle torrentsCount={props.torrentsCount} />)
|
||||
return(
|
||||
const TorrentsDropdown = props => {
|
||||
const title = <TorrentsDropdownTitle torrentsCount={props.torrentsCount} />;
|
||||
return (
|
||||
<NavDropdown title={title} id="navbar-wishlit-dropdown">
|
||||
<LinkContainer to="/torrents/list">
|
||||
<NavDropdown.Item>Downloads</NavDropdown.Item>
|
||||
@ -171,22 +184,27 @@ const TorrentsDropdown = (props) => {
|
||||
</LinkContainer>
|
||||
</NavDropdown>
|
||||
);
|
||||
}
|
||||
};
|
||||
TorrentsDropdown.propTypes = { torrentsCount: PropTypes.number.isRequired };
|
||||
|
||||
const TorrentsDropdownTitle = (props) => {
|
||||
const TorrentsDropdownTitle = props => {
|
||||
if (props.torrentsCount === 0) {
|
||||
return (
|
||||
<span>Torrents</span>
|
||||
);
|
||||
return <span>Torrents</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<span> Torrents
|
||||
<span>
|
||||
<span className="badge badge-info badge-pill">{props.torrentsCount}</span>
|
||||
{" "}
|
||||
Torrents
|
||||
<span>
|
||||
{" "}
|
||||
<span className="badge badge-info badge-pill">
|
||||
{props.torrentsCount}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
TorrentsDropdownTitle.propTypes = { torrentsCount: PropTypes.number.isRequired };
|
||||
};
|
||||
TorrentsDropdownTitle.propTypes = {
|
||||
torrentsCount: PropTypes.number.isRequired
|
||||
};
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React, { useState } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { connect } from "react-redux"
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { Toast } from "react-bootstrap"
|
||||
import { Toast } from "react-bootstrap";
|
||||
|
||||
import { removeNotification } from "../../actions/notifications"
|
||||
import { removeNotification } from "../../actions/notifications";
|
||||
|
||||
const NotificationConnected = ({
|
||||
id,
|
||||
@ -14,39 +14,30 @@ const NotificationConnected = ({
|
||||
imageUrl,
|
||||
autohide,
|
||||
delay,
|
||||
removeNotification,
|
||||
removeNotification
|
||||
}) => {
|
||||
const [show, setShow] = useState(true)
|
||||
const [show, setShow] = useState(true);
|
||||
|
||||
const hide = () => {
|
||||
setShow(false);
|
||||
setTimeout(() => removeNotification(id), 200);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Toast
|
||||
show={show}
|
||||
onClose={hide}
|
||||
autohide={autohide}
|
||||
delay={delay}
|
||||
>
|
||||
<Toast show={show} onClose={hide} autohide={autohide} delay={delay}>
|
||||
<Toast.Header>
|
||||
{icon !== "" &&
|
||||
<i className={`fa fa-${icon} mr-2`} />
|
||||
}
|
||||
{icon !== "" && <i className={`fa fa-${icon} mr-2`} />}
|
||||
<strong className="mr-auto">{title}</strong>
|
||||
</Toast.Header>
|
||||
<Toast.Body>
|
||||
{message !== "" &&
|
||||
<span>{message}</span>
|
||||
}
|
||||
{imageUrl !== "" &&
|
||||
{message !== "" && <span>{message}</span>}
|
||||
{imageUrl !== "" && (
|
||||
<img src={imageUrl} className="img-fluid mt-2 mr-auto" />
|
||||
}
|
||||
)}
|
||||
</Toast.Body>
|
||||
</Toast>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
NotificationConnected.propTypes = {
|
||||
id: PropTypes.string,
|
||||
icon: PropTypes.string,
|
||||
@ -55,7 +46,7 @@ NotificationConnected.propTypes = {
|
||||
imageUrl: PropTypes.string,
|
||||
autohide: PropTypes.bool,
|
||||
delay: PropTypes.number,
|
||||
removeNotification: PropTypes.func,
|
||||
removeNotification: PropTypes.func
|
||||
};
|
||||
|
||||
NotificationConnected.defaultProps = {
|
||||
@ -64,7 +55,9 @@ NotificationConnected.defaultProps = {
|
||||
icon: "",
|
||||
imageUrl: "",
|
||||
title: "Info",
|
||||
message: "",
|
||||
message: ""
|
||||
};
|
||||
|
||||
export const Notification = connect(null, {removeNotification})(NotificationConnected);
|
||||
export const Notification = connect(null, { removeNotification })(
|
||||
NotificationConnected
|
||||
);
|
||||
|
@ -1,14 +1,14 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { List } from "immutable"
|
||||
import { connect } from "react-redux"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { Notification } from "./notification"
|
||||
import { Notification } from "./notification";
|
||||
|
||||
const NotificationsConnected = ({ notifications }) => {
|
||||
return (
|
||||
<div className="notifications">
|
||||
{notifications.map((el) => (
|
||||
{notifications.map(el => (
|
||||
<Notification
|
||||
key={el.get("id")}
|
||||
id={el.get("id")}
|
||||
@ -21,14 +21,14 @@ const NotificationsConnected = ({ notifications }) => {
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
NotificationsConnected.propTypes = {
|
||||
notifications: PropTypes.instanceOf(List),
|
||||
}
|
||||
notifications: PropTypes.instanceOf(List)
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
notifications: state.notifications,
|
||||
const mapStateToProps = state => ({
|
||||
notifications: state.notifications
|
||||
});
|
||||
|
||||
export const Notifications = connect(mapStateToProps)(NotificationsConnected);
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React, { useState } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { connect } from "react-redux"
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { addPolochon } from "../../actions/polochon"
|
||||
import { addPolochon } from "../../actions/polochon";
|
||||
|
||||
import { PolochonEdit } from "./edit"
|
||||
import { PolochonEdit } from "./edit";
|
||||
|
||||
export const PolochonAddConnected = ({ addPolochon }) => {
|
||||
const [modal, setModal] = useState(false);
|
||||
@ -24,10 +24,10 @@ export const PolochonAddConnected = ({ addPolochon }) => {
|
||||
update={addPolochon}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
PolochonAddConnected.propTypes = {
|
||||
addPolochon: PropTypes.func,
|
||||
addPolochon: PropTypes.func
|
||||
};
|
||||
|
||||
export const PolochonAdd = connect(null, {addPolochon})(PolochonAddConnected);
|
||||
export const PolochonAdd = connect(null, { addPolochon })(PolochonAddConnected);
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React, { useState, useEffect } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import React, { useState, useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { FormModal } from "../forms/modal"
|
||||
import { FormInput } from "../forms/input"
|
||||
import { FormModal } from "../forms/modal";
|
||||
import { FormInput } from "../forms/input";
|
||||
|
||||
export const PolochonEdit = ({
|
||||
show,
|
||||
@ -13,7 +13,7 @@ export const PolochonEdit = ({
|
||||
initialName,
|
||||
initialUrl,
|
||||
initialToken,
|
||||
update,
|
||||
update
|
||||
}) => {
|
||||
const [name, setName] = useState(initialName);
|
||||
const [url, setUrl] = useState(initialUrl);
|
||||
@ -23,7 +23,7 @@ export const PolochonEdit = ({
|
||||
setName(initialName);
|
||||
setUrl(initialUrl);
|
||||
setToken(initialToken);
|
||||
}, [id, initialName, initialUrl, initialToken])
|
||||
}, [id, initialName, initialUrl, initialToken]);
|
||||
|
||||
const handleSubmit = () => {
|
||||
update({ id, name, url, token });
|
||||
@ -42,8 +42,8 @@ export const PolochonEdit = ({
|
||||
<FormInput label="URL" value={url} updateValue={setUrl} />
|
||||
<FormInput label="Token" value={token} updateValue={setToken} />
|
||||
</FormModal>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
PolochonEdit.propTypes = {
|
||||
show: PropTypes.bool,
|
||||
title: PropTypes.string,
|
||||
@ -53,7 +53,7 @@ PolochonEdit.propTypes = {
|
||||
id: PropTypes.string,
|
||||
initialName: PropTypes.string,
|
||||
initialUrl: PropTypes.string,
|
||||
initialToken: PropTypes.string,
|
||||
initialToken: PropTypes.string
|
||||
};
|
||||
PolochonEdit.defaultProps = {
|
||||
id: "",
|
||||
@ -61,5 +61,5 @@ PolochonEdit.defaultProps = {
|
||||
icon: "edit",
|
||||
initialName: "",
|
||||
initialUrl: "",
|
||||
initialToken: "",
|
||||
initialToken: ""
|
||||
};
|
||||
|
@ -1,29 +1,25 @@
|
||||
import React, { useEffect } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { connect } from "react-redux"
|
||||
import { List } from "immutable"
|
||||
import React, { useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { List } from "immutable";
|
||||
|
||||
import { getManagedPolochons } from "../../actions/polochon"
|
||||
import { getManagedPolochons } from "../../actions/polochon";
|
||||
|
||||
import { Polochon } from "./polochon"
|
||||
import { PolochonAdd } from "./add"
|
||||
import { Polochon } from "./polochon";
|
||||
import { PolochonAdd } from "./add";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
managedList: state.polochon.get("managed"),
|
||||
const mapStateToProps = state => ({
|
||||
managedList: state.polochon.get("managed")
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
getManagedPolochons,
|
||||
}
|
||||
|
||||
const PolochonListConnected = ({
|
||||
getManagedPolochons,
|
||||
managedList,
|
||||
}) => {
|
||||
getManagedPolochons
|
||||
};
|
||||
|
||||
const PolochonListConnected = ({ getManagedPolochons, managedList }) => {
|
||||
useEffect(() => {
|
||||
getManagedPolochons();
|
||||
}, [getManagedPolochons])
|
||||
}, [getManagedPolochons]);
|
||||
|
||||
return (
|
||||
<div className="row mb-3">
|
||||
@ -46,11 +42,14 @@ const PolochonListConnected = ({
|
||||
<PolochonAdd />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
PolochonListConnected.propTypes = {
|
||||
getManagedPolochons: PropTypes.func,
|
||||
managedList: PropTypes.instanceOf(List),
|
||||
managedList: PropTypes.instanceOf(List)
|
||||
};
|
||||
|
||||
export const PolochonList = connect(mapStateToProps, mapDispatchToProps)(PolochonListConnected);
|
||||
export const PolochonList = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(PolochonListConnected);
|
||||
|
@ -1,12 +1,12 @@
|
||||
import React, { useState } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { List } from "immutable"
|
||||
import { connect } from "react-redux"
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { PolochonUsers } from "./users"
|
||||
import { PolochonEdit } from "./edit"
|
||||
import { PolochonUsers } from "./users";
|
||||
import { PolochonEdit } from "./edit";
|
||||
|
||||
import { updatePolochon, deletePolochon } from "../../actions/polochon"
|
||||
import { updatePolochon, deletePolochon } from "../../actions/polochon";
|
||||
|
||||
export const PolochonConnected = ({
|
||||
id,
|
||||
@ -16,7 +16,7 @@ export const PolochonConnected = ({
|
||||
authToken,
|
||||
users,
|
||||
updatePolochon,
|
||||
deletePolochon,
|
||||
deletePolochon
|
||||
}) => {
|
||||
const [edit, setEdit] = useState(false);
|
||||
|
||||
@ -25,9 +25,7 @@ export const PolochonConnected = ({
|
||||
<div className="card mb-2">
|
||||
<div className="card-header">
|
||||
{name !== "" ? name : "-"}
|
||||
<small className="ml-1">
|
||||
({url !== "" ? url : "-"})
|
||||
</small>
|
||||
<small className="ml-1">({url !== "" ? url : "-"})</small>
|
||||
<span className="pull-right">
|
||||
<i
|
||||
className="fa fa-edit mr-3 clickable"
|
||||
@ -58,8 +56,8 @@ export const PolochonConnected = ({
|
||||
initialToken={token}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
PolochonConnected.propTypes = {
|
||||
id: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
@ -68,7 +66,9 @@ PolochonConnected.propTypes = {
|
||||
authToken: PropTypes.string,
|
||||
users: PropTypes.instanceOf(List),
|
||||
updatePolochon: PropTypes.func,
|
||||
deletePolochon: PropTypes.func,
|
||||
deletePolochon: PropTypes.func
|
||||
};
|
||||
|
||||
export const Polochon = connect(null, { updatePolochon, deletePolochon })(PolochonConnected);
|
||||
export const Polochon = connect(null, { updatePolochon, deletePolochon })(
|
||||
PolochonConnected
|
||||
);
|
||||
|
@ -1,30 +1,26 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { List } from "immutable"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "immutable";
|
||||
|
||||
export const PolochonSelect = ({
|
||||
value,
|
||||
changeValue,
|
||||
polochonList,
|
||||
}) => {
|
||||
export const PolochonSelect = ({ value, changeValue, polochonList }) => {
|
||||
return (
|
||||
<select
|
||||
className="form-control"
|
||||
value={value}
|
||||
onChange={(e) => changeValue(e.target.options[e.target.selectedIndex].value)}
|
||||
onChange={e =>
|
||||
changeValue(e.target.options[e.target.selectedIndex].value)
|
||||
}
|
||||
>
|
||||
{polochonList.map((el, index) => (
|
||||
<option
|
||||
value={el.get("id")}
|
||||
key={index}>
|
||||
<option value={el.get("id")} key={index}>
|
||||
{el.get("name")} ({el.get("url")})
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
};
|
||||
PolochonSelect.propTypes = {
|
||||
value: PropTypes.string,
|
||||
changeValue: PropTypes.func,
|
||||
polochonList: PropTypes.instanceOf(List),
|
||||
polochonList: PropTypes.instanceOf(List)
|
||||
};
|
||||
|
@ -1,13 +1,13 @@
|
||||
import React, { useState, useEffect } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { connect } from "react-redux"
|
||||
import React, { useState, useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { editPolochonUser } from "../../actions/polochon"
|
||||
import { editPolochonUser } from "../../actions/polochon";
|
||||
|
||||
import Toggle from "react-bootstrap-toggle";
|
||||
|
||||
import { FormModal } from "../forms/modal"
|
||||
import { FormInput } from "../forms/input"
|
||||
import { FormModal } from "../forms/modal";
|
||||
import { FormInput } from "../forms/input";
|
||||
|
||||
export const PolochonUserConnected = ({
|
||||
polochonId,
|
||||
@ -15,7 +15,7 @@ export const PolochonUserConnected = ({
|
||||
name,
|
||||
initialToken,
|
||||
initialActivated,
|
||||
editPolochonUser,
|
||||
editPolochonUser
|
||||
}) => {
|
||||
const [edit, setEdit] = useState(false);
|
||||
const [token, setToken] = useState(initialToken);
|
||||
@ -24,26 +24,22 @@ export const PolochonUserConnected = ({
|
||||
useEffect(() => {
|
||||
setActivated(initialActivated);
|
||||
setToken(initialToken);
|
||||
}, [initialActivated, initialToken])
|
||||
}, [initialActivated, initialToken]);
|
||||
|
||||
const handleSubmit = () => {
|
||||
editPolochonUser({
|
||||
polochonId,
|
||||
id,
|
||||
token,
|
||||
activated,
|
||||
activated
|
||||
});
|
||||
setEdit(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<td>
|
||||
{name}
|
||||
</td>
|
||||
<td>
|
||||
{activated ? "Activated" : "Not activated"}
|
||||
</td>
|
||||
<td>{name}</td>
|
||||
<td>{activated ? "Activated" : "Not activated"}</td>
|
||||
<td>
|
||||
<i className="fa fa-edit ml-2" onClick={() => setEdit(true)} />
|
||||
<FormModal
|
||||
@ -63,20 +59,23 @@ export const PolochonUserConnected = ({
|
||||
active={activated}
|
||||
offstyle="info"
|
||||
handlestyle="secondary"
|
||||
onClick={() => setActivated(!activated)} />
|
||||
onClick={() => setActivated(!activated)}
|
||||
/>
|
||||
</div>
|
||||
</FormModal>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
PolochonUserConnected.propTypes = {
|
||||
polochonId: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
initialToken: PropTypes.string,
|
||||
initialActivated: PropTypes.bool,
|
||||
editPolochonUser: PropTypes.func,
|
||||
editPolochonUser: PropTypes.func
|
||||
};
|
||||
|
||||
export const PolochonUser = connect(null, {editPolochonUser})(PolochonUserConnected);
|
||||
export const PolochonUser = connect(null, { editPolochonUser })(
|
||||
PolochonUserConnected
|
||||
);
|
||||
|
@ -1,11 +1,13 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { List } from "immutable"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "immutable";
|
||||
|
||||
import { PolochonUser } from "./user"
|
||||
import { PolochonUser } from "./user";
|
||||
|
||||
export const PolochonUsers = ({ id, users }) => {
|
||||
if (users === null || users.size === 0) { return null }
|
||||
if (users === null || users.size === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<table className="table border border-light table-dark">
|
||||
@ -29,9 +31,9 @@ export const PolochonUsers = ({ id, users }) => {
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
PolochonUsers.propTypes = {
|
||||
id: PropTypes.string,
|
||||
users: PropTypes.instanceOf(List),
|
||||
users: PropTypes.instanceOf(List)
|
||||
};
|
||||
|
@ -1,22 +1,22 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { Map } from "immutable"
|
||||
import { connect } from "react-redux"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Map } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import Loader from "../loader/loader"
|
||||
import Loader from "../loader/loader";
|
||||
|
||||
import { Fanart } from "./details/fanart"
|
||||
import { Header } from "./details/header"
|
||||
import { SeasonsList } from "./details/seasons"
|
||||
import { Fanart } from "./details/fanart";
|
||||
import { Header } from "./details/header";
|
||||
import { SeasonsList } from "./details/seasons";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
const mapStateToProps = state => ({
|
||||
loading: state.showStore.get("loading"),
|
||||
show: state.showStore.get("show"),
|
||||
})
|
||||
show: state.showStore.get("show")
|
||||
});
|
||||
|
||||
const showDetails = ({ show, loading }) => {
|
||||
if (loading === true) {
|
||||
return (<Loader />);
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
return (
|
||||
@ -28,9 +28,9 @@ const showDetails = ({ show, loading }) => {
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
};
|
||||
showDetails.propTypes = {
|
||||
loading: PropTypes.bool,
|
||||
show: PropTypes.instanceOf(Map),
|
||||
show: PropTypes.instanceOf(Map)
|
||||
};
|
||||
export const ShowDetails = connect(mapStateToProps)(showDetails);
|
||||
|
@ -1,31 +1,35 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { Map } from "immutable"
|
||||
import { connect } from "react-redux"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Map } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { showWishlistToggle } from "../../../actions/shows"
|
||||
import { showWishlistToggle } from "../../../actions/shows";
|
||||
|
||||
import { inLibrary, isEpisodeWishlisted, prettyEpisodeName } from "../../../utils"
|
||||
import {
|
||||
inLibrary,
|
||||
isEpisodeWishlisted,
|
||||
prettyEpisodeName
|
||||
} from "../../../utils";
|
||||
|
||||
import { Plot } from "../../details/plot"
|
||||
import { PolochonMetadata } from "../../details/polochon"
|
||||
import { ReleaseDate } from "../../details/releaseDate"
|
||||
import { Runtime } from "../../details/runtime"
|
||||
import { Title } from "../../details/title"
|
||||
import { Plot } from "../../details/plot";
|
||||
import { PolochonMetadata } from "../../details/polochon";
|
||||
import { ReleaseDate } from "../../details/releaseDate";
|
||||
import { Runtime } from "../../details/runtime";
|
||||
import { Title } from "../../details/title";
|
||||
|
||||
import { DownloadAndStream } from "../../buttons/download"
|
||||
import { ShowMore } from "../../buttons/showMore"
|
||||
import { DownloadAndStream } from "../../buttons/download";
|
||||
import { ShowMore } from "../../buttons/showMore";
|
||||
|
||||
import { EpisodeSubtitlesButton } from "./subtitlesButton"
|
||||
import { EpisodeThumb } from "./episodeThumb"
|
||||
import { EpisodeTorrentsButton } from "./torrentsButton"
|
||||
import { EpisodeSubtitlesButton } from "./subtitlesButton";
|
||||
import { EpisodeThumb } from "./episodeThumb";
|
||||
import { EpisodeTorrentsButton } from "./torrentsButton";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
const mapStateToProps = state => ({
|
||||
trackedSeason: state.showStore.getIn(["show", "tracked_season"], null),
|
||||
trackedEpisode: state.showStore.getIn(["show", "tracked_episode"], null),
|
||||
})
|
||||
trackedEpisode: state.showStore.getIn(["show", "tracked_episode"], null)
|
||||
});
|
||||
|
||||
const episode = (props) => (
|
||||
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">
|
||||
@ -34,20 +38,26 @@ const episode = (props) => (
|
||||
wishlisted={isEpisodeWishlisted(
|
||||
props.data,
|
||||
props.trackedSeason,
|
||||
props.trackedEpisode,
|
||||
props.trackedEpisode
|
||||
)}
|
||||
wishlist={() => props.showWishlistToggle(
|
||||
wishlist={() =>
|
||||
props.showWishlistToggle(
|
||||
isEpisodeWishlisted(props.data),
|
||||
props.data.get("show_imdb_id"),
|
||||
props.data.get("season"),
|
||||
props.data.get("episode"),
|
||||
)}
|
||||
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"))}
|
||||
name={prettyEpisodeName(
|
||||
props.showName,
|
||||
props.data.get("season"),
|
||||
props.data.get("episode")
|
||||
)}
|
||||
url={props.data.get("polochon_url")}
|
||||
subtitles={props.data.get("subtitles")}
|
||||
/>
|
||||
@ -60,7 +70,11 @@ const episode = (props) => (
|
||||
light
|
||||
/>
|
||||
<ShowMore
|
||||
id={prettyEpisodeName(props.showName, props.data.get("season"), props.data.get("episode"))}
|
||||
id={prettyEpisodeName(
|
||||
props.showName,
|
||||
props.data.get("season"),
|
||||
props.data.get("episode")
|
||||
)}
|
||||
inLibrary={inLibrary(props.data)}
|
||||
>
|
||||
<EpisodeTorrentsButton
|
||||
@ -82,13 +96,15 @@ const episode = (props) => (
|
||||
</ShowMore>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
episode.propTypes = {
|
||||
data: PropTypes.instanceOf(Map).isRequired,
|
||||
trackedSeason: PropTypes.number,
|
||||
trackedEpisode: PropTypes.number,
|
||||
showName: PropTypes.string.isRequired,
|
||||
showWishlistToggle: PropTypes.func,
|
||||
showWishlistToggle: PropTypes.func
|
||||
};
|
||||
|
||||
export const Episode = connect(mapStateToProps, {showWishlistToggle})(episode);
|
||||
export const Episode = connect(mapStateToProps, { showWishlistToggle })(
|
||||
episode
|
||||
);
|
||||
|
@ -1,13 +1,15 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
export const EpisodeThumb = ({ url }) => {
|
||||
if (url === "") { return null }
|
||||
if (url === "") {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className="mr-0 mr-lg-2 mb-2 mb-lg-0 episode-thumb">
|
||||
<img src={url} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
EpisodeThumb.propTypes = { url: PropTypes.string };
|
||||
EpisodeThumb.defaultProps = { url: "" };
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
export const Fanart = ({ url }) => (
|
||||
<div className="show-fanart mx-n3 mt-n1">
|
||||
@ -8,7 +8,7 @@ export const Fanart = ({ url }) => (
|
||||
src={url}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
Fanart.propTypes = {
|
||||
url: PropTypes.string,
|
||||
}
|
||||
url: PropTypes.string
|
||||
};
|
||||
|
@ -1,21 +1,21 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { Map } from "immutable"
|
||||
import { connect } from "react-redux"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Map } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { isWishlisted } from "../../../utils"
|
||||
import { isWishlisted } from "../../../utils";
|
||||
|
||||
import { showWishlistToggle } from "../../../actions/shows"
|
||||
import { showWishlistToggle } from "../../../actions/shows";
|
||||
|
||||
import { Plot } from "../../details/plot"
|
||||
import { Rating } from "../../details/rating"
|
||||
import { ReleaseDate } from "../../details/releaseDate"
|
||||
import { Title } from "../../details/title"
|
||||
import { TrackingLabel } from "../../details/tracking"
|
||||
import { Plot } from "../../details/plot";
|
||||
import { Rating } from "../../details/rating";
|
||||
import { ReleaseDate } from "../../details/releaseDate";
|
||||
import { Title } from "../../details/title";
|
||||
import { TrackingLabel } from "../../details/tracking";
|
||||
|
||||
import { ImdbBadge } from "../../buttons/imdb"
|
||||
import { ImdbBadge } from "../../buttons/imdb";
|
||||
|
||||
export const header = (props) => (
|
||||
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">
|
||||
@ -30,9 +30,12 @@ export const header = (props) => (
|
||||
<Title
|
||||
title={props.data.get("title")}
|
||||
wishlisted={isWishlisted(props.data)}
|
||||
wishlist={() => props.showWishlistToggle(
|
||||
isWishlisted(props.data), props.data.get("imdb_id"),
|
||||
)}
|
||||
wishlist={() =>
|
||||
props.showWishlistToggle(
|
||||
isWishlisted(props.data),
|
||||
props.data.get("imdb_id")
|
||||
)
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
<p className="card-text">
|
||||
@ -61,7 +64,7 @@ export const header = (props) => (
|
||||
);
|
||||
header.propTypes = {
|
||||
data: PropTypes.instanceOf(Map),
|
||||
showWishlistToggle: PropTypes.func,
|
||||
showWishlistToggle: PropTypes.func
|
||||
};
|
||||
|
||||
export const Header = connect(null, {showWishlistToggle})(header);
|
||||
export const Header = connect(null, { showWishlistToggle })(header);
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React, { useState } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { Map } from "immutable"
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Map } from "immutable";
|
||||
|
||||
import { Episode } from "./episode"
|
||||
import { Episode } from "./episode";
|
||||
|
||||
export const Season = (props) => {
|
||||
export const Season = props => {
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
return (
|
||||
@ -12,8 +12,13 @@ export const Season = (props) => {
|
||||
<div className="card-header clickable" onClick={() => setShow(!show)}>
|
||||
<h5 className="m-0">
|
||||
Season {props.season}
|
||||
<small className="text-primary"> — ({props.data.toList().size} episodes)</small>
|
||||
<i className={`float-right fa fa-chevron-${show ? "down" : "left"}`}></i>
|
||||
<small className="text-primary">
|
||||
{" "}
|
||||
— ({props.data.toList().size} episodes)
|
||||
</small>
|
||||
<i
|
||||
className={`float-right fa fa-chevron-${show ? "down" : "left"}`}
|
||||
></i>
|
||||
</h5>
|
||||
</div>
|
||||
<div className={`card-body ${show ? "d-flex flex-column" : "d-none"}`}>
|
||||
@ -29,12 +34,12 @@ export const Season = (props) => {
|
||||
getEpisodeDetails={props.getEpisodeDetails}
|
||||
refreshSubtitles={props.refreshSubtitles}
|
||||
/>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
Season.propTypes = {
|
||||
data: PropTypes.instanceOf(Map),
|
||||
season: PropTypes.number,
|
||||
@ -42,5 +47,5 @@ Season.propTypes = {
|
||||
addToWishlist: PropTypes.func,
|
||||
addTorrent: PropTypes.func,
|
||||
refreshSubtitles: PropTypes.func,
|
||||
getEpisodeDetails: PropTypes.func,
|
||||
getEpisodeDetails: PropTypes.func
|
||||
};
|
||||
|
@ -1,13 +1,18 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { Map } from "immutable"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Map } from "immutable";
|
||||
|
||||
import { Season } from "./season"
|
||||
import { Season } from "./season";
|
||||
|
||||
export const SeasonsList = (props) => (
|
||||
export const SeasonsList = props => (
|
||||
<div className="col col-12 col-md-10 offset-md-1">
|
||||
{props.data.get("seasons").entrySeq().map(function([season, data]) {
|
||||
if (season === 0) { return null }
|
||||
{props.data
|
||||
.get("seasons")
|
||||
.entrySeq()
|
||||
.map(function([season, data]) {
|
||||
if (season === 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Season
|
||||
key={`season-list-key-${season}`}
|
||||
@ -28,5 +33,5 @@ SeasonsList.propTypes = {
|
||||
addToWishlist: PropTypes.func,
|
||||
addTorrent: PropTypes.func,
|
||||
refreshSubtitles: PropTypes.func,
|
||||
getEpisodeDetails: PropTypes.func,
|
||||
getEpisodeDetails: PropTypes.func
|
||||
};
|
||||
|
@ -1,11 +1,11 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { List } from "immutable"
|
||||
import { connect } from "react-redux"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { searchEpisodeSubtitles } from "../../../actions/subtitles"
|
||||
import { searchEpisodeSubtitles } from "../../../actions/subtitles";
|
||||
|
||||
import { SubtitlesButton } from "../../buttons/subtitles"
|
||||
import { SubtitlesButton } from "../../buttons/subtitles";
|
||||
|
||||
const episodeSubtitlesButton = ({
|
||||
inLibrary,
|
||||
@ -14,7 +14,7 @@ const episodeSubtitlesButton = ({
|
||||
episode,
|
||||
searching,
|
||||
searchEpisodeSubtitles,
|
||||
subtitles,
|
||||
subtitles
|
||||
}) => (
|
||||
<SubtitlesButton
|
||||
subtitles={subtitles}
|
||||
@ -22,7 +22,7 @@ const episodeSubtitlesButton = ({
|
||||
searching={searching}
|
||||
search={() => searchEpisodeSubtitles(imdbId, season, episode)}
|
||||
/>
|
||||
)
|
||||
);
|
||||
|
||||
episodeSubtitlesButton.propTypes = {
|
||||
inLibrary: PropTypes.bool,
|
||||
@ -31,7 +31,9 @@ episodeSubtitlesButton.propTypes = {
|
||||
season: PropTypes.number,
|
||||
episode: PropTypes.number,
|
||||
searchEpisodeSubtitles: PropTypes.func,
|
||||
subtitles: PropTypes.instanceOf(List),
|
||||
}
|
||||
subtitles: PropTypes.instanceOf(List)
|
||||
};
|
||||
|
||||
export const EpisodeSubtitlesButton = connect(null, {searchEpisodeSubtitles})(episodeSubtitlesButton);
|
||||
export const EpisodeSubtitlesButton = connect(null, { searchEpisodeSubtitles })(
|
||||
episodeSubtitlesButton
|
||||
);
|
||||
|
@ -1,11 +1,11 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { connect } from "react-redux"
|
||||
import { List } from "immutable"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { List } from "immutable";
|
||||
|
||||
import { getEpisodeDetails } from "../../../actions/shows"
|
||||
import { TorrentsButton } from "../../buttons/torrents"
|
||||
import { prettyEpisodeName } from "../../../utils"
|
||||
import { getEpisodeDetails } from "../../../actions/shows";
|
||||
import { TorrentsButton } from "../../buttons/torrents";
|
||||
import { prettyEpisodeName } from "../../../utils";
|
||||
|
||||
const episodeTorrentsButton = ({
|
||||
torrents,
|
||||
@ -14,17 +14,17 @@ const episodeTorrentsButton = ({
|
||||
episode,
|
||||
showName,
|
||||
searching,
|
||||
getEpisodeDetails,
|
||||
getEpisodeDetails
|
||||
}) => (
|
||||
<TorrentsButton
|
||||
torrents={torrents}
|
||||
searching={searching}
|
||||
search={() => getEpisodeDetails(imdbId, season, episode)}
|
||||
url={`#/torrents/search/shows/${
|
||||
encodeURI(prettyEpisodeName(showName, season, episode))
|
||||
}`}
|
||||
url={`#/torrents/search/shows/${encodeURI(
|
||||
prettyEpisodeName(showName, season, episode)
|
||||
)}`}
|
||||
/>
|
||||
)
|
||||
);
|
||||
episodeTorrentsButton.propTypes = {
|
||||
torrents: PropTypes.instanceOf(List),
|
||||
showName: PropTypes.string.isRequired,
|
||||
@ -32,7 +32,9 @@ episodeTorrentsButton.propTypes = {
|
||||
episode: PropTypes.number.isRequired,
|
||||
season: PropTypes.number.isRequired,
|
||||
searching: PropTypes.bool.isRequired,
|
||||
getEpisodeDetails: PropTypes.func.isRequired,
|
||||
getEpisodeDetails: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export const EpisodeTorrentsButton = connect(null, {getEpisodeDetails})(episodeTorrentsButton);
|
||||
export const EpisodeTorrentsButton = connect(null, { getEpisodeDetails })(
|
||||
episodeTorrentsButton
|
||||
);
|
||||
|
@ -1,33 +1,39 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { Map } from "immutable"
|
||||
import { connect } from "react-redux"
|
||||
import { selectShow, showWishlistToggle,
|
||||
getShowDetails, updateFilter } from "../../actions/shows"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Map } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
import {
|
||||
selectShow,
|
||||
showWishlistToggle,
|
||||
getShowDetails,
|
||||
updateFilter
|
||||
} from "../../actions/shows";
|
||||
|
||||
import { isWishlisted } from "../../utils"
|
||||
import { isWishlisted } from "../../utils";
|
||||
|
||||
import ListDetails from "../list/details"
|
||||
import ListPosters from "../list/posters"
|
||||
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"),
|
||||
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,
|
||||
selectShow,
|
||||
showWishlistToggle,
|
||||
getShowDetails,
|
||||
updateFilter
|
||||
};
|
||||
|
||||
const ShowList = (props) => {
|
||||
const showDetails = (imdbId) => {
|
||||
const ShowList = props => {
|
||||
const showDetails = imdbId => {
|
||||
props.history.push("/shows/details/" + imdbId);
|
||||
}
|
||||
};
|
||||
|
||||
let selectedShow = Map();
|
||||
if (props.selectedImdbId !== "") {
|
||||
@ -53,14 +59,18 @@ const ShowList = (props) => {
|
||||
<ListDetails
|
||||
data={selectedShow}
|
||||
loading={props.loading}
|
||||
wishlist={() => props.showWishlistToggle(
|
||||
isWishlisted(selectedShow), selectedShow.get("imdb_id"),
|
||||
)}
|
||||
wishlist={() =>
|
||||
props.showWishlistToggle(
|
||||
isWishlisted(selectedShow),
|
||||
selectedShow.get("imdb_id")
|
||||
)
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<button onClick={
|
||||
() => showDetails(selectedShow.get("imdb_id"))}
|
||||
className="btn btn-primary btn-sm w-md-100">
|
||||
<button
|
||||
onClick={() => showDetails(selectedShow.get("imdb_id"))}
|
||||
className="btn btn-primary btn-sm w-md-100"
|
||||
>
|
||||
<i className="fa fa-external-link mr-1" />
|
||||
Details
|
||||
</button>
|
||||
@ -68,7 +78,7 @@ const ShowList = (props) => {
|
||||
</ListDetails>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
ShowList.propTypes = {
|
||||
match: PropTypes.object,
|
||||
history: PropTypes.object,
|
||||
@ -80,6 +90,6 @@ ShowList.propTypes = {
|
||||
showWishlistToggle: PropTypes.func,
|
||||
selectShow: PropTypes.func,
|
||||
getShowDetails: PropTypes.func,
|
||||
updateFilter: PropTypes.func,
|
||||
updateFilter: PropTypes.func
|
||||
};
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ShowList);
|
||||
|
@ -1,13 +1,21 @@
|
||||
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"
|
||||
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 mapStateToProps = state => ({
|
||||
isExplorerFetched: state.showsStore.get("exploreOptions").size !== 0
|
||||
});
|
||||
const mapDispatchToProps = { fetchShows, fetchShowDetails, getShowExploreOptions };
|
||||
const mapDispatchToProps = {
|
||||
fetchShows,
|
||||
fetchShowDetails,
|
||||
getShowExploreOptions
|
||||
};
|
||||
|
||||
const ShowsRoute = ({
|
||||
component: Component,
|
||||
@ -18,7 +26,9 @@ const ShowsRoute = ({
|
||||
...otherProps
|
||||
}) => {
|
||||
return (
|
||||
<Route {...otherProps} render={(props) => {
|
||||
<Route
|
||||
{...otherProps}
|
||||
render={props => {
|
||||
let fetchUrl = "";
|
||||
switch (props.match.path) {
|
||||
case "/shows/polochon":
|
||||
@ -34,30 +44,33 @@ const ShowsRoute = ({
|
||||
if (!isExplorerFetched) {
|
||||
getShowExploreOptions();
|
||||
}
|
||||
fetchUrl = "/shows/explore?source=" +
|
||||
fetchUrl =
|
||||
"/shows/explore?source=" +
|
||||
encodeURI(props.match.params.source) +
|
||||
"&category=" + encodeURI(props.match.params.category);
|
||||
"&category=" +
|
||||
encodeURI(props.match.params.category);
|
||||
break;
|
||||
case "/shows/details/:imdbId":
|
||||
fetchShowDetails(props.match.params.imdbId);
|
||||
fetchUrl = ""
|
||||
fetchUrl = "";
|
||||
}
|
||||
|
||||
if (fetchUrl != "") {
|
||||
fetchShows(fetchUrl);
|
||||
}
|
||||
|
||||
return <Component {...props} />
|
||||
}} />
|
||||
)
|
||||
}
|
||||
return <Component {...props} />;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
ShowsRoute.propTypes = {
|
||||
component: PropTypes.func,
|
||||
match: PropTypes.object,
|
||||
isExplorerFetched: PropTypes.bool.isRequired,
|
||||
fetchShows: PropTypes.func.isRequired,
|
||||
fetchShowDetails: PropTypes.func.isRequired,
|
||||
getShowExploreOptions: PropTypes.func.isRequired,
|
||||
getShowExploreOptions: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ShowsRoute);
|
||||
|
@ -1,26 +1,29 @@
|
||||
import React, { useState } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { Map, List } from "immutable"
|
||||
import { connect } from "react-redux"
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Map, List } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { prettySize } from "../../utils"
|
||||
import { fetchTorrents, addTorrent, removeTorrent } from "../../actions/torrents"
|
||||
import { prettySize } from "../../utils";
|
||||
import {
|
||||
fetchTorrents,
|
||||
addTorrent,
|
||||
removeTorrent
|
||||
} from "../../actions/torrents";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
const mapStateToProps = state => ({
|
||||
torrents: state.torrentStore.get("torrents")
|
||||
});
|
||||
const mapDispatchToProps = {
|
||||
fetchTorrents, addTorrent, removeTorrent,
|
||||
fetchTorrents,
|
||||
addTorrent,
|
||||
removeTorrent
|
||||
};
|
||||
|
||||
const TorrentList = (props) => (
|
||||
const TorrentList = props => (
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<AddTorrent addTorrent={props.addTorrent} />
|
||||
<Torrents
|
||||
torrents={props.torrents}
|
||||
removeTorrent={props.removeTorrent}
|
||||
/>
|
||||
<Torrents torrents={props.torrents} removeTorrent={props.removeTorrent} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -28,38 +31,40 @@ TorrentList.propTypes = {
|
||||
fetchTorrents: PropTypes.func.isRequired,
|
||||
addTorrent: PropTypes.func.isRequired,
|
||||
removeTorrent: PropTypes.func.isRequired,
|
||||
torrents: PropTypes.instanceOf(List),
|
||||
torrents: PropTypes.instanceOf(List)
|
||||
};
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(TorrentList);
|
||||
|
||||
const AddTorrent = (props) => {
|
||||
const AddTorrent = props => {
|
||||
const [url, setUrl] = useState("");
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
const handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
if (url === "") { return; }
|
||||
if (url === "") {
|
||||
return;
|
||||
}
|
||||
props.addTorrent(url);
|
||||
setUrl("");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={(e) => handleSubmit(e)}>
|
||||
<form onSubmit={e => handleSubmit(e)}>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control mb-3 w-100"
|
||||
placeholder="Add torrent URL"
|
||||
onSubmit={handleSubmit}
|
||||
value={url}
|
||||
onChange={(e) => setUrl(e.target.value)}
|
||||
onChange={e => setUrl(e.target.value)}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
};
|
||||
AddTorrent.propTypes = {
|
||||
addTorrent: PropTypes.func.isRequired,
|
||||
addTorrent: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const Torrents = (props) => {
|
||||
const Torrents = props => {
|
||||
if (props.torrents.size === 0) {
|
||||
return (
|
||||
<div className="jumbotron">
|
||||
@ -71,31 +76,29 @@ const Torrents = (props) => {
|
||||
return (
|
||||
<div className="d-flex flex-wrap">
|
||||
{props.torrents.map((el, index) => (
|
||||
<Torrent
|
||||
key={index}
|
||||
data={el}
|
||||
removeTorrent={props.removeTorrent}
|
||||
/>
|
||||
<Torrent key={index} data={el} removeTorrent={props.removeTorrent} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
Torrents.propTypes = {
|
||||
removeTorrent: PropTypes.func.isRequired,
|
||||
torrents: PropTypes.instanceOf(List),
|
||||
torrents: PropTypes.instanceOf(List)
|
||||
};
|
||||
|
||||
const Torrent = (props) => {
|
||||
const Torrent = props => {
|
||||
const handleClick = () => {
|
||||
props.removeTorrent(props.data.get("id"));
|
||||
}
|
||||
};
|
||||
|
||||
const done = props.data.get("is_finished");
|
||||
var progressStyle = done ? "success" : "info progress-bar-striped progress-bar-animated";
|
||||
var progressStyle = done
|
||||
? "success"
|
||||
: "info progress-bar-striped progress-bar-animated";
|
||||
const progressBarClass = "progress-bar bg-" + progressStyle;
|
||||
|
||||
var percentDone = props.data.get("percent_done");
|
||||
const started = (percentDone !== 0);
|
||||
const started = percentDone !== 0;
|
||||
if (started) {
|
||||
percentDone = Number(percentDone).toFixed(1) + "%";
|
||||
}
|
||||
@ -108,32 +111,35 @@ const Torrent = (props) => {
|
||||
<div className="card w-100 mb-3">
|
||||
<h5 className="card-header">
|
||||
<span className="text text-break">{props.data.get("name")}</span>
|
||||
<span className="fa fa-trash clickable pull-right" onClick={() => handleClick()}></span>
|
||||
<span
|
||||
className="fa fa-trash clickable pull-right"
|
||||
onClick={() => handleClick()}
|
||||
></span>
|
||||
</h5>
|
||||
<div className="card-body pb-0">
|
||||
{started &&
|
||||
{started && (
|
||||
<React.Fragment>
|
||||
<div className="progress bg-light">
|
||||
<div
|
||||
className={progressBarClass}
|
||||
style={{width: percentDone}}
|
||||
style={{ width: percentDone }}
|
||||
role="progressbar"
|
||||
aria-valuenow={percentDone}
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
></div>
|
||||
</div>
|
||||
<p>{downloadedSize} / {totalSize} - {percentDone} - {downloadRate}</p>
|
||||
<p>
|
||||
{downloadedSize} / {totalSize} - {percentDone} - {downloadRate}
|
||||
</p>
|
||||
</React.Fragment>
|
||||
}
|
||||
{!started &&
|
||||
<p>Download not yet started</p>
|
||||
}
|
||||
)}
|
||||
{!started && <p>Download not yet started</p>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
Torrent.propTypes = {
|
||||
removeTorrent: PropTypes.func.isRequired,
|
||||
data: PropTypes.instanceOf(Map),
|
||||
data: PropTypes.instanceOf(Map)
|
||||
};
|
||||
|
@ -1,34 +1,37 @@
|
||||
import React, { useState, useEffect } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { connect } from "react-redux"
|
||||
import { addTorrent, searchTorrents } from "../../actions/torrents"
|
||||
import { Map, List } from "immutable"
|
||||
import Loader from "../loader/loader"
|
||||
import React, { useState, useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { addTorrent, searchTorrents } from "../../actions/torrents";
|
||||
import { Map, List } from "immutable";
|
||||
import Loader from "../loader/loader";
|
||||
|
||||
import { OverlayTrigger, Tooltip } from "react-bootstrap"
|
||||
import { OverlayTrigger, Tooltip } from "react-bootstrap";
|
||||
|
||||
import { prettySize } from "../../utils"
|
||||
import { prettySize } from "../../utils";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
const mapStateToProps = state => ({
|
||||
searching: state.torrentStore.get("searching"),
|
||||
results: state.torrentStore.get("searchResults"),
|
||||
results: state.torrentStore.get("searchResults")
|
||||
});
|
||||
const mapDispatchToProps = { addTorrent, searchTorrents };
|
||||
|
||||
const TorrentSearch = (props) => {
|
||||
const TorrentSearch = props => {
|
||||
const [search, setSearch] = useState(props.match.params.search || "");
|
||||
const [type, setType] = useState(props.match.params.type || "");
|
||||
const [url, setUrl] = useState("");
|
||||
|
||||
const getUrl = () =>
|
||||
`/torrents/search/${type}/${encodeURI(search)}`;
|
||||
const getUrl = () => `/torrents/search/${type}/${encodeURI(search)}`;
|
||||
|
||||
useEffect(() => {
|
||||
if (search === "") { return }
|
||||
if (type === "") { return }
|
||||
if (search === "") {
|
||||
return;
|
||||
}
|
||||
if (type === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = getUrl();
|
||||
props.searchTorrents(url)
|
||||
props.searchTorrents(url);
|
||||
props.history.push(url);
|
||||
}, [url]);
|
||||
|
||||
@ -40,7 +43,7 @@ const TorrentSearch = (props) => {
|
||||
className="form-control mb-1 w-100 form-control-lg"
|
||||
placeholder="Search torrents"
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
onChange={e => setSearch(e.target.value)}
|
||||
/>
|
||||
<div className="mb-3 w-100 d-flex">
|
||||
<SearchButton
|
||||
@ -50,7 +53,8 @@ const TorrentSearch = (props) => {
|
||||
handleClick={() => {
|
||||
setType("movies");
|
||||
setUrl(getUrl());
|
||||
}}/>
|
||||
}}
|
||||
/>
|
||||
<SearchButton
|
||||
text="Search shows"
|
||||
type="shows"
|
||||
@ -58,7 +62,8 @@ const TorrentSearch = (props) => {
|
||||
handleClick={() => {
|
||||
setType("shows");
|
||||
setUrl(getUrl());
|
||||
}}/>
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12">
|
||||
@ -71,7 +76,7 @@ const TorrentSearch = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
TorrentSearch.propTypes = {
|
||||
searching: PropTypes.bool.isRequired,
|
||||
results: PropTypes.instanceOf(List),
|
||||
@ -79,12 +84,11 @@ TorrentSearch.propTypes = {
|
||||
match: PropTypes.object,
|
||||
history: PropTypes.object,
|
||||
addTorrent: PropTypes.func.isRequired,
|
||||
searchTorrents: PropTypes.func.isRequired,
|
||||
searchTorrents: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
||||
const SearchButton = (props) => {
|
||||
const variant = (props.type === props.typeFromURL) ? "primary" : "secondary";
|
||||
const SearchButton = props => {
|
||||
const variant = props.type === props.typeFromURL ? "primary" : "secondary";
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
@ -94,17 +98,17 @@ const SearchButton = (props) => {
|
||||
<i className="fa fa-search" aria-hidden="true"></i> {props.text}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
};
|
||||
SearchButton.propTypes = {
|
||||
type: PropTypes.string,
|
||||
typeFromURL: PropTypes.string,
|
||||
text: PropTypes.string,
|
||||
handleClick: PropTypes.func.isRequired,
|
||||
handleClick: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const TorrentList = (props) => {
|
||||
const TorrentList = props => {
|
||||
if (props.searching) {
|
||||
return (<Loader />);
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
if (props.searchFromURL === "") {
|
||||
@ -122,59 +126,65 @@ const TorrentList = (props) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{props.results.map(function(el, index) {
|
||||
return (
|
||||
<Torrent
|
||||
key={index}
|
||||
data={el}
|
||||
addTorrent={props.addTorrent}
|
||||
/>);
|
||||
return <Torrent key={index} data={el} addTorrent={props.addTorrent} />;
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
};
|
||||
TorrentList.propTypes = {
|
||||
searching: PropTypes.bool.isRequired,
|
||||
results: PropTypes.instanceOf(List),
|
||||
searchFromURL: PropTypes.string,
|
||||
addTorrent: PropTypes.func.isRequired,
|
||||
addTorrent: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const Torrent = (props) => (
|
||||
const Torrent = props => (
|
||||
<div className="alert d-flex border-bottom border-secondary align-items-center">
|
||||
<TorrentHealth
|
||||
url={props.data.get("url")}
|
||||
seeders={props.data.get("seeders")}
|
||||
leechers={props.data.get("leechers")}
|
||||
/>
|
||||
<span className="mx-3 text text-start text-break flex-fill">{props.data.get("name")}</span>
|
||||
<span className="mx-3 text text-start text-break flex-fill">
|
||||
{props.data.get("name")}
|
||||
</span>
|
||||
<div>
|
||||
{props.data.get("size") !== 0 &&
|
||||
{props.data.get("size") !== 0 && (
|
||||
<span className="mx-1 badge badge-pill badge-warning">
|
||||
{prettySize(props.data.get("size"))}
|
||||
</span>
|
||||
}
|
||||
)}
|
||||
|
||||
<span className="mx-1 badge badge-pill badge-warning">{props.data.get("quality")}</span>
|
||||
<span className="mx-1 badge badge-pill badge-success">{props.data.get("source")}</span>
|
||||
<span className="mx-1 badge badge-pill badge-info">{props.data.get("upload_user")}</span>
|
||||
<span className="mx-1 badge badge-pill badge-warning">
|
||||
{props.data.get("quality")}
|
||||
</span>
|
||||
<span className="mx-1 badge badge-pill badge-success">
|
||||
{props.data.get("source")}
|
||||
</span>
|
||||
<span className="mx-1 badge badge-pill badge-info">
|
||||
{props.data.get("upload_user")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="align-self-end ml-3">
|
||||
<i className="fa fa-cloud-download clickable" onClick={() => props.addTorrent(props.data.get("url"))}></i>
|
||||
<i
|
||||
className="fa fa-cloud-download clickable"
|
||||
onClick={() => props.addTorrent(props.data.get("url"))}
|
||||
></i>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
Torrent.propTypes = {
|
||||
data: PropTypes.instanceOf(Map),
|
||||
addTorrent: PropTypes.func.isRequired,
|
||||
addTorrent: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const TorrentHealth = (props) => {
|
||||
const TorrentHealth = props => {
|
||||
const seeders = props.seeders || 0;
|
||||
const leechers = props.leechers || 1;
|
||||
|
||||
let color;
|
||||
let health;
|
||||
let ratio = seeders/leechers;
|
||||
let ratio = seeders / leechers;
|
||||
|
||||
if (seeders > 20) {
|
||||
health = "good";
|
||||
@ -192,7 +202,9 @@ const TorrentHealth = (props) => {
|
||||
const className = `align-self-start text text-center text-${color}`;
|
||||
const tooltip = (
|
||||
<Tooltip id={`tooltip-health-${props.url}`}>
|
||||
<p><span className={className}>Health: {health}</span></p>
|
||||
<p>
|
||||
<span className={className}>Health: {health}</span>
|
||||
</p>
|
||||
<p>Seeders: {seeders}</p>
|
||||
<p>Leechers: {props.leechers}</p>
|
||||
</Tooltip>
|
||||
@ -205,11 +217,11 @@ const TorrentHealth = (props) => {
|
||||
</span>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
}
|
||||
};
|
||||
TorrentHealth.propTypes = {
|
||||
url: PropTypes.string,
|
||||
seeders: PropTypes.number,
|
||||
leechers: PropTypes.number,
|
||||
leechers: PropTypes.number
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(TorrentSearch);
|
||||
|
@ -1,20 +1,20 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { connect } from "react-redux"
|
||||
import { Redirect, Link } from "react-router-dom"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { Redirect, Link } from "react-router-dom";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
const mapStateToProps = state => ({
|
||||
isActivated: state.userStore.get("isActivated"),
|
||||
isLogged: state.userStore.get("isLogged"),
|
||||
isLogged: state.userStore.get("isLogged")
|
||||
});
|
||||
|
||||
const UserActivation = (props) => {
|
||||
const UserActivation = props => {
|
||||
if (!props.isLogged) {
|
||||
return (<Redirect to="/users/login"/>);
|
||||
return <Redirect to="/users/login" />;
|
||||
}
|
||||
|
||||
if (props.isActivated) {
|
||||
return (<Redirect to="/"/>);
|
||||
return <Redirect to="/" />;
|
||||
}
|
||||
|
||||
return (
|
||||
@ -22,15 +22,18 @@ const UserActivation = (props) => {
|
||||
<div className="col-12 col-md-8 offset-md-2">
|
||||
<h2>Waiting for activation</h2>
|
||||
<hr />
|
||||
<h3>Hang tight! Your user will soon be activated by the administrators of this site.</h3>
|
||||
<h3>
|
||||
Hang tight! Your user will soon be activated by the administrators of
|
||||
this site.
|
||||
</h3>
|
||||
<Link to="/users/logout">Logout</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
UserActivation.propTypes = {
|
||||
isActivated: PropTypes.bool.isRequired,
|
||||
isLogged: PropTypes.bool.isRequired,
|
||||
isLogged: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(UserActivation);
|
||||
|
@ -1,24 +1,26 @@
|
||||
import React, { useState, useEffect } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { connect } from "react-redux"
|
||||
import Loader from "../loader/loader"
|
||||
import { List } from "immutable"
|
||||
import React, { useState, useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import Loader from "../loader/loader";
|
||||
import { List } from "immutable";
|
||||
|
||||
import { getUserInfos, updateUser } from "../../actions/users"
|
||||
import { getPolochons } from "../../actions/polochon"
|
||||
import { getUserInfos, updateUser } from "../../actions/users";
|
||||
import { getPolochons } from "../../actions/polochon";
|
||||
|
||||
import { PolochonSelect } from "../polochons/select"
|
||||
import { PolochonSelect } from "../polochons/select";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
const mapStateToProps = state => ({
|
||||
loading: state.userStore.get("loading"),
|
||||
publicPolochons: state.polochon.get("public"),
|
||||
polochonId: state.userStore.get("polochonId"),
|
||||
polochonActivated: state.userStore.get("polochonActivated"),
|
||||
polochonActivated: state.userStore.get("polochonActivated")
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
updateUser, getPolochons, getUserInfos,
|
||||
}
|
||||
updateUser,
|
||||
getPolochons,
|
||||
getUserInfos
|
||||
};
|
||||
|
||||
const UserEditConnect = ({
|
||||
loading,
|
||||
@ -27,7 +29,7 @@ const UserEditConnect = ({
|
||||
updateUser,
|
||||
getPolochons,
|
||||
getUserInfos,
|
||||
publicPolochons,
|
||||
publicPolochons
|
||||
}) => {
|
||||
const [id, setId] = useState(polochonId);
|
||||
const [password, setPassword] = useState("");
|
||||
@ -36,37 +38,45 @@ const UserEditConnect = ({
|
||||
useEffect(() => {
|
||||
getPolochons();
|
||||
getUserInfos();
|
||||
}, [getPolochons, getUserInfos])
|
||||
}, [getPolochons, getUserInfos]);
|
||||
|
||||
useEffect(() => {
|
||||
setId(polochonId);
|
||||
}, [polochonId])
|
||||
}, [polochonId]);
|
||||
|
||||
const handleSubmit = (ev) => {
|
||||
const handleSubmit = ev => {
|
||||
ev.preventDefault();
|
||||
updateUser({
|
||||
"password": password,
|
||||
"password_confirm": passwordConfirm,
|
||||
"polochon_id": id,
|
||||
password: password,
|
||||
password_confirm: passwordConfirm,
|
||||
polochon_id: id
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) { return (<Loader />) }
|
||||
if (loading) {
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="row mb-3">
|
||||
<div className="col-12 col-md-8 offset-md-2">
|
||||
<h2>Edit user</h2>
|
||||
<hr />
|
||||
<form className="form-horizontal" onSubmit={(ev) => handleSubmit(ev)}>
|
||||
<form className="form-horizontal" onSubmit={ev => handleSubmit(ev)}>
|
||||
<div className="form-group">
|
||||
<label className="control-label">
|
||||
Polochon
|
||||
{polochonActivated ||
|
||||
<span className="ml-1 text text-primary">(Needs activation from admin)</span>
|
||||
}
|
||||
{polochonActivated || (
|
||||
<span className="ml-1 text text-primary">
|
||||
(Needs activation from admin)
|
||||
</span>
|
||||
)}
|
||||
</label>
|
||||
<PolochonSelect value={id} changeValue={setId} polochonList={publicPolochons} />
|
||||
<PolochonSelect
|
||||
value={id}
|
||||
changeValue={setId}
|
||||
polochonList={publicPolochons}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
@ -78,7 +88,7 @@ const UserEditConnect = ({
|
||||
type="password"
|
||||
autoComplete="off"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
onChange={e => setPassword(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -89,18 +99,22 @@ const UserEditConnect = ({
|
||||
type="password"
|
||||
autoComplete="off"
|
||||
value={passwordConfirm}
|
||||
onChange={(e) => setPasswordConfirm(e.target.value)}
|
||||
onChange={e => setPasswordConfirm(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="submit" className="btn btn-primary pull-right" value="Update"/>
|
||||
<input
|
||||
type="submit"
|
||||
className="btn btn-primary pull-right"
|
||||
value="Update"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
UserEditConnect.propTypes = {
|
||||
loading: PropTypes.bool.isRequired,
|
||||
polochonId: PropTypes.string,
|
||||
@ -108,7 +122,10 @@ UserEditConnect.propTypes = {
|
||||
updateUser: PropTypes.func,
|
||||
getPolochons: PropTypes.func,
|
||||
getUserInfos: PropTypes.func,
|
||||
publicPolochons: PropTypes.instanceOf(List),
|
||||
publicPolochons: PropTypes.instanceOf(List)
|
||||
};
|
||||
|
||||
export const UserEdit = connect(mapStateToProps, mapDispatchToProps)(UserEditConnect);
|
||||
export const UserEdit = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(UserEditConnect);
|
||||
|
@ -1,27 +1,27 @@
|
||||
import React, { useState } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { connect } from "react-redux"
|
||||
import { Redirect, Link } from "react-router-dom"
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { Redirect, Link } from "react-router-dom";
|
||||
|
||||
import { loginUser } from "../../actions/users"
|
||||
import { loginUser } from "../../actions/users";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
const mapStateToProps = state => ({
|
||||
isLogged: state.userStore.get("isLogged"),
|
||||
isLoading: state.userStore.get("loading"),
|
||||
error: state.userStore.get("error"),
|
||||
error: state.userStore.get("error")
|
||||
});
|
||||
const mapDispatchToProps = { loginUser };
|
||||
|
||||
const UserLoginForm = (props) => {
|
||||
const UserLoginForm = props => {
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
const handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
if (!props.isLoading) {
|
||||
props.loginUser(username, password);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (props.isLogged) {
|
||||
return <Redirect to="/" />;
|
||||
@ -31,25 +31,33 @@ const UserLoginForm = (props) => {
|
||||
<div className="row">
|
||||
<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>
|
||||
}
|
||||
<form className="form-horizontal" onSubmit={(e) => handleSubmit(e)}>
|
||||
<hr />
|
||||
{props.error && props.error !== "" && (
|
||||
<div className="alert alert-danger">{props.error}</div>
|
||||
)}
|
||||
<form className="form-horizontal" onSubmit={e => handleSubmit(e)}>
|
||||
<div>
|
||||
<label>Username</label>
|
||||
<br/>
|
||||
<input className="form-control" type="username" autoFocus
|
||||
value={username} onChange={(e) => setUsername(e.target.value)}/>
|
||||
<br />
|
||||
<input
|
||||
className="form-control"
|
||||
type="username"
|
||||
autoFocus
|
||||
value={username}
|
||||
onChange={e => setUsername(e.target.value)}
|
||||
/>
|
||||
<p></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Password</label>
|
||||
<br/>
|
||||
<input className="form-control" type="password" autoComplete="new-password"
|
||||
value={password} onChange={(e) => setPassword(e.target.value)}/>
|
||||
<br />
|
||||
<input
|
||||
className="form-control"
|
||||
type="password"
|
||||
autoComplete="new-password"
|
||||
value={password}
|
||||
onChange={e => setPassword(e.target.value)}
|
||||
/>
|
||||
<p></p>
|
||||
</div>
|
||||
<div>
|
||||
@ -58,28 +66,32 @@ const UserLoginForm = (props) => {
|
||||
No account yet ? <Link to="/users/signup">Create one</Link>
|
||||
</small>
|
||||
</span>
|
||||
{props.isLoading &&
|
||||
{props.isLoading && (
|
||||
<button className="btn btn-primary pull-right">
|
||||
<i className="fa fa-spinner fa-spin"></i>
|
||||
</button>
|
||||
}
|
||||
{props.isLoading ||
|
||||
)}
|
||||
{props.isLoading || (
|
||||
<span className="spaced-icons">
|
||||
<input className="btn btn-primary pull-right" type="submit" value="Log in"/>
|
||||
<input
|
||||
className="btn btn-primary pull-right"
|
||||
type="submit"
|
||||
value="Log in"
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
<br/>
|
||||
)}
|
||||
<br />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
UserLoginForm.propTypes = {
|
||||
loginUser: PropTypes.func.isRequired,
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
isLogged: PropTypes.bool.isRequired,
|
||||
error: PropTypes.string,
|
||||
error: PropTypes.string
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(UserLoginForm);
|
||||
|
@ -1,28 +1,26 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { connect } from "react-redux"
|
||||
import { Redirect } from "react-router-dom"
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { Redirect } from "react-router-dom";
|
||||
|
||||
import { userLogout } from "../../actions/users"
|
||||
import { userLogout } from "../../actions/users";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
isLogged: state.userStore.get("isLogged"),
|
||||
const mapStateToProps = state => ({
|
||||
isLogged: state.userStore.get("isLogged")
|
||||
});
|
||||
const mapDispatchToProps = { userLogout };
|
||||
|
||||
const UserLogout = (props) => {
|
||||
const UserLogout = props => {
|
||||
if (props.isLogged) {
|
||||
props.userLogout();
|
||||
}
|
||||
|
||||
return (
|
||||
<Redirect to="/users/login" />
|
||||
);
|
||||
}
|
||||
return <Redirect to="/users/login" />;
|
||||
};
|
||||
UserLogout.propTypes = {
|
||||
isLogged: PropTypes.bool.isRequired,
|
||||
userLogout: PropTypes.func.isRequired,
|
||||
history: PropTypes.object,
|
||||
history: PropTypes.object
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(UserLogout);
|
||||
|
@ -1,46 +1,38 @@
|
||||
import React, { useEffect } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { connect } from "react-redux"
|
||||
import { Map } from "immutable"
|
||||
import React, { useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { Map } from "immutable";
|
||||
|
||||
import { PolochonList } from "../polochons/list"
|
||||
import { UserEdit } from "./edit"
|
||||
import { PolochonList } from "../polochons/list";
|
||||
import { UserEdit } from "./edit";
|
||||
|
||||
import { getUserModules } from "../../actions/users"
|
||||
import Modules from "../modules/modules"
|
||||
import { getUserModules } from "../../actions/users";
|
||||
import Modules from "../modules/modules";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
modules : state.userStore.get("modules"),
|
||||
modulesLoading : state.userStore.get("modulesLoading"),
|
||||
const mapStateToProps = state => ({
|
||||
modules: state.userStore.get("modules"),
|
||||
modulesLoading: state.userStore.get("modulesLoading")
|
||||
});
|
||||
|
||||
const mapDispatchToProps = { getUserModules }
|
||||
const mapDispatchToProps = { getUserModules };
|
||||
|
||||
const UserProfile = ({
|
||||
modules,
|
||||
modulesLoading,
|
||||
getUserModules
|
||||
|
||||
}) => {
|
||||
const UserProfile = ({ modules, modulesLoading, getUserModules }) => {
|
||||
useEffect(() => {
|
||||
getUserModules();
|
||||
}, [getUserModules])
|
||||
}, [getUserModules]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<UserEdit />
|
||||
<PolochonList />
|
||||
<Modules
|
||||
modules={modules}
|
||||
isLoading={modulesLoading}
|
||||
/>
|
||||
<Modules modules={modules} isLoading={modulesLoading} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
UserProfile.propTypes = {
|
||||
getUserModules: PropTypes.func.isRequired,
|
||||
modules: PropTypes.instanceOf(Map),
|
||||
modulesLoading: PropTypes.bool.isRequired,
|
||||
modulesLoading: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(UserProfile);
|
||||
|
@ -1,86 +1,100 @@
|
||||
import React, { useState } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { connect } from "react-redux"
|
||||
import { Redirect } from "react-router-dom"
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { Redirect } from "react-router-dom";
|
||||
|
||||
import { userSignUp } from "../../actions/users"
|
||||
import { userSignUp } from "../../actions/users";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
const mapStateToProps = state => ({
|
||||
isLogged: state.userStore.get("isLogged"),
|
||||
isLoading: state.userStore.get("loading"),
|
||||
error: state.userStore.get("error"),
|
||||
error: state.userStore.get("error")
|
||||
});
|
||||
const mapDispatchToProps = { userSignUp };
|
||||
|
||||
const UserSignUp = (props) => {
|
||||
const UserSignUp = props => {
|
||||
if (props.isLogged) {
|
||||
return (<Redirect to="/"/>);
|
||||
return <Redirect to="/" />;
|
||||
}
|
||||
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [passwordConfirm, setPasswordConfirm] = useState("");
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
const handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
props.userSignUp({
|
||||
"username": username,
|
||||
"password": password,
|
||||
"password_confirm": passwordConfirm,
|
||||
username: username,
|
||||
password: password,
|
||||
password_confirm: passwordConfirm
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="Row">
|
||||
<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>
|
||||
}
|
||||
{props.error && props.error !== "" && (
|
||||
<div className="alert alert-danger">{props.error}</div>
|
||||
)}
|
||||
|
||||
<form className="form-horizontal" onSubmit={(e) => handleSubmit(e)}>
|
||||
<form className="form-horizontal" onSubmit={e => handleSubmit(e)}>
|
||||
<div className="form-group">
|
||||
<label className="control-label">Username</label>
|
||||
<input autoFocus="autofocus" className="form-control"
|
||||
value={username} onChange={(e) => setUsername(e.target.value)} />
|
||||
<input
|
||||
autoFocus="autofocus"
|
||||
className="form-control"
|
||||
value={username}
|
||||
onChange={e => setUsername(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label className="control-label">Password</label>
|
||||
<input className="form-control" type="password"
|
||||
value={password} onChange={(e) => setPassword(e.target.value)} />
|
||||
<input
|
||||
className="form-control"
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={e => setPassword(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label className="control-label">Password confirm</label>
|
||||
<input className="form-control" type="password"
|
||||
value={passwordConfirm} onChange={(e) => setPasswordConfirm(e.target.value)} />
|
||||
<input
|
||||
className="form-control"
|
||||
type="password"
|
||||
value={passwordConfirm}
|
||||
onChange={e => setPasswordConfirm(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
{props.isLoading &&
|
||||
{props.isLoading && (
|
||||
<button className="btn btn-primary pull-right">
|
||||
<i className="fa fa-spinner fa-spin"></i>
|
||||
</button>
|
||||
}
|
||||
{props.isLoading ||
|
||||
)}
|
||||
{props.isLoading || (
|
||||
<span>
|
||||
<input className="btn btn-primary pull-right" type="submit" value="Sign up"/>
|
||||
<input
|
||||
className="btn btn-primary pull-right"
|
||||
type="submit"
|
||||
value="Sign up"
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
UserSignUp.propTypes = {
|
||||
isLogged: PropTypes.bool.isRequired,
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
userSignUp: PropTypes.func.isRequired,
|
||||
error: PropTypes.string,
|
||||
error: PropTypes.string
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(UserSignUp);
|
||||
|
@ -1,18 +1,18 @@
|
||||
import React, { useState } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { connect } from "react-redux"
|
||||
import { UAParser } from "ua-parser-js"
|
||||
import moment from "moment"
|
||||
import { Map, List } from "immutable"
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { UAParser } from "ua-parser-js";
|
||||
import moment from "moment";
|
||||
import { Map, List } from "immutable";
|
||||
|
||||
import { getUserTokens, deleteUserToken } from "../../actions/users"
|
||||
import { getUserTokens, deleteUserToken } from "../../actions/users";
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
tokens: state.userStore.get("tokens"),
|
||||
const mapStateToProps = state => ({
|
||||
tokens: state.userStore.get("tokens")
|
||||
});
|
||||
const mapDispatchToProps = { getUserTokens, deleteUserToken };
|
||||
|
||||
const UserTokens = (props) => {
|
||||
const UserTokens = props => {
|
||||
const [fetched, setIsFetched] = useState(false);
|
||||
if (!fetched) {
|
||||
props.getUserTokens();
|
||||
@ -28,15 +28,15 @@ const UserTokens = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
UserTokens.propTypes = {
|
||||
tokens: PropTypes.instanceOf(List),
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
getUserTokens: PropTypes.func.isRequired,
|
||||
deleteUserToken: PropTypes.func.isRequired,
|
||||
deleteUserToken: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const Token = (props) => {
|
||||
const Token = props => {
|
||||
const ua = UAParser(props.data.get("user_agent"));
|
||||
return (
|
||||
<div className="card mt-3">
|
||||
@ -54,40 +54,46 @@ const Token = (props) => {
|
||||
<p>Created: {moment(props.data.get("created_at")).fromNow()}</p>
|
||||
</div>
|
||||
<div className="col-12 col-md-6">
|
||||
<p>Device: <Device {...ua.device}/></p>
|
||||
<p>OS: <OS {...ua.os}/></p>
|
||||
<p>Browser: <Browser {...ua.browser}/></p>
|
||||
<p>
|
||||
Device: <Device {...ua.device} />
|
||||
</p>
|
||||
<p>
|
||||
OS: <OS {...ua.os} />
|
||||
</p>
|
||||
<p>
|
||||
Browser: <Browser {...ua.browser} />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
Token.propTypes = {
|
||||
data: PropTypes.instanceOf(Map).isRequired,
|
||||
data: PropTypes.instanceOf(Map).isRequired
|
||||
};
|
||||
|
||||
const Actions = (props) => {
|
||||
const Actions = props => {
|
||||
const handleClick = () => {
|
||||
props.deleteToken(props.data.get("token"));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<span
|
||||
className="fa fa-trash fa-lg pull-right clickable"
|
||||
onClick={handleClick}>
|
||||
</span>
|
||||
onClick={handleClick}
|
||||
></span>
|
||||
);
|
||||
}
|
||||
};
|
||||
Actions.propTypes = {
|
||||
data: PropTypes.instanceOf(Map).isRequired,
|
||||
deleteToken: PropTypes.func.isRequired,
|
||||
deleteToken: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const Logo = (props) => {
|
||||
const Logo = props => {
|
||||
var className;
|
||||
if (props.ua === "canape-cli") {
|
||||
className = "terminal";
|
||||
} else if (props.device.type == "mobile" ){
|
||||
} else if (props.device.type == "mobile") {
|
||||
className = "mobile";
|
||||
} else {
|
||||
switch (props.browser.name) {
|
||||
@ -109,10 +115,10 @@ const Logo = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
return (<span className={`fa fa-${className}`}></span>);
|
||||
}
|
||||
return <span className={`fa fa-${className}`}></span>;
|
||||
};
|
||||
|
||||
const OS = (props) => {
|
||||
const OS = props => {
|
||||
var osName = "-";
|
||||
|
||||
if (props.name !== undefined) {
|
||||
@ -123,24 +129,20 @@ const OS = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<span> {osName}</span>
|
||||
);
|
||||
}
|
||||
return <span> {osName}</span>;
|
||||
};
|
||||
|
||||
const Device = (props) => {
|
||||
const Device = props => {
|
||||
var deviceName = "-";
|
||||
|
||||
if (props.model !== undefined) {
|
||||
deviceName = props.model;
|
||||
}
|
||||
|
||||
return (
|
||||
<span> {deviceName}</span>
|
||||
);
|
||||
}
|
||||
return <span> {deviceName}</span>;
|
||||
};
|
||||
|
||||
const Browser = (props) => {
|
||||
const Browser = props => {
|
||||
var browserName = "-";
|
||||
if (props.name !== undefined) {
|
||||
browserName = props.name;
|
||||
@ -150,9 +152,7 @@ const Browser = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<span> {browserName}</span>
|
||||
);
|
||||
}
|
||||
return <span> {browserName}</span>;
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(UserTokens);
|
||||
|
@ -1,22 +1,26 @@
|
||||
import { useEffect, useState } from "react"
|
||||
import { connect } from "react-redux"
|
||||
import { setFetchedTorrents } from "../actions/torrents"
|
||||
import { newMovieEvent } from "../actions/movies"
|
||||
import { newEpisodeEvent } from "../actions/shows"
|
||||
import { useEffect, useState } from "react";
|
||||
import { connect } 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 mapStateToProps = state => ({
|
||||
isLogged: state.userStore.get("isLogged")
|
||||
});
|
||||
const mapDispatchToProps = { setFetchedTorrents, newMovieEvent, newEpisodeEvent };
|
||||
const mapDispatchToProps = {
|
||||
setFetchedTorrents,
|
||||
newMovieEvent,
|
||||
newEpisodeEvent
|
||||
};
|
||||
|
||||
const WsHandler = (props) => {
|
||||
const WsHandler = props => {
|
||||
const [ws, setWs] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const intervalID = setInterval(() => {
|
||||
if (!ws) {
|
||||
connect();
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
if (ws.readyState === WebSocket.CLOSED) {
|
||||
@ -38,41 +42,51 @@ const WsHandler = (props) => {
|
||||
|
||||
const stop = () => {
|
||||
if (!ws) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
ws.close()
|
||||
ws.close();
|
||||
setWs(null);
|
||||
}
|
||||
};
|
||||
|
||||
const connect = () => {
|
||||
if (ws) { return; }
|
||||
if (ws) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!props.isLogged) {
|
||||
stop();
|
||||
}
|
||||
|
||||
const type = (location.protocol === "https:") ? "wss" : "ws";
|
||||
const socket = new WebSocket(type + ":" + location.host + "/events")
|
||||
const type = location.protocol === "https:" ? "wss" : "ws";
|
||||
const socket = new WebSocket(type + ":" + location.host + "/events");
|
||||
socket.onopen = () => {
|
||||
socket.send(JSON.stringify({
|
||||
"type": "subscribe",
|
||||
"message": "torrents",
|
||||
}))
|
||||
socket.send(JSON.stringify({
|
||||
"type": "subscribe",
|
||||
"message": "newVideo",
|
||||
}))
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
type: "subscribe",
|
||||
message: "torrents"
|
||||
})
|
||||
);
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
type: "subscribe",
|
||||
message: "newVideo"
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
socket.onmessage = event => {
|
||||
if (!event.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
socket.onmessage = (event) => {
|
||||
if (!event.data) { return }
|
||||
|
||||
const data = JSON.parse(event.data);
|
||||
if (!data.type){ return }
|
||||
if (!data.type) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.status !== "ok") {
|
||||
// TODO: handle this somehow
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
switch (data.type) {
|
||||
@ -81,20 +95,22 @@ const WsHandler = (props) => {
|
||||
break;
|
||||
case "newVideo":
|
||||
if (data.message.type === "movie") {
|
||||
props.newMovieEvent(data.message.data)
|
||||
props.newMovieEvent(data.message.data);
|
||||
} else if (data.message.type === "episode") {
|
||||
props.newEpisodeEvent(data.message.data)
|
||||
props.newEpisodeEvent(data.message.data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
socket.onerror = () => { stop(); }
|
||||
socket.onerror = () => {
|
||||
stop();
|
||||
};
|
||||
|
||||
setWs(socket)
|
||||
}
|
||||
setWs(socket);
|
||||
};
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(WsHandler);
|
||||
|
@ -1,21 +1,26 @@
|
||||
import { Map, List, fromJS } from "immutable"
|
||||
import { Map, List, fromJS } from "immutable";
|
||||
|
||||
export const defaultState = Map({
|
||||
"fetchingModules": false,
|
||||
"users": List(),
|
||||
"stats": Map({}),
|
||||
"modules": Map({}),
|
||||
fetchingModules: false,
|
||||
users: List(),
|
||||
stats: Map({}),
|
||||
modules: Map({})
|
||||
});
|
||||
|
||||
const handlers = {
|
||||
"ADMIN_LIST_USERS_FULFILLED": (state, action) => state.set("users", fromJS(action.payload.response.data)),
|
||||
"ADMIN_GET_STATS_FULFILLED": (state, action) => state.set("stats", fromJS(action.payload.response.data)),
|
||||
"ADMIN_GET_MODULES_PENDING": state => state.set("fetchingModules", true),
|
||||
"ADMIN_GET_MODULES_FULFILLED": (state, action) => state.merge(fromJS({
|
||||
"modules": action.payload.response.data,
|
||||
"fetchingModules": false,
|
||||
})),
|
||||
}
|
||||
ADMIN_LIST_USERS_FULFILLED: (state, action) =>
|
||||
state.set("users", fromJS(action.payload.response.data)),
|
||||
ADMIN_GET_STATS_FULFILLED: (state, action) =>
|
||||
state.set("stats", fromJS(action.payload.response.data)),
|
||||
ADMIN_GET_MODULES_PENDING: state => state.set("fetchingModules", true),
|
||||
ADMIN_GET_MODULES_FULFILLED: (state, action) =>
|
||||
state.merge(
|
||||
fromJS({
|
||||
modules: action.payload.response.data,
|
||||
fetchingModules: false
|
||||
})
|
||||
)
|
||||
};
|
||||
|
||||
export default (state = defaultState, action) =>
|
||||
handlers[action.type] ? handlers[action.type](state, action) : state;
|
||||
|
@ -1,29 +1,37 @@
|
||||
import { Map } from "immutable"
|
||||
import { Map } from "immutable";
|
||||
|
||||
const defaultState = Map({
|
||||
show: false,
|
||||
message: "",
|
||||
type: "",
|
||||
type: ""
|
||||
});
|
||||
|
||||
|
||||
const handlers = {
|
||||
"ADD_ALERT_ERROR": (state, action) => state.merge(Map({
|
||||
ADD_ALERT_ERROR: (state, action) =>
|
||||
state.merge(
|
||||
Map({
|
||||
message: action.payload.message,
|
||||
show: true,
|
||||
type: "error",
|
||||
})),
|
||||
"ADD_ALERT_OK": (state, action) => state.merge(Map({
|
||||
type: "error"
|
||||
})
|
||||
),
|
||||
ADD_ALERT_OK: (state, action) =>
|
||||
state.merge(
|
||||
Map({
|
||||
message: action.payload.message,
|
||||
show: true,
|
||||
type: "success",
|
||||
})),
|
||||
"DISMISS_ALERT": state => state.merge(Map({
|
||||
type: "success"
|
||||
})
|
||||
),
|
||||
DISMISS_ALERT: state =>
|
||||
state.merge(
|
||||
Map({
|
||||
message: "",
|
||||
show: false,
|
||||
type: "",
|
||||
})),
|
||||
}
|
||||
type: ""
|
||||
})
|
||||
)
|
||||
};
|
||||
|
||||
export default (state = defaultState, action) =>
|
||||
handlers[action.type] ? handlers[action.type](state, action) : state;
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { combineReducers } from "redux";
|
||||
|
||||
import movieStore from "./movies"
|
||||
import showsStore from "./shows"
|
||||
import showStore from "./show"
|
||||
import userStore from "./users"
|
||||
import alerts from "./alerts"
|
||||
import torrentStore from "./torrents"
|
||||
import adminStore from "./admins"
|
||||
import polochon from "./polochon"
|
||||
import notifications from "./notifications"
|
||||
import movieStore from "./movies";
|
||||
import showsStore from "./shows";
|
||||
import showStore from "./show";
|
||||
import userStore from "./users";
|
||||
import alerts from "./alerts";
|
||||
import torrentStore from "./torrents";
|
||||
import adminStore from "./admins";
|
||||
import polochon from "./polochon";
|
||||
import notifications from "./notifications";
|
||||
|
||||
export default combineReducers({
|
||||
movieStore,
|
||||
@ -19,5 +19,5 @@ export default combineReducers({
|
||||
torrentStore,
|
||||
adminStore,
|
||||
polochon,
|
||||
notifications,
|
||||
notifications
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { OrderedMap, Map, fromJS } from "immutable"
|
||||
import { OrderedMap, Map, fromJS } from "immutable";
|
||||
|
||||
const defaultState = Map({
|
||||
loading: false,
|
||||
@ -6,61 +6,95 @@ const defaultState = Map({
|
||||
filter: "",
|
||||
selectedImdbId: "",
|
||||
lastFetchUrl: "",
|
||||
exploreOptions: Map(),
|
||||
exploreOptions: Map()
|
||||
});
|
||||
|
||||
const handlers = {
|
||||
"MOVIE_LIST_FETCH_PENDING": state => state.set("loading", true),
|
||||
"MOVIE_LIST_FETCH_ERROR": state => state.set("loading", false),
|
||||
"MOVIE_LIST_FETCH_FULFILLED": (state, action) => {
|
||||
let allMoviesInPolochon = true
|
||||
let movies = Map()
|
||||
action.payload.response.data.map(function (movie) {
|
||||
MOVIE_LIST_FETCH_PENDING: state => state.set("loading", true),
|
||||
MOVIE_LIST_FETCH_ERROR: state => state.set("loading", false),
|
||||
MOVIE_LIST_FETCH_FULFILLED: (state, action) => {
|
||||
let allMoviesInPolochon = true;
|
||||
let movies = Map();
|
||||
action.payload.response.data.map(function(movie) {
|
||||
movie.fetchingDetails = false;
|
||||
movie.fetchingSubtitles = false;
|
||||
if (movie.polochon_url === "") {
|
||||
allMoviesInPolochon = false
|
||||
allMoviesInPolochon = false;
|
||||
}
|
||||
movies = movies.set(movie.imdb_id, fromJS(movie));
|
||||
})
|
||||
});
|
||||
|
||||
// Select the first movie if the list is not empty
|
||||
let selectedImdbId = "";
|
||||
if (movies.size > 0) {
|
||||
// Sort by year
|
||||
movies = movies.sort((a,b) => {
|
||||
movies = movies.sort((a, b) => {
|
||||
if (!allMoviesInPolochon) {
|
||||
return b.get("year") - a.get("year")
|
||||
return b.get("year") - a.get("year");
|
||||
}
|
||||
|
||||
const dateA = new Date(a.get("date_added"))
|
||||
const dateB = new Date(b.get("date_added"))
|
||||
const dateA = new Date(a.get("date_added"));
|
||||
const dateB = new Date(b.get("date_added"));
|
||||
return dateA > dateB ? -1 : dateA < dateB ? 1 : 0;
|
||||
});
|
||||
|
||||
selectedImdbId = movies.first().get("imdb_id");
|
||||
}
|
||||
|
||||
return state.delete("movies").merge(Map({
|
||||
return state.delete("movies").merge(
|
||||
Map({
|
||||
movies: movies,
|
||||
filter: "",
|
||||
loading: false,
|
||||
selectedImdbId: selectedImdbId,
|
||||
}))
|
||||
selectedImdbId: selectedImdbId
|
||||
})
|
||||
);
|
||||
},
|
||||
"MOVIE_GET_DETAILS_PENDING" : (state, action) => state.setIn(["movies", action.payload.main.imdbId, "fetchingDetails"], true),
|
||||
"MOVIE_GET_DETAILS_FULFILLED" : (state, action) => state.setIn(["movies", action.payload.response.data.imdb_id], fromJS(action.payload.response.data))
|
||||
.setIn(["movies", action.payload.response.data.imdb_id, "fetchingDetails"], false)
|
||||
.setIn(["movies", action.payload.response.data.imdb_id, "fetchingSubtitles"], false),
|
||||
"MOVIE_UPDATE_STORE_WISHLIST" : (state, action) => state.setIn(["movies", action.payload.imdbId, "wishlisted"], action.payload.wishlisted),
|
||||
"MOVIE_GET_EXPLORE_OPTIONS_FULFILLED" : (state, action) => state.set("exploreOptions", fromJS(action.payload.response.data)),
|
||||
"UPDATE_LAST_MOVIE_FETCH_URL" : (state, action) => state.set("lastFetchUrl", action.payload.url),
|
||||
"MOVIE_SUBTITLES_UPDATE_PENDING" : (state, action) => state.setIn(["movies", action.payload.main.imdbId, "fetchingSubtitles"], true),
|
||||
"MOVIE_SUBTITLES_UPDATE_FULFILLED" : (state, action) => state.setIn(["movies", action.payload.main.imdbId, "fetchingSubtitles"], false)
|
||||
.setIn(["movies", action.payload.main.imdbId, "subtitles"], fromJS(action.payload.response.data)),
|
||||
"SELECT_MOVIE" : (state, action) => state.set("selectedImdbId", action.payload.imdbId),
|
||||
"MOVIE_UPDATE_FILTER" : (state, action) => state.set("filter", action.payload.filter),
|
||||
}
|
||||
MOVIE_GET_DETAILS_PENDING: (state, action) =>
|
||||
state.setIn(
|
||||
["movies", action.payload.main.imdbId, "fetchingDetails"],
|
||||
true
|
||||
),
|
||||
MOVIE_GET_DETAILS_FULFILLED: (state, action) =>
|
||||
state
|
||||
.setIn(
|
||||
["movies", action.payload.response.data.imdb_id],
|
||||
fromJS(action.payload.response.data)
|
||||
)
|
||||
.setIn(
|
||||
["movies", action.payload.response.data.imdb_id, "fetchingDetails"],
|
||||
false
|
||||
)
|
||||
.setIn(
|
||||
["movies", action.payload.response.data.imdb_id, "fetchingSubtitles"],
|
||||
false
|
||||
),
|
||||
MOVIE_UPDATE_STORE_WISHLIST: (state, action) =>
|
||||
state.setIn(
|
||||
["movies", action.payload.imdbId, "wishlisted"],
|
||||
action.payload.wishlisted
|
||||
),
|
||||
MOVIE_GET_EXPLORE_OPTIONS_FULFILLED: (state, action) =>
|
||||
state.set("exploreOptions", fromJS(action.payload.response.data)),
|
||||
UPDATE_LAST_MOVIE_FETCH_URL: (state, action) =>
|
||||
state.set("lastFetchUrl", action.payload.url),
|
||||
MOVIE_SUBTITLES_UPDATE_PENDING: (state, action) =>
|
||||
state.setIn(
|
||||
["movies", action.payload.main.imdbId, "fetchingSubtitles"],
|
||||
true
|
||||
),
|
||||
MOVIE_SUBTITLES_UPDATE_FULFILLED: (state, action) =>
|
||||
state
|
||||
.setIn(["movies", action.payload.main.imdbId, "fetchingSubtitles"], false)
|
||||
.setIn(
|
||||
["movies", action.payload.main.imdbId, "subtitles"],
|
||||
fromJS(action.payload.response.data)
|
||||
),
|
||||
SELECT_MOVIE: (state, action) =>
|
||||
state.set("selectedImdbId", action.payload.imdbId),
|
||||
MOVIE_UPDATE_FILTER: (state, action) =>
|
||||
state.set("filter", action.payload.filter)
|
||||
};
|
||||
|
||||
export default (state = defaultState, action) =>
|
||||
handlers[action.type] ? handlers[action.type](state, action) : state;
|
||||
|
@ -1,15 +1,20 @@
|
||||
import { List, fromJS } from "immutable"
|
||||
import { List, fromJS } from "immutable";
|
||||
|
||||
const defaultState = List();
|
||||
|
||||
const handlers = {
|
||||
"ADD_NOTIFICATION": (state, action) => state.push(fromJS({
|
||||
id: Math.random().toString(36).substring(7),
|
||||
ADD_NOTIFICATION: (state, action) =>
|
||||
state.push(
|
||||
fromJS({
|
||||
id: Math.random()
|
||||
.toString(36)
|
||||
.substring(7),
|
||||
...action.payload
|
||||
})),
|
||||
"REMOVE_NOTIFICATION": (state, action) =>
|
||||
state.filter((e) => (e.get("id") !== action.payload.id)),
|
||||
}
|
||||
})
|
||||
),
|
||||
REMOVE_NOTIFICATION: (state, action) =>
|
||||
state.filter(e => e.get("id") !== action.payload.id)
|
||||
};
|
||||
|
||||
export default (state = defaultState, action) =>
|
||||
handlers[action.type] ? handlers[action.type](state, action) : state;
|
||||
|
@ -1,28 +1,29 @@
|
||||
import { List, Map, fromJS } from "immutable"
|
||||
import { List, Map, fromJS } from "immutable";
|
||||
|
||||
const defaultState = Map({
|
||||
loadingPublic: false,
|
||||
loadingManaged: false,
|
||||
public: List(),
|
||||
managed: List(),
|
||||
managed: List()
|
||||
});
|
||||
|
||||
const handlers = {
|
||||
"PUBLIC_POLOCHON_LIST_FETCH_PENDING": state => state.set("loadingPublic", true),
|
||||
"PUBLIC_POLOCHON_LIST_FETCH_FULFILLED": (state, action) => {
|
||||
PUBLIC_POLOCHON_LIST_FETCH_PENDING: state => state.set("loadingPublic", true),
|
||||
PUBLIC_POLOCHON_LIST_FETCH_FULFILLED: (state, action) => {
|
||||
return state.merge({
|
||||
loadingPublic: false,
|
||||
public: List(fromJS(action.payload.response.data)),
|
||||
public: List(fromJS(action.payload.response.data))
|
||||
});
|
||||
},
|
||||
"MANAGED_POLOCHON_LIST_FETCH_PENDING": state => state.set("loadingManaged", true),
|
||||
"MANAGED_POLOCHON_LIST_FETCH_FULFILLED": (state, action) => {
|
||||
MANAGED_POLOCHON_LIST_FETCH_PENDING: state =>
|
||||
state.set("loadingManaged", true),
|
||||
MANAGED_POLOCHON_LIST_FETCH_FULFILLED: (state, action) => {
|
||||
return state.merge({
|
||||
loadingManaged: false,
|
||||
managed: List(fromJS(action.payload.response.data)),
|
||||
managed: List(fromJS(action.payload.response.data))
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default (state = defaultState, action) =>
|
||||
handlers[action.type] ? handlers[action.type](state, action) : state;
|
||||
|
@ -1,35 +1,82 @@
|
||||
import { OrderedMap, Map, fromJS } from "immutable"
|
||||
import { OrderedMap, Map, fromJS } from "immutable";
|
||||
|
||||
const defaultState = Map({
|
||||
loading: false,
|
||||
show: Map({
|
||||
seasons: OrderedMap(),
|
||||
}),
|
||||
seasons: OrderedMap()
|
||||
})
|
||||
});
|
||||
|
||||
const handlers = {
|
||||
"SHOW_FETCH_DETAILS_PENDING": state => state.set("loading", true),
|
||||
"SHOW_FETCH_DETAILS_FULFILLED": (state, action) => sortEpisodes(state, action.payload.response.data),
|
||||
"SHOW_UPDATE_STORE_WISHLIST": (state, action) => {
|
||||
return state.mergeDeep(fromJS({
|
||||
"show": {
|
||||
"tracked_season": action.payload.season,
|
||||
"tracked_episode": action.payload.episode,
|
||||
SHOW_FETCH_DETAILS_PENDING: state => state.set("loading", true),
|
||||
SHOW_FETCH_DETAILS_FULFILLED: (state, action) =>
|
||||
sortEpisodes(state, action.payload.response.data),
|
||||
SHOW_UPDATE_STORE_WISHLIST: (state, action) => {
|
||||
return state.mergeDeep(
|
||||
fromJS({
|
||||
show: {
|
||||
tracked_season: action.payload.season,
|
||||
tracked_episode: action.payload.episode
|
||||
}
|
||||
}))},
|
||||
"EPISODE_GET_DETAILS_PENDING": (state, action) => state.setIn(["show", "seasons", action.payload.main.season, action.payload.main.episode, "fetching"], true),
|
||||
"EPISODE_GET_DETAILS_FULFILLED": (state, action) => {
|
||||
let data = action.payload.response.data;
|
||||
if (!data) { return state }
|
||||
data.fetching = false;
|
||||
return state.setIn(["show", "seasons", data.season, data.episode], fromJS(data));
|
||||
})
|
||||
);
|
||||
},
|
||||
"EPISODE_SUBTITLES_UPDATE_PENDING" : (state, action) =>
|
||||
state.setIn(["show", "seasons", action.payload.main.season, action.payload.main.episode, "fetchingSubtitles"], true),
|
||||
"EPISODE_SUBTITLES_UPDATE_FULFILLED": (state, action) =>
|
||||
state.setIn(["show", "seasons", action.payload.main.season, action.payload.main.episode, "subtitles"], fromJS(action.payload.response.data))
|
||||
.setIn(["show", "seasons", action.payload.main.season, action.payload.main.episode, "fetchingSubtitles"], false),
|
||||
}
|
||||
EPISODE_GET_DETAILS_PENDING: (state, action) =>
|
||||
state.setIn(
|
||||
[
|
||||
"show",
|
||||
"seasons",
|
||||
action.payload.main.season,
|
||||
action.payload.main.episode,
|
||||
"fetching"
|
||||
],
|
||||
true
|
||||
),
|
||||
EPISODE_GET_DETAILS_FULFILLED: (state, action) => {
|
||||
let data = action.payload.response.data;
|
||||
if (!data) {
|
||||
return state;
|
||||
}
|
||||
data.fetching = false;
|
||||
return state.setIn(
|
||||
["show", "seasons", data.season, data.episode],
|
||||
fromJS(data)
|
||||
);
|
||||
},
|
||||
EPISODE_SUBTITLES_UPDATE_PENDING: (state, action) =>
|
||||
state.setIn(
|
||||
[
|
||||
"show",
|
||||
"seasons",
|
||||
action.payload.main.season,
|
||||
action.payload.main.episode,
|
||||
"fetchingSubtitles"
|
||||
],
|
||||
true
|
||||
),
|
||||
EPISODE_SUBTITLES_UPDATE_FULFILLED: (state, action) =>
|
||||
state
|
||||
.setIn(
|
||||
[
|
||||
"show",
|
||||
"seasons",
|
||||
action.payload.main.season,
|
||||
action.payload.main.episode,
|
||||
"subtitles"
|
||||
],
|
||||
fromJS(action.payload.response.data)
|
||||
)
|
||||
.setIn(
|
||||
[
|
||||
"show",
|
||||
"seasons",
|
||||
action.payload.main.season,
|
||||
action.payload.main.episode,
|
||||
"fetchingSubtitles"
|
||||
],
|
||||
false
|
||||
)
|
||||
};
|
||||
|
||||
const sortEpisodes = (state, show) => {
|
||||
let episodes = show.episodes;
|
||||
@ -52,19 +99,19 @@ const sortEpisodes = (state, show) => {
|
||||
// Sort the episodes
|
||||
ret = ret.updateIn(["show", "seasons"], function(seasons) {
|
||||
return seasons.map(function(episodes) {
|
||||
return episodes.sort((a,b) => a.get("episode") - b.get("episode"));
|
||||
return episodes.sort((a, b) => a.get("episode") - b.get("episode"));
|
||||
});
|
||||
});
|
||||
|
||||
// Sort the seasons
|
||||
ret = ret.updateIn(["show", "seasons"], function(seasons) {
|
||||
return seasons.sort(function(a,b) {
|
||||
return seasons.sort(function(a, b) {
|
||||
return a.first().get("season") - b.first().get("season");
|
||||
});
|
||||
});
|
||||
|
||||
return ret
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
export default (state = defaultState, action) =>
|
||||
handlers[action.type] ? handlers[action.type](state, action) : state;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { OrderedMap, Map, fromJS } from "immutable"
|
||||
import { OrderedMap, Map, fromJS } from "immutable";
|
||||
|
||||
const defaultState = Map({
|
||||
loading: false,
|
||||
@ -6,14 +6,14 @@ const defaultState = Map({
|
||||
filter: "",
|
||||
selectedImdbId: "",
|
||||
lastFetchUrl: "",
|
||||
exploreOptions: Map(),
|
||||
exploreOptions: Map()
|
||||
});
|
||||
|
||||
const handlers = {
|
||||
"SHOW_LIST_FETCH_PENDING": state => state.set("loading", true),
|
||||
"SHOW_LIST_FETCH_FULFILLED": (state, action) => {
|
||||
SHOW_LIST_FETCH_PENDING: state => state.set("loading", true),
|
||||
SHOW_LIST_FETCH_FULFILLED: (state, action) => {
|
||||
let shows = Map();
|
||||
action.payload.response.data.map(function (show) {
|
||||
action.payload.response.data.map(function(show) {
|
||||
show.fetchingDetails = false;
|
||||
show.fetchingSubtitles = false;
|
||||
shows = shows.set(show.imdb_id, fromJS(show));
|
||||
@ -22,26 +22,30 @@ const handlers = {
|
||||
let selectedImdbId = "";
|
||||
if (shows.size > 0) {
|
||||
// Sort by year
|
||||
shows = shows.sort((a,b) => b.get("year") - a.get("year"));
|
||||
shows = shows.sort((a, b) => b.get("year") - a.get("year"));
|
||||
selectedImdbId = shows.first().get("imdb_id");
|
||||
}
|
||||
|
||||
return state.delete("shows").merge(Map({
|
||||
return state.delete("shows").merge(
|
||||
Map({
|
||||
shows: shows,
|
||||
filter: "",
|
||||
loading: false,
|
||||
selectedImdbId: selectedImdbId,
|
||||
}));
|
||||
selectedImdbId: selectedImdbId
|
||||
})
|
||||
);
|
||||
},
|
||||
"SHOW_GET_DETAILS_PENDING": (state, action) => state.setIn(["shows", action.payload.main.imdbId, "fetchingDetails"], true),
|
||||
"SHOW_GET_DETAILS_FULFILLED": (state, action) => {
|
||||
SHOW_GET_DETAILS_PENDING: (state, action) =>
|
||||
state.setIn(["shows", action.payload.main.imdbId, "fetchingDetails"], true),
|
||||
SHOW_GET_DETAILS_FULFILLED: (state, action) => {
|
||||
let show = action.payload.response.data;
|
||||
show.fetchingDetails = false;
|
||||
show.fetchingSubtitles = false;
|
||||
return state.setIn(["shows", show.imdb_id], fromJS(show));
|
||||
},
|
||||
"SHOW_GET_EXPLORE_OPTIONS_FULFILLED": (state, action) => state.set("exploreOptions", fromJS(action.payload.response.data)),
|
||||
"SHOW_UPDATE_STORE_WISHLIST": (state, action) => {
|
||||
SHOW_GET_EXPLORE_OPTIONS_FULFILLED: (state, action) =>
|
||||
state.set("exploreOptions", fromJS(action.payload.response.data)),
|
||||
SHOW_UPDATE_STORE_WISHLIST: (state, action) => {
|
||||
let season = action.payload.season;
|
||||
let episode = action.payload.episode;
|
||||
if (action.payload.wishlisted) {
|
||||
@ -53,15 +57,21 @@ const handlers = {
|
||||
}
|
||||
}
|
||||
|
||||
return state.mergeIn(["shows", action.payload.imdbId], Map({
|
||||
"tracked_season": season,
|
||||
"tracked_episode": episode,
|
||||
}));
|
||||
return state.mergeIn(
|
||||
["shows", action.payload.imdbId],
|
||||
Map({
|
||||
tracked_season: season,
|
||||
tracked_episode: episode
|
||||
})
|
||||
);
|
||||
},
|
||||
"UPDATE_LAST_SHOWS_FETCH_URL": (state, action) => state.set("lastFetchUrl", action.payload.url),
|
||||
"SELECT_SHOW": (state, action) => state.set("selectedImdbId", action.payload.imdbId),
|
||||
"SHOWS_UPDATE_FILTER": (state, action) => state.set("filter", action.payload.filter),
|
||||
}
|
||||
UPDATE_LAST_SHOWS_FETCH_URL: (state, action) =>
|
||||
state.set("lastFetchUrl", action.payload.url),
|
||||
SELECT_SHOW: (state, action) =>
|
||||
state.set("selectedImdbId", action.payload.imdbId),
|
||||
SHOWS_UPDATE_FILTER: (state, action) =>
|
||||
state.set("filter", action.payload.filter)
|
||||
};
|
||||
|
||||
export default (state = defaultState, action) =>
|
||||
handlers[action.type] ? handlers[action.type](state, action) : state;
|
||||
|
@ -1,24 +1,30 @@
|
||||
import { Map, List, fromJS } from "immutable"
|
||||
import { Map, List, fromJS } from "immutable";
|
||||
|
||||
const defaultState = Map({
|
||||
"fetching": false,
|
||||
"searching": false,
|
||||
"torrents": List(),
|
||||
"searchResults": List(),
|
||||
fetching: false,
|
||||
searching: false,
|
||||
torrents: List(),
|
||||
searchResults: List()
|
||||
});
|
||||
|
||||
const handlers = {
|
||||
"TORRENTS_FETCH_PENDING": state => state.set("fetching", true),
|
||||
"TORRENTS_FETCH_FULFILLED": (state, action) => state.merge(fromJS({
|
||||
TORRENTS_FETCH_PENDING: state => state.set("fetching", true),
|
||||
TORRENTS_FETCH_FULFILLED: (state, action) =>
|
||||
state.merge(
|
||||
fromJS({
|
||||
fetching: false,
|
||||
torrents: action.payload.response.data,
|
||||
})),
|
||||
"TORRENTS_SEARCH_PENDING": state => state.set("searching", true),
|
||||
"TORRENTS_SEARCH_FULFILLED": (state, action) => state.merge(fromJS({
|
||||
torrents: action.payload.response.data
|
||||
})
|
||||
),
|
||||
TORRENTS_SEARCH_PENDING: state => state.set("searching", true),
|
||||
TORRENTS_SEARCH_FULFILLED: (state, action) =>
|
||||
state.merge(
|
||||
fromJS({
|
||||
searching: false,
|
||||
searchResults: action.payload.response.data,
|
||||
})),
|
||||
}
|
||||
searchResults: action.payload.response.data
|
||||
})
|
||||
)
|
||||
};
|
||||
|
||||
export default (state = defaultState, action) =>
|
||||
handlers[action.type] ? handlers[action.type](state, action) : state;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Map, List, fromJS } from "immutable"
|
||||
import { Map, List, fromJS } from "immutable";
|
||||
|
||||
import jwtDecode from "jwt-decode"
|
||||
import Cookies from "universal-cookie"
|
||||
import jwtDecode from "jwt-decode";
|
||||
import Cookies from "universal-cookie";
|
||||
|
||||
const defaultState = Map({
|
||||
error: "",
|
||||
@ -18,52 +18,66 @@ const defaultState = Map({
|
||||
polochonActivated: false,
|
||||
tokens: List(),
|
||||
modules: Map(),
|
||||
modulesLoading: false,
|
||||
modulesLoading: false
|
||||
});
|
||||
|
||||
let initialState = defaultState
|
||||
let initialState = defaultState;
|
||||
const token = localStorage.getItem("token");
|
||||
if (token && token !== "") {
|
||||
initialState = updateFromToken(initialState, token)
|
||||
initialState = updateFromToken(initialState, token);
|
||||
}
|
||||
|
||||
const handlers = {
|
||||
"USER_LOGIN_PENDING": state => state.set("loading", true),
|
||||
"USER_LOGIN_ERROR": (state, action) => {
|
||||
USER_LOGIN_PENDING: state => state.set("loading", true),
|
||||
USER_LOGIN_ERROR: (state, action) => {
|
||||
state.set("loading", false);
|
||||
return logoutUser(action.payload.response.message);
|
||||
},
|
||||
"USER_LOGIN_FULFILLED": (state, action) => {
|
||||
return updateFromToken(state, action.payload.response.data.token)
|
||||
USER_LOGIN_FULFILLED: (state, action) => {
|
||||
return updateFromToken(state, action.payload.response.data.token);
|
||||
},
|
||||
"USER_SIGNUP_PENDING": state => state.set("loading", true),
|
||||
"USER_SIGNUP_ERROR": (state, action) => state.merge(Map({
|
||||
USER_SIGNUP_PENDING: state => state.set("loading", true),
|
||||
USER_SIGNUP_ERROR: (state, action) =>
|
||||
state.merge(
|
||||
Map({
|
||||
error: action.payload.response.message,
|
||||
loading: false,
|
||||
})),
|
||||
"USER_SIGNUP_FULFILLED": state => state.merge(Map({error: "", loading: false})),
|
||||
"USER_SET_TOKEN": (state, action) => updateFromToken(state, action.payload.token),
|
||||
"USER_LOGOUT": () => logoutUser(),
|
||||
"GET_USER_PENDING": state => state.set("loading", true),
|
||||
"GET_USER_FULFILLED": (state, action) => state.merge(Map({
|
||||
loading: false
|
||||
})
|
||||
),
|
||||
USER_SIGNUP_FULFILLED: state =>
|
||||
state.merge(Map({ error: "", loading: false })),
|
||||
USER_SET_TOKEN: (state, action) =>
|
||||
updateFromToken(state, action.payload.token),
|
||||
USER_LOGOUT: () => logoutUser(),
|
||||
GET_USER_PENDING: state => state.set("loading", true),
|
||||
GET_USER_FULFILLED: (state, action) =>
|
||||
state.merge(
|
||||
Map({
|
||||
polochonToken: action.payload.response.data.token,
|
||||
polochonUrl: action.payload.response.data.url,
|
||||
polochonName: action.payload.response.data.name,
|
||||
polochonId: action.payload.response.data.id,
|
||||
polochonActivated: action.payload.response.data.activated,
|
||||
loading: false,
|
||||
})),
|
||||
"GET_USER_TOKENS_PENDING": state => state.set("loading", true),
|
||||
"GET_USER_TOKENS_FULFILLED": (state, action) => state.merge(Map({
|
||||
"tokens": fromJS(action.payload.response.data),
|
||||
"loading": false,
|
||||
})),
|
||||
"GET_USER_MODULES_PENDING": state => state.set("modulesLoading", true),
|
||||
"GET_USER_MODULES_FULFILLED": (state, action) => state.merge(Map({
|
||||
"modules": fromJS(action.payload.response.data),
|
||||
"modulesLoading": false,
|
||||
})),
|
||||
}
|
||||
loading: false
|
||||
})
|
||||
),
|
||||
GET_USER_TOKENS_PENDING: state => state.set("loading", true),
|
||||
GET_USER_TOKENS_FULFILLED: (state, action) =>
|
||||
state.merge(
|
||||
Map({
|
||||
tokens: fromJS(action.payload.response.data),
|
||||
loading: false
|
||||
})
|
||||
),
|
||||
GET_USER_MODULES_PENDING: state => state.set("modulesLoading", true),
|
||||
GET_USER_MODULES_FULFILLED: (state, action) =>
|
||||
state.merge(
|
||||
Map({
|
||||
modules: fromJS(action.payload.response.data),
|
||||
modulesLoading: false
|
||||
})
|
||||
)
|
||||
};
|
||||
|
||||
function logoutUser(error) {
|
||||
localStorage.removeItem("token");
|
||||
@ -72,7 +86,7 @@ function logoutUser(error) {
|
||||
if (error !== "") {
|
||||
return defaultState.set("error", error);
|
||||
} else {
|
||||
return defaultState
|
||||
return defaultState;
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,14 +97,16 @@ function updateFromToken(state, token) {
|
||||
const cookies = new Cookies();
|
||||
cookies.set("token", token);
|
||||
|
||||
return state.merge(Map({
|
||||
return state.merge(
|
||||
Map({
|
||||
error: "",
|
||||
isLogged: true,
|
||||
isTokenSet: true,
|
||||
isAdmin: decodedToken.isAdmin,
|
||||
isActivated: decodedToken.isActivated,
|
||||
username: decodedToken.username,
|
||||
}))
|
||||
username: decodedToken.username
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export default (state = initialState, action) =>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import axios from "axios"
|
||||
import axios from "axios";
|
||||
|
||||
// This functions returns an axios instance, the token is added to the
|
||||
// configuration if found in the localStorage
|
||||
@ -6,17 +6,22 @@ export function configureAxios(headers = {}) {
|
||||
// Get the token from the localStorate
|
||||
const token = localStorage.getItem("token");
|
||||
if (token) {
|
||||
headers = { "Authorization": `Bearer ${token}` };
|
||||
headers = { Authorization: `Bearer ${token}` };
|
||||
}
|
||||
|
||||
return axios.create({
|
||||
headers
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// This function takes en event prefix to dispatch evens during the life of the
|
||||
// request, it also take a promise (axios request)
|
||||
export function request(eventPrefix, promise, callbackEvents = null, mainPayload = null) {
|
||||
export function request(
|
||||
eventPrefix,
|
||||
promise,
|
||||
callbackEvents = null,
|
||||
mainPayload = null
|
||||
) {
|
||||
// Events
|
||||
const pending = `${eventPrefix}_PENDING`;
|
||||
const fulfilled = `${eventPrefix}_FULFILLED`;
|
||||
@ -26,37 +31,36 @@ export function request(eventPrefix, promise, callbackEvents = null, mainPayload
|
||||
dispatch({
|
||||
type: pending,
|
||||
payload: {
|
||||
main: mainPayload,
|
||||
main: mainPayload
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return promise
|
||||
.then(response => {
|
||||
if (response.data.status === "error")
|
||||
{
|
||||
if (response.data.status === "error") {
|
||||
dispatch({
|
||||
type: "ADD_ALERT_ERROR",
|
||||
payload: {
|
||||
message: response.data.message,
|
||||
main: mainPayload,
|
||||
main: mainPayload
|
||||
}
|
||||
});
|
||||
dispatch({
|
||||
type: errored,
|
||||
payload: {
|
||||
response: response.data,
|
||||
main: mainPayload,
|
||||
},
|
||||
})
|
||||
main: mainPayload
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
dispatch({
|
||||
type: fulfilled,
|
||||
payload: {
|
||||
response: response.data,
|
||||
main: mainPayload,
|
||||
},
|
||||
})
|
||||
main: mainPayload
|
||||
}
|
||||
});
|
||||
if (callbackEvents) {
|
||||
for (let event of callbackEvents) {
|
||||
if (typeof event === "function") {
|
||||
@ -70,16 +74,16 @@ export function request(eventPrefix, promise, callbackEvents = null, mainPayload
|
||||
// Unauthorized
|
||||
if (error.response && error.response.status == 401) {
|
||||
dispatch({
|
||||
type: "USER_LOGOUT",
|
||||
})
|
||||
type: "USER_LOGOUT"
|
||||
});
|
||||
}
|
||||
dispatch({
|
||||
type: "ADD_ALERT_ERROR",
|
||||
payload: {
|
||||
message: error.response.data,
|
||||
main: mainPayload,
|
||||
}
|
||||
})
|
||||
})
|
||||
main: mainPayload
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -17,12 +17,7 @@ if (process.env.NODE_ENV === "development") {
|
||||
// Export the store
|
||||
const store = createStore(
|
||||
rootReducer,
|
||||
compose(
|
||||
applyMiddleware(
|
||||
...middlewares,
|
||||
),
|
||||
),
|
||||
compose(applyMiddleware(...middlewares))
|
||||
);
|
||||
|
||||
export default store;
|
||||
|
||||
|
@ -1,59 +1,64 @@
|
||||
export const prettyDurationFromMinutes = (runtime) => {
|
||||
export const prettyDurationFromMinutes = runtime => {
|
||||
const hours = Math.floor(runtime / 60);
|
||||
const minutes = (runtime % 60);
|
||||
const minutes = runtime % 60;
|
||||
|
||||
let duration = "";
|
||||
if (hours > 0) { duration += hours + "h" }
|
||||
if (minutes > 0) { duration += ("0" + minutes).slice(-2) }
|
||||
if (hours === 0) { duration += " min" }
|
||||
if (hours > 0) {
|
||||
duration += hours + "h";
|
||||
}
|
||||
if (minutes > 0) {
|
||||
duration += ("0" + minutes).slice(-2);
|
||||
}
|
||||
if (hours === 0) {
|
||||
duration += " min";
|
||||
}
|
||||
|
||||
return duration;
|
||||
}
|
||||
};
|
||||
|
||||
const pad = (d) => (d < 10) ? "0" + d.toString() : d.toString();
|
||||
const pad = d => (d < 10 ? "0" + d.toString() : d.toString());
|
||||
|
||||
export const prettyEpisodeName = (showName, season, episode) =>
|
||||
`${showName} S${pad(season)}E${pad(episode)}`;
|
||||
|
||||
export const inLibrary = (element) =>
|
||||
element.get("polochon_url", "") !== "";
|
||||
export const inLibrary = element => element.get("polochon_url", "") !== "";
|
||||
|
||||
export const isWishlisted = (element) => {
|
||||
const wishlisted = element.get("wishlisted", undefined)
|
||||
export const isWishlisted = element => {
|
||||
const wishlisted = element.get("wishlisted", undefined);
|
||||
if (wishlisted !== undefined) {
|
||||
return wishlisted;
|
||||
}
|
||||
|
||||
const trackedSeason = element.get("tracked_season", null);
|
||||
const trackedEpisode = element.get("tracked_episode", null);
|
||||
if ((trackedSeason !== null) && (trackedEpisode !== null)) {
|
||||
if (trackedSeason !== null && trackedEpisode !== null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const isEpisodeWishlisted = (element, trackedSeason, trackedEpisode) => {
|
||||
if ((trackedSeason === null) && (trackedEpisode === null)) {
|
||||
if (trackedSeason === null && trackedEpisode === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((trackedSeason === 0) && (trackedEpisode === 0)) {
|
||||
return true
|
||||
if (trackedSeason === 0 && trackedEpisode === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const season = element.get("season", 0);
|
||||
const episode = element.get("episode", 0);
|
||||
if (season < trackedSeason) {
|
||||
return false
|
||||
return false;
|
||||
} else if (season > trackedSeason) {
|
||||
return true
|
||||
return true;
|
||||
} else {
|
||||
return (episode >= trackedEpisode)
|
||||
return episode >= trackedEpisode;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const prettySize = (fileSizeInBytes) => {
|
||||
export const prettySize = fileSizeInBytes => {
|
||||
var i = -1;
|
||||
var byteUnits = [" kB", " MB", " GB", " TB", "PB", "EB", "ZB", "YB"];
|
||||
do {
|
||||
@ -62,4 +67,4 @@ export const prettySize = (fileSizeInBytes) => {
|
||||
} while (fileSizeInBytes > 1024);
|
||||
|
||||
return Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i];
|
||||
}
|
||||
};
|
||||
|
@ -1,5 +1,3 @@
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require("autoprefixer")
|
||||
]
|
||||
}
|
||||
plugins: [require("autoprefixer")]
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
var webpack = require("webpack");
|
||||
var path = require("path");
|
||||
var WebpackPwaManifest = require("webpack-pwa-manifest")
|
||||
var WebpackPwaManifest = require("webpack-pwa-manifest");
|
||||
var HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
var { CleanWebpackPlugin } = require("clean-webpack-plugin");
|
||||
|
||||
@ -19,7 +19,7 @@ const config = {
|
||||
entry: path.join(SRC_DIR, "js/app.js"),
|
||||
output: {
|
||||
path: BUILD_DIR,
|
||||
filename: "[contenthash]-app.js",
|
||||
filename: "[contenthash]-app.js"
|
||||
},
|
||||
optimization: {
|
||||
runtimeChunk: "single",
|
||||
@ -28,10 +28,10 @@ const config = {
|
||||
vendor: {
|
||||
test: /[\\/]node_modules[\\/]/,
|
||||
name: "vendors",
|
||||
chunks: "all",
|
||||
},
|
||||
},
|
||||
},
|
||||
chunks: "all"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
@ -47,44 +47,39 @@ const config = {
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
"style-loader",
|
||||
"css-loader",
|
||||
"sass-loader",
|
||||
"postcss-loader",
|
||||
],
|
||||
use: ["style-loader", "css-loader", "sass-loader", "postcss-loader"]
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpg|svg|ico)$/,
|
||||
use: ["file-loader?name=[hash]-[name].[ext]"],
|
||||
use: ["file-loader?name=[hash]-[name].[ext]"]
|
||||
},
|
||||
{
|
||||
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||
use: ["url-loader?limit=10000&mimetype=application/font-woff"],
|
||||
use: ["url-loader?limit=10000&mimetype=application/font-woff"]
|
||||
},
|
||||
{
|
||||
test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||
use: ["file-loader"],
|
||||
use: ["file-loader"]
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
|
||||
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV)
|
||||
}),
|
||||
new webpack.HashedModuleIdsPlugin(),
|
||||
new CleanWebpackPlugin({
|
||||
cleanOnceBeforeBuildPatterns: ["**/*", "!img/**/*", "!img"],
|
||||
cleanOnceBeforeBuildPatterns: ["**/*", "!img/**/*", "!img"]
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
template: path.join(SRC_DIR, "index.html"),
|
||||
template: path.join(SRC_DIR, "index.html")
|
||||
}),
|
||||
new WebpackPwaManifest({
|
||||
fingerprints: true,
|
||||
inject: true,
|
||||
ios: {
|
||||
"apple-mobile-web-app-status-bar-style": "default",
|
||||
"apple-mobile-web-app-title": "Canapé",
|
||||
"apple-mobile-web-app-title": "Canapé"
|
||||
},
|
||||
name: "Canapé",
|
||||
short_name: "Canapé",
|
||||
@ -97,23 +92,23 @@ const config = {
|
||||
icons: [
|
||||
{
|
||||
src: path.resolve(__dirname, "img/android-chrome-512x512.png"),
|
||||
sizes: [96, 128, 192, 256, 384, 512],
|
||||
sizes: [96, 128, 192, 256, 384, 512]
|
||||
},
|
||||
{
|
||||
src: path.resolve(__dirname, "img/apple-touch-icon.png"),
|
||||
sizes: [80, 120, 152, 167, 180],
|
||||
ios: true
|
||||
},
|
||||
],
|
||||
}),
|
||||
}
|
||||
]
|
||||
})
|
||||
],
|
||||
resolve: {
|
||||
extensions: [".js"],
|
||||
},
|
||||
extensions: [".js"]
|
||||
}
|
||||
};
|
||||
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
config.devtool = "#cheap-module-source-map"
|
||||
config.devtool = "#cheap-module-source-map";
|
||||
}
|
||||
|
||||
module.exports = config;
|
||||
|
Loading…
x
Reference in New Issue
Block a user