Skip to content

Commit

Permalink
add sorting torrent list on categories, tags, search
Browse files Browse the repository at this point in the history
  • Loading branch information
tdjsnelling committed Mar 19, 2023
1 parent 5eb4dd3 commit edc5e82
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 41 deletions.
32 changes: 20 additions & 12 deletions api/src/controllers/torrent.js
Original file line number Diff line number Diff line change
Expand Up @@ -452,10 +452,19 @@ export const getTorrentsPage = async ({
tag,
uploadedBy,
userId,
sort,
tracker,
}) => {
const queryNGrams = nGrams(query, false, 2, false).join(" ");

const [sortField, sortDirString] = sort?.split(":") ?? [];
const sortDir = sortDirString === "asc" ? 1 : -1;

const combinedSort = {};
if (sortField) combinedSort[sortField] = sortDir;
if (query) combinedSort.confidenceScore = { $meta: "textScore" };
combinedSort.created = -1;

const torrents = await Torrent.aggregate([
...(query
? [
Expand Down Expand Up @@ -529,17 +538,6 @@ export const getTorrentsPage = async ({
},
]
: []),
{
$sort: query
? { confidenceScore: { $meta: "textScore" } }
: { created: -1 },
},
{
$skip: skip,
},
{
$limit: limit,
},
{
$lookup: {
from: "comments",
Expand Down Expand Up @@ -588,6 +586,15 @@ export const getTorrentsPage = async ({
},
},
{ $unwind: { path: "$fetchedBy", preserveNullAndEmptyArrays: true } },
{
$sort: combinedSort,
},
{
$skip: skip,
},
{
$limit: limit,
},
]);

const [count] = await Torrent.aggregate([
Expand Down Expand Up @@ -682,7 +689,7 @@ export const listAll = async (req, res, next) => {
};

export const searchTorrents = (tracker) => async (req, res, next) => {
const { query, category, source, tag, page } = req.query;
const { query, category, source, tag, page, sort } = req.query;
try {
const torrents = await getTorrentsPage({
skip: page ? parseInt(page) : 0,
Expand All @@ -691,6 +698,7 @@ export const searchTorrents = (tracker) => async (req, res, next) => {
source,
tag: tag ? decodeURIComponent(tag) : undefined,
userId: req.userId,
sort: sort ? decodeURIComponent(sort) : undefined,
tracker,
});
res.json(torrents);
Expand Down
53 changes: 51 additions & 2 deletions client/components/List.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import React from "react";
import React, { useState } from "react";
import Link from "next/link";
import { useRouter } from "next/router";
import { toPath } from "lodash";
import qs from "qs";
import { CaretUp } from "@styled-icons/boxicons-regular/CaretUp";
import { CaretDown } from "@styled-icons/boxicons-regular/CaretDown";
import Box from "../components/Box";
import Text from "../components/Text";

Expand Down Expand Up @@ -47,7 +51,17 @@ const ListItem = ({ children }) => {
);
};

const getSortIcon = (accessor, sort = "") => {
const [sortAccessor, sortDirection] = sort.split(":");
if (accessor !== sortAccessor) return null;
if (sortDirection === "asc") return CaretUp;
if (sortDirection === "desc") return CaretDown;
return null;
};

const List = ({ data = [], columns = [], ...rest }) => {
const router = useRouter();
const { sort } = router.query;
return (
<Box overflowX="auto">
<Box minWidth="700px">
Expand All @@ -65,7 +79,42 @@ const List = ({ data = [], columns = [], ...rest }) => {
fontWeight={600}
fontSize={1}
textAlign={col.rightAlign ? "right" : "left"}
_css={{ textTransform: "uppercase" }}
_css={{
textTransform: "uppercase",
cursor: col.sortable ? "pointer" : "text",
userSelect: col.sortable ? "none" : "auto",
}}
onClick={
col.sortable
? () => {
const query = window.location.search;
const parsed = qs.parse(query.replace("?", ""));
if (parsed.sort) {
const [accessor, direction] = parsed.sort.split(":");
if (accessor === col.accessor) {
if (direction === "asc")
parsed.sort = `${col.accessor}:desc`;
else if (direction === "desc") delete parsed.sort;
} else {
parsed.sort = `${col.accessor}:asc`;
}
} else {
parsed.sort = `${col.accessor}:asc`;
}
router.replace(
Object.keys(parsed).length
? `${window.location.pathname}?${qs.stringify(
parsed
)}`
: window.location.pathname
);
}
: undefined
}
icon={getSortIcon(col.accessor, sort)}
iconTextWrapperProps={{
justifyContent: col.rightAlign ? "flex-end" : "flex-start",
}}
>
{col.header}
</Text>
Expand Down
58 changes: 49 additions & 9 deletions client/components/TorrentList.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from "react";
import React, { useEffect } from "react";
import getConfig from "next/config";
import { useRouter } from "next/router";
import moment from "moment";
import slugify from "slugify";
import qs from "qs";
import { ListUl } from "@styled-icons/boxicons-regular/ListUl";
import { Upload } from "@styled-icons/boxicons-regular/Upload";
import { Download } from "@styled-icons/boxicons-regular/Download";
Expand All @@ -18,28 +19,62 @@ import Text from "./Text";
import Box from "./Box";
import Button from "./Button";

const TorrentList = ({ torrents = [], categories, total }) => {
const pageSize = 25;

const TorrentList = ({
torrents = [],
setTorrents,
categories,
total,
fetchPath,
token,
}) => {
const {
publicRuntimeConfig: { SQ_SITE_WIDE_FREELEECH },
} = getConfig();

const router = useRouter();
const {
asPath,
query: { page: pageParam },
query: { page: pageParam, sort },
} = router;

const page = pageParam ? parseInt(pageParam) - 1 : 0;

const maxPage = Math.floor(total / 25);
const maxPage = total > pageSize ? Math.floor(total / pageSize) : 0;
const canPrevPage = page > 0;
const canNextPage = page < maxPage;

const setPage = (number) => {
if (number === 0) router.push(asPath.split("?")[0]);
else router.push(`${asPath.split("?")[0]}?page=${number + 1}`);
const query = qs.parse(window.location.search.replace("?", ""));
if (number === 0) delete query.page;
else query.page = number + 1;
router.push(
Object.keys(query).length
? `${window.location.pathname}?${qs.stringify(query)}`
: window.location.pathname
);
};

useEffect(() => {
const fetchTorrents = async () => {
try {
const searchRes = await fetch(
`${fetchPath}?${qs.stringify(router.query)}`,
{
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
}
);

const results = await searchRes.json();
setTorrents(results.torrents);
} catch (e) {}
};
if (fetchPath && token) fetchTorrents();
}, [sort, page]);

return (
<>
<List
Expand Down Expand Up @@ -95,6 +130,7 @@ const TorrentList = ({ torrents = [], categories, total }) => {
),
gridWidth: "100px",
rightAlign: true,
sortable: !!token,
},
{
header: "Leechers",
Expand All @@ -109,6 +145,7 @@ const TorrentList = ({ torrents = [], categories, total }) => {
),
gridWidth: "100px",
rightAlign: true,
sortable: !!token,
},
{
header: "Downloads",
Expand All @@ -121,8 +158,9 @@ const TorrentList = ({ torrents = [], categories, total }) => {
{value || 0}
</Text>
),
gridWidth: "100px",
gridWidth: "115px",
rightAlign: true,
sortable: !!token,
},
{
header: "Comments",
Expand All @@ -135,8 +173,9 @@ const TorrentList = ({ torrents = [], categories, total }) => {
{value || 0}
</Text>
),
gridWidth: "100px",
gridWidth: "110px",
rightAlign: true,
sortable: !!token,
},
{
header: "Uploaded",
Expand All @@ -146,6 +185,7 @@ const TorrentList = ({ torrents = [], categories, total }) => {
),
gridWidth: "140px",
rightAlign: true,
sortable: !!token,
},
]}
/>
Expand Down
17 changes: 11 additions & 6 deletions client/pages/categories/[category].js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useState } from "react";
import { useRouter } from "next/router";
import getConfig from "next/config";
import qs from "qs";
Expand All @@ -8,14 +8,16 @@ import SEO from "../../components/SEO";
import Text from "../../components/Text";
import TorrentList from "../../components/TorrentList";

const Category = ({ results }) => {
const Category = ({ results, token }) => {
const [torrents, setTorrents] = useState(results?.torrents ?? []);

const router = useRouter();
const {
query: { category: categorySlug },
} = router;

const {
publicRuntimeConfig: { SQ_TORRENT_CATEGORIES },
publicRuntimeConfig: { SQ_TORRENT_CATEGORIES, SQ_API_URL },
} = getConfig();

const category = Object.keys(SQ_TORRENT_CATEGORIES).find(
Expand All @@ -28,11 +30,14 @@ const Category = ({ results }) => {
<Text as="h1" mb={5}>
Browse {category}
</Text>
{results?.torrents.length ? (
{torrents.length ? (
<TorrentList
torrents={results.torrents}
torrents={torrents}
setTorrents={setTorrents}
categories={SQ_TORRENT_CATEGORIES}
total={results.total}
fetchPath={`${SQ_API_URL}/torrent/search`}
token={token}
/>
) : (
<Text color="grey">No results.</Text>
Expand Down Expand Up @@ -74,7 +79,7 @@ export const getServerSideProps = withAuthServerSideProps(
throw "banned";
}
const results = await searchRes.json();
return { props: { results } };
return { props: { results, token } };
} catch (e) {
if (e === "banned") throw "banned";
return { props: {} };
Expand Down
17 changes: 11 additions & 6 deletions client/pages/search/[[...query]].js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useState } from "react";
import getConfig from "next/config";
import { useRouter } from "next/router";
import qs from "qs";
Expand All @@ -10,15 +10,17 @@ import Button from "../../components/Button";
import Box from "../../components/Box";
import TorrentList from "../../components/TorrentList";

const Search = ({ results, error }) => {
const Search = ({ results, error, token }) => {
const [torrents, setTorrents] = useState(results?.torrents ?? []);

const router = useRouter();
let {
query: { query },
} = router;
query = query ? decodeURIComponent(query) : "";

const {
publicRuntimeConfig: { SQ_TORRENT_CATEGORIES },
publicRuntimeConfig: { SQ_TORRENT_CATEGORIES, SQ_API_URL },
} = getConfig();

const handleSearch = (e) => {
Expand All @@ -44,11 +46,14 @@ const Search = ({ results, error }) => {
<>
{query && (
<>
{results.torrents.length ? (
{torrents.length ? (
<TorrentList
torrents={results.torrents}
torrents={torrents}
setTorrents={setTorrents}
categories={SQ_TORRENT_CATEGORIES}
total={results.total}
fetchPath={`${SQ_API_URL}/torrent/search`}
token={token}
/>
) : (
<Text color="grey">No results.</Text>
Expand Down Expand Up @@ -92,7 +97,7 @@ export const getServerSideProps = withAuthServerSideProps(
return { props: { error: message } };
} else {
const results = await searchRes.json();
return { props: { results } };
return { props: { results, token } };
}
} catch (e) {
if (e === "banned") throw "banned";
Expand Down
Loading

0 comments on commit edc5e82

Please sign in to comment.