mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-10 15:35:55 +01:00
✨ Add torrent module
This commit is contained in:
@@ -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."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -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[]}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -23,7 +23,7 @@ export const dashboardNamespaces = [
|
||||
'modules/dlspeed',
|
||||
'modules/usenet',
|
||||
'modules/search',
|
||||
'modules/torrents',
|
||||
'modules/torrents-status',
|
||||
'modules/weather',
|
||||
'modules/ping',
|
||||
'modules/docker',
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user