import { TorrentState } from '@ctrl/shared-torrent'; import { Badge, Center, Flex, Group, Loader, ScrollArea, Stack, Table, Text, Title, useMantineTheme, } from '@mantine/core'; import { useElementSize } from '@mantine/hooks'; import { IconFileDownload } from '@tabler/icons'; import dayjs from 'dayjs'; import duration from 'dayjs/plugin/duration'; import relativeTime from 'dayjs/plugin/relativeTime'; import { useTranslation } from 'next-i18next'; import { useGetDownloadClientsQueue } from '../../hooks/widgets/download-speed/useGetNetworkSpeed'; import { NormalizedDownloadQueueResponse } from '../../types/api/downloads/queue/NormalizedDownloadQueueResponse'; import { AppIntegrationType } from '../../types/app'; import { defineWidget } from '../helper'; import { IWidget } from '../widgets'; import { BitTorrrentQueueItem } from './TorrentQueueItem'; dayjs.extend(duration); dayjs.extend(relativeTime); const downloadAppTypes: AppIntegrationType['type'][] = ['deluge', 'qBittorrent', 'transmission']; const definition = defineWidget({ id: 'torrents-status', icon: IconFileDownload, options: { displayCompletedTorrents: { type: 'switch', defaultValue: true, }, displayStaleTorrents: { type: 'switch', defaultValue: true, }, }, gridstack: { minWidth: 2, minHeight: 2, maxWidth: 12, maxHeight: 14, }, component: TorrentTile, }); export type ITorrent = IWidget; interface TorrentTileProps { widget: ITorrent; } function TorrentTile({ widget }: TorrentTileProps) { const { t } = useTranslation('modules/torrents-status'); const MIN_WIDTH_MOBILE = useMantineTheme().breakpoints.xs; const { width } = useElementSize(); const { data, isError, isInitialLoading, dataUpdatedAt, }: { data: NormalizedDownloadQueueResponse | undefined; isError: boolean; isInitialLoading: boolean; dataUpdatedAt: number; } = useGetDownloadClientsQueue(); if (isError) { return ( {t('card.errors.generic.title')} {t('card.errors.generic.text')} ); } if (isInitialLoading || !data) { return ( {t('card.loading.title')} Homarr is establishing a connection... ); } if (data.apps.length === 0) { return ( {t('card.errors.noDownloadClients.title')} {t('card.errors.noDownloadClients.text')} ); } if (!data || Object.values(data.apps).length < 1) { return (
{t('card.table.body.nothingFound')}
); } const torrents = data.apps .flatMap((app) => (app.type === 'torrent' ? app.torrents : [])) .filter((torrent) => (widget.properties.displayCompletedTorrents ? true : !torrent.isCompleted)) .filter((torrent) => widget.properties.displayStaleTorrents ? true : torrent.isCompleted || torrent.downloadSpeed > 0 ); const difference = new Date().getTime() - dataUpdatedAt; const duration = dayjs.duration(difference, 'ms'); const humanizedDuration = duration.humanize(); return ( {width > MIN_WIDTH_MOBILE && } {width > MIN_WIDTH_MOBILE && } {width > MIN_WIDTH_MOBILE && } {torrents.map((torrent, index) => ( ))}
{t('card.table.header.name')} {t('card.table.header.size')}{t('card.table.header.download')}{t('card.table.header.upload')}{t('card.table.header.estimatedTimeOfArrival')}{t('card.table.header.progress')}
{data.apps.some((x) => !x.success) && ( {t('card.footer.error')} )} {t('card.footer.lastUpdated', { time: humanizedDuration })}
); } export default definition;