Add torrent module

This commit is contained in:
Manuel Ruwe
2022-12-31 17:48:46 +01:00
parent dff63400b8
commit 9d7d126b55
7 changed files with 55 additions and 15 deletions

View File

@@ -1,5 +1,10 @@
{
"settings": {
"label": "Settings"
},
"errors": {
"unmappedOptions": {
"text": "<b>Un-used parameter in configuration detected</b><br /><code>{{key}}</code>. Homarr is unable to interprete and use this parameter. To avoid any unexpected behavior, back up your configuration and correct your configuration."
}
}
}

View File

@@ -1,10 +1,14 @@
{
"descriptor": {
"name": "Torrent",
"description": "Show the current download speed of supported services",
"name": "BitTorrent",
"description": "Displays a list of the torrent which are currently downloading",
"settings": {
"hideComplete": {
"label": "Hide completed torrents"
"title": "Settings for BitTorrent integration",
"displayCompletedTorrents": {
"label": "Display completed torrents"
},
"displayStaleTorrents": {
"label": "Display stale torrents"
}
}
},

View File

@@ -1,6 +1,7 @@
import { Button, Group, MultiSelect, Stack, Switch, TextInput } from '@mantine/core';
import { Alert, Button, Group, MultiSelect, Stack, Switch, TextInput, Text } from '@mantine/core';
import { ContextModalProps } from '@mantine/modals';
import { useTranslation } from 'next-i18next';
import { IconAlertTriangle } from '@tabler/icons';
import { Trans, useTranslation } from 'next-i18next';
import { useState } from 'react';
import Widgets from '../../../../widgets';
import type { IWidgetOptionValue } from '../../../../widgets/widgets';
@@ -31,6 +32,8 @@ export const WidgetsEditModal = ({
if (!configName || !innerProps.options) return null;
console.log(`loaded namespace modules/${innerProps.widgetId}`);
const handleChange = (key: string, value: IntegrationOptionsValueType) => {
setModuleProperties((prev) => {
const copyOfPrev: any = { ...prev };
@@ -66,12 +69,28 @@ export const WidgetsEditModal = ({
return (
<Stack>
{items.map(([key, value]) => {
{items.map(([key, value], index) => {
const option = (currentWidgetDefinition as any).options[key] as IWidgetOptionValue;
if (!option) {
return (
<Alert icon={<IconAlertTriangle />} color="red">
<Text>
<Trans
i18nKey="modules/common:errors.unmappedOptions.text"
values={{ key }}
components={{ b: <b />, code: <code /> }}
/>
</Text>
</Alert>
);
}
switch (option.type) {
case 'switch':
return (
<Switch
key={`${option.type}-${index}`}
label={t(`descriptor.settings.${key}.label`)}
checked={value as boolean}
onChange={(ev) => handleChange(key, ev.currentTarget.checked)}
@@ -80,6 +99,7 @@ export const WidgetsEditModal = ({
case 'text':
return (
<TextInput
key={`${option.type}-${index}`}
label={t(`descriptor.settings.${key}.label`)}
value={value as string}
onChange={(ev) => handleChange(key, ev.currentTarget.value)}
@@ -88,6 +108,7 @@ export const WidgetsEditModal = ({
case 'multi-select':
return (
<MultiSelect
key={`${option.type}-${index}`}
data={getMutliselectData(key)}
label={t(`descriptor.settings.${key}.label`)}
value={value as string[]}

View File

@@ -11,7 +11,7 @@ interface TorrentsDataRequestParams {
export const useGetTorrentData = (params: TorrentsDataRequestParams) =>
useQuery({
queryKey: ['torrentsData', params.appId],
queryFn: async () => fetchData(),
queryFn: fetchData,
refetchOnWindowFocus: true,
refetchInterval(_: any, query: Query) {
if (query.state.fetchFailureCount < 3) {

View File

@@ -23,7 +23,7 @@ export const dashboardNamespaces = [
'modules/dlspeed',
'modules/usenet',
'modules/search',
'modules/torrents',
'modules/torrents-status',
'modules/weather',
'modules/ping',
'modules/docker',

View File

@@ -10,11 +10,9 @@ interface WidgetWrapperProps {
children: ReactNode;
}
export const WidgetWrapper = ({ widgetId, widget, className, children }: WidgetWrapperProps) => {
return (
export const WidgetWrapper = ({ widgetId, widget, className, children }: WidgetWrapperProps) => (
<HomarrCardWrapper className={className}>
<WidgetsMenu integration={widgetId} widget={widget} />
{children}
</HomarrCardWrapper>
);
};

View File

@@ -1,4 +1,4 @@
import { NormalizedTorrent } from '@ctrl/shared-torrent';
import { NormalizedTorrent, TorrentState } from '@ctrl/shared-torrent';
import {
Center,
Group,
@@ -52,7 +52,7 @@ interface BitTorrentTileProps {
}
function BitTorrentTile({ widget }: BitTorrentTileProps) {
const { t } = useTranslation('modules/torrents');
const { t } = useTranslation('modules/torrents-status');
const MIN_WIDTH_MOBILE = useMantineTheme().breakpoints.xs;
const { width } = useElementSize();
@@ -112,6 +112,18 @@ function BitTorrentTile({ widget }: BitTorrentTileProps) {
);
}
const filter = (torrent: NormalizedTorrent) => {
if (!widget.properties.displayCompletedTorrents && torrent.isCompleted) {
return false;
}
if (!widget.properties.displayStaleTorrents && !torrent.isCompleted && torrent.eta <= 0) {
return false;
}
return true;
};
return (
<ScrollArea sx={{ height: 300, width: '100%' }}>
<Table highlightOnHover p="sm">
@@ -126,7 +138,7 @@ function BitTorrentTile({ widget }: BitTorrentTileProps) {
</tr>
</thead>
<tbody>
{data.map((item: NormalizedTorrent, index: number) => (
{data.filter(filter).map((item: NormalizedTorrent, index: number) => (
<BitTorrrentQueueItem key={index} torrent={item} />
))}
</tbody>