From d94843be9f7820b51e6c66a2786dcde71cad6da4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Delattre?= Date: Mon, 13 Apr 2020 16:31:47 +0200 Subject: [PATCH] Group the torrents by types in the torrent list By the way let's show more infos about what's being downloaded. --- frontend/js/components/navbar.js | 2 +- .../js/components/torrents/list/poster.js | 17 +++++++ .../js/components/torrents/list/progress.js | 36 +++++++------- .../js/components/torrents/list/torrent.js | 43 ++++++++-------- .../components/torrents/list/torrentGroup.js | 49 +++++++++++++++++++ .../js/components/torrents/list/torrents.js | 12 +++-- frontend/js/reducers/torrents.js | 37 +++++++++++++- frontend/js/utils.js | 3 ++ 8 files changed, 152 insertions(+), 47 deletions(-) create mode 100644 frontend/js/components/torrents/list/poster.js create mode 100644 frontend/js/components/torrents/list/torrentGroup.js diff --git a/frontend/js/components/navbar.js b/frontend/js/components/navbar.js index c0f0e5d..13964f1 100644 --- a/frontend/js/components/navbar.js +++ b/frontend/js/components/navbar.js @@ -170,7 +170,7 @@ const TorrentsDropdown = () => { }; const TorrentsDropdownTitle = () => { - const count = useSelector((state) => state.torrents.torrents.length); + const count = useSelector((state) => state.torrents.count); if (count === 0) { return Torrents; } diff --git a/frontend/js/components/torrents/list/poster.js b/frontend/js/components/torrents/list/poster.js new file mode 100644 index 0000000..9d8a919 --- /dev/null +++ b/frontend/js/components/torrents/list/poster.js @@ -0,0 +1,17 @@ +import React from "react"; +import PropTypes from "prop-types"; + +export const Poster = ({ url }) => { + if (!url || url === "") { + return null; + } + + return ( +
+ +
+ ); +}; +Poster.propTypes = { + url: PropTypes.string, +}; diff --git a/frontend/js/components/torrents/list/progress.js b/frontend/js/components/torrents/list/progress.js index eb63bd4..02f7cf8 100644 --- a/frontend/js/components/torrents/list/progress.js +++ b/frontend/js/components/torrents/list/progress.js @@ -20,25 +20,27 @@ export const Progress = ({ torrent }) => { const totalSize = prettySize(torrent.status.total_size); const downloadRate = prettySize(torrent.status.download_rate) + "/s"; return ( -
+
+
+
+
{started && ( - <> -
-
-
-

- {downloadedSize} / {totalSize} - {percentDone} - {downloadRate} -

- +

+ {downloadedSize} / {totalSize} - {percentDone} - {downloadRate} +

+ )} + {!started && ( +

+ Not yet started +

)} - {!started &&

Download not yet started

}
); }; diff --git a/frontend/js/components/torrents/list/torrent.js b/frontend/js/components/torrents/list/torrent.js index ccc3cc3..c438058 100644 --- a/frontend/js/components/torrents/list/torrent.js +++ b/frontend/js/components/torrents/list/torrent.js @@ -2,7 +2,7 @@ import React from "react"; import PropTypes from "prop-types"; import { useDispatch } from "react-redux"; -import { prettyEpisodeName } from "../../../utils"; +import { prettyEpisodeNameWithoutShow } from "../../../utils"; import { removeTorrent } from "../../../actions/torrents"; import { Progress } from "./progress"; @@ -10,32 +10,31 @@ import { Progress } from "./progress"; export const Torrent = ({ torrent }) => { const dispatch = useDispatch(); - const torrentTitle = (torrent) => { - switch (torrent.type) { - case "movie": - return torrent.video ? torrent.video.title : torrent.status.name; - case "episode": - return torrent.video - ? prettyEpisodeName( - torrent.video.show_title, - torrent.video.season, - torrent.video.episode - ) - : torrent.status.name; - default: - return torrent.status.name; + const title = (torrent) => { + if (torrent.type !== "episode" || !torrent.video) { + return ""; } + + return ( + prettyEpisodeNameWithoutShow( + torrent.video.season, + torrent.video.episode + ) + " - " + ); }; return ( -
-
- {torrentTitle(torrent)} - +
+
+ {title(torrent)} + {torrent.status.name} +
+
dispatch(removeTorrent(torrent.status.id))} - > -
+ /> +
); diff --git a/frontend/js/components/torrents/list/torrentGroup.js b/frontend/js/components/torrents/list/torrentGroup.js new file mode 100644 index 0000000..9e3d568 --- /dev/null +++ b/frontend/js/components/torrents/list/torrentGroup.js @@ -0,0 +1,49 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { useSelector } from "react-redux"; + +import { Torrent } from "./torrent"; +import { Poster } from "./poster"; + +export const TorrentGroup = ({ torrentKey }) => { + const torrents = useSelector((state) => + state.torrents.torrents.get(torrentKey) + ); + + if (torrents.length === 0) { + return null; + } + + const title = (torrent) => { + switch (torrent.type) { + case "movie": + return torrent.video.title; + case "episode": + return torrent.video.show_title; + default: + return "Files"; + } + }; + + return ( +
+
+ +
+
+

{title(torrents[0])}

+ {torrents.map((torrent, i) => ( + + ))} +
+
+
+
+ ); +}; +TorrentGroup.propTypes = { + torrentKey: PropTypes.string.isRequired, +}; diff --git a/frontend/js/components/torrents/list/torrents.js b/frontend/js/components/torrents/list/torrents.js index 5a08b38..970e4c9 100644 --- a/frontend/js/components/torrents/list/torrents.js +++ b/frontend/js/components/torrents/list/torrents.js @@ -1,12 +1,14 @@ import React from "react"; import { useSelector } from "react-redux"; -import { Torrent } from "./torrent"; +import { TorrentGroup } from "./torrentGroup"; export const Torrents = () => { - const torrents = useSelector((state) => state.torrents.torrents); + const torrentsKeys = useSelector((state) => + Array.from(state.torrents.torrents.keys()) + ); - if (torrents.length === 0) { + if (torrentsKeys.length === 0) { return (

No torrents

@@ -16,8 +18,8 @@ export const Torrents = () => { return (
- {torrents.map((torrent, index) => ( - + {torrentsKeys.map((key) => ( + ))}
); diff --git a/frontend/js/reducers/torrents.js b/frontend/js/reducers/torrents.js index 3dee8bb..920ba1b 100644 --- a/frontend/js/reducers/torrents.js +++ b/frontend/js/reducers/torrents.js @@ -3,10 +3,42 @@ import { produce } from "immer"; const defaultState = { fetching: false, searching: false, - torrents: [], + torrents: new Map(), + count: 0, searchResults: [], }; +// Group the torrents by imdb id +const formatTorrents = (input) => { + let torrents = new Map(); + if (!input) { + return torrents; + } + + input.forEach((t) => { + let key; + switch (t.type) { + case "movie": + key = t.video ? t.video.imdb_id : "unknown"; + break; + case "episode": + key = t.video ? t.video.show_imdb_id : "unknown"; + break; + default: + key = "unknown"; + break; + } + + if (!torrents.has(key)) { + torrents.set(key, []); + } + + torrents.get(key).push(t); + }); + + return torrents; +}; + export default (state = defaultState, action) => produce(state, (draft) => { switch (action.type) { @@ -16,7 +48,8 @@ export default (state = defaultState, action) => case "TORRENTS_FETCH_FULFILLED": draft.fetching = false; - draft.torrents = action.payload.response.data; + draft.torrents = formatTorrents(action.payload.response.data); + draft.count = action.payload.response.data.length; break; case "TORRENTS_SEARCH_PENDING": diff --git a/frontend/js/utils.js b/frontend/js/utils.js index e6ba682..bbb19c0 100644 --- a/frontend/js/utils.js +++ b/frontend/js/utils.js @@ -18,6 +18,9 @@ export const prettyDurationFromMinutes = (runtime) => { const pad = (d) => (d < 10 ? "0" + d.toString() : d.toString()); +export const prettyEpisodeNameWithoutShow = (season, episode) => + `S${pad(season)}E${pad(episode)}`; + export const prettyEpisodeName = (showName, season, episode) => `${showName} S${pad(season)}E${pad(episode)}`; -- 2.47.1