From fd9467ef1f4d09b8eecb14cef3592719bec87aa0 Mon Sep 17 00:00:00 2001 From: ajnart Date: Tue, 3 Jan 2023 22:51:30 +0900 Subject: [PATCH 01/46] Fix icon URL path #545 --- .../Tabs/AppereanceTab/IconSelector/IconSelector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/IconSelector/IconSelector.tsx b/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/IconSelector/IconSelector.tsx index 8a39ed21d..8ec13cbb3 100644 --- a/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/IconSelector/IconSelector.tsx +++ b/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/IconSelector/IconSelector.tsx @@ -36,7 +36,7 @@ export const IconSelector = ({ onChange, allowAppNamePropagation, form }: IconSe const { data, isLoading } = useRepositoryIconsQuery({ url: 'https://api.github.com/repos/walkxcode/Dashboard-Icons/contents/png', converter: (item) => ({ - url: `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${item.name}`, + url: `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/${item.name}`, fileName: item.name, }), }); From 831e671956676982574aab02f2987e53f09d047b Mon Sep 17 00:00:00 2001 From: Manuel Ruwe Date: Tue, 3 Jan 2023 20:36:58 +0100 Subject: [PATCH 02/46] =?UTF-8?q?=F0=9F=90=9B=20Accidential=20overwrite=20?= =?UTF-8?q?of=20app=20secrets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/api/configs/[slug].ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/pages/api/configs/[slug].ts b/src/pages/api/configs/[slug].ts index 3d1ea5605..a4bc6b5f1 100644 --- a/src/pages/api/configs/[slug].ts +++ b/src/pages/api/configs/[slug].ts @@ -45,13 +45,21 @@ function Put(req: NextApiRequest, res: NextApiResponse) { (previousProperty) => previousProperty.field === property.field ); + if (property.value !== undefined && property.value !== null) { + Consola.info( + 'Detected credential change of private secret. Value will be overwritten in configuration' + ); + return { + field: property.field, + type: property.type, + value: property.value, + }; + } + return { field: property.field, type: property.type, - value: - property.value !== undefined || property.value === null - ? property.value - : previousProperty?.value, + value: previousProperty?.value, }; }), }, From 501714113ff1702323cffbe1061d276e1ab34e02 Mon Sep 17 00:00:00 2001 From: Manuel Ruwe Date: Tue, 3 Jan 2023 21:13:53 +0100 Subject: [PATCH 03/46] =?UTF-8?q?=F0=9F=90=9B=20Fix=20overflowing=20torren?= =?UTF-8?q?t=20module=20issue=20and=20added=20updated=20last=20text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/widgets/bitTorrent/BitTorrentTile.tsx | 62 +++++++++++++++-------- src/widgets/date/DateTile.tsx | 2 +- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/widgets/bitTorrent/BitTorrentTile.tsx b/src/widgets/bitTorrent/BitTorrentTile.tsx index 654ca0020..b9485f021 100644 --- a/src/widgets/bitTorrent/BitTorrentTile.tsx +++ b/src/widgets/bitTorrent/BitTorrentTile.tsx @@ -1,6 +1,7 @@ -import { NormalizedTorrent, TorrentState } from '@ctrl/shared-torrent'; +import { NormalizedTorrent } from '@ctrl/shared-torrent'; import { Center, + Flex, Group, Loader, ScrollArea, @@ -12,6 +13,9 @@ import { } 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 { useEffect, useState } from 'react'; import { useConfigContext } from '../../config/provider'; @@ -21,6 +25,9 @@ import { defineWidget } from '../helper'; import { IWidget } from '../widgets'; import { BitTorrrentQueueItem } from './BitTorrentQueueItem'; +dayjs.extend(duration); +dayjs.extend(relativeTime); + const downloadAppTypes: AppIntegrationType['type'][] = ['deluge', 'qBittorrent', 'transmission']; const definition = defineWidget({ @@ -62,7 +69,9 @@ function BitTorrentTile({ widget }: BitTorrentTileProps) { []; const [selectedAppId, setSelectedApp] = useState(downloadApps[0]?.id); - const { data, isFetching, isError } = useGetTorrentData({ appId: selectedAppId! }); + const { data, isError, isInitialLoading, dataUpdatedAt } = useGetTorrentData({ + appId: selectedAppId!, + }); useEffect(() => { if (!selectedAppId && downloadApps.length) { @@ -92,7 +101,7 @@ function BitTorrentTile({ widget }: BitTorrentTileProps) { ); } - if (isFetching) { + if (isInitialLoading) { return ( @@ -124,26 +133,35 @@ function BitTorrentTile({ widget }: BitTorrentTileProps) { return true; }; + 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 && } - - - - - {data.filter(filter).map((item: NormalizedTorrent, index: number) => ( - - ))} - -
{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')}
-
+ + + + + + + + {width > MIN_WIDTH_MOBILE && } + {width > MIN_WIDTH_MOBILE && } + {width > MIN_WIDTH_MOBILE && } + + + + + {data.filter(filter).map((item: NormalizedTorrent, index: number) => ( + + ))} + +
{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')}
+
+ + Last updated {humanizedDuration} ago + +
); } diff --git a/src/widgets/date/DateTile.tsx b/src/widgets/date/DateTile.tsx index 653f813c5..a7aee4fc9 100644 --- a/src/widgets/date/DateTile.tsx +++ b/src/widgets/date/DateTile.tsx @@ -1,4 +1,4 @@ -import { Center, Stack, Text, Title } from '@mantine/core'; +import { Stack, Text, Title } from '@mantine/core'; import { IconClock } from '@tabler/icons'; import dayjs from 'dayjs'; import { useEffect, useRef, useState } from 'react'; From 8a91edbd59007bdf0584a7ec3b318c185a146277 Mon Sep 17 00:00:00 2001 From: Manuel Ruwe Date: Tue, 3 Jan 2023 21:23:37 +0100 Subject: [PATCH 04/46] =?UTF-8?q?=F0=9F=90=9B=20Fix=20filtering=20and=20da?= =?UTF-8?q?ta=20array=20for=20integration=20options=20select?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InputElements/IntegrationSelector.tsx | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/IntegrationSelector.tsx b/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/IntegrationSelector.tsx index e5a64d992..b1b6dc523 100644 --- a/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/IntegrationSelector.tsx +++ b/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/IntegrationSelector.tsx @@ -36,7 +36,7 @@ export const IntegrationSelector = ({ form }: IntegrationSelectorProps) => { label: 'Transmission', }, { - value: 'qbittorrent', + value: 'qBittorrent', image: 'https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/qbittorrent.png', label: 'qBittorrent', }, @@ -100,16 +100,20 @@ export const IntegrationSelector = ({ form }: IntegrationSelectorProps) => { placeholder={t('integration.type.placeholder')} itemComponent={SelectItemComponent} data={data} - maxDropdownHeight={150} + maxDropdownHeight={250} dropdownPosition="bottom" clearable variant="default" searchable + filter={(value, item) => + item.label?.toLowerCase().includes(value.toLowerCase().trim()) || + item.description?.toLowerCase().includes(value.toLowerCase().trim()) + } icon={ form.values.integration?.type && ( x.value === form.values.integration?.type)?.image} - alt="test" + alt="integration" width={20} height={20} /> @@ -119,6 +123,7 @@ export const IntegrationSelector = ({ form }: IntegrationSelectorProps) => { form.setFieldValue('integration.properties', getNewProperties(value)); inputProps.onChange(value); }} + withinPortal {...inputProps} /> ); @@ -126,17 +131,23 @@ export const IntegrationSelector = ({ form }: IntegrationSelectorProps) => { interface ItemProps extends React.ComponentPropsWithoutRef<'div'> { image: string; + description: string; label: string; } const SelectItemComponent = forwardRef( - ({ image, label, ...others }: ItemProps, ref) => ( + ({ image, label, description, ...others }: ItemProps, ref) => (
integration icon
{label} + {description && ( + + {description} + + )}
From 6fd4608b22e49dac19b361c53e642f8dfe01c531 Mon Sep 17 00:00:00 2001 From: Manuel Ruwe Date: Tue, 3 Jan 2023 21:53:27 +0100 Subject: [PATCH 05/46] =?UTF-8?q?=E2=9C=A8=20Add=20visual=20hint=20for=20d?= =?UTF-8?q?efined=20and=20undefined=20credentials?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/layout/modals/add-app.json | 10 ++- .../InputElements/GenericSecretInput.tsx | 67 +++++++++++++++++-- .../IntegrationOptionsRenderer.tsx | 3 + src/types/app.ts | 4 +- 4 files changed, 75 insertions(+), 9 deletions(-) diff --git a/public/locales/en/layout/modals/add-app.json b/public/locales/en/layout/modals/add-app.json index 1f08dd639..ff66e30df 100644 --- a/public/locales/en/layout/modals/add-app.json +++ b/public/locales/en/layout/modals/add-app.json @@ -46,11 +46,17 @@ "type": { "label": "Integration configuration", "description": "Treats this app as the selected integration and provides you with per-app configuration", - "placeholder": "Select an integration" + "placeholder": "Select an integration", + "defined": "Defined", + "undefined": "Undefined", + "public": "Public", + "private": "Private", + "explanationPublic": "A private secret will be sent to the server. Once your browser has refreshed the page, it will never be sent to the client.", + "explanationPrivate": "A public secret will always be sent to the client and is accessible over the API. It should not contain any confidential values such as usernames, passwords, tokens, certificates and similar" }, "secrets": { "description": "To update a secret, enter a value and click the save button. To remove a secret, use the clear button.", - "warning": "Please note that Homarr removes secrets from the configuration for security reasons. Thus, you can only either define or unset any credentials. Your credentials act as the main access for your integrations and you should never share them with anybody else. Make sure to store and manage your secrets safely.", + "warning": "Your credentials act as the access for your integrations and you should never share them with anybody else. The official Homarr team will never ask for credentials. Make sure to store and manage your secrets safely.", "clear": "Clear secret", "save": "Save secret", "update": "Update secret" diff --git a/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/GenericSecretInput.tsx b/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/GenericSecretInput.tsx index 778fc61b3..a1627ceff 100644 --- a/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/GenericSecretInput.tsx +++ b/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/GenericSecretInput.tsx @@ -9,15 +9,21 @@ import { Stack, ThemeIcon, Title, + Text, + Badge, + Tooltip, } from '@mantine/core'; -import { TablerIcon } from '@tabler/icons'; +import { IconLock, TablerIcon } from '@tabler/icons'; import { useTranslation } from 'next-i18next'; import { useState } from 'react'; +import { AppIntegrationPropertyAccessabilityType } from '../../../../../../../../types/app'; interface GenericSecretInputProps { label: string; value: string; setIcon: TablerIcon; + secretIsPresent: boolean; + type: AppIntegrationPropertyAccessabilityType; onClickUpdateButton: (value: string | undefined) => void; } @@ -25,6 +31,8 @@ export const GenericSecretInput = ({ label, value, setIcon, + secretIsPresent, + type, onClickUpdateButton, ...props }: GenericSecretInputProps) => { @@ -36,17 +44,61 @@ export const GenericSecretInput = ({ const { t } = useTranslation(['layout/modals/add-app', 'common']); return ( - + - + - - {t(label)} - + + + {t(label)} + + + + {secretIsPresent ? ( + + {t('integration.type.defined')} + + ) : ( + + {t('integration.type.undefined')} + + )} + {type === 'private' ? ( + + + {t('integration.type.private')} + + + ) : ( + + + {t('integration.type.public')} + + + )} + + + + {type === 'private' + ? 'Private: Once saved, you cannot read out this value again' + : 'Public: Can be read out repeatedly'} + @@ -80,4 +132,7 @@ const useStyles = createStyles(() => ({ alignSelfCenter: { alignSelf: 'center', }, + textTransformUnset: { + textTransform: 'inherit', + }, })); diff --git a/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/IntegrationOptionsRenderer/IntegrationOptionsRenderer.tsx b/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/IntegrationOptionsRenderer/IntegrationOptionsRenderer.tsx index 182539796..145dd3f00 100644 --- a/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/IntegrationOptionsRenderer/IntegrationOptionsRenderer.tsx +++ b/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/IntegrationOptionsRenderer/IntegrationOptionsRenderer.tsx @@ -45,6 +45,7 @@ export const IntegrationOptionsRenderer = ({ form }: IntegrationOptionsRendererP const formValue = form.values.integration?.properties[indexInFormValue]; const isPresent = formValue?.isDefined; + const accessabilityType = formValue?.type; if (!definition) { return ( @@ -57,6 +58,7 @@ export const IntegrationOptionsRenderer = ({ form }: IntegrationOptionsRendererP secretIsPresent={isPresent} setIcon={IconKey} value={formValue.value} + type={accessabilityType} {...form.getInputProps(`integration.properties.${index}.value`)} /> ); @@ -72,6 +74,7 @@ export const IntegrationOptionsRenderer = ({ form }: IntegrationOptionsRendererP value="" secretIsPresent={isPresent} setIcon={definition.icon} + type={accessabilityType} {...form.getInputProps(`integration.properties.${index}.value`)} /> ); diff --git a/src/types/app.ts b/src/types/app.ts index 0f97d65ad..7edeadee9 100644 --- a/src/types/app.ts +++ b/src/types/app.ts @@ -52,12 +52,14 @@ export type ConfigAppIntegrationType = Omit & }; export type AppIntegrationPropertyType = { - type: 'private' | 'public'; + type: AppIntegrationPropertyAccessabilityType; field: IntegrationField; value?: string | null; isDefined: boolean; }; +export type AppIntegrationPropertyAccessabilityType = 'private' | 'public'; + type ConfigAppIntegrationPropertyType = Omit; export type IntegrationField = 'apiKey' | 'password' | 'username'; From e6f888dbe580345dc2741c30783fba68eb0d7e33 Mon Sep 17 00:00:00 2001 From: ajnart Date: Wed, 4 Jan 2023 21:51:25 +0900 Subject: [PATCH 06/46] Add default status check --- src/components/Dashboard/Dashboard.tsx | 2 ++ .../Components/Overview/AvailableElementsOverview.tsx | 4 ++-- .../layout/header/Actions/Docker/ContainerActionBar.tsx | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/Dashboard/Dashboard.tsx b/src/components/Dashboard/Dashboard.tsx index 35acd4d39..022f4462d 100644 --- a/src/components/Dashboard/Dashboard.tsx +++ b/src/components/Dashboard/Dashboard.tsx @@ -1,3 +1,4 @@ +import { i18n } from 'next-i18next'; import { MobileRibbons } from './Mobile/Ribbon/MobileRibbon'; import { DashboardDetailView } from './Views/DetailView'; import { DashboardEditView } from './Views/EditView'; @@ -5,6 +6,7 @@ import { useEditModeStore } from './Views/useEditModeStore'; export const Dashboard = () => { const isEditMode = useEditModeStore((x) => x.enabled); + i18n?.changeLanguage('cimode'); return ( <> diff --git a/src/components/Dashboard/Modals/SelectElement/Components/Overview/AvailableElementsOverview.tsx b/src/components/Dashboard/Modals/SelectElement/Components/Overview/AvailableElementsOverview.tsx index 5c45e4f03..febf8ed0d 100644 --- a/src/components/Dashboard/Modals/SelectElement/Components/Overview/AvailableElementsOverview.tsx +++ b/src/components/Dashboard/Modals/SelectElement/Components/Overview/AvailableElementsOverview.tsx @@ -97,8 +97,8 @@ export const AvailableElementTypes = ({ iconUrl: '/imgs/logo/logo.png', }, network: { - enabledStatusChecker: false, - okStatus: [], + enabledStatusChecker: true, + okStatus: [200], }, behaviour: { isOpeningNewTab: true, diff --git a/src/components/layout/header/Actions/Docker/ContainerActionBar.tsx b/src/components/layout/header/Actions/Docker/ContainerActionBar.tsx index d24662c89..0b74c0f04 100644 --- a/src/components/layout/header/Actions/Docker/ContainerActionBar.tsx +++ b/src/components/layout/header/Actions/Docker/ContainerActionBar.tsx @@ -170,8 +170,8 @@ export default function ContainerActionBar({ selected, reload }: ContainerAction iconUrl: '/imgs/logo/logo.png', // TODO: find icon automatically }, network: { - enabledStatusChecker: false, - okStatus: [], + enabledStatusChecker: true, + okStatus: [200], }, behaviour: { isOpeningNewTab: true, From 4c68eed3aa9e1c800ba3c922e1105483f7ce9ddc Mon Sep 17 00:00:00 2001 From: ajnart Date: Wed, 4 Jan 2023 21:51:43 +0900 Subject: [PATCH 07/46] Fix ping system API return codes --- src/pages/api/modules/ping.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/api/modules/ping.ts b/src/pages/api/modules/ping.ts index 2528a8776..270ed49f8 100644 --- a/src/pages/api/modules/ping.ts +++ b/src/pages/api/modules/ping.ts @@ -17,7 +17,7 @@ async function Get(req: NextApiRequest, res: NextApiResponse) { } else if (error.code === 'ECONNABORTED') { res.status(408).json('Request Timeout'); } else { - res.status(500).json('Server Error'); + res.status(error.response ? error.response.status : 500).json('Server Error'); } }); // // Make a request to the URL From 9900a2f0784ed12c8b5dbacad88ee0327c1e26be Mon Sep 17 00:00:00 2001 From: ajnart Date: Wed, 4 Jan 2023 21:59:15 +0900 Subject: [PATCH 08/46] Fix Docker disabled search bar --- src/components/layout/header/Actions/Docker/DockerTable.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/layout/header/Actions/Docker/DockerTable.tsx b/src/components/layout/header/Actions/Docker/DockerTable.tsx index 6f525d49e..2925e3990 100644 --- a/src/components/layout/header/Actions/Docker/DockerTable.tsx +++ b/src/components/layout/header/Actions/Docker/DockerTable.tsx @@ -118,7 +118,6 @@ export default function DockerTable({ icon={} value={search} onChange={handleSearchChange} - disabled={usedContainers.length === 0} /> From 4aa50cc9cd60ff67edca71b79e03b5f0ae01bab2 Mon Sep 17 00:00:00 2001 From: ajnart Date: Wed, 4 Jan 2023 22:17:37 +0900 Subject: [PATCH 09/46] Fix AddToHomarr button --- .../Actions/Docker/ContainerActionBar.tsx | 26 ++++++++++++------- src/tools/types.ts | 1 + 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/components/layout/header/Actions/Docker/ContainerActionBar.tsx b/src/components/layout/header/Actions/Docker/ContainerActionBar.tsx index 0b74c0f04..ca2036c07 100644 --- a/src/components/layout/header/Actions/Docker/ContainerActionBar.tsx +++ b/src/components/layout/header/Actions/Docker/ContainerActionBar.tsx @@ -16,8 +16,11 @@ import { useTranslation } from 'next-i18next'; import { useState } from 'react'; import { TFunction } from 'react-i18next'; import { v4 as uuidv4 } from 'uuid'; +import { useConfigContext } from '../../../../../config/provider'; +import { tryMatchService } from '../../../../../tools/addToHomarr'; import { openContextModalGeneric } from '../../../../../tools/mantineModalManagerExtensions'; import { AppType } from '../../../../../types/app'; +import { appTileDefinition } from '../../../../Dashboard/Tiles/Apps/AppTile'; let t: TFunction<'modules/docker', undefined>; @@ -68,6 +71,8 @@ export interface ContainerActionBarProps { export default function ContainerActionBar({ selected, reload }: ContainerActionBarProps) { t = useTranslation('modules/docker').t; const [isLoading, setisLoading] = useState(false); + const { name: configName, config } = useConfigContext(); + const getLowestWrapper = () => config?.wrappers.sort((a, b) => a.position - b.position)[0]; return ( @@ -158,16 +163,17 @@ export default function ContainerActionBar({ selected, reload }: ContainerAction radius="md" disabled={selected.length === 0 || selected.length > 1} onClick={() => { + const app = tryMatchService(selected.at(0)!); const containerUrl = `http://localhost:${selected[0].Ports[0].PublicPort}`; - openContextModalGeneric<{ service: AppType }>({ - modal: 'editService', + openContextModalGeneric<{ app: AppType; allowAppNamePropagation: boolean }>({ + modal: 'editApp', innerProps: { - service: { + app: { id: uuidv4(), - name: selected[0].Names[0], + name: app.name ? app.name : selected[0].Names[0].substring(1), url: containerUrl, appearance: { - iconUrl: '/imgs/logo/logo.png', // TODO: find icon automatically + iconUrl: app.icon ? app.icon : '/imgs/logo/logo.png', }, network: { enabledStatusChecker: true, @@ -178,9 +184,9 @@ export default function ContainerActionBar({ selected, reload }: ContainerAction externalUrl: '', }, area: { - type: 'sidebar', // TODO: Set the wrapper automatically + type: 'wrapper', properties: { - location: 'right', + id: getLowestWrapper()?.id ?? 'default', }, }, shape: { @@ -189,8 +195,8 @@ export default function ContainerActionBar({ selected, reload }: ContainerAction y: 0, }, size: { - height: 1, - width: 1, + width: appTileDefinition.minWidth, + height: appTileDefinition.minHeight, }, }, integration: { @@ -198,7 +204,9 @@ export default function ContainerActionBar({ selected, reload }: ContainerAction properties: [], }, }, + allowAppNamePropagation: true, }, + size: 'xl', }); }} > diff --git a/src/tools/types.ts b/src/tools/types.ts index 83f5b24de..cb59c6f3a 100644 --- a/src/tools/types.ts +++ b/src/tools/types.ts @@ -102,6 +102,7 @@ export const portmap = [ { name: 'nzbget', value: '6789' }, ]; +//TODO: Fix this to be used in the docker add to homarr button export const MatchingImages: { image: string; type: ServiceType; From 92d395f71d96d488a9874f2ed252007d1196b868 Mon Sep 17 00:00:00 2001 From: ajnart Date: Wed, 4 Jan 2023 22:39:58 +0900 Subject: [PATCH 10/46] Styling settings and translations --- src/components/About/AboutModal.tsx | 2 + .../Settings/Common/CommonSettings.tsx | 16 ++++--- .../Settings/Common/Config/ConfigActions.tsx | 2 +- src/components/Settings/Common/Credits.tsx | 48 ++++++------------- .../Customization/CustomizationSettings.tsx | 10 ++-- src/components/Settings/SettingsDrawer.tsx | 6 +-- 6 files changed, 35 insertions(+), 49 deletions(-) diff --git a/src/components/About/AboutModal.tsx b/src/components/About/AboutModal.tsx index 0a29bf330..8d258ebd3 100644 --- a/src/components/About/AboutModal.tsx +++ b/src/components/About/AboutModal.tsx @@ -31,6 +31,7 @@ import { CURRENT_VERSION } from '../../../data/constants'; import { useConfigContext } from '../../config/provider'; import { useConfigStore } from '../../config/store'; import { usePrimaryGradient } from '../layout/useGradient'; +import Credits from '../Settings/Common/Credits'; interface AboutModalProps { opened: boolean; @@ -113,6 +114,7 @@ export const AboutModal = ({ opened, closeModal, newVersionAvailable }: AboutMod Discord + ); }; diff --git a/src/components/Settings/Common/CommonSettings.tsx b/src/components/Settings/Common/CommonSettings.tsx index d61d1aecc..28fd7500c 100644 --- a/src/components/Settings/Common/CommonSettings.tsx +++ b/src/components/Settings/Common/CommonSettings.tsx @@ -1,4 +1,4 @@ -import { Space, Stack, Text } from '@mantine/core'; +import { ScrollArea, Space, Stack, Text } from '@mantine/core'; import { useConfigContext } from '../../../config/provider'; import ConfigChanger from '../../Config/ConfigChanger'; import ConfigActions from './Config/ConfigActions'; @@ -17,12 +17,14 @@ export default function CommonSettings() { } return ( - - - - - - + + + + + + + + ); } diff --git a/src/components/Settings/Common/Config/ConfigActions.tsx b/src/components/Settings/Common/Config/ConfigActions.tsx index 46a71b03d..6a1232b04 100644 --- a/src/components/Settings/Common/Config/ConfigActions.tsx +++ b/src/components/Settings/Common/Config/ConfigActions.tsx @@ -35,7 +35,7 @@ export default function ConfigActions() { closeModal={createCopyModal.close} initialConfigName={config.configProperties.name} /> - + {t('buttons.download')} diff --git a/src/components/Settings/Common/Credits.tsx b/src/components/Settings/Common/Credits.tsx index 677f283e3..704fa2fc9 100644 --- a/src/components/Settings/Common/Credits.tsx +++ b/src/components/Settings/Common/Credits.tsx @@ -9,40 +9,22 @@ export default function Credits() { return ( - - component="a" href="https://github.com/ajnart/homarr" size="lg"> - - - + {t('credits.madeWithLove')} + - {CURRENT_VERSION} - - - - - {t('credits.madeWithLove')} - - ajnart - - - component="a" href="https://discord.gg/aCsmEV5RgA" size="lg"> - - - + ajnart + + {' '}and you ! + ); } diff --git a/src/components/Settings/Customization/CustomizationSettings.tsx b/src/components/Settings/Customization/CustomizationSettings.tsx index 5b6def2dc..b4ed1d3bb 100644 --- a/src/components/Settings/Customization/CustomizationSettings.tsx +++ b/src/components/Settings/Customization/CustomizationSettings.tsx @@ -1,4 +1,5 @@ import { Button, ScrollArea, Stack } from '@mantine/core'; +import { useTranslation } from 'next-i18next'; import { useConfigContext } from '../../../config/provider'; import { useConfigStore } from '../../../config/store'; import { LayoutSelector } from './Layout/LayoutSelector'; @@ -14,6 +15,7 @@ import { ShadeSelector } from './Theme/ShadeSelector'; export default function CustomizationSettings() { const { config, name: configName } = useConfigContext(); + const { t } = useTranslation('common'); const { updateConfig } = useConfigStore(); @@ -26,8 +28,8 @@ export default function CustomizationSettings() { }; return ( - - + + @@ -47,7 +49,9 @@ export default function CustomizationSettings() { - + ); } diff --git a/src/components/Settings/SettingsDrawer.tsx b/src/components/Settings/SettingsDrawer.tsx index bdd4bc4e8..fe08e323b 100644 --- a/src/components/Settings/SettingsDrawer.tsx +++ b/src/components/Settings/SettingsDrawer.tsx @@ -2,7 +2,6 @@ import { Drawer, ScrollArea, Tabs, Title } from '@mantine/core'; import { useTranslation } from 'next-i18next'; import CommonSettings from './Common/CommonSettings'; -import Credits from './Common/Credits'; import CustomizationSettings from './Customization/CustomizationSettings'; function SettingsMenu({ newVersionAvailable }: { newVersionAvailable: string }) { @@ -15,9 +14,7 @@ function SettingsMenu({ newVersionAvailable }: { newVersionAvailable: string }) {t('tabs.customizations')} - - - + @@ -48,7 +45,6 @@ export function SettingsDrawer({ onClose={closeDrawer} > - ); } From 14d4bdbac74a3d712167014e458bba9dc5f904c6 Mon Sep 17 00:00:00 2001 From: ajnart Date: Wed, 4 Jan 2023 22:45:51 +0900 Subject: [PATCH 11/46] Make docker drawer full size --- src/components/layout/header/Actions/Docker/DockerModule.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/layout/header/Actions/Docker/DockerModule.tsx b/src/components/layout/header/Actions/Docker/DockerModule.tsx index 5a561dcc9..c8c916b28 100644 --- a/src/components/layout/header/Actions/Docker/DockerModule.tsx +++ b/src/components/layout/header/Actions/Docker/DockerModule.tsx @@ -60,6 +60,7 @@ export default function DockerMenuButton(props: any) { opened={opened} onClose={() => setOpened(false)} padding="xl" + position="right" size="full" title={} > From b0bdb4c09014ab4f21a98ee4a164dde8819fbfca Mon Sep 17 00:00:00 2001 From: ajnart Date: Wed, 4 Jan 2023 22:46:05 +0900 Subject: [PATCH 12/46] Add small notification when adding widget --- .../Components/WidgetsTab/WidgetElementType.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/Dashboard/Modals/SelectElement/Components/WidgetsTab/WidgetElementType.tsx b/src/components/Dashboard/Modals/SelectElement/Components/WidgetsTab/WidgetElementType.tsx index 01caf6c8b..af0e61902 100644 --- a/src/components/Dashboard/Modals/SelectElement/Components/WidgetsTab/WidgetElementType.tsx +++ b/src/components/Dashboard/Modals/SelectElement/Components/WidgetsTab/WidgetElementType.tsx @@ -1,5 +1,6 @@ import { useModals } from '@mantine/modals'; -import { TablerIcon } from '@tabler/icons'; +import { showNotification } from '@mantine/notifications'; +import { IconChecks, TablerIcon } from '@tabler/icons'; import { useTranslation } from 'next-i18next'; import { useConfigContext } from '../../../../../../config/provider'; import { useConfigStore } from '../../../../../../config/store'; @@ -61,8 +62,13 @@ export const WidgetElementType = ({ id, image, disabled, widget }: WidgetElement true, !isEditMode ); - closeModal('selectElement'); + showNotification({ + title: t('descriptor.name'), + message: t('descriptor.description'), + icon: , + color: 'teal', + }); }; return ( From a341cbd166c8dc826edb44e20c244cd310bd6c3f Mon Sep 17 00:00:00 2001 From: ajnart Date: Wed, 4 Jan 2023 22:46:25 +0900 Subject: [PATCH 13/46] Remove i18n cimode --- src/components/Dashboard/Dashboard.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/Dashboard/Dashboard.tsx b/src/components/Dashboard/Dashboard.tsx index 022f4462d..35acd4d39 100644 --- a/src/components/Dashboard/Dashboard.tsx +++ b/src/components/Dashboard/Dashboard.tsx @@ -1,4 +1,3 @@ -import { i18n } from 'next-i18next'; import { MobileRibbons } from './Mobile/Ribbon/MobileRibbon'; import { DashboardDetailView } from './Views/DetailView'; import { DashboardEditView } from './Views/EditView'; @@ -6,7 +5,6 @@ import { useEditModeStore } from './Views/useEditModeStore'; export const Dashboard = () => { const isEditMode = useEditModeStore((x) => x.enabled); - i18n?.changeLanguage('cimode'); return ( <> From 6abdc9be6f27e36ea6439c2c25fb80e2da51e562 Mon Sep 17 00:00:00 2001 From: ajnart Date: Wed, 4 Jan 2023 23:54:25 +0900 Subject: [PATCH 14/46] Fix sidebar width --- src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts b/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts index 8d1bcd0a3..aa3bb769f 100644 --- a/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts +++ b/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts @@ -20,7 +20,7 @@ export const initializeGridstack = ( ) => { if (!wrapperRef.current) return; // calculates the currently available count of columns - const columnCount = areaType === 'sidebar' ? 4 : isLargerThanSm || typeof isLargerThanSm === 'undefined' ? 12 : 6; + const columnCount = areaType === 'sidebar' ? 1 : isLargerThanSm || typeof isLargerThanSm === 'undefined' ? 12 : 6; const minRow = areaType !== 'sidebar' ? 1 : Math.floor(wrapperRef.current.offsetHeight / 64); // initialize gridstack const newGrid = gridRef; From 3f957ee67c6a54df75413a63982e9e27c36f4f68 Mon Sep 17 00:00:00 2001 From: ajnart Date: Wed, 4 Jan 2023 23:54:34 +0900 Subject: [PATCH 15/46] Sidebar styling --- .../Dashboard/Wrappers/Sidebar/Sidebar.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/Dashboard/Wrappers/Sidebar/Sidebar.tsx b/src/components/Dashboard/Wrappers/Sidebar/Sidebar.tsx index cefbf3538..501ab664e 100644 --- a/src/components/Dashboard/Wrappers/Sidebar/Sidebar.tsx +++ b/src/components/Dashboard/Wrappers/Sidebar/Sidebar.tsx @@ -1,5 +1,6 @@ import { Card } from '@mantine/core'; import { RefObject } from 'react'; +import { useCardStyles } from '../../../layout/useCardStyles'; import { useGridstack } from '../gridstack/use-gridstack'; import { WrapperContent } from '../WrapperContent'; @@ -11,16 +12,13 @@ export const DashboardSidebar = ({ location }: DashboardSidebarProps) => { const { refs, apps, widgets } = useGridstack('sidebar', location); const minRow = useMinRowForFullHeight(refs.wrapper); + const { + cx, + classes: { card: cardClass }, + } = useCardStyles(false); return ( - +
Date: Wed, 4 Jan 2023 20:33:29 +0100 Subject: [PATCH 16/46] =?UTF-8?q?=F0=9F=92=84=20Improve=20design=20and=20a?= =?UTF-8?q?dd=20translations=20for=20mobile=20ribbons?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/layout/mobile/drawer.json | 3 ++ .../Ribbon/MobileRibbonSidebarDrawer.tsx | 36 ++++++++++++------- src/tools/translation-namespaces.ts | 1 + 3 files changed, 27 insertions(+), 13 deletions(-) create mode 100644 public/locales/en/layout/mobile/drawer.json diff --git a/public/locales/en/layout/mobile/drawer.json b/public/locales/en/layout/mobile/drawer.json new file mode 100644 index 000000000..ac34cee62 --- /dev/null +++ b/public/locales/en/layout/mobile/drawer.json @@ -0,0 +1,3 @@ +{ + "title": "{{position}} sidebar" +} \ No newline at end of file diff --git a/src/components/Dashboard/Mobile/Ribbon/MobileRibbonSidebarDrawer.tsx b/src/components/Dashboard/Mobile/Ribbon/MobileRibbonSidebarDrawer.tsx index b09749ea2..8a60c345d 100644 --- a/src/components/Dashboard/Mobile/Ribbon/MobileRibbonSidebarDrawer.tsx +++ b/src/components/Dashboard/Mobile/Ribbon/MobileRibbonSidebarDrawer.tsx @@ -1,4 +1,5 @@ import { Drawer, Title } from '@mantine/core'; +import { useTranslation } from 'next-i18next'; import { DashboardSidebar } from '../../Wrappers/Sidebar/Sidebar'; interface MobileRibbonSidebarDrawerProps { @@ -10,16 +11,25 @@ interface MobileRibbonSidebarDrawerProps { export const MobileRibbonSidebarDrawer = ({ location, ...props -}: MobileRibbonSidebarDrawerProps) => ( - {location} sidebar} - style={{ - display: 'flex', - justifyContent: 'center', - }} - {...props} - > - - -); +}: MobileRibbonSidebarDrawerProps) => { + const { t } = useTranslation('layout/mobile/drawer'); + return ( + {t('title', { position: location })}} + style={{ + display: 'flex', + justifyContent: 'center', + }} + styles={{ + title: { + width: '100%', + }, + }} + {...props} + > + + + ); +}; diff --git a/src/tools/translation-namespaces.ts b/src/tools/translation-namespaces.ts index dd33af58a..1715194db 100644 --- a/src/tools/translation-namespaces.ts +++ b/src/tools/translation-namespaces.ts @@ -6,6 +6,7 @@ export const dashboardNamespaces = [ 'layout/modals/change-position', 'layout/modals/about', 'layout/header/actions/toggle-edit-mode', + 'layout/mobile/drawer', 'settings/common', 'settings/general/theme-selector', 'settings/general/config-changer', From 930a17843ffceb2cfffcd4721d62f11d8bd260c7 Mon Sep 17 00:00:00 2001 From: ajnart Date: Thu, 5 Jan 2023 22:42:56 +0900 Subject: [PATCH 17/46] Usenet styling and fixes --- src/widgets/useNet/UseNetTile.tsx | 11 +++++++---- src/widgets/useNet/UsenetHistoryList.tsx | 2 +- src/widgets/useNet/UsenetQueueList.tsx | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/widgets/useNet/UseNetTile.tsx b/src/widgets/useNet/UseNetTile.tsx index c3c8d3272..8bb003478 100644 --- a/src/widgets/useNet/UseNetTile.tsx +++ b/src/widgets/useNet/UseNetTile.tsx @@ -17,7 +17,11 @@ import dayjs from 'dayjs'; import duration from 'dayjs/plugin/duration'; import { useTranslation } from 'next-i18next'; import { useConfigContext } from '../../config/provider'; -import { useGetUsenetInfo, usePauseUsenetQueue, useResumeUsenetQueue } from '../../hooks/widgets/dashDot/api'; +import { + useGetUsenetInfo, + usePauseUsenetQueue, + useResumeUsenetQueue, +} from '../../hooks/widgets/dashDot/api'; import { humanFileSize } from '../../tools/humanFileSize'; import { AppIntegrationType } from '../../types/app'; import { defineWidget } from '../helper'; @@ -52,6 +56,8 @@ function UseNetTile({}: UseNetTileProps) { const downloadApps = config?.apps.filter((x) => x.integration && downloadAppTypes.includes(x.integration.type)) ?? []; + const { ref, width, height } = useElementSize(); + const MIN_WIDTH_MOBILE = useMantineTheme().breakpoints.xs; const [selectedAppId, setSelectedApp] = useState(downloadApps[0]?.id); const { data } = useGetUsenetInfo({ appId: selectedAppId! }); @@ -80,9 +86,6 @@ function UseNetTile({}: UseNetTileProps) { return null; } - const { ref, width, height } = useElementSize(); - const MIN_WIDTH_MOBILE = useMantineTheme().breakpoints.xs; - return ( diff --git a/src/widgets/useNet/UsenetHistoryList.tsx b/src/widgets/useNet/UsenetHistoryList.tsx index 119356dea..f5e4a3e17 100644 --- a/src/widgets/useNet/UsenetHistoryList.tsx +++ b/src/widgets/useNet/UsenetHistoryList.tsx @@ -39,7 +39,7 @@ export const UsenetHistoryList: FunctionComponent = ({ a const { data, isLoading, isError, error } = useGetUsenetHistory({ limit: PAGE_SIZE, offset: (page - 1) * PAGE_SIZE, - appId: appId, + appId, }); const totalPages = Math.ceil((data?.total || 1) / PAGE_SIZE); diff --git a/src/widgets/useNet/UsenetQueueList.tsx b/src/widgets/useNet/UsenetQueueList.tsx index 3138048ee..4ad919692 100644 --- a/src/widgets/useNet/UsenetQueueList.tsx +++ b/src/widgets/useNet/UsenetQueueList.tsx @@ -44,7 +44,7 @@ export const UsenetQueueList: FunctionComponent = ({ appId const { data, isLoading, isError, error } = useGetUsenetDownloads({ limit: PAGE_SIZE, offset: (page - 1) * PAGE_SIZE, - appId: appId, + appId, }); const totalPages = Math.ceil((data?.total || 1) / PAGE_SIZE); From a43780719ea3b6b42ffc821c458c68e5b87dc5b6 Mon Sep 17 00:00:00 2001 From: ajnart Date: Thu, 5 Jan 2023 22:43:27 +0900 Subject: [PATCH 18/46] Add new types of options --- src/widgets/widgets.d.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/widgets/widgets.d.ts b/src/widgets/widgets.d.ts index 729ed2e07..19e41ed12 100644 --- a/src/widgets/widgets.d.ts +++ b/src/widgets/widgets.d.ts @@ -1,6 +1,5 @@ -import { IconSun, TablerIcon } from '@tabler/icons'; +import { TablerIcon } from '@tabler/icons'; import React from 'react'; -import { BaseTileProps } from '../components/Dashboard/Tiles/type'; // Type of widgets which are safed to config export type IWidget = { @@ -32,6 +31,7 @@ export type IWidgetOptionValue = | IMultiSelectOptionValue | ISwitchOptionValue | ITextInputOptionValue + | ISliderInputOptionValue | INumberInputOptionValue; // will show a multi-select with specified data @@ -56,7 +56,16 @@ export type ITextInputOptionValue = { // will show a number-input export type INumberInputOptionValue = { type: 'number'; - defaultValue: string; + defaultValue: number; +}; + +// will show a slider-input +export type ISliderInputOptionValue = { + type: 'slider'; + defaultValue: number; + min: number; + max: number; + step: number; }; // is used to type the widget definitions which will be used to display all widgets From f14552f664721744978e1f5ae922bc5430f86f42 Mon Sep 17 00:00:00 2001 From: ajnart Date: Thu, 5 Jan 2023 22:43:56 +0900 Subject: [PATCH 19/46] add NumberInput, Slider to widget edit modal --- .../Tiles/Widgets/WidgetsEditModal.tsx | 127 +++++++++++++----- 1 file changed, 93 insertions(+), 34 deletions(-) diff --git a/src/components/Dashboard/Tiles/Widgets/WidgetsEditModal.tsx b/src/components/Dashboard/Tiles/Widgets/WidgetsEditModal.tsx index 20518c414..9ea5fad5a 100644 --- a/src/components/Dashboard/Tiles/Widgets/WidgetsEditModal.tsx +++ b/src/components/Dashboard/Tiles/Widgets/WidgetsEditModal.tsx @@ -1,4 +1,15 @@ -import { Alert, Button, Group, MultiSelect, Stack, Switch, TextInput, Text } from '@mantine/core'; +import { + Alert, + Button, + Group, + MultiSelect, + Stack, + Switch, + TextInput, + Text, + NumberInput, + Slider, +} from '@mantine/core'; import { ContextModalProps } from '@mantine/modals'; import { IconAlertTriangle } from '@tabler/icons'; import { Trans, useTranslation } from 'next-i18next'; @@ -8,6 +19,7 @@ import type { IWidgetOptionValue } from '../../../../widgets/widgets'; import { useConfigContext } from '../../../../config/provider'; import { useConfigStore } from '../../../../config/store'; import { IWidget } from '../../../../widgets/widgets'; +import { useColorTheme } from '../../../../tools/color'; export type WidgetEditModalInnerProps = { widgetId: string; @@ -83,39 +95,15 @@ export const WidgetsEditModal = ({ ); } - - switch (option.type) { - case 'switch': - return ( - handleChange(key, ev.currentTarget.checked)} - /> - ); - case 'text': - return ( - handleChange(key, ev.currentTarget.value)} - /> - ); - case 'multi-select': - return ( - handleChange(key, v)} - /> - ); - default: - return null; - } + return WidgetOptionTypeSwitch( + option, + index, + t, + key, + value, + handleChange, + getMutliselectData + ); })} - + + ); } diff --git a/src/components/Settings/Customization/Theme/OpacitySelector.tsx b/src/components/Settings/Customization/Theme/OpacitySelector.tsx index cc7888af6..f0eaf3bd1 100644 --- a/src/components/Settings/Customization/Theme/OpacitySelector.tsx +++ b/src/components/Settings/Customization/Theme/OpacitySelector.tsx @@ -32,7 +32,7 @@ export function OpacitySelector({ defaultValue }: OpacitySelectorProps) { }; return ( - + {t('label')} Date: Thu, 5 Jan 2023 22:45:04 +0900 Subject: [PATCH 22/46] Add torrent refresh interval selector with slider --- src/hooks/widgets/torrents/useGetTorrentData.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hooks/widgets/torrents/useGetTorrentData.tsx b/src/hooks/widgets/torrents/useGetTorrentData.tsx index 796a7b99c..b3136e03f 100644 --- a/src/hooks/widgets/torrents/useGetTorrentData.tsx +++ b/src/hooks/widgets/torrents/useGetTorrentData.tsx @@ -6,6 +6,7 @@ const POLLING_INTERVAL = 2000; interface TorrentsDataRequestParams { appId: string; + refreshInterval: number; } export const useGetTorrentData = (params: TorrentsDataRequestParams) => @@ -15,7 +16,7 @@ export const useGetTorrentData = (params: TorrentsDataRequestParams) => refetchOnWindowFocus: true, refetchInterval(_: any, query: Query) { if (query.state.fetchFailureCount < 3) { - return 5000; + return params.refreshInterval; } return false; }, From d6cd6aa53dd945718bbf8fca2824ed3e6cc353f4 Mon Sep 17 00:00:00 2001 From: ajnart Date: Thu, 5 Jan 2023 22:45:17 +0900 Subject: [PATCH 23/46] Made settings auto save upon closing the drawer --- src/components/Settings/SettingsDrawer.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/components/Settings/SettingsDrawer.tsx b/src/components/Settings/SettingsDrawer.tsx index fe08e323b..c1eea6857 100644 --- a/src/components/Settings/SettingsDrawer.tsx +++ b/src/components/Settings/SettingsDrawer.tsx @@ -1,5 +1,7 @@ import { Drawer, ScrollArea, Tabs, Title } from '@mantine/core'; import { useTranslation } from 'next-i18next'; +import { useConfigContext } from '../../config/provider'; +import { useConfigStore } from '../../config/store'; import CommonSettings from './Common/CommonSettings'; import CustomizationSettings from './Customization/CustomizationSettings'; @@ -34,6 +36,8 @@ export function SettingsDrawer({ newVersionAvailable, }: SettingsDrawerProps & { newVersionAvailable: string }) { const { t } = useTranslation('settings/common'); + const { config, name: configName } = useConfigContext(); + const { updateConfig } = useConfigStore(); return ( {t('title')}} opened={opened} - onClose={closeDrawer} + onClose={() => { + closeDrawer(); + if (!configName || !config) { + return; + } + + updateConfig(configName, (_) => config, false, true); + }} > From c03426f801a7dd10f58cc04cccebd10a1deab0e8 Mon Sep 17 00:00:00 2001 From: ajnart Date: Thu, 5 Jan 2023 22:45:35 +0900 Subject: [PATCH 24/46] Add torrent refresh interval selector with slider --- src/widgets/bitTorrent/BitTorrentTile.tsx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/widgets/bitTorrent/BitTorrentTile.tsx b/src/widgets/bitTorrent/BitTorrentTile.tsx index b9485f021..e6d2368cf 100644 --- a/src/widgets/bitTorrent/BitTorrentTile.tsx +++ b/src/widgets/bitTorrent/BitTorrentTile.tsx @@ -42,6 +42,13 @@ const definition = defineWidget({ type: 'switch', defaultValue: true, }, + refreshInterval: { + type: 'slider', + defaultValue: 1, + min: 1, + max: 60, + step: 1, + }, }, gridstack: { minWidth: 4, @@ -71,6 +78,7 @@ function BitTorrentTile({ widget }: BitTorrentTileProps) { const [selectedAppId, setSelectedApp] = useState(downloadApps[0]?.id); const { data, isError, isInitialLoading, dataUpdatedAt } = useGetTorrentData({ appId: selectedAppId!, + refreshInterval: widget.properties.refreshInterval * 1000, }); useEffect(() => { @@ -103,7 +111,13 @@ function BitTorrentTile({ widget }: BitTorrentTileProps) { if (isInitialLoading) { return ( - + {t('card.loading.title')} From 422abaec15a9efb48da033c7709dfea06237e8dc Mon Sep 17 00:00:00 2001 From: ajnart Date: Thu, 5 Jan 2023 23:14:08 +0900 Subject: [PATCH 25/46] Slider option styling --- .../Tiles/Widgets/WidgetsEditModal.tsx | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/components/Dashboard/Tiles/Widgets/WidgetsEditModal.tsx b/src/components/Dashboard/Tiles/Widgets/WidgetsEditModal.tsx index 9ea5fad5a..d28d5cf44 100644 --- a/src/components/Dashboard/Tiles/Widgets/WidgetsEditModal.tsx +++ b/src/components/Dashboard/Tiles/Widgets/WidgetsEditModal.tsx @@ -115,6 +115,7 @@ export const WidgetsEditModal = ({ ); }; +// Widget switch // Widget options are computed based on their type. // here you can define new types for options (along with editing the widgets.d.ts file) function WidgetOptionTypeSwitch( @@ -170,16 +171,19 @@ function WidgetOptionTypeSwitch( ); case 'slider': return ( - handleChange(key, v)} - /> + + {t(`descriptor.settings.${key}.label`)} + handleChange(key, v)} + /> + ); default: return null; From 5c9ab2fd34282443c24bda721568155e0dd921cb Mon Sep 17 00:00:00 2001 From: ajnart Date: Thu, 5 Jan 2023 23:14:36 +0900 Subject: [PATCH 26/46] Add refreshInterval for dashdot widget --- src/widgets/dashDot/DashDotTile.tsx | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/widgets/dashDot/DashDotTile.tsx b/src/widgets/dashDot/DashDotTile.tsx index 531a25313..33687365b 100644 --- a/src/widgets/dashDot/DashDotTile.tsx +++ b/src/widgets/dashDot/DashDotTile.tsx @@ -13,6 +13,13 @@ const definition = defineWidget({ id: 'dashdot', icon: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/dashdot.png', options: { + refreshInterval: { + type: 'slider', + defaultValue: 5, + min: 1, + max: 60, + step: 1, + }, cpuMultiView: { type: 'switch', defaultValue: false, @@ -54,9 +61,12 @@ function DashDotTile({ widget }: DashDotTileProps) { const { classes } = useDashDotTileStyles(); const { t } = useTranslation('modules/dashdot'); - const dashDotUrl = widget?.properties.url; + const dashDotUrl = widget.properties.url; - const { data: info } = useDashDotInfo({ dashDotUrl }); + const { data: info } = useDashDotInfo({ + dashDotUrl, + refreshInterval: widget.properties.refreshInterval, + }); const graphs = widget?.properties.graphs.map((g) => ({ id: g, @@ -109,9 +119,16 @@ function DashDotTile({ widget }: DashDotTileProps) { ); } -const useDashDotInfo = ({ dashDotUrl }: { dashDotUrl: string }) => { +const useDashDotInfo = ({ + dashDotUrl, + refreshInterval, +}: { + dashDotUrl: string; + refreshInterval: number; +}) => { const { name: configName } = useConfigContext(); return useQuery({ + refetchInterval: refreshInterval * 1000 ?? 50000, queryKey: [ 'dashdot/info', { From f55e9c3b3de27ae6abcf2a4882c57bc7a2dac24b Mon Sep 17 00:00:00 2001 From: ajnart Date: Fri, 6 Jan 2023 01:06:22 +0900 Subject: [PATCH 27/46] enable reactStrictMode --- next.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/next.config.js b/next.config.js index 6d3cf54a0..dde3ec977 100644 --- a/next.config.js +++ b/next.config.js @@ -8,7 +8,7 @@ module.exports = withBundleAnalyzer({ images: { domains: ['cdn.jsdelivr.net'], }, - reactStrictMode: false, + reactStrictMode: true, output: 'standalone', i18n, }); From e2da386fdad6d594e831e0fae3b377403d11b212 Mon Sep 17 00:00:00 2001 From: ajnart Date: Fri, 6 Jan 2023 01:06:44 +0900 Subject: [PATCH 28/46] Transform StatusCodes into number[] --- src/tools/acceptableStatusCodes.ts | 38 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/tools/acceptableStatusCodes.ts b/src/tools/acceptableStatusCodes.ts index 33885ec95..3d942fd8d 100644 --- a/src/tools/acceptableStatusCodes.ts +++ b/src/tools/acceptableStatusCodes.ts @@ -1,21 +1,21 @@ export const StatusCodes = [ - { value: '200', label: '200 - OK', group: 'Sucessful responses' }, - { value: '204', label: '204 - No Content', group: 'Sucessful responses' }, - { value: '301', label: '301 - Moved Permanently', group: 'Redirection responses' }, - { value: '302', label: '302 - Found / Moved Temporarily', group: 'Redirection responses' }, - { value: '304', label: '304 - Not Modified', group: 'Redirection responses' }, - { value: '307', label: '307 - Temporary Redirect', group: 'Redirection responses' }, - { value: '308', label: '308 - Permanent Redirect', group: 'Redirection responses' }, - { value: '400', label: '400 - Bad Request', group: 'Client error responses' }, - { value: '401', label: '401 - Unauthorized', group: 'Client error responses' }, - { value: '403', label: '403 - Forbidden', group: 'Client error responses' }, - { value: '404', label: '404 - Not Found', group: 'Client error responses' }, - { value: '405', label: '405 - Method Not Allowed', group: 'Client error responses' }, - { value: '408', label: '408 - Request Timeout', group: 'Client error responses' }, - { value: '410', label: '410 - Gone', group: 'Client error responses' }, - { value: '429', label: '429 - Too Many Requests', group: 'Client error responses' }, - { value: '500', label: '500 - Internal Server Error', group: 'Server error responses' }, - { value: '502', label: '502 - Bad Gateway', group: 'Server error responses' }, - { value: '503', label: '503 - Service Unavailable', group: 'Server error responses' }, - { value: '054', label: '504 - Gateway Timeout Error', group: 'Server error responses' }, + { value: 200, label: '200 - OK', group: 'Sucessful responses' }, + { value: 204, label: '204 - No Content', group: 'Sucessful responses' }, + { value: 301, label: '301 - Moved Permanently', group: 'Redirection responses' }, + { value: 302, label: '302 - Found / Moved Temporarily', group: 'Redirection responses' }, + { value: 304, label: '304 - Not Modified', group: 'Redirection responses' }, + { value: 307, label: '307 - Temporary Redirect', group: 'Redirection responses' }, + { value: 308, label: '308 - Permanent Redirect', group: 'Redirection responses' }, + { value: 400, label: '400 - Bad Request', group: 'Client error responses' }, + { value: 401, label: '401 - Unauthorized', group: 'Client error responses' }, + { value: 403, label: '403 - Forbidden', group: 'Client error responses' }, + { value: 404, label: '404 - Not Found', group: 'Client error responses' }, + { value: 405, label: '405 - Method Not Allowed', group: 'Client error responses' }, + { value: 408, label: '408 - Request Timeout', group: 'Client error responses' }, + { value: 410, label: '410 - Gone', group: 'Client error responses' }, + { value: 429, label: '429 - Too Many Requests', group: 'Client error responses' }, + { value: 500, label: '500 - Internal Server Error', group: 'Server error responses' }, + { value: 502, label: '502 - Bad Gateway', group: 'Server error responses' }, + { value: 503, label: '503 - Service Unavailable', group: 'Server error responses' }, + { value: 504, label: '504 - Gateway Timeout Error', group: 'Server error responses' }, ]; From 50bcd292717d8d25d160fbd48227b1ba20366a57 Mon Sep 17 00:00:00 2001 From: ajnart Date: Fri, 6 Jan 2023 01:07:09 +0900 Subject: [PATCH 29/46] Buttons styling --- .../header/Actions/AddElementAction/AddElementAction.tsx | 6 ++++-- src/components/layout/header/SettingsMenu.tsx | 8 +++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/layout/header/Actions/AddElementAction/AddElementAction.tsx b/src/components/layout/header/Actions/AddElementAction/AddElementAction.tsx index 3c64032e2..b2b7af4c2 100644 --- a/src/components/layout/header/Actions/AddElementAction/AddElementAction.tsx +++ b/src/components/layout/header/Actions/AddElementAction/AddElementAction.tsx @@ -2,6 +2,7 @@ import { ActionIcon, Button, Tooltip } from '@mantine/core'; import { openContextModal } from '@mantine/modals'; import { IconApps } from '@tabler/icons'; import { useTranslation } from 'next-i18next'; +import { useColorTheme } from '../../../../../tools/color'; interface AddElementActionProps { type: 'action-icon' | 'button'; @@ -9,15 +10,16 @@ interface AddElementActionProps { export const AddElementAction = ({ type }: AddElementActionProps) => { const { t } = useTranslation('layout/element-selector/selector'); + const { primaryColor, secondaryColor } = useColorTheme(); switch (type) { case 'button': return ( From 52dcdf7ba3dcb88d36255c49b36646845a2bbc16 Mon Sep 17 00:00:00 2001 From: ajnart Date: Fri, 6 Jan 2023 01:08:07 +0900 Subject: [PATCH 30/46] Use Notification instead of Popover for edit mode --- .../Actions/ToggleEditMode/ToggleEditMode.tsx | 108 +++++++++--------- 1 file changed, 56 insertions(+), 52 deletions(-) diff --git a/src/components/layout/header/Actions/ToggleEditMode/ToggleEditMode.tsx b/src/components/layout/header/Actions/ToggleEditMode/ToggleEditMode.tsx index aed97282c..09956261d 100644 --- a/src/components/layout/header/Actions/ToggleEditMode/ToggleEditMode.tsx +++ b/src/components/layout/header/Actions/ToggleEditMode/ToggleEditMode.tsx @@ -1,19 +1,20 @@ import axios from 'axios'; import Consola from 'consola'; -import { ActionIcon, Button, Group, Popover, Text } from '@mantine/core'; -import { IconEditCircle, IconEditCircleOff, IconX } from '@tabler/icons'; +import { ActionIcon, Button, Group, Title, Tooltip } from '@mantine/core'; +import { IconEditCircle, IconEditCircleOff } from '@tabler/icons'; import { getCookie } from 'cookies-next'; import { Trans, useTranslation } from 'next-i18next'; -import { useEffect, useState } from 'react'; +import { useEffect } from 'react'; +import { cleanNotifications, showNotification } from '@mantine/notifications'; import { useConfigContext } from '../../../../../config/provider'; import { useScreenSmallerThan } from '../../../../../hooks/useScreenSmallerThan'; import { useEditModeStore } from '../../../../Dashboard/Views/useEditModeStore'; import { AddElementAction } from '../AddElementAction/AddElementAction'; +import { useColorTheme } from '../../../../../tools/color'; export const ToggleEditModeAction = () => { const { enabled, toggleEditMode } = useEditModeStore(); - const [popoverManuallyHidden, setPopoverManuallyHidden] = useState(); const { t } = useTranslation('layout/header/actions/toggle-edit-mode'); @@ -29,21 +30,45 @@ export const ToggleEditModeAction = () => { const toggleButtonClicked = () => { toggleEditMode(); + if (!enabled) { + showNotification({ + styles: (theme) => ({ + root: { + backgroundColor: theme.colors.orange[7], + borderColor: theme.colors.orange[7], - setPopoverManuallyHidden(false); + '&::before': { backgroundColor: theme.white }, + }, + title: { color: theme.white }, + description: { color: theme.white }, + closeButton: { + color: theme.white, + '&:hover': { backgroundColor: theme.colors.orange[7] }, + }, + }), + radius: 'md', + autoClose: false, + title: {t('popover.title')}, + message: , + }); + } else { + cleanNotifications(); + } }; + const { primaryColor, secondaryColor } = useColorTheme(); const ToggleButtonDesktop = () => ( - + + + ); const ToggleActionIconMobile = () => ( @@ -59,45 +84,24 @@ export const ToggleEditModeAction = () => { ); return ( - - - {smallerThanSm ? ( - enabled ? ( - - - - - ) : ( + <> + {smallerThanSm ? ( + enabled ? ( + + - ) - ) : enabled ? ( - - - {enabled && } - + ) : ( + + ) + ) : enabled ? ( + - )} - - - -
- setPopoverManuallyHidden(true)}> - - -
- - {t('popover.title')} - - - - -
-
+ {enabled && } + + ) : ( + + )} + ); }; From 6fb199105bc98c0d05c84a1189d424facf864a0a Mon Sep 17 00:00:00 2001 From: ajnart Date: Fri, 6 Jan 2023 01:10:16 +0900 Subject: [PATCH 31/46] Use ID for notification --- .../layout/header/Actions/ToggleEditMode/ToggleEditMode.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/layout/header/Actions/ToggleEditMode/ToggleEditMode.tsx b/src/components/layout/header/Actions/ToggleEditMode/ToggleEditMode.tsx index 09956261d..927d0aa3e 100644 --- a/src/components/layout/header/Actions/ToggleEditMode/ToggleEditMode.tsx +++ b/src/components/layout/header/Actions/ToggleEditMode/ToggleEditMode.tsx @@ -5,7 +5,7 @@ import { IconEditCircle, IconEditCircleOff } from '@tabler/icons'; import { getCookie } from 'cookies-next'; import { Trans, useTranslation } from 'next-i18next'; import { useEffect } from 'react'; -import { cleanNotifications, showNotification } from '@mantine/notifications'; +import { hideNotification, showNotification } from '@mantine/notifications'; import { useConfigContext } from '../../../../../config/provider'; import { useScreenSmallerThan } from '../../../../../hooks/useScreenSmallerThan'; @@ -47,12 +47,13 @@ export const ToggleEditModeAction = () => { }, }), radius: 'md', + id: 'toggle-edit-mode', autoClose: false, title: {t('popover.title')}, message: , }); } else { - cleanNotifications(); + hideNotification('toggle-edit-mode'); } }; const { primaryColor, secondaryColor } = useColorTheme(); From 2d320ff6e96800c9464b01b1dd2f18f80d3cbe98 Mon Sep 17 00:00:00 2001 From: ajnart Date: Fri, 6 Jan 2023 01:11:02 +0900 Subject: [PATCH 32/46] =?UTF-8?q?=F0=9F=92=84=20Lint=20+=20Prettier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Config/LoadConfig.tsx | 4 +++- .../Components/InputElements/GenericSecretInput.tsx | 6 +++--- .../Dashboard/Wrappers/gridstack/init-gridstack.ts | 3 ++- src/components/Settings/Common/CommonSettings.tsx | 2 +- src/components/Settings/Common/Credits.tsx | 9 +++------ .../Settings/Customization/CustomizationSettings.tsx | 2 +- src/components/Settings/SettingsDrawer.tsx | 2 +- src/config/store.ts | 2 +- src/hooks/widgets/dashDot/api.ts | 5 ++++- src/pages/api/modules/torrents.ts | 3 ++- src/widgets/WidgetWrapper.tsx | 10 +++++----- src/widgets/helper.ts | 4 +--- 12 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/components/Config/LoadConfig.tsx b/src/components/Config/LoadConfig.tsx index e653a19ba..0f40c62ff 100644 --- a/src/components/Config/LoadConfig.tsx +++ b/src/components/Config/LoadConfig.tsx @@ -36,7 +36,9 @@ export const LoadConfigComponent = () => { let newConfig: ConfigType = JSON.parse(fileText); if (!newConfig.schemaVersion) { - console.warn('a legacy configuration schema was deteced and migrated to the current schema'); + console.warn( + 'a legacy configuration schema was deteced and migrated to the current schema' + ); const oldConfig = JSON.parse(fileText) as Config; newConfig = migrateConfig(oldConfig); } diff --git a/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/GenericSecretInput.tsx b/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/GenericSecretInput.tsx index a1627ceff..ad8cca248 100644 --- a/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/GenericSecretInput.tsx +++ b/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/GenericSecretInput.tsx @@ -13,7 +13,7 @@ import { Badge, Tooltip, } from '@mantine/core'; -import { IconLock, TablerIcon } from '@tabler/icons'; +import { TablerIcon } from '@tabler/icons'; import { useTranslation } from 'next-i18next'; import { useState } from 'react'; import { AppIntegrationPropertyAccessabilityType } from '../../../../../../../../types/app'; @@ -76,7 +76,7 @@ export const GenericSecretInput = ({ withArrow > - {t('integration.type.private')} + {t('integration.type.private')}
) : ( @@ -88,7 +88,7 @@ export const GenericSecretInput = ({ withArrow > - {t('integration.type.public')} + {t('integration.type.public')} )} diff --git a/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts b/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts index aa3bb769f..ba8d2d413 100644 --- a/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts +++ b/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts @@ -20,7 +20,8 @@ export const initializeGridstack = ( ) => { if (!wrapperRef.current) return; // calculates the currently available count of columns - const columnCount = areaType === 'sidebar' ? 1 : isLargerThanSm || typeof isLargerThanSm === 'undefined' ? 12 : 6; + const columnCount = + areaType === 'sidebar' ? 1 : isLargerThanSm || typeof isLargerThanSm === 'undefined' ? 12 : 6; const minRow = areaType !== 'sidebar' ? 1 : Math.floor(wrapperRef.current.offsetHeight / 64); // initialize gridstack const newGrid = gridRef; diff --git a/src/components/Settings/Common/CommonSettings.tsx b/src/components/Settings/Common/CommonSettings.tsx index 438644e30..e404bd9bb 100644 --- a/src/components/Settings/Common/CommonSettings.tsx +++ b/src/components/Settings/Common/CommonSettings.tsx @@ -1,5 +1,5 @@ import { ScrollArea, Space, Stack, Text } from '@mantine/core'; -import { useElementSize, useViewportSize } from '@mantine/hooks'; +import { useViewportSize } from '@mantine/hooks'; import { useConfigContext } from '../../../config/provider'; import ConfigChanger from '../../Config/ConfigChanger'; import ConfigActions from './Config/ConfigActions'; diff --git a/src/components/Settings/Common/Credits.tsx b/src/components/Settings/Common/Credits.tsx index 704fa2fc9..9a1fac1a4 100644 --- a/src/components/Settings/Common/Credits.tsx +++ b/src/components/Settings/Common/Credits.tsx @@ -1,9 +1,6 @@ -import { Group, ActionIcon, Anchor, Text } from '@mantine/core'; -import { IconBrandDiscord, IconBrandGithub } from '@tabler/icons'; +import { Group, Anchor, Text } from '@mantine/core'; import { useTranslation } from 'next-i18next'; -import { CURRENT_VERSION } from '../../../../data/constants'; - export default function Credits() { const { t } = useTranslation('settings/common'); @@ -22,8 +19,8 @@ export default function Credits() { style={{ color: 'inherit', fontStyle: 'inherit', fontSize: 'inherit' }} > ajnart - - {' '}and you ! + {' '} + and you !
); diff --git a/src/components/Settings/Customization/CustomizationSettings.tsx b/src/components/Settings/Customization/CustomizationSettings.tsx index bc95fc785..8265ac4a6 100644 --- a/src/components/Settings/Customization/CustomizationSettings.tsx +++ b/src/components/Settings/Customization/CustomizationSettings.tsx @@ -1,4 +1,4 @@ -import { Button, ScrollArea, Stack } from '@mantine/core'; +import { ScrollArea, Stack } from '@mantine/core'; import { useViewportSize } from '@mantine/hooks'; import { useTranslation } from 'next-i18next'; import { useConfigContext } from '../../../config/provider'; diff --git a/src/components/Settings/SettingsDrawer.tsx b/src/components/Settings/SettingsDrawer.tsx index c1eea6857..ab8a792b7 100644 --- a/src/components/Settings/SettingsDrawer.tsx +++ b/src/components/Settings/SettingsDrawer.tsx @@ -1,4 +1,4 @@ -import { Drawer, ScrollArea, Tabs, Title } from '@mantine/core'; +import { Drawer, Tabs, Title } from '@mantine/core'; import { useTranslation } from 'next-i18next'; import { useConfigContext } from '../../config/provider'; import { useConfigStore } from '../../config/store'; diff --git a/src/config/store.ts b/src/config/store.ts index 0839aee60..9f2fc9633 100644 --- a/src/config/store.ts +++ b/src/config/store.ts @@ -70,7 +70,7 @@ interface UseConfigStoreType { addConfig: ( name: string, config: ConfigType, - shouldSaveConfigToFileSystem: boolean, + shouldSaveConfigToFileSystem: boolean ) => Promise; updateConfig: ( name: string, diff --git a/src/hooks/widgets/dashDot/api.ts b/src/hooks/widgets/dashDot/api.ts index 6e8496c85..4032469e5 100644 --- a/src/hooks/widgets/dashDot/api.ts +++ b/src/hooks/widgets/dashDot/api.ts @@ -1,7 +1,10 @@ import { useMutation, useQuery } from '@tanstack/react-query'; import axios from 'axios'; import { Results } from 'sabnzbd-api'; -import { UsenetQueueRequestParams, UsenetQueueResponse } from '../../../pages/api/modules/usenet/queue'; +import { + UsenetQueueRequestParams, + UsenetQueueResponse, +} from '../../../pages/api/modules/usenet/queue'; import { UsenetHistoryRequestParams, UsenetHistoryResponse, diff --git a/src/pages/api/modules/torrents.ts b/src/pages/api/modules/torrents.ts index f47a80191..81600cace 100644 --- a/src/pages/api/modules/torrents.ts +++ b/src/pages/api/modules/torrents.ts @@ -40,7 +40,8 @@ async function Post(req: NextApiRequest, res: NextApiResponse) { ); await Promise.all( delugeApp.map((app) => { - const password = app.integration?.properties.find((x) => x.field === 'password')?.value ?? undefined; + const password = + app.integration?.properties.find((x) => x.field === 'password')?.value ?? undefined; const test = new Deluge({ baseUrl: app.url, password, diff --git a/src/widgets/WidgetWrapper.tsx b/src/widgets/WidgetWrapper.tsx index c321cbc12..da0837307 100644 --- a/src/widgets/WidgetWrapper.tsx +++ b/src/widgets/WidgetWrapper.tsx @@ -11,8 +11,8 @@ interface WidgetWrapperProps { } export const WidgetWrapper = ({ widgetId, widget, className, children }: WidgetWrapperProps) => ( - - - {children} - - ); + + + {children} + +); diff --git a/src/widgets/helper.ts b/src/widgets/helper.ts index 1344584bb..238fd88a4 100644 --- a/src/widgets/helper.ts +++ b/src/widgets/helper.ts @@ -5,6 +5,4 @@ import { IWidgetDefinition } from './widgets'; // The options of IWidgetDefinition are so heavily typed that it even used 'true' as type export const defineWidget = >( options: TOptions -) => { - return options; -}; +) => options; From b2ceb5345ab1aef250d4f130cd2dd9309a1acb01 Mon Sep 17 00:00:00 2001 From: ajnart Date: Fri, 6 Jan 2023 11:17:29 +0900 Subject: [PATCH 33/46] Add torrent refreshinterval translation --- public/locales/en/modules/torrents-status.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/public/locales/en/modules/torrents-status.json b/public/locales/en/modules/torrents-status.json index d3ab4be86..83d75d6dd 100644 --- a/public/locales/en/modules/torrents-status.json +++ b/public/locales/en/modules/torrents-status.json @@ -4,6 +4,9 @@ "description": "Displays a list of the torrent which are currently downloading", "settings": { "title": "Settings for BitTorrent integration", + "refreshInterval": { + "label": "Refresh interval (in seconds)" + }, "displayCompletedTorrents": { "label": "Display completed torrents" }, From f2d45b67e741c8dc7f69179265a7926bb0a756c7 Mon Sep 17 00:00:00 2001 From: ajnart Date: Fri, 6 Jan 2023 11:18:29 +0900 Subject: [PATCH 34/46] Widget options always visible, use defaultValue --- .../Dashboard/Tiles/Widgets/WidgetsEditModal.tsx | 11 ++++++++--- .../Dashboard/Tiles/Widgets/WidgetsMenu.tsx | 11 +++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/components/Dashboard/Tiles/Widgets/WidgetsEditModal.tsx b/src/components/Dashboard/Tiles/Widgets/WidgetsEditModal.tsx index d28d5cf44..b5714c805 100644 --- a/src/components/Dashboard/Tiles/Widgets/WidgetsEditModal.tsx +++ b/src/components/Dashboard/Tiles/Widgets/WidgetsEditModal.tsx @@ -24,6 +24,7 @@ import { useColorTheme } from '../../../../tools/color'; export type WidgetEditModalInnerProps = { widgetId: string; options: IWidget['properties']; + widgetOptions: IWidget['properties']; }; type IntegrationOptionsValueType = IWidget['properties'][string]; @@ -35,7 +36,11 @@ export const WidgetsEditModal = ({ }: ContextModalProps) => { const { t } = useTranslation([`modules/${innerProps.widgetId}`, 'common']); const [moduleProperties, setModuleProperties] = useState(innerProps.options); - const items = Object.entries(moduleProperties ?? {}) as [string, IntegrationOptionsValueType][]; + // const items = Object.entries(moduleProperties ?? {}) as [string, IntegrationOptionsValueType][]; + const items = Object.entries(innerProps.widgetOptions ?? {}) as [ + string, + IntegrationOptionsValueType + ][]; // Find the Key in the "Widgets" Object that matches the widgetId const currentWidgetDefinition = Widgets[innerProps.widgetId as keyof typeof Widgets]; @@ -79,8 +84,9 @@ export const WidgetsEditModal = ({ return ( - {items.map(([key, value], index) => { + {items.map(([key, defaultValue], index) => { const option = (currentWidgetDefinition as any).options[key] as IWidgetOptionValue; + const value = moduleProperties[key] ?? defaultValue; if (!option) { return ( @@ -176,7 +182,6 @@ function WidgetOptionTypeSwitch( { const { t } = useTranslation(`modules/${integration}`); if (!widget) return null; + // Match widget.id with WidgetsDefinitions + // First get the keys + const keys = Object.keys(WidgetsDefinitions); + // Then find the key that matches the widget.id + const widgetDefinition = keys.find((key) => key === widget.id); + // Then get the widget definition + const widgetDefinitionObject = + WidgetsDefinitions[widgetDefinition as keyof typeof WidgetsDefinitions]; const handleDeleteClick = () => { openContextModalGeneric({ @@ -50,6 +59,8 @@ export const WidgetsMenu = ({ integration, widget }: WidgetsMenuProps) => { innerProps: { widgetId: integration, options: widget.properties, + // Cast as the right type for the correct widget + widgetOptions: widgetDefinitionObject.options as any, }, }); }; From 7df1717d140b42de98e6765d861813c6c2f76bea Mon Sep 17 00:00:00 2001 From: ajnart Date: Fri, 6 Jan 2023 11:18:47 +0900 Subject: [PATCH 35/46] Undo ugly button styling --- .../header/Actions/AddElementAction/AddElementAction.tsx | 5 +---- .../layout/header/Actions/ToggleEditMode/ToggleEditMode.tsx | 5 +---- src/components/layout/header/SettingsMenu.tsx | 4 +--- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/components/layout/header/Actions/AddElementAction/AddElementAction.tsx b/src/components/layout/header/Actions/AddElementAction/AddElementAction.tsx index b2b7af4c2..c979b5ca8 100644 --- a/src/components/layout/header/Actions/AddElementAction/AddElementAction.tsx +++ b/src/components/layout/header/Actions/AddElementAction/AddElementAction.tsx @@ -2,7 +2,6 @@ import { ActionIcon, Button, Tooltip } from '@mantine/core'; import { openContextModal } from '@mantine/modals'; import { IconApps } from '@tabler/icons'; import { useTranslation } from 'next-i18next'; -import { useColorTheme } from '../../../../../tools/color'; interface AddElementActionProps { type: 'action-icon' | 'button'; @@ -10,16 +9,14 @@ interface AddElementActionProps { export const AddElementAction = ({ type }: AddElementActionProps) => { const { t } = useTranslation('layout/element-selector/selector'); - const { primaryColor, secondaryColor } = useColorTheme(); switch (type) { case 'button': return ( From b3364c53efdae676838687b055c6493ffa1168a1 Mon Sep 17 00:00:00 2001 From: ajnart Date: Fri, 6 Jan 2023 11:19:07 +0900 Subject: [PATCH 36/46] remove refreshInterval from dashdot options --- src/widgets/dashDot/DashDotTile.tsx | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/widgets/dashDot/DashDotTile.tsx b/src/widgets/dashDot/DashDotTile.tsx index 33687365b..fcf8271c1 100644 --- a/src/widgets/dashDot/DashDotTile.tsx +++ b/src/widgets/dashDot/DashDotTile.tsx @@ -13,13 +13,6 @@ const definition = defineWidget({ id: 'dashdot', icon: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/dashdot.png', options: { - refreshInterval: { - type: 'slider', - defaultValue: 5, - min: 1, - max: 60, - step: 1, - }, cpuMultiView: { type: 'switch', defaultValue: false, @@ -65,7 +58,6 @@ function DashDotTile({ widget }: DashDotTileProps) { const { data: info } = useDashDotInfo({ dashDotUrl, - refreshInterval: widget.properties.refreshInterval, }); const graphs = widget?.properties.graphs.map((g) => ({ @@ -119,16 +111,10 @@ function DashDotTile({ widget }: DashDotTileProps) { ); } -const useDashDotInfo = ({ - dashDotUrl, - refreshInterval, -}: { - dashDotUrl: string; - refreshInterval: number; -}) => { +const useDashDotInfo = ({ dashDotUrl }: { dashDotUrl: string }) => { const { name: configName } = useConfigContext(); return useQuery({ - refetchInterval: refreshInterval * 1000 ?? 50000, + refetchInterval: 50000, queryKey: [ 'dashdot/info', { From 2b21ba43cd36a729df9245c3a0f0c176424c36a1 Mon Sep 17 00:00:00 2001 From: ajnart Date: Fri, 6 Jan 2023 11:20:14 +0900 Subject: [PATCH 37/46] Fix usage of console.log() statement --- src/components/Config/LoadConfig.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Config/LoadConfig.tsx b/src/components/Config/LoadConfig.tsx index 0f40c62ff..6f374bcd6 100644 --- a/src/components/Config/LoadConfig.tsx +++ b/src/components/Config/LoadConfig.tsx @@ -2,6 +2,7 @@ import { Group, Stack, Text, Title, useMantineTheme } from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import { showNotification } from '@mantine/notifications'; import { IconCheck as Check, IconPhoto, IconUpload, IconX, IconX as X } from '@tabler/icons'; +import Consola from 'consola'; import { setCookie } from 'cookies-next'; import { useTranslation } from 'next-i18next'; import { useConfigStore } from '../../config/store'; @@ -36,7 +37,7 @@ export const LoadConfigComponent = () => { let newConfig: ConfigType = JSON.parse(fileText); if (!newConfig.schemaVersion) { - console.warn( + Consola.warn( 'a legacy configuration schema was deteced and migrated to the current schema' ); const oldConfig = JSON.parse(fileText) as Config; From 77ff6ead560bd67ecaef0d128b22fbd44f30ae97 Mon Sep 17 00:00:00 2001 From: ajnart Date: Fri, 6 Jan 2023 11:36:03 +0900 Subject: [PATCH 38/46] Fix MobileRibbons position --- src/components/Dashboard/Mobile/Ribbon/MobileRibbon.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/Dashboard/Mobile/Ribbon/MobileRibbon.tsx b/src/components/Dashboard/Mobile/Ribbon/MobileRibbon.tsx index 3c958e5cb..66b38249f 100644 --- a/src/components/Dashboard/Mobile/Ribbon/MobileRibbon.tsx +++ b/src/components/Dashboard/Mobile/Ribbon/MobileRibbon.tsx @@ -1,4 +1,4 @@ -import { ActionIcon, createStyles } from '@mantine/core'; +import { ActionIcon, createStyles, Space } from '@mantine/core'; import { useDisclosure } from '@mantine/hooks'; import { IconChevronLeft, IconChevronRight } from '@tabler/icons'; import { useConfigContext } from '../../../../config/provider'; @@ -35,7 +35,9 @@ export const MobileRibbons = () => { location="left" /> - ) : null} + ) : ( + + )} {layoutSettings.enabledRightSidebar ? ( <> From bce33252e604ac61350fe4f7db009e22444a4ed5 Mon Sep 17 00:00:00 2001 From: ajnart Date: Fri, 6 Jan 2023 12:04:37 +0900 Subject: [PATCH 39/46] Fix config changer --- .../en/settings/general/config-changer.json | 4 +- src/components/Config/ConfigChanger.tsx | 49 +++++++++++++------ 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/public/locales/en/settings/general/config-changer.json b/public/locales/en/settings/general/config-changer.json index 2ca62d86f..927547d02 100644 --- a/public/locales/en/settings/general/config-changer.json +++ b/public/locales/en/settings/general/config-changer.json @@ -1,6 +1,8 @@ { "configSelect": { - "label": "Config loader" + "label": "Config loader", + "loadingNew": "Loading your config...", + "pleaseWait": "Please wait until your new config is loaded" }, "modal": { "title": "Choose the name of your new config", diff --git a/src/components/Config/ConfigChanger.tsx b/src/components/Config/ConfigChanger.tsx index c9f1d204f..7d5c43c33 100644 --- a/src/components/Config/ConfigChanger.tsx +++ b/src/components/Config/ConfigChanger.tsx @@ -1,5 +1,7 @@ -import { Center, Loader, Select, Tooltip } from '@mantine/core'; +import { Center, Dialog, Loader, Notification, Select, Tooltip } from '@mantine/core'; +import { useToggle } from '@mantine/hooks'; import { useQuery } from '@tanstack/react-query'; +import { setCookie } from 'cookies-next'; import { useTranslation } from 'next-i18next'; import { useState } from 'react'; import { useConfigContext } from '../../config/provider'; @@ -7,23 +9,26 @@ import { useConfigContext } from '../../config/provider'; export default function ConfigChanger() { const { t } = useTranslation('settings/general/config-changer'); const { name: configName } = useConfigContext(); - //const loadConfig = useConfigStore((x) => x.loadConfig); + // const loadConfig = useConfigStore((x) => x.loadConfig); const { data: configs, isLoading, isError } = useConfigsQuery(); const [activeConfig, setActiveConfig] = useState(configName); + const [isRefreshing, toggle] = useToggle(); const onConfigChange = (value: string) => { // TODO: check what should happen here with @manuel-rw // Wheter it should check for the current url and then load the new config only on index // Or it should always load the selected config and open index or ? --> change url to page + setCookie('config-name', value ?? 'default', { + maxAge: 60 * 60 * 24 * 30, + sameSite: 'strict', + }); setActiveConfig(value); - /* - loadConfig(e ?? 'default'); - setCookie('config-name', e ?? 'default', { - maxAge: 60 * 60 * 24 * 30, - sameSite: 'strict', - }); - */ + toggle(); + // Use timeout to wait for the cookie to be set + setTimeout(() => { + window.location.reload(); + }, 1000); }; // If configlist is empty, return a loading indicator @@ -38,12 +43,26 @@ export default function ConfigChanger() { } return ( - + toggle()} + size="lg" + radius="md" + > + + {t('configSelect.pleaseWait')} + + + ); } From 8f49b2ecfb3a05683e7859f153db6d9d32dd987f Mon Sep 17 00:00:00 2001 From: ajnart Date: Fri, 6 Jan 2023 12:04:52 +0900 Subject: [PATCH 40/46] Fix torrent widget tooltip --- src/widgets/bitTorrent/BitTorrentQueueItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/bitTorrent/BitTorrentQueueItem.tsx b/src/widgets/bitTorrent/BitTorrentQueueItem.tsx index d5d9ecb89..3c2283a58 100644 --- a/src/widgets/bitTorrent/BitTorrentQueueItem.tsx +++ b/src/widgets/bitTorrent/BitTorrentQueueItem.tsx @@ -18,7 +18,7 @@ export const BitTorrrentQueueItem = ({ torrent }: BitTorrentQueueItemProps) => { return (
- + Date: Fri, 6 Jan 2023 12:11:47 +0900 Subject: [PATCH 41/46] Moving things around --- src/components/layout/header/Header.tsx | 2 +- .../Actions => modules}/Docker/ContainerActionBar.tsx | 10 +++++----- .../Actions => modules}/Docker/ContainerState.tsx | 0 .../header/Actions => modules}/Docker/DockerModule.tsx | 2 +- .../header/Actions => modules}/Docker/DockerTable.tsx | 0 src/pages/api/modules/overseerr/[id].tsx | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) rename src/{components/layout/header/Actions => modules}/Docker/ContainerActionBar.tsx (94%) rename src/{components/layout/header/Actions => modules}/Docker/ContainerState.tsx (100%) rename src/{components/layout/header/Actions => modules}/Docker/DockerModule.tsx (97%) rename src/{components/layout/header/Actions => modules}/Docker/DockerTable.tsx (100%) diff --git a/src/components/layout/header/Header.tsx b/src/components/layout/header/Header.tsx index 21f0cd648..c70fa7128 100644 --- a/src/components/layout/header/Header.tsx +++ b/src/components/layout/header/Header.tsx @@ -4,7 +4,7 @@ import { CURRENT_VERSION, REPO_URL } from '../../../../data/constants'; import { useConfigContext } from '../../../config/provider'; import { Logo } from '../Logo'; import { useCardStyles } from '../useCardStyles'; -import DockerMenuButton from './Actions/Docker/DockerModule'; +import DockerMenuButton from '../../../modules/Docker/DockerModule'; import { ToggleEditModeAction } from './Actions/ToggleEditMode/ToggleEditMode'; import { Search } from './Search'; import { SettingsMenu } from './SettingsMenu'; diff --git a/src/components/layout/header/Actions/Docker/ContainerActionBar.tsx b/src/modules/Docker/ContainerActionBar.tsx similarity index 94% rename from src/components/layout/header/Actions/Docker/ContainerActionBar.tsx rename to src/modules/Docker/ContainerActionBar.tsx index ca2036c07..5a0ad3efd 100644 --- a/src/components/layout/header/Actions/Docker/ContainerActionBar.tsx +++ b/src/modules/Docker/ContainerActionBar.tsx @@ -16,11 +16,11 @@ import { useTranslation } from 'next-i18next'; import { useState } from 'react'; import { TFunction } from 'react-i18next'; import { v4 as uuidv4 } from 'uuid'; -import { useConfigContext } from '../../../../../config/provider'; -import { tryMatchService } from '../../../../../tools/addToHomarr'; -import { openContextModalGeneric } from '../../../../../tools/mantineModalManagerExtensions'; -import { AppType } from '../../../../../types/app'; -import { appTileDefinition } from '../../../../Dashboard/Tiles/Apps/AppTile'; +import { useConfigContext } from '../../config/provider'; +import { tryMatchService } from '../../tools/addToHomarr'; +import { openContextModalGeneric } from '../../tools/mantineModalManagerExtensions'; +import { AppType } from '../../types/app'; +import { appTileDefinition } from '../../components/Dashboard/Tiles/Apps/AppTile'; let t: TFunction<'modules/docker', undefined>; diff --git a/src/components/layout/header/Actions/Docker/ContainerState.tsx b/src/modules/Docker/ContainerState.tsx similarity index 100% rename from src/components/layout/header/Actions/Docker/ContainerState.tsx rename to src/modules/Docker/ContainerState.tsx diff --git a/src/components/layout/header/Actions/Docker/DockerModule.tsx b/src/modules/Docker/DockerModule.tsx similarity index 97% rename from src/components/layout/header/Actions/Docker/DockerModule.tsx rename to src/modules/Docker/DockerModule.tsx index c8c916b28..54704ec3c 100644 --- a/src/components/layout/header/Actions/Docker/DockerModule.tsx +++ b/src/modules/Docker/DockerModule.tsx @@ -5,7 +5,7 @@ import axios from 'axios'; import Docker from 'dockerode'; import { useTranslation } from 'next-i18next'; import { useEffect, useState } from 'react'; -import { useConfigContext } from '../../../../../config/provider'; +import { useConfigContext } from '../../config/provider'; import ContainerActionBar from './ContainerActionBar'; import DockerTable from './DockerTable'; diff --git a/src/components/layout/header/Actions/Docker/DockerTable.tsx b/src/modules/Docker/DockerTable.tsx similarity index 100% rename from src/components/layout/header/Actions/Docker/DockerTable.tsx rename to src/modules/Docker/DockerTable.tsx diff --git a/src/pages/api/modules/overseerr/[id].tsx b/src/pages/api/modules/overseerr/[id].tsx index 3b0240271..55d253400 100644 --- a/src/pages/api/modules/overseerr/[id].tsx +++ b/src/pages/api/modules/overseerr/[id].tsx @@ -3,7 +3,7 @@ import { getCookie } from 'cookies-next'; import axios from 'axios'; import Consola from 'consola'; import { getConfig } from '../../../../tools/config/getConfig'; -import { MediaType } from '../../../../modules/overseerr/SearchResult'; +import type { MediaType } from '../../../../modules/overseerr/SearchResult'; async function Get(req: NextApiRequest, res: NextApiResponse) { // Get the slug of the request From cb9ee81aa2717cf898b3f789e84bc3b904f3c0b7 Mon Sep 17 00:00:00 2001 From: ajnart Date: Fri, 6 Jan 2023 12:23:54 +0900 Subject: [PATCH 42/46] Address comments --- public/locales/en/modules/search.json | 3 ++- .../Settings/Common/SearchEngine/SearchEngineSelector.tsx | 2 +- src/components/layout/header/Search.tsx | 3 --- src/components/layout/header/SmallAppItem.tsx | 1 - 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/public/locales/en/modules/search.json b/public/locales/en/modules/search.json index cf3f50de0..8b3ed6302 100644 --- a/public/locales/en/modules/search.json +++ b/public/locales/en/modules/search.json @@ -26,5 +26,6 @@ } }, "tip": "You can select the search bar with the shortcut ", - "switchedSearchEngine": "Switched to searching with {{searchEngine}}" + "switchedSearchEngine": "Switched to searching with {{searchEngine}}", + "configurationName": "Search engine configuration" } \ No newline at end of file diff --git a/src/components/Settings/Common/SearchEngine/SearchEngineSelector.tsx b/src/components/Settings/Common/SearchEngine/SearchEngineSelector.tsx index 3e9fe1d4d..c001184e2 100644 --- a/src/components/Settings/Common/SearchEngine/SearchEngineSelector.tsx +++ b/src/components/Settings/Common/SearchEngine/SearchEngineSelector.tsx @@ -50,7 +50,7 @@ export const SearchEngineSelector = ({ searchEngine }: Props) => { /> - Search engine configuration + {t('configurationName')} diff --git a/src/components/layout/header/Search.tsx b/src/components/layout/header/Search.tsx index 619b10fce..5c28e7fcb 100644 --- a/src/components/layout/header/Search.tsx +++ b/src/components/layout/header/Search.tsx @@ -55,9 +55,6 @@ export function Search() { const [searchQuery, setSearchQuery] = useState(''); const [debounced, cancel] = useDebouncedValue(searchQuery, 250); - // TODO: ask manuel-rw about overseerr - // Answer: We can simply check if there is a app of the type overseer and display results if there is one. - // Overseerr is not use anywhere else, so it makes no sense to add a standalone toggle for displaying results const isOverseerrEnabled = config?.apps.some( (x) => x.integration.type === 'overseerr' || x.integration.type === 'jellyseerr' ); diff --git a/src/components/layout/header/SmallAppItem.tsx b/src/components/layout/header/SmallAppItem.tsx index caa198785..02aeed486 100644 --- a/src/components/layout/header/SmallAppItem.tsx +++ b/src/components/layout/header/SmallAppItem.tsx @@ -8,7 +8,6 @@ interface smallAppItem { export default function SmallAppItem(props: any) { const { app }: { app: smallAppItem } = props; - // TODO : Use Next/link return ( {app.icon && } From b333d6b0a61a9e305c6df75b69e7cd6c36d88151 Mon Sep 17 00:00:00 2001 From: ajnart Date: Fri, 6 Jan 2023 13:39:35 +0900 Subject: [PATCH 43/46] Add automatic overflow scoll for widgets --- src/styles/global.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/global.scss b/src/styles/global.scss index 4db17a0a3..a000ae014 100644 --- a/src/styles/global.scss +++ b/src/styles/global.scss @@ -48,7 +48,7 @@ } .grid-stack > .grid-stack-item > .grid-stack-item-content { - overflow-y: hidden; + overflow-y: auto; } .grid-stack.grid-stack-animate { From 7a2a180d7fdfdf8b0410f01d590f257f762db8b9 Mon Sep 17 00:00:00 2001 From: ajnart Date: Fri, 6 Jan 2023 13:39:45 +0900 Subject: [PATCH 44/46] Fix usenet pageination --- src/widgets/useNet/UseNetTile.tsx | 8 +- src/widgets/useNet/UsenetHistoryList.tsx | 83 ++++++----- src/widgets/useNet/UsenetQueueList.tsx | 171 ++++++++++++----------- 3 files changed, 132 insertions(+), 130 deletions(-) diff --git a/src/widgets/useNet/UseNetTile.tsx b/src/widgets/useNet/UseNetTile.tsx index 8bb003478..494df5132 100644 --- a/src/widgets/useNet/UseNetTile.tsx +++ b/src/widgets/useNet/UseNetTile.tsx @@ -46,11 +46,13 @@ const definition = defineWidget({ }, }); -export type IWeatherWidget = IWidget; +export type IUsenetWidget = IWidget; -interface UseNetTileProps {} +interface UseNetTileProps { + widget: IUsenetWidget; +} -function UseNetTile({}: UseNetTileProps) { +function UseNetTile({ widget }: UseNetTileProps) { const { t } = useTranslation('modules/usenet'); const { config } = useConfigContext(); const downloadApps = diff --git a/src/widgets/useNet/UsenetHistoryList.tsx b/src/widgets/useNet/UsenetHistoryList.tsx index f5e4a3e17..28ee10ccf 100644 --- a/src/widgets/useNet/UsenetHistoryList.tsx +++ b/src/widgets/useNet/UsenetHistoryList.tsx @@ -4,8 +4,8 @@ import { Code, Group, Pagination, - ScrollArea, Skeleton, + Stack, Table, Text, Title, @@ -28,7 +28,7 @@ interface UsenetHistoryListProps { appId: string; } -const PAGE_SIZE = 10; +const PAGE_SIZE = 13; export const UsenetHistoryList: FunctionComponent = ({ appId }) => { const [page, setPage] = useState(1); @@ -81,50 +81,49 @@ export const UsenetHistoryList: FunctionComponent = ({ a } return ( - <> - - - - - - + +
{t('modules/usenet:history.header.name')}{t('modules/usenet:history.header.size')}
+ + + + + {durationBreakpoint < width ? ( + + ) : null} + + + + {data.items.map((history) => ( + + + {durationBreakpoint < width ? ( - + ) : null} - - - {data.items.map((history) => ( - - - - {durationBreakpoint < width ? ( - - ) : null} - - ))} - -
{t('modules/usenet:history.header.name')}{t('modules/usenet:history.header.size')}{t('modules/usenet:history.header.duration')}
+ + + {history.name} + + + + {humanFileSize(history.size)} + {t('modules/usenet:history.header.duration')} + {parseDuration(history.time, t)} +
- - - {history.name} - - - - {humanFileSize(history.size)} - - {parseDuration(history.time, t)} -
-
+ ))} + +
{totalPages > 1 && ( = ({ a onChange={setPage} /> )} - +
); }; diff --git a/src/widgets/useNet/UsenetQueueList.tsx b/src/widgets/useNet/UsenetQueueList.tsx index 4ad919692..5fec4f842 100644 --- a/src/widgets/useNet/UsenetQueueList.tsx +++ b/src/widgets/useNet/UsenetQueueList.tsx @@ -1,6 +1,7 @@ import { ActionIcon, Alert, + Button, Center, Code, Group, @@ -8,6 +9,7 @@ import { Progress, ScrollArea, Skeleton, + Stack, Table, Text, Title, @@ -30,7 +32,7 @@ interface UsenetQueueListProps { appId: string; } -const PAGE_SIZE = 10; +const PAGE_SIZE = 13; export const UsenetQueueList: FunctionComponent = ({ appId }) => { const theme = useMantineTheme(); @@ -38,7 +40,7 @@ export const UsenetQueueList: FunctionComponent = ({ appId const progressbarBreakpoint = theme.breakpoints.xs; const progressBreakpoint = 400; const sizeBreakpoint = 300; - const { ref, width, height } = useElementSize(); + const { ref, width } = useElementSize(); const [page, setPage] = useState(1); const { data, isLoading, isError, error } = useGetUsenetDownloads({ @@ -85,103 +87,102 @@ export const UsenetQueueList: FunctionComponent = ({ appId ); } + // TODO: Set ScollArea dynamic height based on the widget size return ( - <> - - - - - + +
- {t('queue.header.name')}
+ + + + {sizeBreakpoint < width ? ( + + ) : null} + + {progressBreakpoint < width ? ( + + ) : null} + + + + {data.items.map((nzb) => ( + + + {sizeBreakpoint < width ? ( - + ) : null} - + {progressBreakpoint < width ? ( - + ) : null} - - - {data.items.map((nzb) => ( - - - - {sizeBreakpoint < width ? ( - - ) : null} - - {progressBreakpoint < width ? ( - - ) : null} - - ))} - -
+ {t('queue.header.name')}{t('queue.header.size')}{t('queue.header.eta')} width ? 100 : 200 }}> + {t('queue.header.progress')} +
+ {nzb.state === 'paused' ? ( + + + + + + ) : ( + + + + + + )} + + + + {nzb.name} + + + {t('queue.header.size')} + {humanFileSize(nzb.size)} + {t('queue.header.eta')} + {nzb.eta <= 0 ? ( + + {t('queue.paused')} + + ) : ( + {dayjs.duration(nzb.eta, 's').format('H:mm:ss')} + )} + width ? 100 : 200 }}> - {t('queue.header.progress')} - + + {nzb.progress.toFixed(1)}% + + {width > progressbarBreakpoint ? ( + 0 ? theme.primaryColor : 'lightgrey'} + value={nzb.progress} + size="lg" + style={{ width: '100%' }} + /> + ) : null} +
- {nzb.state === 'paused' ? ( - - - - - - ) : ( - - - - - - )} - - - - {nzb.name} - - - - {humanFileSize(nzb.size)} - - {nzb.eta <= 0 ? ( - - {t('queue.paused')} - - ) : ( - {dayjs.duration(nzb.eta, 's').format('H:mm:ss')} - )} - - - {nzb.progress.toFixed(1)}% - - {width > progressbarBreakpoint ? ( - 0 ? theme.primaryColor : 'lightgrey'} - value={nzb.progress} - size="lg" - style={{ width: '100%' }} - /> - ) : null} -
-
+ ))} + + {totalPages > 1 && ( )} - + ); }; From 43fe7ae63454eba3d9e279ea945bbdb06e4377c2 Mon Sep 17 00:00:00 2001 From: Manuel Ruwe Date: Fri, 6 Jan 2023 21:55:10 +0100 Subject: [PATCH 45/46] =?UTF-8?q?=F0=9F=90=9B=20Fix=20zIndex=20for=20integ?= =?UTF-8?q?rationOptions=20modal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Dashboard/Tiles/Widgets/WidgetsMenu.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Dashboard/Tiles/Widgets/WidgetsMenu.tsx b/src/components/Dashboard/Tiles/Widgets/WidgetsMenu.tsx index 0c9520c26..bc0a7c11e 100644 --- a/src/components/Dashboard/Tiles/Widgets/WidgetsMenu.tsx +++ b/src/components/Dashboard/Tiles/Widgets/WidgetsMenu.tsx @@ -62,6 +62,7 @@ export const WidgetsMenu = ({ integration, widget }: WidgetsMenuProps) => { // Cast as the right type for the correct widget widgetOptions: widgetDefinitionObject.options as any, }, + zIndex: 5, }); }; From 8be8ca2215aa7f9c3594d7809625995d0ba3e7cf Mon Sep 17 00:00:00 2001 From: Manuel Ruwe Date: Fri, 6 Jan 2023 23:00:53 +0100 Subject: [PATCH 46/46] =?UTF-8?q?=F0=9F=94=A7=20Update=20default=20config?= =?UTF-8?q?=20icons?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/configs/default.json | 230 +++++++++++++++++++------------------- 1 file changed, 115 insertions(+), 115 deletions(-) diff --git a/data/configs/default.json b/data/configs/default.json index 2a5cd2b09..9c48b9103 100644 --- a/data/configs/default.json +++ b/data/configs/default.json @@ -17,120 +17,6 @@ } ], "apps": [ - { - "id": "76217a87-7151-42d0-b0cf-1b72aef63f83", - "name": "Small app", - "url": "https://homarr.dev", - "appearance": { - "iconUrl": "/imgs/logo/logo.png" - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [] - }, - "behaviour": { - "isOpeningNewTab": true, - "externalUrl": "https://homarr.dev" - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "location": { - "x": 0, - "y": 2 - }, - "size": { - "width": 2, - "height": 2 - } - }, - "integration": { - "type": null, - "properties": [] - } - }, - { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a337", - "name": "Discord", - "url": "https://discord.com/invite/aCsmEV5RgA", - "behaviour": { - "onClickUrl": "https://discord.com/invite/aCsmEV5RgA", - "isOpeningNewTab": true, - "externalUrl": "https://discord.com/invite/aCsmEV5RgA" - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [ - 200 - ] - }, - "appearance": { - "iconUrl": "https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/discord.png" - }, - "integration": { - "type": null, - "properties": [] - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "location": { - "x": 0, - "y": 4 - }, - "size": { - "width": 4, - "height": 3 - } - } - }, - { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990", - "name": "Donate", - "url": "https://ko-fi.com/ajnart", - "behaviour": { - "onClickUrl": "https://ko-fi.com/ajnart", - "externalUrl": "https://ko-fi.com/ajnart", - "isOpeningNewTab": true - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [ - 200 - ] - }, - "appearance": { - "iconUrl": "https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/61e1116779fc0a9bd5bdbcc7_Frame%206.png" - }, - "integration": { - "type": null, - "properties": [] - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "location": { - "x": 2, - "y": 2 - }, - "size": { - "width": 2, - "height": 2 - } - } - }, { "id": "e41a11f5-9c6e-41bc-ac0e-4c4c47582faa", "name": "Your app", @@ -183,7 +69,7 @@ ] }, "appearance": { - "iconUrl": "https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/github.png" + "iconUrl": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/github.png" }, "integration": { "type": null, @@ -206,6 +92,45 @@ } } }, + { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a337", + "name": "Discord", + "url": "https://discord.com/invite/aCsmEV5RgA", + "behaviour": { + "onClickUrl": "https://discord.com/invite/aCsmEV5RgA", + "isOpeningNewTab": true, + "externalUrl": "https://discord.com/invite/aCsmEV5RgA" + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [ + 200 + ] + }, + "appearance": { + "iconUrl": "https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/discord.png" + }, + "integration": { + "type": null, + "properties": [] + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "location": { + "x": 0, + "y": 4 + }, + "size": { + "width": 4, + "height": 3 + } + } + }, { "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a", "name": "Documentation", @@ -245,6 +170,81 @@ } } }, + { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990", + "name": "Donate", + "url": "https://ko-fi.com/ajnart", + "behaviour": { + "onClickUrl": "https://ko-fi.com/ajnart", + "externalUrl": "https://ko-fi.com/ajnart", + "isOpeningNewTab": true + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [ + 200 + ] + }, + "appearance": { + "iconUrl": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/ko-fi.png" + }, + "integration": { + "type": null, + "properties": [] + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "location": { + "x": 2, + "y": 2 + }, + "size": { + "width": 2, + "height": 2 + } + } + }, + { + "id": "76217a87-7151-42d0-b0cf-1b72aef63f83", + "name": "Small app", + "url": "https://homarr.dev", + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [] + }, + "behaviour": { + "isOpeningNewTab": true, + "externalUrl": "https://homarr.dev" + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "location": { + "x": 0, + "y": 2 + }, + "size": { + "width": 2, + "height": 2 + } + }, + "integration": { + "type": null, + "properties": [] + } + }, { "id": "5df743d9-5cb1-457c-85d2-64ff86855652", "name": "Your app",