update torrents list view
This commit is contained in:
@@ -5,20 +5,15 @@ import {
|
||||
ArrowsClockwiseIcon,
|
||||
CaretDownIcon,
|
||||
CaretUpIcon,
|
||||
CheckCircleIcon,
|
||||
DownloadSimpleIcon,
|
||||
TrashIcon,
|
||||
} from "@phosphor-icons/react";
|
||||
import { useDownloadTorrentMutation } from "../api/useDownloadTorrentMutation";
|
||||
import { useDeleteItemMutation } from "../api/useDeleteItemMutation";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import dayjs from "dayjs";
|
||||
import relativeTime from "dayjs/plugin/relativeTime";
|
||||
import { humanFileSize } from "../utils/humanFileSize";
|
||||
import { useDeleteTorrentMutation } from "../api/useDeleteTorrentMutation";
|
||||
import { useRefreshItemMutation } from "../api/useRefreshItemMutation";
|
||||
import { Loader } from "./Loader";
|
||||
import { categories } from "../lib/categories";
|
||||
import { Torrent } from "./Torrent";
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
@@ -34,9 +29,7 @@ export const Item = ({ item }: ItemProps) => {
|
||||
const { data: torrents } = useItemTorrentsQuery(item.id, open);
|
||||
|
||||
const deleteMutation = useDeleteItemMutation();
|
||||
const downloadMutation = useDownloadTorrentMutation();
|
||||
const refreshMutation = useRefreshItemMutation();
|
||||
const deleteTorrentMutation = useDeleteTorrentMutation();
|
||||
|
||||
const Icon = open ? CaretUpIcon : CaretDownIcon;
|
||||
|
||||
@@ -61,10 +54,6 @@ export const Item = ({ item }: ItemProps) => {
|
||||
}
|
||||
}, [item]);
|
||||
|
||||
const handleDownloadTorrent = (torrentId: number) => {
|
||||
downloadMutation.mutate({ torrentId });
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
if (!confirm("Do you want to delete this item?")) return;
|
||||
|
||||
@@ -95,22 +84,6 @@ export const Item = ({ item }: ItemProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const handleDeleteTorrent = (torrentId: number) => {
|
||||
deleteTorrentMutation.mutate(
|
||||
{
|
||||
id: torrentId,
|
||||
},
|
||||
{
|
||||
onSuccess() {
|
||||
queryClient.invalidateQueries({ queryKey: ["items"] });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["items", item.id, "torrents"],
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="border-t border-b border-neutral-900">
|
||||
<div
|
||||
@@ -163,49 +136,7 @@ export const Item = ({ item }: ItemProps) => {
|
||||
</div>
|
||||
{filteredTorrents && filteredTorrents.length > 0 ? (
|
||||
filteredTorrents.map((torrent) => (
|
||||
<div
|
||||
key={torrent.id}
|
||||
className="flex justify-between items-center hover:bg-neutral-200 group"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<span>
|
||||
<a
|
||||
href={torrent.guid}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{torrent.title}
|
||||
</a>{" "}
|
||||
[{formatCategory(torrent.category)}] [{torrent.indexer}]
|
||||
</span>
|
||||
{torrent.downloaded && (
|
||||
<span title="Torrent files downloaded">
|
||||
<CheckCircleIcon size={20} color="green" />
|
||||
</span>
|
||||
)}
|
||||
<button
|
||||
className="hidden group-hover:inline text-[#b00420] cursor-pointer"
|
||||
onClick={() => handleDeleteTorrent(torrent.id)}
|
||||
>
|
||||
<TrashIcon size={16} />
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<span>Seeds: {torrent.seeders}</span>
|
||||
<span>Peers: {torrent.peers}</span>
|
||||
<span>
|
||||
PubDate: {dayjs(torrent.pubdate).format("DD.MM.YYYY")} at{" "}
|
||||
{dayjs(torrent.pubdate).format("HH:mm")}
|
||||
</span>
|
||||
<button
|
||||
className="cursor-pointer"
|
||||
onClick={() => handleDownloadTorrent(torrent.id)}
|
||||
>
|
||||
<DownloadSimpleIcon size={24} />
|
||||
</button>
|
||||
<span>{humanFileSize(torrent.size)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<Torrent key={torrent.id} itemId={item.id} torrent={torrent} />
|
||||
))
|
||||
) : (
|
||||
<span>No torrents yet</span>
|
||||
@@ -215,7 +146,3 @@ export const Item = ({ item }: ItemProps) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const formatCategory = (category: number): string => {
|
||||
return categories[category as keyof typeof categories] || "";
|
||||
};
|
||||
|
||||
116
web/src/components/Torrent.tsx
Normal file
116
web/src/components/Torrent.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import { useState } from "react";
|
||||
import type { ItemTorrent } from "../api/useItemTorrentsQuery";
|
||||
import { categories } from "../lib/categories";
|
||||
import {
|
||||
ArrowSquareOutIcon,
|
||||
CaretDownIcon,
|
||||
CaretUpIcon,
|
||||
CheckCircleIcon,
|
||||
DownloadSimpleIcon,
|
||||
TrashIcon,
|
||||
} from "@phosphor-icons/react";
|
||||
import dayjs from "dayjs";
|
||||
import { humanFileSize } from "../utils/humanFileSize";
|
||||
import { useDeleteTorrentMutation } from "../api/useDeleteTorrentMutation";
|
||||
import { useDownloadTorrentMutation } from "../api/useDownloadTorrentMutation";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
export type TorrentProps = {
|
||||
itemId: number;
|
||||
torrent: ItemTorrent;
|
||||
};
|
||||
|
||||
export const Torrent = ({ itemId, torrent }: TorrentProps) => {
|
||||
const queryClient = useQueryClient();
|
||||
const downloadMutation = useDownloadTorrentMutation();
|
||||
const deleteMutation = useDeleteTorrentMutation();
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const ChevronIcon = open ? CaretUpIcon : CaretDownIcon;
|
||||
|
||||
const handleDownload = () => {
|
||||
downloadMutation.mutate({ torrentId: torrent.id });
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
deleteMutation.mutate(
|
||||
{
|
||||
id: torrent.id,
|
||||
},
|
||||
{
|
||||
onSuccess() {
|
||||
queryClient.invalidateQueries({ queryKey: ["items"] });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["items", itemId, "torrents"],
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
<div
|
||||
className="flex items-center gap-2 hover:bg-neutral-200 cursor-pointer"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => setOpen((prev) => !prev)}
|
||||
>
|
||||
<p>{torrent.title}</p>
|
||||
{torrent.downloaded && (
|
||||
<span title="Torrent files downloaded">
|
||||
<CheckCircleIcon size={20} color="green" />
|
||||
</span>
|
||||
)}
|
||||
<div className="ml-auto flex items-center gap-1">
|
||||
<span>[{formatCategory(torrent.category)}]</span>
|
||||
<span>{humanFileSize(torrent.size)}</span>
|
||||
<span>
|
||||
<ChevronIcon size={20} />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{open && (
|
||||
<div className="p-2 bg-neutral-100 rounded-md text-[15px]">
|
||||
<p>Indexer: {torrent.indexer}</p>
|
||||
<p>Seeders: {torrent.seeders}</p>
|
||||
<p>Peers: {torrent.peers}</p>
|
||||
<p>
|
||||
Published: {dayjs(torrent.pubdate).format("DD.MM.YYYY")} at{" "}
|
||||
{dayjs(torrent.pubdate).format("HH:mm")}
|
||||
</p>
|
||||
<div className="flex items-center gap-2 mt-2">
|
||||
<a
|
||||
href={torrent.guid}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<ArrowSquareOutIcon size={18} /> Open
|
||||
</a>
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer flex items-center gap-1"
|
||||
onClick={handleDownload}
|
||||
>
|
||||
<DownloadSimpleIcon size={18} /> Download
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="text-[#b00420] cursor-pointer flex items-center gap-1"
|
||||
onClick={handleDelete}
|
||||
>
|
||||
<TrashIcon size={18} /> Delete torrent
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const formatCategory = (category: number): string => {
|
||||
return categories[category as keyof typeof categories] || "";
|
||||
};
|
||||
Reference in New Issue
Block a user