Skip to content

Commit 8fa663b

Browse files
committed
bookmarking of torrents
1 parent 50e2059 commit 8fa663b

6 files changed

Lines changed: 138 additions & 4 deletions

File tree

api/src/controllers/torrent.js

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,32 @@ export const fetchTorrent = (tracker) => async (req, res, next) => {
307307
},
308308
},
309309
{ $unwind: { path: "$uploadedBy", preserveNullAndEmptyArrays: true } },
310+
{
311+
$lookup: {
312+
from: "users",
313+
as: "fetchedBy",
314+
let: { torrentId: "$_id" },
315+
pipeline: [
316+
{ $match: { $expr: { $eq: ["$_id", req.userId] } } },
317+
{
318+
$project: {
319+
bookmarks: 1,
320+
},
321+
},
322+
{
323+
$addFields: {
324+
bookmarked: { $in: ["$$torrentId", "$bookmarks"] },
325+
},
326+
},
327+
{
328+
$project: {
329+
bookmarked: 1,
330+
},
331+
},
332+
],
333+
},
334+
},
335+
{ $unwind: { path: "$fetchedBy", preserveNullAndEmptyArrays: true } },
310336
{
311337
$lookup: {
312338
from: "comments",
@@ -428,6 +454,7 @@ export const getTorrentsPage = async ({
428454
category,
429455
source,
430456
tag,
457+
uploadedBy,
431458
userId,
432459
tracker,
433460
}) => {
@@ -485,11 +512,11 @@ export const getTorrentsPage = async ({
485512
},
486513
]
487514
: []),
488-
...(userId
515+
...(uploadedBy
489516
? [
490517
{
491518
$match: {
492-
uploadedBy: userId,
519+
uploadedBy,
493520
},
494521
},
495522
]
@@ -525,6 +552,32 @@ export const getTorrentsPage = async ({
525552
preserveNullAndEmptyArrays: true,
526553
},
527554
},
555+
{
556+
$lookup: {
557+
from: "users",
558+
as: "fetchedBy",
559+
let: { torrentId: "$_id" },
560+
pipeline: [
561+
{ $match: { $expr: { $eq: ["$_id", userId] } } },
562+
{
563+
$project: {
564+
bookmarks: 1,
565+
},
566+
},
567+
{
568+
$addFields: {
569+
bookmarked: { $in: ["$$torrentId", "$bookmarks"] },
570+
},
571+
},
572+
{
573+
$project: {
574+
bookmarked: 1,
575+
},
576+
},
577+
],
578+
},
579+
},
580+
{ $unwind: { path: "$fetchedBy", preserveNullAndEmptyArrays: true } },
528581
]);
529582

530583
const [count] = await Torrent.aggregate([
@@ -592,7 +645,11 @@ export const listLatest = (tracker) => async (req, res, next) => {
592645
count = parseInt(count) || 25;
593646
count = Math.min(count, 100);
594647
try {
595-
const { torrents } = await getTorrentsPage({ limit: count, tracker });
648+
const { torrents } = await getTorrentsPage({
649+
limit: count,
650+
userId: req.userId,
651+
tracker,
652+
});
596653
res.json(torrents);
597654
} catch (e) {
598655
next(e);
@@ -608,6 +665,7 @@ export const searchTorrents = (tracker) => async (req, res, next) => {
608665
category,
609666
source,
610667
tag,
668+
userId: req.userId,
611669
tracker,
612670
});
613671
res.json(torrents);
@@ -734,3 +792,29 @@ export const toggleFreeleech = async (req, res, next) => {
734792
next(e);
735793
}
736794
};
795+
796+
export const toggleBookmark = async (req, res, next) => {
797+
const { infoHash } = req.params;
798+
try {
799+
const torrent = await Torrent.findOne({ infoHash }).lean();
800+
801+
if (!torrent) {
802+
res.status(404).send("Torrent could not be found");
803+
return;
804+
}
805+
806+
const user = await User.findOne({ _id: req.userId }).lean();
807+
808+
const isBookmarked = (await user.bookmarks?.length)
809+
? user.bookmarks.map((b) => b.toString()).includes(torrent._id.toString())
810+
: false;
811+
812+
await User.findOneAndUpdate(
813+
{ _id: req.userId },
814+
{ [isBookmarked ? "$pull" : "$addToSet"]: { bookmarks: torrent._id } }
815+
);
816+
res.sendStatus(200);
817+
} catch (e) {
818+
next(e);
819+
}
820+
};

api/src/controllers/user.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,11 @@ export const fetchUser = (tracker) => async (req, res, next) => {
683683
const { ratio } = await getUserRatio(user._id);
684684
user.ratio = ratio;
685685

686-
const { torrents } = await getTorrentsPage({ userId: user._id, tracker });
686+
const { torrents } = await getTorrentsPage({
687+
uploadedBy: user._id,
688+
userId: req.userId,
689+
tracker,
690+
});
687691
user.torrents = torrents;
688692

689693
res.json(user);

api/src/routes/torrent.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
toggleFreeleech,
1111
uploadTorrent,
1212
editTorrent,
13+
toggleBookmark,
1314
} from "../controllers/torrent";
1415
import { createReport } from "../controllers/moderation";
1516

@@ -25,6 +26,7 @@ export default (tracker) => {
2526
router.post("/unvote/:infoHash/:vote", removeVote);
2627
router.post("/report/:infoHash", createReport);
2728
router.post("/toggle-freeleech/:infoHash", toggleFreeleech);
29+
router.post("/bookmark/:infoHash", toggleBookmark);
2830
router.get("/latest", listLatest(tracker));
2931
router.get("/search", searchTorrents(tracker));
3032
return router;

api/src/schema/user.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const User = new mongoose.Schema({
1919
qr: String,
2020
backup: [String],
2121
},
22+
bookmarks: [mongoose.Schema.ObjectId],
2223
});
2324

2425
export default mongoose.model("user", User);

client/components/TorrentList.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { ChevronsLeft } from "@styled-icons/boxicons-solid/ChevronsLeft";
1111
import { ChevronLeft } from "@styled-icons/boxicons-solid/ChevronLeft";
1212
import { ChevronsRight } from "@styled-icons/boxicons-solid/ChevronsRight";
1313
import { ChevronRight } from "@styled-icons/boxicons-solid/ChevronRight";
14+
import { Bookmark } from "@styled-icons/boxicons-solid/Bookmark";
1415
import List from "./List";
1516
import Text from "./Text";
1617
import Box from "./Box";
@@ -52,6 +53,9 @@ const TorrentList = ({ torrents = [], categories, total }) => {
5253
cell: ({ value, row }) => (
5354
<Text title={value}>
5455
{value}
56+
{row.fetchedBy?.bookmarked && (
57+
<Box as={Bookmark} size={16} color="primary" ml={2} />
58+
)}
5559
{(row.freeleech || SQ_SITE_WIDE_FREELEECH === true) && (
5660
<Text as="span" fontSize={0} color="primary" ml={3}>
5761
FL!

client/pages/torrent/[infoHash].js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import { File } from "@styled-icons/boxicons-regular/File";
1313
import { Folder } from "@styled-icons/boxicons-regular/Folder";
1414
import { Like } from "@styled-icons/boxicons-regular/Like";
1515
import { Dislike } from "@styled-icons/boxicons-regular/Dislike";
16+
import { Bookmark as BookmarkEmpty } from "@styled-icons/boxicons-regular/Bookmark";
17+
import { Bookmark } from "@styled-icons/boxicons-solid/Bookmark";
1618
import { withAuthServerSideProps } from "../../utils/withAuth";
1719
import SEO from "../../components/SEO";
1820
import Box from "../../components/Box";
@@ -124,6 +126,7 @@ const Torrent = ({ token, torrent = {}, userId, userRole, uid }) => {
124126
const [comments, setComments] = useState(torrent.comments);
125127
const [isFreeleech, setIsFreeleech] = useState(torrent.freeleech);
126128
const [hasGroup, setHasGroup] = useState(!!torrent.group);
129+
const [bookmarked, setBookmarked] = useState(torrent.fetchedBy.bookmarked);
127130

128131
const { addNotification } = useContext(NotificationContext);
129132
const { setLoading } = useContext(LoadingContext);
@@ -410,6 +413,39 @@ const Torrent = ({ token, torrent = {}, userId, userRole, uid }) => {
410413
setLoading(false);
411414
};
412415

416+
const handleBookmark = async () => {
417+
setLoading(true);
418+
419+
try {
420+
const bookmarkRes = await fetch(
421+
`${SQ_API_URL}/torrent/bookmark/${torrent.infoHash}`,
422+
{
423+
method: "POST",
424+
headers: {
425+
Authorization: `Bearer ${token}`,
426+
},
427+
}
428+
);
429+
430+
if (bookmarkRes.status !== 200) {
431+
const reason = await bookmarkRes.text();
432+
throw new Error(reason);
433+
}
434+
435+
addNotification(
436+
"success",
437+
`Torrent ${bookmarked ? "removed from" : "added to"} bookmarks`
438+
);
439+
440+
setBookmarked((b) => !b);
441+
} catch (e) {
442+
addNotification("error", `Could not bookmark torrent: ${e.message}`);
443+
console.error(e);
444+
}
445+
446+
setLoading(false);
447+
};
448+
413449
const category = Object.keys(SQ_TORRENT_CATEGORIES).find(
414450
(c) => slugify(c, { lower: true }) === torrent.type
415451
);
@@ -441,6 +477,9 @@ const Torrent = ({ token, torrent = {}, userId, userRole, uid }) => {
441477
)}
442478
</Text>
443479
<Box display="flex" alignItems="center" ml={3}>
480+
<Button onClick={handleBookmark} variant="secondary" px="10px" mr={3}>
481+
{bookmarked ? <Bookmark size={18} /> : <BookmarkEmpty size={18} />}
482+
</Button>
444483
{(userRole === "admin" || userId === torrent.uploadedBy._id) && (
445484
<>
446485
<Button

0 commit comments

Comments
 (0)