From 6a8ee0338ed2fcb000ad9dd37ccaa76ff7fc6013 Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Sat, 2 Dec 2023 16:15:39 +0100 Subject: [PATCH] Rename `sort` -> `type` --- .../Customize/Search/SearchCustomization.tsx | 10 +-- .../Widget/ChangeWidgetPositionModal.tsx | 4 +- .../Board/Items/Widget/WidgetsMenu.tsx | 8 +- .../Board/Items/Widget/widget-actions.ts | 8 +- .../Board/Sections/SectionContent.tsx | 2 +- .../WidgetsTab/AvailableWidgetsTab.tsx | 2 +- .../WidgetsTab/WidgetElementType.tsx | 10 +-- src/components/layout/header/Search.tsx | 2 +- src/modules/common/MediaDisplay.tsx | 2 +- src/server/api/routers/board/db/widget.ts | 2 +- .../api/routers/board/mapping/widget.ts | 4 +- src/server/api/routers/calendar.ts | 20 ++--- src/server/api/routers/config.ts | 20 ----- src/server/api/routers/download.ts | 6 +- src/server/api/routers/integration.ts | 2 +- src/server/api/routers/overseerr.ts | 2 +- .../api/routers/usenet/client/usenetClient.ts | 2 +- src/server/db/items.ts | 80 ++++--------------- src/server/db/queries/integrations.ts | 6 +- src/server/db/queries/widget.ts | 16 ++-- src/server/db/schema.ts | 10 +-- src/validations/widget.ts | 7 +- src/widgets/WidgetWrapper.tsx | 2 +- src/widgets/download-speed/Tile.tsx | 21 +++-- .../download-speed/useGetNetworkSpeed.tsx | 18 ----- src/widgets/torrent/TorrentTile.tsx | 17 ++-- 26 files changed, 101 insertions(+), 182 deletions(-) delete mode 100644 src/widgets/download-speed/useGetNetworkSpeed.tsx diff --git a/src/components/Board/Customize/Search/SearchCustomization.tsx b/src/components/Board/Customize/Search/SearchCustomization.tsx index 38a573fc4..b7e53f52e 100644 --- a/src/components/Board/Customize/Search/SearchCustomization.tsx +++ b/src/components/Board/Customize/Search/SearchCustomization.tsx @@ -37,7 +37,7 @@ export const SearchCustomization = ({ allMediaIntegrations }: IntegrationCustomi data={allMediaIntegrations.map((x) => ({ value: x.id, label: x.name, - sort: x.sort, + type: x.type, }))} /> @@ -45,16 +45,16 @@ export const SearchCustomization = ({ allMediaIntegrations }: IntegrationCustomi }; interface ItemProps extends React.ComponentPropsWithoutRef<'div'> { - sort: IntegrationType; + type: IntegrationType; label: string; } const IntegrationSelectItem = forwardRef( - ({ label, sort, ...others }: ItemProps, ref) => { + ({ label, type, ...others }: ItemProps, ref) => { return (
- + {label} @@ -89,7 +89,7 @@ const IntegrationSelectValue = })} > - + {label} { - const { t } = useTranslation(`modules/${widget.sort}`); + const { t } = useTranslation(`modules/${widget.type}`); const wrapperColumnCount = useWrapperColumnCount(); const resizeGridItem = useResizeGridItem(); const { removeItem } = useItemActions(); if (!widget || !wrapperColumnCount) return null; // Then get the widget definition - const widgetDefinitionObject = WidgetsDefinitions[widget.sort as keyof typeof WidgetsDefinitions]; + const widgetDefinitionObject = WidgetsDefinitions[widget.type as keyof typeof WidgetsDefinitions]; const handleDeleteClick = () => { openRemoveItemModal({ - name: widget.sort, + name: widget.type, onConfirm() { removeItem({ itemId: widget.id, @@ -55,7 +55,7 @@ export const WidgetsMenu = ({ widget }: WidgetsMenuProps) => { title: {t('descriptor.settings.title')}, innerProps: { widgetId: widget.id, - widgetType: widget.sort, + widgetType: widget.type, options: widget.options, widgetOptions: widgetDefinitionObject.options, }, diff --git a/src/components/Board/Items/Widget/widget-actions.ts b/src/components/Board/Items/Widget/widget-actions.ts index c8cad8ea3..2ff6cb4de 100644 --- a/src/components/Board/Items/Widget/widget-actions.ts +++ b/src/components/Board/Items/Widget/widget-actions.ts @@ -1,7 +1,7 @@ import { useCallback } from 'react'; import { v4 } from 'uuid'; import { z } from 'zod'; -import { widgetCreationSchema, widgetSortSchema } from '~/validations/widget'; +import { widgetCreationSchema, widgetTypeSchema } from '~/validations/widget'; import { IWidgetDefinition } from '~/widgets/widgets'; import { useUpdateBoard } from '../../board-actions'; @@ -13,7 +13,7 @@ type UpdateWidgetOptions = { }; type CreateWidget = { - sort: z.infer; + type: z.infer; definition: IWidgetDefinition; }; @@ -47,7 +47,7 @@ export const useWidgetActions = () => { ); const createWidget = useCallback( - ({ sort, definition }: CreateWidget) => { + ({ type, definition }: CreateWidget) => { updateBoard((prev) => { if (!prev) return prev; @@ -58,7 +58,7 @@ export const useWidgetActions = () => { const widget = { id: v4(), kind: 'widget', - sort, + type, options: Object.entries(definition.options).reduce( (prev, [k, v]) => { const newPrev = prev; diff --git a/src/components/Board/Sections/SectionContent.tsx b/src/components/Board/Sections/SectionContent.tsx index cceaa7910..d56cae1f8 100644 --- a/src/components/Board/Sections/SectionContent.tsx +++ b/src/components/Board/Sections/SectionContent.tsx @@ -40,7 +40,7 @@ export function SectionContent({ items, refs }: SectionContentProps) { ))} {widgets.map((widget) => { - const definition = Widgets[widget.sort]; + const definition = Widgets[widget.type]; if (!definition) return null; return ( diff --git a/src/components/Board/SelectElement/WidgetsTab/AvailableWidgetsTab.tsx b/src/components/Board/SelectElement/WidgetsTab/AvailableWidgetsTab.tsx index 767fbc27b..12537eafa 100644 --- a/src/components/Board/SelectElement/WidgetsTab/AvailableWidgetsTab.tsx +++ b/src/components/Board/SelectElement/WidgetsTab/AvailableWidgetsTab.tsx @@ -26,7 +26,7 @@ export const AvailableIntegrationElements = ({ {objectEntries(widgets).map(([k, v]) => ( - + ))} diff --git a/src/components/Board/SelectElement/WidgetsTab/WidgetElementType.tsx b/src/components/Board/SelectElement/WidgetsTab/WidgetElementType.tsx index 5a4163607..ecd08ee44 100644 --- a/src/components/Board/SelectElement/WidgetsTab/WidgetElementType.tsx +++ b/src/components/Board/SelectElement/WidgetsTab/WidgetElementType.tsx @@ -2,14 +2,14 @@ import { useModals } from '@mantine/modals'; import { showNotification } from '@mantine/notifications'; import { Icon, IconChecks } from '@tabler/icons-react'; import { useTranslation } from 'next-i18next'; -import { WidgetSort } from '~/server/db/items'; +import { WidgetType } from '~/server/db/items'; import { IWidgetDefinition } from '~/widgets/widgets'; import { useWidgetActions } from '../../Items/Widget/widget-actions'; import { GenericAvailableElementType } from '../Shared/GenericElementType'; interface WidgetElementTypeProps { - sort: WidgetSort; + type: WidgetType; image: string | Icon; disabled?: boolean; widget: IWidgetDefinition; @@ -17,19 +17,19 @@ interface WidgetElementTypeProps { } export const WidgetElementType = ({ - sort, + type, image, disabled, widget, modalId, }: WidgetElementTypeProps) => { const { closeModal } = useModals(); - const { t } = useTranslation(`modules/${sort}`); + const { t } = useTranslation(`modules/${type}`); const { createWidget } = useWidgetActions(); const handleAddition = async () => { createWidget({ - sort, + type, definition: widget, }); closeModal(modalId); diff --git a/src/components/layout/header/Search.tsx b/src/components/layout/header/Search.tsx index 5a4457bbc..6c4e0d076 100644 --- a/src/components/layout/header/Search.tsx +++ b/src/components/layout/header/Search.tsx @@ -49,7 +49,7 @@ export const Search = ({ isMobile, autoFocus }: SearchProps) => { ) .filter( (engine) => - engine.sort !== 'movie' || board?.mediaIntegrations.some((x) => x.sort === engine.value) + engine.sort !== 'movie' || board?.mediaIntegrations.some((x) => x.type === engine.value) ) .map((engine) => ({ ...engine, diff --git a/src/modules/common/MediaDisplay.tsx b/src/modules/common/MediaDisplay.tsx index 9c4e84b41..6c32a59e7 100644 --- a/src/modules/common/MediaDisplay.tsx +++ b/src/modules/common/MediaDisplay.tsx @@ -246,7 +246,7 @@ export function MediaDisplay({ media }: { media: IMedia }) { {media.type === 'overseer' && !media.overseerrResult?.mediaInfo?.mediaAddedAt && ( <> diff --git a/src/server/api/routers/board/db/widget.ts b/src/server/api/routers/board/db/widget.ts index 1bf729abb..903d08219 100644 --- a/src/server/api/routers/board/db/widget.ts +++ b/src/server/api/routers/board/db/widget.ts @@ -44,7 +44,7 @@ export const applyCreateWidgetChanges = ( const widgetId = randomUUID(); changes.widgets.create.push({ id: widgetId, - sort: widget.sort, + type: widget.type, itemId: widget.id, }); diff --git a/src/server/api/routers/board/mapping/widget.ts b/src/server/api/routers/board/mapping/widget.ts index 570a09788..4ced12082 100644 --- a/src/server/api/routers/board/mapping/widget.ts +++ b/src/server/api/routers/board/mapping/widget.ts @@ -6,12 +6,12 @@ export type MapWidget = Awaited>[n export const mapWidget = (widgetItem: MapWidget) => { const { sectionId, itemId, id, ...commonLayoutItem } = widgetItem.item.layouts.at(0)!; const common = { ...commonLayoutItem, id: itemId }; - const { id: _id, itemId: _itemId, sort, item, options, integrations, ...widget } = widgetItem; + const { id: _id, itemId: _itemId, type, item, options, integrations, ...widget } = widgetItem; return { ...common, ...widget, kind: 'widget' as const, - sort, + type, options: mapWidgetOptions(options), integrations: integrations.map((x) => ({ ...x.integration, diff --git a/src/server/api/routers/calendar.ts b/src/server/api/routers/calendar.ts index fa95f70bc..8b602a9c1 100644 --- a/src/server/api/routers/calendar.ts +++ b/src/server/api/routers/calendar.ts @@ -43,10 +43,10 @@ export const calendarRouter = createTRPCRouter({ ]); const promises = widget.integrations.map(async (integration) => { - const endpoint = integrationTypeEndpointMap.get(integration.sort); + const endpoint = integrationTypeEndpointMap.get(integration.type); if (!endpoint) { return { - sort: integration.sort, + type: integration.type, items: [], success: false, }; @@ -62,7 +62,7 @@ export const calendarRouter = createTRPCRouter({ const end = new Date(input.year, input.month, 0); // Last day of month const apiKey = integration.secrets.find((x) => x.key === 'apiKey')?.value; - if (!apiKey) return { sort: integration.sort, items: [], success: false }; + if (!apiKey) return { type: integration.type, items: [], success: false }; const url = new URL(`${origin}${endpoint}`); url.searchParams.set('apiKey', apiKey); url.searchParams.set('end', end.toISOString()); @@ -74,13 +74,13 @@ export const calendarRouter = createTRPCRouter({ return axios .get(url.toString()) - .then((x) => ({ sort: integration.sort, items: x.data as unknown[], success: true })) + .then((x) => ({ type: integration.type, items: x.data as unknown[], success: true })) .catch((err) => { Consola.error( - `failed to process request for integration '${integration.sort}' (${integration.name}): ${err}` + `failed to process request for integration '${integration.type}' (${integration.name}): ${err}` ); return { - sort: integration.sort, + type: integration.type, items: [], success: false, }; @@ -95,10 +95,10 @@ export const calendarRouter = createTRPCRouter({ } return { - tvShows: medias.filter((m) => m.sort === 'sonarr').flatMap((m) => m.items), - movies: medias.filter((m) => m.sort === 'radarr').flatMap((m) => m.items), - books: medias.filter((m) => m.sort === 'readarr').flatMap((m) => m.items), - musics: medias.filter((m) => m.sort === 'lidarr').flatMap((m) => m.items), + tvShows: medias.filter((m) => m.type === 'sonarr').flatMap((m) => m.items), + movies: medias.filter((m) => m.type === 'radarr').flatMap((m) => m.items), + books: medias.filter((m) => m.type === 'readarr').flatMap((m) => m.items), + musics: medias.filter((m) => m.type === 'lidarr').flatMap((m) => m.items), totalCount: medias.reduce((p, c) => p + c.items.length, 0), }; }), diff --git a/src/server/api/routers/config.ts b/src/server/api/routers/config.ts index 3388da688..40ec8285e 100644 --- a/src/server/api/routers/config.ts +++ b/src/server/api/routers/config.ts @@ -127,26 +127,6 @@ export const configRouter = createTRPCRouter({ newConfig = { ...newConfig, - widgets: [ - ...newConfig.widgets.map((x) => { - if (x.type !== 'rss') { - return x; - } - - const rssWidget = x as IRssWidget; - - return { - ...rssWidget, - properties: { - ...rssWidget.properties, - rssFeedUrl: - typeof rssWidget.properties.rssFeedUrl === 'string' - ? [rssWidget.properties.rssFeedUrl] - : rssWidget.properties.rssFeedUrl, - }, - } as IRssWidget; - }), - ], }; // Save the body in the /data/config folder with the slug as filename diff --git a/src/server/api/routers/download.ts b/src/server/api/routers/download.ts index e091c286f..6aacb4045 100644 --- a/src/server/api/routers/download.ts +++ b/src/server/api/routers/download.ts @@ -25,7 +25,7 @@ export const downloadRouter = createTRPCRouter({ z.object({ boardId: z.string(), widgetId: z.string(), - sort: z.enum(['torrents-status', 'dlspeed']), + type: z.enum(['torrents-status', 'dlspeed']), }) ) .query(async ({ input, ctx }) => { @@ -33,7 +33,7 @@ export const downloadRouter = createTRPCRouter({ input.boardId, input.widgetId, ctx.session?.user, - input.sort + input.type ); if (!widget) { @@ -107,7 +107,7 @@ const GetDataFromClient = async ( .reduce((acc, torrent) => acc + torrent, 0), }); - switch (integration.sort) { + switch (integration.type) { case 'deluge': { return reduceTorrent( await new Deluge({ diff --git a/src/server/api/routers/integration.ts b/src/server/api/routers/integration.ts index 876bc7776..f0d7f5d84 100644 --- a/src/server/api/routers/integration.ts +++ b/src/server/api/routers/integration.ts @@ -7,7 +7,7 @@ import { adminProcedure, createTRPCRouter } from '../trpc'; export const integrationRouter = createTRPCRouter({ allMedia: adminProcedure.query(async () => { return await db.query.integrations.findMany({ - where: inArray(integrations.sort, ['jellyseerr', 'overseerr']), + where: inArray(integrations.type, ['jellyseerr', 'overseerr']), }); }), }); diff --git a/src/server/api/routers/overseerr.ts b/src/server/api/routers/overseerr.ts index 63a7a3d46..ad84e3407 100644 --- a/src/server/api/routers/overseerr.ts +++ b/src/server/api/routers/overseerr.ts @@ -32,7 +32,7 @@ export const overseerrRouter = createTRPCRouter({ .where( and( eq(boardIntegrations.boardId, input.boardId), - eq(integrations.sort, input.integration) + eq(integrations.type, input.integration) ) ) .get(); diff --git a/src/server/api/routers/usenet/client/usenetClient.ts b/src/server/api/routers/usenet/client/usenetClient.ts index 5d67680a7..5fd469da1 100644 --- a/src/server/api/routers/usenet/client/usenetClient.ts +++ b/src/server/api/routers/usenet/client/usenetClient.ts @@ -13,7 +13,7 @@ export interface UsenetClient { } export function createUsenetClient(integration: WidgetIntegration) { - if (integration.sort === 'nzbGet') return new NzbgetUsenetClient(integration); + if (integration.type === 'nzbGet') return new NzbgetUsenetClient(integration); return new SabnzbdUsenetClient(integration); } diff --git a/src/server/db/items.ts b/src/server/db/items.ts index 0c93128a0..b5a9f20e2 100644 --- a/src/server/db/items.ts +++ b/src/server/db/items.ts @@ -1,61 +1,37 @@ import { IconKey, IconPassword, IconUser, TablerIconsProps } from '@tabler/icons-react'; import widgets from '~/widgets'; -import { objectEntries, objectKeys } from '../../tools/object'; +import { objectKeys } from '../../tools/object'; -type IntegrationTypeDefinition = { +export type IntegrationTypeDefinition = { secrets: IntegrationSecretKey[]; iconUrl: string; label: string; - groups: IntegrationGroup[]; + testEndpoint?: string; }; -type IntegrationGroup = - | 'mediaServer' - | 'mediaRequest' - | 'mediaApp' - | 'usenet' - | 'torrent' - | 'download' - | 'dns'; type IntegrationSecretDefinition = { - visibility: IntegrationSecretVisibility; icon: (props: TablerIconsProps) => JSX.Element; }; export const colorSchemes = ['environment', 'light', 'dark'] as const; export const firstDaysOfWeek = ['monday', 'saturday', 'sunday'] as const; -export const integrationSecretVisibility = ['private', 'public'] as const; export const integrationSecrets = { apiKey: { - visibility: 'private', icon: IconKey, }, username: { - visibility: 'public', icon: IconUser, }, password: { - visibility: 'private', icon: IconPassword, }, } satisfies Record; -export const widgetSorts = objectKeys(widgets); -export const widgetOptionTypes = [ - 'string', - 'number', - 'boolean', - 'object', - 'array', - 'null', -] as const; + +export const widgetTypes = objectKeys(widgets); +export const widgetOptionTypes = ['string', 'number', 'boolean', 'object', 'array', 'null'] as const; export const boardBackgroundImageAttachmentTypes = ['fixed', 'scroll'] as const; -export const boardBackgroundImageRepeatTypes = [ - 'repeat', - 'repeat-x', - 'repeat-y', - 'no-repeat', -] as const; +export const boardBackgroundImageRepeatTypes = ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'] as const; export const boardBackgroundImageSizeTypes = ['cover', 'contain'] as const; export const appNamePositions = ['right', 'left', 'top', 'bottom'] as const; export const appNameStyles = ['normal', 'hide', 'hover'] as const; @@ -66,107 +42,95 @@ export const statusCodeTypes = [ 'clientError', 'serverError', ] as const; -export const sectionTypes = ['sidebar', 'empty', 'category', 'hidden'] as const; -export const layoutKinds = ['mobile', 'desktop'] as const; +const sectionTypes = ['sidebar', 'empty', 'category', 'hidden'] as const; +const layoutKinds = ['mobile', 'desktop'] as const; export const integrationTypes = { readarr: { secrets: ['apiKey'], iconUrl: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/readarr.png', label: 'Readarr', - groups: ['mediaApp'], + testEndpoint: '/api', }, radarr: { secrets: ['apiKey'], iconUrl: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/radarr.png', label: 'Radarr', - groups: ['mediaApp'], + testEndpoint: '/api', }, sonarr: { secrets: ['apiKey'], iconUrl: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/sonarr.png', label: 'Sonarr', - groups: ['mediaApp'], + testEndpoint: '/api', }, lidarr: { secrets: ['apiKey'], iconUrl: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/lidarr.png', label: 'Lidarr', - groups: ['mediaApp'], + testEndpoint: '/api', }, sabnzbd: { - secrets: [], + secrets: ['username', 'password'], iconUrl: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/sabnzbd.png', label: 'SABnzbd', - groups: ['usenet', 'download'], }, jellyseerr: { secrets: ['apiKey'], iconUrl: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/jellyseerr.png', label: 'Jellyseerr', - groups: ['mediaRequest'], }, overseerr: { secrets: ['apiKey'], iconUrl: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/overseerr.png', label: 'Overseerr', - groups: ['mediaRequest'], }, deluge: { secrets: ['password'], iconUrl: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/deluge.png', label: 'Deluge', - groups: ['torrent'], }, qBittorrent: { secrets: ['username', 'password'], iconUrl: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/qbittorrent.png', label: 'qBittorrent', - groups: ['torrent'], }, transmission: { secrets: ['username', 'password'], iconUrl: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/transmission.png', label: 'Transmission', - groups: ['torrent'], }, plex: { secrets: ['apiKey'], iconUrl: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/plex.png', label: 'Plex', - groups: ['mediaServer'], }, jellyfin: { secrets: ['username', 'password'], iconUrl: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/jellyfin.png', label: 'Jellyfin', - groups: ['mediaServer'], }, nzbGet: { secrets: ['username', 'password'], iconUrl: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/nzbget.png', label: 'NZBGet', - groups: ['usenet', 'download'], }, pihole: { secrets: ['apiKey'], iconUrl: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/pi-hole.png', label: 'PiHole', - groups: ['dns'], }, adGuardHome: { secrets: ['username', 'password'], iconUrl: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/adguard-home.png', label: 'AdGuard Home', - groups: ['dns'], }, } satisfies Record; export type ColorScheme = (typeof colorSchemes)[number]; export type FirstDayOfWeek = (typeof firstDaysOfWeek)[number]; export type IntegrationType = keyof typeof integrationTypes; -export type IntegrationSecretVisibility = (typeof integrationSecretVisibility)[number]; export type IntegrationSecretKey = keyof typeof integrationSecrets; -export type WidgetSort = (typeof widgetSorts)[number]; +export type WidgetType = (typeof widgetTypes)[number]; export type WidgetOptionType = (typeof widgetOptionTypes)[number]; export type BoardBackgroundImageAttachmentType = (typeof boardBackgroundImageAttachmentTypes)[number]; @@ -177,17 +141,3 @@ export type AppNameStyle = (typeof appNameStyles)[number]; export type StatusCodeType = (typeof statusCodeTypes)[number]; export type SectionType = (typeof sectionTypes)[number]; export type LayoutKind = (typeof layoutKinds)[number]; - -type InferIntegrationTypeFromGroup = { - [key in keyof typeof integrationTypes]: (typeof integrationTypes)[key] extends { - groups: TGroup[]; - } - ? key - : never; -}[keyof typeof integrationTypes]; - -export const integrationGroup = (group: TGroup) => { - return objectEntries(integrationTypes) - .filter(([, { groups }]) => groups.some((g) => group === g)) - .map(([key]) => key) as InferIntegrationTypeFromGroup[]; -}; diff --git a/src/server/db/queries/integrations.ts b/src/server/db/queries/integrations.ts index f36df172f..ccd8276eb 100644 --- a/src/server/db/queries/integrations.ts +++ b/src/server/db/queries/integrations.ts @@ -15,7 +15,7 @@ export async function getIntegrations( export const getIntegrationsForWidget = async ( boardId: string, - sorts: TIntegrations[], + types: TIntegrations[], user: User | null | undefined, widgetId: 'ignore' | (string & {}) @@ -24,7 +24,7 @@ export const getIntegrationsForWidget = async = 1 ? inArray(integrations.sort, sorts) : undefined, + types.length >= 1 ? inArray(integrations.type, types) : undefined, widgetId !== 'ignore' ? eq(items.id, widgetId) : undefined ), with: { @@ -46,7 +46,7 @@ export const getIntegrationsForWidget = async x.widget?.integrations ?? []) - .map((x) => ({ ...x.integration, sort: x.integration.sort as TIntegrations })); + .map((x) => ({ ...x.integration, type: x.integration.type as TIntegrations })); }; export async function getIntegrationAsync(integrationId: string) { diff --git a/src/server/db/queries/widget.ts b/src/server/db/queries/widget.ts index c95e8f6c8..ccd6cae34 100644 --- a/src/server/db/queries/widget.ts +++ b/src/server/db/queries/widget.ts @@ -8,18 +8,18 @@ import { InferWidgetOptions } from '~/widgets/widgets'; import { db } from '..'; import { boards, items, widgets } from '../schema'; -export const getWidgetAsync = async ( +export const getWidgetAsync = async ( boardId: string, id: string, user: User | null | undefined, - sort: TSort + type: TType ) => { const widgetItem = await db.query.items.findFirst({ where: and( eq(items.boardId, boardId), eq(items.id, id), user ? undefined : eq(boards.allowGuests, true), - eq(widgets.sort, sort) + eq(widgets.type, type) ), with: { widget: { @@ -44,21 +44,21 @@ export const getWidgetAsync = async a.path.localeCompare(b.path)) + widgetItem.widget.options.sort((a, b) => a.path.localeCompare(b.path)) ); - objectEntries(widgetDefinitions[sort].options).forEach(([key, definition]) => { + objectEntries(widgetDefinitions[type].options).forEach(([key, definition]) => { mappedOptions[key] = mappedOptions[key] ?? definition.defaultValue; }); return { id: widgetItem.id, - sort: widgetItem.widget!.sort, - options: mappedOptions as InferWidgetOptions<(typeof widgetDefinitions)[TSort]>, + type: widgetItem.widget.type, + options: mappedOptions as InferWidgetOptions<(typeof widgetDefinitions)[TType]>, integrations: widgetItem.widget!.integrations.map((i) => ({ ...i.integration, })), diff --git a/src/server/db/schema.ts b/src/server/db/schema.ts index 200738462..330cc9f86 100644 --- a/src/server/db/schema.ts +++ b/src/server/db/schema.ts @@ -1,5 +1,5 @@ import type { MantineColor } from '@mantine/core'; -import { type InferSelectModel, relations } from 'drizzle-orm'; +import { relations, type InferSelectModel } from 'drizzle-orm'; import { index, int, integer, primaryKey, sqliteTable, text } from 'drizzle-orm/sqlite-core'; import { type AdapterAccount } from 'next-auth/adapters'; @@ -12,12 +12,11 @@ import type { ColorScheme, FirstDayOfWeek, IntegrationSecretKey, - IntegrationSecretVisibility, IntegrationType, LayoutKind, SectionType, WidgetOptionType, - WidgetSort, + WidgetType, } from './items'; export const users = sqliteTable('user', { @@ -156,7 +155,7 @@ export const boards = sqliteTable('board', { export const integrations = sqliteTable('integration', { id: text('id').notNull().primaryKey(), - sort: text('sort').$type().notNull(), + type: text('type').$type().notNull(), name: text('name').notNull(), url: text('url').notNull(), }); @@ -166,7 +165,6 @@ export const integrationSecrets = sqliteTable( { key: text('key').$type().notNull(), value: text('value'), - visibility: text('visibility').$type().notNull(), integrationId: text('integration_id') .notNull() .references(() => integrations.id, { onDelete: 'cascade' }), @@ -189,7 +187,7 @@ export const boardIntegrations = sqliteTable( export const widgets = sqliteTable('widget', { id: text('id').notNull().primaryKey(), - sort: text('sort').$type().notNull(), + type: text('sort').$type().notNull(), itemId: text('item_id') .notNull() .references(() => items.id, { onDelete: 'cascade' }), diff --git a/src/validations/widget.ts b/src/validations/widget.ts index 7965acb2d..69a8418b9 100644 --- a/src/validations/widget.ts +++ b/src/validations/widget.ts @@ -1,15 +1,14 @@ import { z } from 'zod'; -import { widgetSorts } from '~/server/db/items'; +import { widgetTypes } from '~/server/db/items'; -import { appSchema } from './app'; import { commonItemSchema } from './item'; -export const widgetSortSchema = z.enum([widgetSorts[0], ...widgetSorts.slice(1)]); +export const widgetTypeSchema = z.enum([widgetTypes[0], ...widgetTypes.slice(1)]); export const widgetCreationSchema = z.object({ id: z.string(), kind: z.literal('widget'), - sort: widgetSortSchema, + type: widgetTypeSchema, options: z.record(z.string(), z.unknown()), }); diff --git a/src/widgets/WidgetWrapper.tsx b/src/widgets/WidgetWrapper.tsx index 5818d9c98..6e510c521 100644 --- a/src/widgets/WidgetWrapper.tsx +++ b/src/widgets/WidgetWrapper.tsx @@ -14,7 +14,7 @@ interface WidgetWrapperProps { // If a property has no value, set it to the default value const useWidgetWithDefaultOptionValues = (widget: T): T => { - const definition = Widgets[widget.sort]; + const definition = Widgets[widget.type]; const newProps: Record = {}; diff --git a/src/widgets/download-speed/Tile.tsx b/src/widgets/download-speed/Tile.tsx index c70ef09c1..6949e10fa 100644 --- a/src/widgets/download-speed/Tile.tsx +++ b/src/widgets/download-speed/Tile.tsx @@ -24,9 +24,9 @@ import { NormalizedDownloadQueueResponse, TorrentTotalDownload, } from '~/types/api/downloads/queue/NormalizedDownloadQueueResponse'; +import { api } from '~/utils/api'; import definition, { ITorrentNetworkTraffic } from './TorrentNetworkTrafficTile'; -import { useGetDownloadClientsQueue } from './useGetNetworkSpeed'; interface TorrentNetworkTrafficTileProps { widget: ITorrentNetworkTraffic; @@ -42,11 +42,16 @@ export default function TorrentNetworkTrafficTile({ widget }: TorrentNetworkTraf const [clientDataHistory, setClientDataHistory] = useListState(); - const { data, dataUpdatedAt } = useGetDownloadClientsQueue({ - boardId, - widgetId: widget.id, - sort: 'dlspeed', - }); + const { data, dataUpdatedAt } = api.download.get.useQuery( + { + boardId, + widgetId: widget.id, + type: definition.id, + }, + { + refetchInterval: 3000, + } + ); useEffect(() => { if (data) { @@ -169,7 +174,7 @@ export default function TorrentNetworkTrafficTile({ widget }: TorrentNetworkTraf return ( - + {current.name} @@ -257,7 +262,7 @@ export default function TorrentNetworkTrafficTile({ widget }: TorrentNetworkTraf withArrow withinPortal > - + ); })} diff --git a/src/widgets/download-speed/useGetNetworkSpeed.tsx b/src/widgets/download-speed/useGetNetworkSpeed.tsx deleted file mode 100644 index 7e9e23cab..000000000 --- a/src/widgets/download-speed/useGetNetworkSpeed.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { RouterInputs, api } from '~/utils/api'; - -export const useGetDownloadClientsQueue = ({ - boardId, - widgetId, - sort, -}: RouterInputs['download']['get']) => { - return api.download.get.useQuery( - { - boardId, - widgetId, - sort, - }, - { - refetchInterval: 3000, - } - ); -}; diff --git a/src/widgets/torrent/TorrentTile.tsx b/src/widgets/torrent/TorrentTile.tsx index a1b491f28..8063fa907 100644 --- a/src/widgets/torrent/TorrentTile.tsx +++ b/src/widgets/torrent/TorrentTile.tsx @@ -22,8 +22,8 @@ import { useCardStyles } from '~/components/layout/Common/useCardStyles'; import { MIN_WIDTH_MOBILE } from '~/constants/constants'; import { NormalizedDownloadQueueResponse } from '~/types/api/downloads/queue/NormalizedDownloadQueueResponse'; import { AppIntegrationType } from '~/types/app'; +import { api } from '~/utils/api'; -import { useGetDownloadClientsQueue } from '../download-speed/useGetNetworkSpeed'; import { defineWidget } from '../helper'; import { InferWidget } from '../widgets'; import { BitTorrentQueueItem } from './TorrentQueueItem'; @@ -99,11 +99,16 @@ function TorrentTile({ widget }: TorrentTileProps) { isError: boolean; isInitialLoading: boolean; dataUpdatedAt: number; - } = useGetDownloadClientsQueue({ - boardId, - widgetId: widget.id, - sort: 'torrents-status', - }); + } = api.download.get.useQuery( + { + boardId, + widgetId: widget.id, + type: definition.id, + }, + { + refetchInterval: 3000, + } + ); if (isError) { return (