From ebedc39949a024ed057c7d20f599750b2749681e Mon Sep 17 00:00:00 2001 From: SeDemal Date: Tue, 10 Dec 2024 23:41:42 +0100 Subject: [PATCH] feat: Add quick filters for integration and status + minor UI improvement from feedback --- packages/widgets/src/downloads/component.tsx | 88 ++++++++++++++++++-- 1 file changed, 83 insertions(+), 5 deletions(-) diff --git a/packages/widgets/src/downloads/component.tsx b/packages/widgets/src/downloads/component.tsx index 37aac837c2..d3929a004c 100644 --- a/packages/widgets/src/downloads/component.tsx +++ b/packages/widgets/src/downloads/component.tsx @@ -10,10 +10,12 @@ import { AvatarGroup, Button, Center, + Chip, Divider, Group, Modal, Paper, + Popover, Progress, Space, Stack, @@ -26,6 +28,7 @@ import type { IconProps } from "@tabler/icons-react"; import { IconAlertTriangle, IconCirclesRelation, + IconFilter, IconInfinity, IconInfoCircle, IconPlayerPause, @@ -44,9 +47,15 @@ import { getIconUrl, getIntegrationKindsByCategory } from "@homarr/definitions"; import type { ExtendedClientStatus, ExtendedDownloadClientItem } from "@homarr/integrations"; import { useScopedI18n } from "@homarr/translation/client"; +import type { DownloadClientItem } from "../../../integrations/src/interfaces/downloads/download-client-items"; import type { WidgetComponentProps } from "../definition"; import { NoIntegrationSelectedError } from "../errors"; +interface FilterSelect { + names: string[]; + statuses: ExtendedDownloadClientItem["state"][]; +} + //Ratio table for relative width between columns const columnsRatios: Record = { actions: 2, @@ -109,6 +118,18 @@ export default function DownloadClientsWidget({ const [clickedIndex, setClickedIndex] = useState(0); const [opened, { open, close }] = useDisclosure(false); + //User quick settings for filters + const [quickFilters, setQuickFilters] = useState({ names: [], statuses: [] }); + const availableStatuses = useMemo(() => { + //Redefine list of available statuses from current items + const statuses = Array.from(new Set(currentItems.flatMap(({ data }) => data.items.map(({ state }) => state)))); + //Reset user filters accordingly to remove unavailable statuses + setQuickFilters(({ names, statuses: prevStatuses }) => { + return { names, statuses: prevStatuses.filter((status) => statuses.includes(status)) }; + }); + return statuses; + }, [currentItems]); + //Get API mutation functions const { mutate: mutateResumeItem } = clientApi.widget.downloads.resumeItem.useMutation(); const { mutate: mutatePauseItem } = clientApi.widget.downloads.pauseItem.useMutation(); @@ -165,6 +186,12 @@ export default function DownloadClientsWidget({ progress !== 1)) || (type === "usenet" && ((progress === 1 && options.showCompletedUsenet) || progress !== 1)), ) + //Filter following user quick setting + .filter( + ({ state }) => + (quickFilters.names.length === 0 || quickFilters.names.includes(pair.integration.name)) && + (quickFilters.statuses.length === 0 || quickFilters.statuses.includes(state)), + ) //Add extrapolated data and actions if user is allowed interaction .map((item): ExtendedDownloadClientItem => { const received = Math.floor(item.size * item.progress); @@ -200,6 +227,7 @@ export default function DownloadClientsWidget({ options.filterIsWhitelist, options.showCompletedTorrent, options.showCompletedUsenet, + quickFilters, ], ); @@ -665,7 +693,13 @@ export default function DownloadClientsWidget({ {(globalTraffic.up / globalTraffic.down).toFixed(2)} )} - + @@ -753,10 +787,13 @@ const NormalizedLine = ({ interface ClientsControlProps { clients: ExtendedClientStatus[]; + filters: FilterSelect; + setFilters: (filters: FilterSelect) => void; + availableStatuses: DownloadClientItem["state"][]; style?: MantineStyleProp; } -const ClientsControl = ({ clients, style }: ClientsControlProps) => { +const ClientsControl = ({ clients, filters, setFilters, availableStatuses, style }: ClientsControlProps) => { const integrationsStatuses = clients.reduce( (acc, { status, integration: { id }, interact }) => status && interact ? (acc[status.paused ? "paused" : "active"].push(id), acc) : acc, @@ -767,13 +804,53 @@ const ClientsControl = ({ clients, style }: ClientsControlProps) => { clients.reduce((count, { status }) => count + (status?.rates.down ?? 0), 0), "/s", ); + const chipStyle = { + "--chip-fz": "var(--button-fz)", + "--chip-size": "calc(var(--ratio-width) * 0.9)", + "--chip-icon-size": "calc(var(--chip-fz)*2/3)", + "--chip-padding": "var(--chip-fz)", + "--chip-checked-padding": "var(--chip-icon-size)", + "--chip-spacing": "var(--space-size)", + }; const { mutate: mutateResumeQueue } = clientApi.widget.downloads.resume.useMutation(); const { mutate: mutatePauseQueue } = clientApi.widget.downloads.pause.useMutation(); const [opened, { open, close }] = useDisclosure(false); const t = useScopedI18n("widget.downloads"); return ( - + + + + + + + + + {t("items.integration.columnTitle")} + setFilters({ ...filters, names })}> + {clients.map(({ integration }) => ( + + ))} + + {t("items.state.columnTitle")} + setFilters({ ...filters, statuses: statuses as typeof filters.statuses })} + > + {availableStatuses.map((status) => ( + + ))} + + + + + {clients.map((client) => ( ))} @@ -792,7 +869,7 @@ const ClientsControl = ({ clients, style }: ClientsControlProps) => { )}