From 9d566330be881b113b55efeb27f958fe19321660 Mon Sep 17 00:00:00 2001 From: ajnart Date: Mon, 23 Jan 2023 01:34:36 +0900 Subject: [PATCH 01/41] work on next13 --- package.json | 21 +- src/components/About/AboutModal.tsx | 10 +- .../InputElements/IntegrationSelector.tsx | 2 +- .../IntegrationOptionsRenderer.tsx | 2 - .../Tabs/NetworkTab/NetworkTab.tsx | 4 +- .../Components/Shared/GenericElementType.tsx | 4 +- .../Dashboard/Tiles/Apps/AppTile.tsx | 5 +- src/modules/Docker/ContainerActionBar.tsx | 1 - src/pages/404.tsx | 5 +- src/pages/index.tsx | 2 + src/tools/addToHomarr.ts | 2 +- src/tools/bytesHelper.ts | 3 + .../mutations/useDeleteConfigMutation.tsx | 2 +- src/tools/isToday.ts | 2 +- src/tools/percentage.ts | 5 +- src/widgets/calendar/CalendarTile.tsx | 1 + src/widgets/useNet/UsenetQueueList.tsx | 2 - src/widgets/weather/WeatherTile.tsx | 2 +- src/widgets/weather/useWeatherForCity.ts | 1 + yarn.lock | 352 +++++++++--------- 20 files changed, 220 insertions(+), 208 deletions(-) diff --git a/package.json b/package.json index 41ffd4c40..adc842f74 100644 --- a/package.json +++ b/package.json @@ -32,15 +32,14 @@ "@dnd-kit/utilities": "^3.2.0", "@emotion/react": "^11.10.5", "@emotion/server": "^11.10.0", - "@mantine/carousel": "^5.9.3", - "@mantine/core": "^5.9.3", - "@mantine/dates": "^5.9.3", - "@mantine/dropzone": "^5.9.3", - "@mantine/form": "^5.9.3", - "@mantine/hooks": "^5.9.3", - "@mantine/modals": "^5.9.3", - "@mantine/next": "^5.9.3", - "@mantine/notifications": "^5.9.3", + "@mantine/core": "^5.10.1", + "@mantine/dates": "^5.10.1", + "@mantine/dropzone": "^5.10.1", + "@mantine/form": "^5.10.1", + "@mantine/hooks": "^5.10.1", + "@mantine/modals": "^5.10.1", + "@mantine/next": "^5.10.1", + "@mantine/notifications": "^5.10.1", "@mantine/prism": "^5.9.3", "@nivo/core": "^0.79.0", "@nivo/line": "^0.79.1", @@ -49,7 +48,7 @@ "axios": "^0.27.2", "consola": "^2.15.3", "cookies-next": "^2.1.1", - "dayjs": "^1.11.6", + "dayjs": "^1.11.7", "dockerode": "^3.3.2", "embla-carousel-react": "^7.0.0", "fily-publish-gridstack": "^0.0.13", @@ -58,7 +57,7 @@ "i18next-browser-languagedetector": "^6.1.5", "i18next-http-backend": "^1.4.1", "js-file-download": "^0.4.12", - "next": "12.2.0", + "next": "^13.1.4", "next-i18next": "^11.3.0", "nzbget-api": "^0.0.3", "ping": "^0.4.2", diff --git a/src/components/About/AboutModal.tsx b/src/components/About/AboutModal.tsx index 8d258ebd3..5617731f4 100644 --- a/src/components/About/AboutModal.tsx +++ b/src/components/About/AboutModal.tsx @@ -51,7 +51,15 @@ export const AboutModal = ({ opened, closeModal, newVersionAvailable }: AboutMod opened={opened} title={ - + Homarr logo {t('about')} Homarr 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 b6c37a8a9..7ba366339 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 @@ -126,11 +126,11 @@ export const IntegrationSelector = ({ form }: IntegrationSelectorProps) => { /> ) } + {...inputProps} onChange={(value) => { form.setFieldValue('integration.properties', getNewProperties(value)); inputProps.onChange(value); }} - {...inputProps} /> ); }; 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 0d8d4bfd9..a22721f78 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 @@ -61,7 +61,6 @@ export const IntegrationOptionsRenderer = ({ form }: IntegrationOptionsRendererP label={`${property} (potentionally unmapped)`} secretIsPresent={isPresent} setIcon={IconKey} - value={formValue.value} type={accessabilityType} {...form.getInputProps(`integration.properties.${index}.value`)} /> @@ -76,7 +75,6 @@ export const IntegrationOptionsRenderer = ({ form }: IntegrationOptionsRendererP }} key={`input-${definition.label}`} label={definition.label} - value="" secretIsPresent={isPresent} setIcon={definition.icon} type={accessabilityType} diff --git a/src/components/Dashboard/Modals/EditAppModal/Tabs/NetworkTab/NetworkTab.tsx b/src/components/Dashboard/Modals/EditAppModal/Tabs/NetworkTab/NetworkTab.tsx index 7c1fe0c2c..4d735a19f 100644 --- a/src/components/Dashboard/Modals/EditAppModal/Tabs/NetworkTab/NetworkTab.tsx +++ b/src/components/Dashboard/Modals/EditAppModal/Tabs/NetworkTab/NetworkTab.tsx @@ -27,9 +27,9 @@ export const NetworkTab = ({ form }: NetworkTabProps) => { data={StatusCodes} clearable searchable - defaultValue={form.values.network.okStatus} + defaultValue={form.values.network.statusCodes} variant="default" - {...form.getInputProps('network.okStatus')} + {...form.getInputProps('network.statusCodes')} /> )} diff --git a/src/components/Dashboard/Modals/SelectElement/Components/Shared/GenericElementType.tsx b/src/components/Dashboard/Modals/SelectElement/Components/Shared/GenericElementType.tsx index 07938aba2..fbf9cd169 100644 --- a/src/components/Dashboard/Modals/SelectElement/Components/Shared/GenericElementType.tsx +++ b/src/components/Dashboard/Modals/SelectElement/Components/Shared/GenericElementType.tsx @@ -24,7 +24,9 @@ export const GenericAvailableElementType = ({ const { t } = useTranslation('layout/modals/about'); const Icon = - typeof image === 'string' ? () => : image; + typeof image === 'string' + ? () => {name} + : image; return ( diff --git a/src/components/Dashboard/Tiles/Apps/AppTile.tsx b/src/components/Dashboard/Tiles/Apps/AppTile.tsx index 992ae1606..fff6d200c 100644 --- a/src/components/Dashboard/Tiles/Apps/AppTile.tsx +++ b/src/components/Dashboard/Tiles/Apps/AppTile.tsx @@ -1,7 +1,8 @@ -import { Box, Stack, Title, UnstyledButton } from '@mantine/core'; +import { Anchor, Box, Stack, Title, UnstyledButton } from '@mantine/core'; import { NextLink } from '@mantine/next'; import { createStyles } from '@mantine/styles'; import { motion } from 'framer-motion'; +import Link from 'next/link'; import { AppType } from '../../../../types/app'; import { useCardStyles } from '../../../layout/useCardStyles'; import { useEditModeStore } from '../../Views/useEditModeStore'; @@ -71,7 +72,7 @@ export const AppTile = ({ className, app }: AppTileProps) => { ) : ( 0 ? app.behaviour.externalUrl : app.url} target={app.behaviour.isOpeningNewTab ? '_blank' : '_self'} className={cx(classes.button)} diff --git a/src/modules/Docker/ContainerActionBar.tsx b/src/modules/Docker/ContainerActionBar.tsx index 6d1000025..3b8943908 100644 --- a/src/modules/Docker/ContainerActionBar.tsx +++ b/src/modules/Docker/ContainerActionBar.tsx @@ -20,7 +20,6 @@ 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/pages/404.tsx b/src/pages/404.tsx index 5ed2a87c5..ea1ea65ec 100644 --- a/src/pages/404.tsx +++ b/src/pages/404.tsx @@ -11,6 +11,7 @@ import { import { NextLink } from '@mantine/next'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +import Link from 'next/link'; const useStyles = createStyles((theme) => ({ root: { @@ -85,9 +86,9 @@ export default function Custom404() { The config you are trying to access does not exist. Please check the URL and try again. - + - + diff --git a/src/pages/index.tsx b/src/pages/index.tsx index dd416158b..c20dd8456 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -10,6 +10,8 @@ import { getServerSideTranslations } from '../tools/getServerSideTranslations'; import { dashboardNamespaces } from '../tools/translation-namespaces'; import { DashboardServerSideProps } from '../types/dashboardPageType'; import { LoadConfigComponent } from '../components/Config/LoadConfig'; +import dayjs from 'dayjs'; +import { useTranslation } from 'next-i18next'; export async function getServerSideProps({ req, diff --git a/src/tools/addToHomarr.ts b/src/tools/addToHomarr.ts index 5f97bf49d..e3b88f3d9 100644 --- a/src/tools/addToHomarr.ts +++ b/src/tools/addToHomarr.ts @@ -1,5 +1,5 @@ import Dockerode from 'dockerode'; -import { Config, MatchingImages, ServiceType, tryMatchPort } from './types'; +import { MatchingImages, ServiceType, tryMatchPort } from './types'; async function MatchIcon(name: string) { const res = await fetch( diff --git a/src/tools/bytesHelper.ts b/src/tools/bytesHelper.ts index bcbe882d0..c4d06aa07 100644 --- a/src/tools/bytesHelper.ts +++ b/src/tools/bytesHelper.ts @@ -1,3 +1,6 @@ +/* eslint-disable no-param-reassign */ +/* eslint-disable no-plusplus */ +/* eslint-disable consistent-return */ export const bytes = { toPerSecondString: (bytes?: number) => { if (!bytes) return '-'; diff --git a/src/tools/config/mutations/useDeleteConfigMutation.tsx b/src/tools/config/mutations/useDeleteConfigMutation.tsx index 6885d79e6..d51e12208 100644 --- a/src/tools/config/mutations/useDeleteConfigMutation.tsx +++ b/src/tools/config/mutations/useDeleteConfigMutation.tsx @@ -1,5 +1,5 @@ import { showNotification } from '@mantine/notifications'; -import { IconCheck, IconX } from '@tabler/icons'; +import { IconX } from '@tabler/icons'; import { useMutation } from '@tanstack/react-query'; import { useTranslation } from 'next-i18next'; diff --git a/src/tools/isToday.ts b/src/tools/isToday.ts index 8ca8441f0..06c46db48 100644 --- a/src/tools/isToday.ts +++ b/src/tools/isToday.ts @@ -3,6 +3,6 @@ export const isToday = (date: Date) => { return ( today.getDate() === date.getDate() && today.getMonth() === date.getMonth() && - date.getFullYear() === date.getFullYear() + today.getFullYear() === date.getFullYear() ); }; diff --git a/src/tools/percentage.ts b/src/tools/percentage.ts index eaf588c34..7aa201361 100644 --- a/src/tools/percentage.ts +++ b/src/tools/percentage.ts @@ -1,3 +1,2 @@ -export const percentage = (partialValue: number, totalValue: number) => { - return ((100 * partialValue) / totalValue).toFixed(1); -}; +export const percentage = (partialValue: number, totalValue: number) => + ((100 * partialValue) / totalValue).toFixed(1); diff --git a/src/widgets/calendar/CalendarTile.tsx b/src/widgets/calendar/CalendarTile.tsx index 7518a038b..4c904a90f 100644 --- a/src/widgets/calendar/CalendarTile.tsx +++ b/src/widgets/calendar/CalendarTile.tsx @@ -73,6 +73,7 @@ function CalendarTile({ widget }: CalendarTileProps) { style={{ position: 'relative', top: -15 }} onMonthChange={setMonth} size="xs" + locale={i18n.resolvedLanguage} fullWidth onChange={() => {}} firstDayOfWeek={widget.properties.sundayStart ? 'sunday' : 'monday'} diff --git a/src/widgets/useNet/UsenetQueueList.tsx b/src/widgets/useNet/UsenetQueueList.tsx index 5fec4f842..aa3dc99e6 100644 --- a/src/widgets/useNet/UsenetQueueList.tsx +++ b/src/widgets/useNet/UsenetQueueList.tsx @@ -1,13 +1,11 @@ import { ActionIcon, Alert, - Button, Center, Code, Group, Pagination, Progress, - ScrollArea, Skeleton, Stack, Table, diff --git a/src/widgets/weather/WeatherTile.tsx b/src/widgets/weather/WeatherTile.tsx index ad8efde4d..ba7b9eff7 100644 --- a/src/widgets/weather/WeatherTile.tsx +++ b/src/widgets/weather/WeatherTile.tsx @@ -76,7 +76,7 @@ function WeatherTile({ widget }: WeatherTileProps) { align="center" style={{ height: '100%', width: '100%' }} > - + {getPerferedUnit( diff --git a/src/widgets/weather/useWeatherForCity.ts b/src/widgets/weather/useWeatherForCity.ts index a785c92eb..fac773d66 100644 --- a/src/widgets/weather/useWeatherForCity.ts +++ b/src/widgets/weather/useWeatherForCity.ts @@ -47,6 +47,7 @@ const fetchWeather = async (coordinates?: Coordinates) => { const res = await fetch( `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&daily=weathercode,temperature_2m_max,temperature_2m_min¤t_weather=true&timezone=Europe%2FLondon` ); + // eslint-disable-next-line consistent-return return (await res.json()) as WeatherResponse; }; diff --git a/yarn.lock b/yarn.lock index 08cd3cf7f..72a52cb27 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1114,127 +1114,113 @@ __metadata: languageName: node linkType: hard -"@mantine/carousel@npm:^5.9.3": - version: 5.9.3 - resolution: "@mantine/carousel@npm:5.9.3" - dependencies: - "@mantine/utils": 5.9.3 - peerDependencies: - "@mantine/core": 5.9.3 - "@mantine/hooks": 5.9.3 - embla-carousel-react: ^7.0.0 - react: ">=16.8.0" - checksum: 4c64a170d3dacfe94c2bfdd40f21f2eeb48e91df4fcfe4b06c0990a19ec787d36bb9c7eaf83632aead451e1f45e438144a484e50d5ccd5a3314e61c9169d5a2e - languageName: node - linkType: hard - "@mantine/core@npm:^5.9.3": - version: 5.9.3 - resolution: "@mantine/core@npm:5.9.3" + version: 5.10.1 + resolution: "@mantine/core@npm:5.10.1" dependencies: "@floating-ui/react-dom-interactions": ^0.10.1 - "@mantine/styles": 5.9.3 - "@mantine/utils": 5.9.3 + "@mantine/styles": 5.10.1 + "@mantine/utils": 5.10.1 "@radix-ui/react-scroll-area": 1.0.2 react-textarea-autosize: 8.3.4 peerDependencies: - "@mantine/hooks": 5.9.3 + "@mantine/hooks": 5.10.1 react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 8a84074c5af3607034fc8b31a73b04281e7b62e421395d884e392e74b966b616d9f01211ec35d7d491effaa04e0a02f7c69cd2b70813ebc2cb21e34d31afd0f2 + checksum: 8b4ecd6951e6c0c618064ca0ca130c81835887ec69a4534a8315aa5fd9c2c1d7e9bbda44ba5942d47c0c65490f1edd2436320dc37f24261844705db68ef7e6a1 languageName: node linkType: hard -"@mantine/dates@npm:^5.9.3": - version: 5.9.3 - resolution: "@mantine/dates@npm:5.9.3" +"@mantine/dates@npm:^5.10.1": + version: 5.10.1 + resolution: "@mantine/dates@npm:5.10.1" dependencies: - "@mantine/utils": 5.9.3 + "@mantine/utils": 5.10.1 peerDependencies: - "@mantine/core": 5.9.3 - "@mantine/hooks": 5.9.3 + "@mantine/core": 5.10.1 + "@mantine/hooks": 5.10.1 dayjs: ">=1.0.0" react: ">=16.8.0" - checksum: fc7c8d19ab10c1d997a882debae74f21fa3a2a59ee834b901713e5ddd9feba7197f61fb68a9a27794071d83d7690564fc45793a6966eebf5f55dff368f837aee + checksum: 68f2d26e7dc91429c2281bab4af8ba895ac26fe08527484467b2bc891ce213f5548de61f0d5afe144927b0a2cd4131dd2117657171ab5c3720bd3cfeadf3fe39 languageName: node linkType: hard -"@mantine/dropzone@npm:^5.9.3": - version: 5.9.3 - resolution: "@mantine/dropzone@npm:5.9.3" +"@mantine/dropzone@npm:^5.10.1": + version: 5.10.1 + resolution: "@mantine/dropzone@npm:5.10.1" dependencies: - "@mantine/utils": 5.9.3 + "@mantine/utils": 5.10.1 react-dropzone: 14.2.3 peerDependencies: - "@mantine/core": 5.9.3 - "@mantine/hooks": 5.9.3 + "@mantine/core": 5.10.1 + "@mantine/hooks": 5.10.1 react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 4409c1a302ff7a964045cdd1c5aa17db6d6c0554a67d85f900424a4503f19022488eaf76a83c51e0d76c3c2477498e606bf9c3b2b7897480cdfdd6de9b2038cb + checksum: e5e7b35e775117fb3e5d4e74798d53bbfb82f846aae173b06ae17762c79b4b114deec7c4dccb858c30a38dee09d6c34add939f171ec343cbb70ce7bd3ee56ad9 languageName: node linkType: hard -"@mantine/form@npm:^5.9.3": - version: 5.9.3 - resolution: "@mantine/form@npm:5.9.3" +"@mantine/form@npm:^5.10.1": + version: 5.10.1 + resolution: "@mantine/form@npm:5.10.1" dependencies: fast-deep-equal: ^3.1.3 klona: ^2.0.5 peerDependencies: react: ">=16.8.0" - checksum: e531b059c2a80a4286c62b722b17df1c13f7e6a76341692cdf8c9fa6b25e45555a5998185364d362320a83bed35a6f824c6a30cc94bc44d277140ac62a263a5a + checksum: 2d6b19894f48b95f45bfdc525e456897581be178f4577715c4bec9ddac90d135e74edf94860334a0d43cee75d8c79fb093a7328c7eef2c70361bcbcb8cd17354 languageName: node linkType: hard -"@mantine/hooks@npm:^5.9.3": - version: 5.9.3 - resolution: "@mantine/hooks@npm:5.9.3" +"@mantine/hooks@npm:^5.10.1": + version: 5.10.1 + resolution: "@mantine/hooks@npm:5.10.1" peerDependencies: react: ">=16.8.0" - checksum: 53ceb36bad2b5eeeca5ce612116cd2089ea31cfc93b7d0280c4917b90592dea8d9c584e08d2eb907553060750810b7601170d1f3221f0504b847a68f28854624 + checksum: 9b01c87ba8709bfd3bc7990b3bca5beb6d82ce66c089a39fcd63a4abf9dd64a5e85caee98ac22a68e0755a9ae815491561f5bc4fa5693d011a04bb22d8f211d1 languageName: node linkType: hard -"@mantine/modals@npm:^5.9.3": - version: 5.9.3 - resolution: "@mantine/modals@npm:5.9.3" +"@mantine/modals@npm:^5.10.1": + version: 5.10.1 + resolution: "@mantine/modals@npm:5.10.1" dependencies: - "@mantine/utils": 5.9.3 + "@mantine/utils": 5.10.1 peerDependencies: - "@mantine/core": 5.9.3 - "@mantine/hooks": 5.9.3 + "@mantine/core": 5.10.1 + "@mantine/hooks": 5.10.1 react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 8bad3c4542a937e529c5ebe4161ccf43c7b45288ef5024789c534c4e4dda36e67e9820f563eccda7aa47375002423c0d6c4b99ddbd4a0d1fb72bb8af09ce39b8 + checksum: 143c51476c4acb8de8b99f308199fee125e2a774b60427baa282e015910a24945fe75bca48baa199592f42b89b1a37de15ca77c6e0a3ea7b7824b36d422cf4fd languageName: node linkType: hard -"@mantine/next@npm:^5.9.3": - version: 5.9.3 - resolution: "@mantine/next@npm:5.9.3" +"@mantine/next@npm:^5.10.1": + version: 5.10.1 + resolution: "@mantine/next@npm:5.10.1" dependencies: - "@mantine/ssr": 5.9.3 - "@mantine/styles": 5.9.3 + "@mantine/ssr": 5.10.1 + "@mantine/styles": 5.10.1 peerDependencies: next: "*" react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 336cdf9732f64202498b33f725bdf51e27eb3b322e8176b7d7427fb595689e26049d3ebb775ba65882619f4048ed1a8bdca17893bdd560ab65c76551de016f21 + checksum: 511a51e18d62d6afec25bc496b36e51d245c070ef7e0879b906a30f5fd114b574f5a4f5cef441a5751bf600812339981add649b9dba5a17b05877a88afb6b81f languageName: node linkType: hard -"@mantine/notifications@npm:^5.9.3": - version: 5.9.3 - resolution: "@mantine/notifications@npm:5.9.3" +"@mantine/notifications@npm:^5.10.1": + version: 5.10.1 + resolution: "@mantine/notifications@npm:5.10.1" dependencies: - "@mantine/utils": 5.9.3 + "@mantine/utils": 5.10.1 react-transition-group: 4.4.2 peerDependencies: - "@mantine/core": 5.9.3 - "@mantine/hooks": 5.9.3 + "@mantine/core": 5.10.1 + "@mantine/hooks": 5.10.1 react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: de92616f88b79271080f02edaaa92c9e76a85e5dad2831c747cbde312e23336351ee12f4179bb57b8a54d628644d4f8a7843b8358179bf5f657c2a995e5a1c6f + checksum: 80e065db28757af2bdf50093529238e91fb85d936087e6e40219d530f51b860f5f510c52526b42e1ca58a4cb3d939cea080e549a4659e9cdcba3c045e3bfee00 languageName: node linkType: hard @@ -1253,24 +1239,24 @@ __metadata: languageName: node linkType: hard -"@mantine/ssr@npm:5.9.3": - version: 5.9.3 - resolution: "@mantine/ssr@npm:5.9.3" +"@mantine/ssr@npm:5.10.1": + version: 5.10.1 + resolution: "@mantine/ssr@npm:5.10.1" dependencies: - "@mantine/styles": 5.9.3 + "@mantine/styles": 5.10.1 html-react-parser: 1.4.12 peerDependencies: "@emotion/react": ">=11.9.0" "@emotion/server": ">=11.4.0" react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 44cc9f8d1ae122c563251be2e50f175dd61b85dda5354d68a66870ebd49b21b593b061d79245954d02fd8ad736e3cf6590e4dff5a177ec74d5498401178ce800 + checksum: 3a4cf7516661e96d5b736f67aeed07217c0808453f079e2a1361e9cc86cd71a1749e8aba3d94d21099f7a27d1a1dc14b14701138cf57d196169786300cedf845 languageName: node linkType: hard -"@mantine/styles@npm:5.9.3": - version: 5.9.3 - resolution: "@mantine/styles@npm:5.9.3" +"@mantine/styles@npm:5.10.1": + version: 5.10.1 + resolution: "@mantine/styles@npm:5.10.1" dependencies: clsx: 1.1.1 csstype: 3.0.9 @@ -1278,7 +1264,16 @@ __metadata: "@emotion/react": ">=11.9.0" react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 8ba6db16ecc62d5c5128b4e72b6ce8358b4dd3ef89e53b7deaa510d1d7289641b39b1cff2551c00d66791b41e33e6e5d8f213a37d4d10b006fa7329e3c0e31d5 + checksum: 877d28258a7f1a7e1ff9681e90be7ccdfe5bf75638f9ec9a2f9ad13153d1f27610b5f9bc30867ae211cad6533ed02ee816b36654b5c00ca0c2a47d5ee6287e4f + languageName: node + linkType: hard + +"@mantine/utils@npm:5.10.1": + version: 5.10.1 + resolution: "@mantine/utils@npm:5.10.1" + peerDependencies: + react: ">=16.8.0" + checksum: a5ab487a0b24cc12e0b4ef1e5c49439dfa03e29f8f631eb45565fb6991a31aa762816d520904f22a36eff849b7fa2c0996f35af7e1581e4a069c5a1c81b231d4 languageName: node linkType: hard @@ -1365,10 +1360,10 @@ __metadata: languageName: node linkType: hard -"@next/env@npm:12.2.0": - version: 12.2.0 - resolution: "@next/env@npm:12.2.0" - checksum: 5fb317bdb5eb2d5df12ff55e335368792dba21874c5ece3cabf8cd312cec911a1d54ecf368e69dc08640b0244669b8a98c86cd035c7874b17640602e67c1b9d9 +"@next/env@npm:13.1.4": + version: 13.1.4 + resolution: "@next/env@npm:13.1.4" + checksum: 68e222462d35959f183aa17829e3b9045830d0a1030a64bc40fe556bde70584cab7f4bde3bf53abbdbf110fefb2c00a0c573180a50536676919016cded945e09 languageName: node linkType: hard @@ -1381,93 +1376,93 @@ __metadata: languageName: node linkType: hard -"@next/swc-android-arm-eabi@npm:12.2.0": - version: 12.2.0 - resolution: "@next/swc-android-arm-eabi@npm:12.2.0" +"@next/swc-android-arm-eabi@npm:13.1.4": + version: 13.1.4 + resolution: "@next/swc-android-arm-eabi@npm:13.1.4" conditions: os=android & cpu=arm languageName: node linkType: hard -"@next/swc-android-arm64@npm:12.2.0": - version: 12.2.0 - resolution: "@next/swc-android-arm64@npm:12.2.0" +"@next/swc-android-arm64@npm:13.1.4": + version: 13.1.4 + resolution: "@next/swc-android-arm64@npm:13.1.4" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@next/swc-darwin-arm64@npm:12.2.0": - version: 12.2.0 - resolution: "@next/swc-darwin-arm64@npm:12.2.0" +"@next/swc-darwin-arm64@npm:13.1.4": + version: 13.1.4 + resolution: "@next/swc-darwin-arm64@npm:13.1.4" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@next/swc-darwin-x64@npm:12.2.0": - version: 12.2.0 - resolution: "@next/swc-darwin-x64@npm:12.2.0" +"@next/swc-darwin-x64@npm:13.1.4": + version: 13.1.4 + resolution: "@next/swc-darwin-x64@npm:13.1.4" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@next/swc-freebsd-x64@npm:12.2.0": - version: 12.2.0 - resolution: "@next/swc-freebsd-x64@npm:12.2.0" +"@next/swc-freebsd-x64@npm:13.1.4": + version: 13.1.4 + resolution: "@next/swc-freebsd-x64@npm:13.1.4" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@next/swc-linux-arm-gnueabihf@npm:12.2.0": - version: 12.2.0 - resolution: "@next/swc-linux-arm-gnueabihf@npm:12.2.0" +"@next/swc-linux-arm-gnueabihf@npm:13.1.4": + version: 13.1.4 + resolution: "@next/swc-linux-arm-gnueabihf@npm:13.1.4" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@next/swc-linux-arm64-gnu@npm:12.2.0": - version: 12.2.0 - resolution: "@next/swc-linux-arm64-gnu@npm:12.2.0" +"@next/swc-linux-arm64-gnu@npm:13.1.4": + version: 13.1.4 + resolution: "@next/swc-linux-arm64-gnu@npm:13.1.4" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@next/swc-linux-arm64-musl@npm:12.2.0": - version: 12.2.0 - resolution: "@next/swc-linux-arm64-musl@npm:12.2.0" +"@next/swc-linux-arm64-musl@npm:13.1.4": + version: 13.1.4 + resolution: "@next/swc-linux-arm64-musl@npm:13.1.4" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@next/swc-linux-x64-gnu@npm:12.2.0": - version: 12.2.0 - resolution: "@next/swc-linux-x64-gnu@npm:12.2.0" +"@next/swc-linux-x64-gnu@npm:13.1.4": + version: 13.1.4 + resolution: "@next/swc-linux-x64-gnu@npm:13.1.4" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@next/swc-linux-x64-musl@npm:12.2.0": - version: 12.2.0 - resolution: "@next/swc-linux-x64-musl@npm:12.2.0" +"@next/swc-linux-x64-musl@npm:13.1.4": + version: 13.1.4 + resolution: "@next/swc-linux-x64-musl@npm:13.1.4" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@next/swc-win32-arm64-msvc@npm:12.2.0": - version: 12.2.0 - resolution: "@next/swc-win32-arm64-msvc@npm:12.2.0" +"@next/swc-win32-arm64-msvc@npm:13.1.4": + version: 13.1.4 + resolution: "@next/swc-win32-arm64-msvc@npm:13.1.4" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@next/swc-win32-ia32-msvc@npm:12.2.0": - version: 12.2.0 - resolution: "@next/swc-win32-ia32-msvc@npm:12.2.0" +"@next/swc-win32-ia32-msvc@npm:13.1.4": + version: 13.1.4 + resolution: "@next/swc-win32-ia32-msvc@npm:13.1.4" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@next/swc-win32-x64-msvc@npm:12.2.0": - version: 12.2.0 - resolution: "@next/swc-win32-x64-msvc@npm:12.2.0" +"@next/swc-win32-x64-msvc@npm:13.1.4": + version: 13.1.4 + resolution: "@next/swc-win32-x64-msvc@npm:13.1.4" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -1911,12 +1906,12 @@ __metadata: languageName: node linkType: hard -"@swc/helpers@npm:0.4.2": - version: 0.4.2 - resolution: "@swc/helpers@npm:0.4.2" +"@swc/helpers@npm:0.4.14": + version: 0.4.14 + resolution: "@swc/helpers@npm:0.4.14" dependencies: tslib: ^2.4.0 - checksum: 0b8c86ad03b17b8fe57dc4498e25dc294ea6bc42558a6b92d8fcd789351dac80199409bef38a2e3ac06aae0fedddfc0ab9c34409acbf74e55d1bbbd74f68b6b7 + checksum: 273fd3f3fc461a92f3790cc551ea054745c6d6959afbe1232e6d7aa1c722bbc114d308aab96bef5c78fc0303c85c7b472ef00e2253251cc89737f3b1af56e5a5 languageName: node linkType: hard @@ -2979,13 +2974,20 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001332, caniuse-lite@npm:^1.0.30001400": +"caniuse-lite@npm:^1.0.30001400": version: 1.0.30001439 resolution: "caniuse-lite@npm:1.0.30001439" checksum: 3912dd536c9735713ca85e47721988bbcefb881ddb4886b0b9923fa984247fd22cba032cf268e57d158af0e8a2ae2eae042ae01942a1d6d7849fa9fa5d62fb82 languageName: node linkType: hard +"caniuse-lite@npm:^1.0.30001406": + version: 1.0.30001446 + resolution: "caniuse-lite@npm:1.0.30001446" + checksum: b31a7e1837783afd7f3d4cb742689996c0a09d67394ddaa0609fd2bce00ceea65c448e25f91c03ba0f2d0e345b7e28fd5bc636c6760c949621a654c0effe74b5 + languageName: node + linkType: hard + "chalk@npm:^2.0.0": version: 2.4.2 resolution: "chalk@npm:2.4.2" @@ -3068,6 +3070,13 @@ __metadata: languageName: node linkType: hard +"client-only@npm:0.0.1": + version: 0.0.1 + resolution: "client-only@npm:0.0.1" + checksum: 0c16bf660dadb90610553c1d8946a7fdfb81d624adea073b8440b7d795d5b5b08beb3c950c6a2cf16279365a3265158a236876d92bce16423c485c322d7dfaf8 + languageName: node + linkType: hard + "cliui@npm:^8.0.1": version: 8.0.1 resolution: "cliui@npm:8.0.1" @@ -3444,7 +3453,7 @@ __metadata: languageName: node linkType: hard -"dayjs@npm:^1.11.6": +"dayjs@npm:^1.11.7": version: 1.11.7 resolution: "dayjs@npm:1.11.7" checksum: 5003a7c1dd9ed51385beb658231c3548700b82d3548c0cfbe549d85f2d08e90e972510282b7506941452c58d32136d6362f009c77ca55381a09c704e9f177ebb @@ -4900,15 +4909,14 @@ __metadata: "@dnd-kit/utilities": ^3.2.0 "@emotion/react": ^11.10.5 "@emotion/server": ^11.10.0 - "@mantine/carousel": ^5.9.3 "@mantine/core": ^5.9.3 - "@mantine/dates": ^5.9.3 - "@mantine/dropzone": ^5.9.3 - "@mantine/form": ^5.9.3 - "@mantine/hooks": ^5.9.3 - "@mantine/modals": ^5.9.3 - "@mantine/next": ^5.9.3 - "@mantine/notifications": ^5.9.3 + "@mantine/dates": ^5.10.1 + "@mantine/dropzone": ^5.10.1 + "@mantine/form": ^5.10.1 + "@mantine/hooks": ^5.10.1 + "@mantine/modals": ^5.10.1 + "@mantine/next": ^5.10.1 + "@mantine/notifications": ^5.10.1 "@mantine/prism": ^5.9.3 "@next/bundle-analyzer": ^12.1.4 "@next/eslint-plugin-next": ^12.1.4 @@ -4926,7 +4934,7 @@ __metadata: axios: ^0.27.2 consola: ^2.15.3 cookies-next: ^2.1.1 - dayjs: ^1.11.6 + dayjs: ^1.11.7 dockerode: ^3.3.2 embla-carousel-react: ^7.0.0 eslint: ^8.20.0 @@ -4947,7 +4955,7 @@ __metadata: i18next-http-backend: ^1.4.1 jest: ^28.1.3 js-file-download: ^0.4.12 - next: 12.2.0 + next: ^13.1.4 next-i18next: ^11.3.0 nzbget-api: ^0.0.3 ping: ^0.4.2 @@ -6483,7 +6491,7 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.1.30": +"nanoid@npm:^3.3.4": version: 3.3.4 resolution: "nanoid@npm:3.3.4" bin: @@ -6538,34 +6546,33 @@ __metadata: languageName: node linkType: hard -"next@npm:12.2.0": - version: 12.2.0 - resolution: "next@npm:12.2.0" +"next@npm:^13.1.4": + version: 13.1.4 + resolution: "next@npm:13.1.4" dependencies: - "@next/env": 12.2.0 - "@next/swc-android-arm-eabi": 12.2.0 - "@next/swc-android-arm64": 12.2.0 - "@next/swc-darwin-arm64": 12.2.0 - "@next/swc-darwin-x64": 12.2.0 - "@next/swc-freebsd-x64": 12.2.0 - "@next/swc-linux-arm-gnueabihf": 12.2.0 - "@next/swc-linux-arm64-gnu": 12.2.0 - "@next/swc-linux-arm64-musl": 12.2.0 - "@next/swc-linux-x64-gnu": 12.2.0 - "@next/swc-linux-x64-musl": 12.2.0 - "@next/swc-win32-arm64-msvc": 12.2.0 - "@next/swc-win32-ia32-msvc": 12.2.0 - "@next/swc-win32-x64-msvc": 12.2.0 - "@swc/helpers": 0.4.2 - caniuse-lite: ^1.0.30001332 - postcss: 8.4.5 - styled-jsx: 5.0.2 - use-sync-external-store: 1.1.0 + "@next/env": 13.1.4 + "@next/swc-android-arm-eabi": 13.1.4 + "@next/swc-android-arm64": 13.1.4 + "@next/swc-darwin-arm64": 13.1.4 + "@next/swc-darwin-x64": 13.1.4 + "@next/swc-freebsd-x64": 13.1.4 + "@next/swc-linux-arm-gnueabihf": 13.1.4 + "@next/swc-linux-arm64-gnu": 13.1.4 + "@next/swc-linux-arm64-musl": 13.1.4 + "@next/swc-linux-x64-gnu": 13.1.4 + "@next/swc-linux-x64-musl": 13.1.4 + "@next/swc-win32-arm64-msvc": 13.1.4 + "@next/swc-win32-ia32-msvc": 13.1.4 + "@next/swc-win32-x64-msvc": 13.1.4 + "@swc/helpers": 0.4.14 + caniuse-lite: ^1.0.30001406 + postcss: 8.4.14 + styled-jsx: 5.1.1 peerDependencies: fibers: ">= 3.1.0" node-sass: ^6.0.0 || ^7.0.0 - react: ^17.0.2 || ^18.0.0-0 - react-dom: ^17.0.2 || ^18.0.0-0 + react: ^18.2.0 + react-dom: ^18.2.0 sass: ^1.3.0 dependenciesMeta: "@next/swc-android-arm-eabi": @@ -6603,7 +6610,7 @@ __metadata: optional: true bin: next: dist/bin/next - checksum: 38456c33935122ac1581367e4982034be23269039a8470a66443d710334336f8f3fb587f25d172d138d84cf18c01d3a76627fb610c2e2e57bd1692277c23fa2b + checksum: cc399e2245c5bf27ac2101672e8ce591d8258bce777de788b812f3e5d346f99349c74be8567b7340270c314bd1522e90de9a36187f9b74dc759aa114a3fb5036 languageName: node linkType: hard @@ -7049,14 +7056,14 @@ __metadata: languageName: node linkType: hard -"postcss@npm:8.4.5": - version: 8.4.5 - resolution: "postcss@npm:8.4.5" +"postcss@npm:8.4.14": + version: 8.4.14 + resolution: "postcss@npm:8.4.14" dependencies: - nanoid: ^3.1.30 + nanoid: ^3.3.4 picocolors: ^1.0.0 - source-map-js: ^1.0.1 - checksum: b78abdd89c10f7b48f4bdcd376104a19d6e9c7495ab521729bdb3df315af6c211360e9f06887ad3bc0ab0f61a04b91d68ea11462997c79cced58b9ccd66fac07 + source-map-js: ^1.0.2 + checksum: fe58766ff32e4becf65a7d57678995cfd239df6deed2fe0557f038b47c94e4132e7e5f68b5aa820c13adfec32e523b693efaeb65798efb995ce49ccd83953816 languageName: node linkType: hard @@ -7787,7 +7794,7 @@ __metadata: languageName: node linkType: hard -"source-map-js@npm:>=0.6.2 <2.0.0, source-map-js@npm:^1.0.1": +"source-map-js@npm:>=0.6.2 <2.0.0, source-map-js@npm:^1.0.2": version: 1.0.2 resolution: "source-map-js@npm:1.0.2" checksum: c049a7fc4deb9a7e9b481ae3d424cc793cb4845daa690bc5a05d428bf41bf231ced49b4cf0c9e77f9d42fdb3d20d6187619fc586605f5eabe995a316da8d377c @@ -8023,9 +8030,11 @@ __metadata: languageName: node linkType: hard -"styled-jsx@npm:5.0.2": - version: 5.0.2 - resolution: "styled-jsx@npm:5.0.2" +"styled-jsx@npm:5.1.1": + version: 5.1.1 + resolution: "styled-jsx@npm:5.1.1" + dependencies: + client-only: 0.0.1 peerDependencies: react: ">= 16.8.0 || 17.x.x || ^18.0.0-0" peerDependenciesMeta: @@ -8033,7 +8042,7 @@ __metadata: optional: true babel-plugin-macros: optional: true - checksum: 86d55819ebeabd283a574d2f44f7d3f8fa6b8c28fa41687ece161bf1e910e04965611618921d8f5cd33dc6dae1033b926a70421ae5ea045440a9861edc3e0d87 + checksum: 523a33b38603492547e861b98e29c873939b04e15fbe5ef16132c6f1e15958126647983c7d4675325038b428a5e91183d996e90141b18bdd1bbadf6e2c45b2fa languageName: node linkType: hard @@ -8458,15 +8467,6 @@ __metadata: languageName: node linkType: hard -"use-sync-external-store@npm:1.1.0": - version: 1.1.0 - resolution: "use-sync-external-store@npm:1.1.0" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 8993a0b642f91d7fcdbb02b7b3ac984bd3af4769686f38291fe7fcfe73dfb73d6c64d20dfb7e5e7fbf5a6da8f5392d6f8e5b00c243a04975595946e82c02b883 - languageName: node - linkType: hard - "use-sync-external-store@npm:1.2.0, use-sync-external-store@npm:^1.2.0": version: 1.2.0 resolution: "use-sync-external-store@npm:1.2.0" From 1ffd984eb3215eb57664a2a1229201071bed5922 Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Sun, 29 Jan 2023 20:30:10 +0100 Subject: [PATCH 02/41] =?UTF-8?q?=F0=9F=A5=85=20Add=20error=20handling=20f?= =?UTF-8?q?or=20download=20queue=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/api/modules/downloads/index.ts | 29 ++++++++++++++++--- .../queue/NormalizedDownloadQueueResponse.ts | 3 +- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/pages/api/modules/downloads/index.ts b/src/pages/api/modules/downloads/index.ts index 58c584a55..099669738 100644 --- a/src/pages/api/modules/downloads/index.ts +++ b/src/pages/api/modules/downloads/index.ts @@ -1,5 +1,6 @@ import { Deluge } from '@ctrl/deluge'; import { AllClientData } from '@ctrl/shared-torrent'; +import Consola from 'consola'; import { getCookie } from 'cookies-next'; import dayjs from 'dayjs'; import { NextApiRequest, NextApiResponse } from 'next'; @@ -18,16 +19,36 @@ const Get = async (request: NextApiRequest, response: NextApiResponse) => { const configName = getCookie('config-name', { req: request }); const config = getConfig(configName?.toString() ?? 'default'); - const clientData: Promise<NormalizedDownloadAppStat | undefined>[] = config.apps.map((app) => - GetDataFromClient(app) - ); + const failedClients: string[] = []; + + const clientData: Promise<NormalizedDownloadAppStat>[] = config.apps.map(async (app) => { + try { + const response = await GetDataFromClient(app); + + if (!response) { + return { + success: false, + } as NormalizedDownloadAppStat; + } + + return response; + } catch (err) { + Consola.error( + `Error communicating with your download client '${app.name}' (${app.id}): ${err}` + ); + failedClients.push(app.id); + return { + success: false, + } as NormalizedDownloadAppStat; + } + }); const settledPromises = await Promise.allSettled(clientData); const data: NormalizedDownloadAppStat[] = settledPromises .filter((x) => x.status === 'fulfilled') .map((promise) => (promise as PromiseFulfilledResult<NormalizedDownloadAppStat>).value) - .filter((x) => x !== undefined); + .filter((x) => x !== undefined && x.type !== undefined); const responseBody = { apps: data } as NormalizedDownloadQueueResponse; diff --git a/src/types/api/downloads/queue/NormalizedDownloadQueueResponse.ts b/src/types/api/downloads/queue/NormalizedDownloadQueueResponse.ts index 853c756ad..06a38fe22 100644 --- a/src/types/api/downloads/queue/NormalizedDownloadQueueResponse.ts +++ b/src/types/api/downloads/queue/NormalizedDownloadQueueResponse.ts @@ -8,16 +8,17 @@ export type NormalizedDownloadQueueResponse = { export type NormalizedDownloadAppStat = { success: boolean; appId: string; - totalDownload: number; } & (TorrentTotalDownload | UsenetTotalDownloas); export type TorrentTotalDownload = { type: 'torrent'; torrents: NormalizedTorrent[]; + totalDownload: number; totalUpload: number; }; export type UsenetTotalDownloas = { type: 'usenet'; + totalDownload: number; nzbs: UsenetQueueItem[]; }; From 1977c7478e9a1af5192d0112f1a6a7662e77e49f Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Mon, 30 Jan 2023 12:51:54 +0100 Subject: [PATCH 03/41] =?UTF-8?q?=F0=9F=90=9B=20Fix=20mismatched=20torrent?= =?UTF-8?q?=20clients?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/api/modules/downloads/index.ts | 16 ++++++++++++---- .../queue/NormalizedDownloadQueueResponse.ts | 1 + 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/pages/api/modules/downloads/index.ts b/src/pages/api/modules/downloads/index.ts index 099669738..22c33a9ba 100644 --- a/src/pages/api/modules/downloads/index.ts +++ b/src/pages/api/modules/downloads/index.ts @@ -1,4 +1,6 @@ import { Deluge } from '@ctrl/deluge'; +import { QBittorrent } from '@ctrl/qbittorrent'; +import { Transmission } from '@ctrl/transmission'; import { AllClientData } from '@ctrl/shared-torrent'; import Consola from 'consola'; import { getCookie } from 'cookies-next'; @@ -32,7 +34,7 @@ const Get = async (request: NextApiRequest, response: NextApiResponse) => { } return response; - } catch (err) { + } catch (err: any) { Consola.error( `Error communicating with your download client '${app.name}' (${app.id}): ${err}` ); @@ -50,7 +52,11 @@ const Get = async (request: NextApiRequest, response: NextApiResponse) => { .map((promise) => (promise as PromiseFulfilledResult<NormalizedDownloadAppStat>).value) .filter((x) => x !== undefined && x.type !== undefined); - const responseBody = { apps: data } as NormalizedDownloadQueueResponse; + const responseBody = { apps: data, failedApps: failedClients } as NormalizedDownloadQueueResponse; + + if (failedClients.length > 0) { + Consola.warn(`${failedClients.length} download clients failed. Please check your configuration and the above log`); + } return response.status(200).json(responseBody); }; @@ -85,7 +91,7 @@ const GetDataFromClient = async ( } case 'transmission': { return reduceTorrent( - await new Deluge({ + await new Transmission({ baseUrl: app.url, username: findField(app, 'username'), password: findField(app, 'password'), @@ -94,7 +100,7 @@ const GetDataFromClient = async ( } case 'qBittorrent': { return reduceTorrent( - await new Deluge({ + await new QBittorrent({ baseUrl: app.url, username: findField(app, 'username'), password: findField(app, 'password'), @@ -147,6 +153,7 @@ const GetDataFromClient = async ( if (!err) { resolve(result); } else { + Consola.error(`Error while listing groups: ${err}`); reject(err); } }); @@ -160,6 +167,7 @@ const GetDataFromClient = async ( if (!err) { resolve(result); } else { + Consola.error(`Error while retrieving NZBGet stats: ${err}`); reject(err); } }); diff --git a/src/types/api/downloads/queue/NormalizedDownloadQueueResponse.ts b/src/types/api/downloads/queue/NormalizedDownloadQueueResponse.ts index 06a38fe22..476e7fe85 100644 --- a/src/types/api/downloads/queue/NormalizedDownloadQueueResponse.ts +++ b/src/types/api/downloads/queue/NormalizedDownloadQueueResponse.ts @@ -3,6 +3,7 @@ import { UsenetQueueItem } from '../../../../widgets/useNet/types'; export type NormalizedDownloadQueueResponse = { apps: NormalizedDownloadAppStat[]; + failedApps: string[]; }; export type NormalizedDownloadAppStat = { From 561055d5d6e01d260289ff30d732b82d5c88b5f2 Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Mon, 30 Jan 2023 21:03:27 +0100 Subject: [PATCH 04/41] =?UTF-8?q?=F0=9F=90=9B=20Fix=20overwriting=20not=20?= =?UTF-8?q?affected=20apps=20and=20widgets=20in=20categories=20#665?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Wrappers/Category/useCategoryActions.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/Dashboard/Wrappers/Category/useCategoryActions.tsx b/src/components/Dashboard/Wrappers/Category/useCategoryActions.tsx index 2ea0698ee..7225f3b5d 100644 --- a/src/components/Dashboard/Wrappers/Category/useCategoryActions.tsx +++ b/src/components/Dashboard/Wrappers/Category/useCategoryActions.tsx @@ -200,7 +200,11 @@ export const useCategoryActions = (configName: string | undefined, category: Cat return false; } - return app.area.properties.id !== mainWrapperId; + if (app.area.properties.id === mainWrapperId) { + return false; + } + + return app.area.properties.id === currentItem.id; }; const isWidgetAffectedFilter = (widget: IWidget<string, any>): boolean => { @@ -212,7 +216,11 @@ export const useCategoryActions = (configName: string | undefined, category: Cat return false; } - return widget.area.properties.id !== mainWrapperId; + if (widget.area.properties.id === mainWrapperId) { + return false; + } + + return widget.area.properties.id === currentItem.id; }; return { From 84a6c38d9c439171a83dc432c83b85a6981bded0 Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Mon, 30 Jan 2023 21:12:45 +0100 Subject: [PATCH 05/41] =?UTF-8?q?=F0=9F=90=9B=20Search=20bar=20not=20respe?= =?UTF-8?q?cting=20the=20open=20target=20#666?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout/header/Search.tsx | 37 +++++++++++++++---------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/components/layout/header/Search.tsx b/src/components/layout/header/Search.tsx index bf9753425..ba8d799d0 100644 --- a/src/components/layout/header/Search.tsx +++ b/src/components/layout/header/Search.tsx @@ -20,6 +20,7 @@ import React, { forwardRef, useEffect, useRef, useState } from 'react'; import { useConfigContext } from '../../../config/provider'; import { OverseerrMediaDisplay } from '../../../modules/common'; import { IModule } from '../../../modules/ModuleTypes'; +import { ConfigType } from '../../../types/config'; import { searchUrls } from '../../Settings/Common/SearchEngine/SearchEngineSelector'; import Tip from '../Tip'; import { useCardStyles } from '../useCardStyles'; @@ -137,9 +138,7 @@ export function Search() { const textInput = useRef<HTMLInputElement>(null); useHotkeys([['mod+K', () => textInput.current?.focus()]]); const { classes } = useStyles(); - const openInNewTab = config?.settings.common.searchEngine.properties.openInNewTab - ? '_blank' - : '_self'; + const openTarget = getOpenTarget(config); const [opened, setOpened] = useState(false); const { @@ -166,6 +165,7 @@ export function Search() { if (!isModuleEnabled) { return null; } + //TODO: Fix the bug where clicking anything inside the Modal to ask for a movie // will close it (Because it closes the underlying Popover) return ( @@ -194,7 +194,7 @@ export function Search() { setOpened(false); if (item.url) { setSearchQuery(''); - window.open(item.openedUrl ? item.openedUrl : item.url, openInNewTab); + window.open(item.openedUrl ? item.openedUrl : item.url, openTarget); } }} // Replace %s if it is in selectedSearchEngine.url with searchQuery, otherwise append searchQuery at the end of it @@ -205,9 +205,9 @@ export function Search() { autocompleteData.length === 0 ) { if (selectedSearchEngine.url.includes('%s')) { - window.open(selectedSearchEngine.url.replace('%s', searchQuery), openInNewTab); + window.open(selectedSearchEngine.url.replace('%s', searchQuery), openTarget); } else { - window.open(selectedSearchEngine.url + searchQuery, openInNewTab); + window.open(selectedSearchEngine.url + searchQuery, openTarget); } } }} @@ -220,14 +220,15 @@ export function Search() { </Popover.Target> <Popover.Dropdown> <ScrollArea style={{ height: '80vh', maxWidth: '90vw' }} offsetScrollbars> - {OverseerrResults && OverseerrResults.slice(0, 4).map((result: any, index: number) => ( - <React.Fragment key={index}> - <OverseerrMediaDisplay key={result.id} media={result} /> - {index < OverseerrResults.length - 1 && index < 3 && ( - <Divider variant="dashed" my="xs" /> - )} - </React.Fragment> - ))} + {OverseerrResults && + OverseerrResults.slice(0, 4).map((result: any, index: number) => ( + <React.Fragment key={index}> + <OverseerrMediaDisplay key={result.id} media={result} /> + {index < OverseerrResults.length - 1 && index < 3 && ( + <Divider variant="dashed" my="xs" /> + )} + </React.Fragment> + ))} </ScrollArea> </Popover.Dropdown> </Popover> @@ -299,3 +300,11 @@ export function Search() { }); } } + +const getOpenTarget = (config: ConfigType | undefined): '_blank' | '_self' => { + if (!config || config.settings.common.searchEngine.properties.openInNewTab === undefined) { + return '_blank'; + } + + return config.settings.common.searchEngine.properties.openInNewTab ? '_blank' : '_self'; +}; From f28f0b98a04c1e6dec2e301df7d02ce15ea11f94 Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Mon, 30 Jan 2023 21:41:16 +0100 Subject: [PATCH 06/41] =?UTF-8?q?=F0=9F=9A=B8=20Add=20hotkey=20for=20enter?= =?UTF-8?q?ing=20and=20exiting=20edit=20mode=20#660?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../layout/header/Actions/ToggleEditMode/ToggleEditMode.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/layout/header/Actions/ToggleEditMode/ToggleEditMode.tsx b/src/components/layout/header/Actions/ToggleEditMode/ToggleEditMode.tsx index 4bfdc89a6..bee5be707 100644 --- a/src/components/layout/header/Actions/ToggleEditMode/ToggleEditMode.tsx +++ b/src/components/layout/header/Actions/ToggleEditMode/ToggleEditMode.tsx @@ -4,6 +4,7 @@ import { ActionIcon, Button, Group, Text, Title, Tooltip } from '@mantine/core'; import { IconEditCircle, IconEditCircleOff } from '@tabler/icons'; import { getCookie } from 'cookies-next'; import { Trans, useTranslation } from 'next-i18next'; +import { useHotkeys } from '@mantine/hooks'; import { hideNotification, showNotification } from '@mantine/notifications'; import { useConfigContext } from '../../../../../config/provider'; import { useScreenSmallerThan } from '../../../../../hooks/useScreenSmallerThan'; @@ -23,6 +24,8 @@ export const ToggleEditModeAction = () => { const { config } = useConfigContext(); const { classes } = useCardStyles(true); + useHotkeys([['ctrl+E', toggleEditMode]]); + const toggleButtonClicked = () => { toggleEditMode(); if (enabled || config === undefined || config?.schemaVersion === undefined) { From fd7a73c1e716b16adfb931184fd731ba106de77d Mon Sep 17 00:00:00 2001 From: ajnart <ajnart@pm.me> Date: Tue, 31 Jan 2023 10:06:24 +0900 Subject: [PATCH 07/41] Add max width to the Calendar --- src/widgets/calendar/MediaList.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/widgets/calendar/MediaList.tsx b/src/widgets/calendar/MediaList.tsx index 6fee8be0f..153e8608c 100644 --- a/src/widgets/calendar/MediaList.tsx +++ b/src/widgets/calendar/MediaList.tsx @@ -18,16 +18,10 @@ export const MediaList = ({ medias }: MediaListProps) => { return ( <ScrollArea + style={{ height: '80vh', maxWidth: '90vw' }} offsetScrollbars - scrollbarSize={5} pt={5} className={classes.scrollArea} - styles={{ - viewport: { - maxHeight: 450, - minHeight: 210, - }, - }} > {mapMedias(medias.tvShows, SonarrMediaDisplay, lastMediaType === 'tv-show')} {mapMedias(medias.movies, RadarrMediaDisplay, lastMediaType === 'movie')} From e9eebadce62358bdaf71d318bd520f56524ad567 Mon Sep 17 00:00:00 2001 From: ajnart <ajnart@pm.me> Date: Tue, 31 Jan 2023 10:10:02 +0900 Subject: [PATCH 08/41] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Add?= =?UTF-8?q?=20react=20query=20dev=20tool?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + src/pages/_app.tsx | 2 ++ yarn.lock | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/package.json b/package.json index ea144ed3e..4cf737dd8 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@nivo/line": "^0.79.1", "@tabler/icons": "^1.106.0", "@tanstack/react-query": "^4.2.1", + "@tanstack/react-query-devtools": "^4.24.4", "axios": "^0.27.2", "consola": "^2.15.3", "cookies-next": "^2.1.1", diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 829fba3bd..088141e6c 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -9,6 +9,7 @@ import { appWithTranslation } from 'next-i18next'; import { AppProps } from 'next/app'; import Head from 'next/head'; import { useState } from 'react'; +import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { ChangeAppPositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeAppPositionModal'; import { ChangeWidgetPositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal'; import { EditAppModal } from '../components/Dashboard/Modals/EditAppModal/EditAppModal'; @@ -102,6 +103,7 @@ function App(this: any, props: AppProps & { colorScheme: ColorScheme }) { </MantineProvider> </ColorTheme.Provider> </ColorSchemeProvider> + <ReactQueryDevtools initialIsOpen={false} /> </QueryClientProvider> </> ); diff --git a/yarn.lock b/yarn.lock index 08cd3cf7f..97cdc592e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1953,6 +1953,15 @@ __metadata: languageName: node linkType: hard +"@tanstack/match-sorter-utils@npm:^8.7.0": + version: 8.7.6 + resolution: "@tanstack/match-sorter-utils@npm:8.7.6" + dependencies: + remove-accents: 0.4.2 + checksum: 3f3dda277e6e55ca1224a28b38a2deb3ac912c2f2f5263a32fa0d9126c6b6d05feb475539729fd248f1eb88b612109db90b847ec8fdfc05d0f4073c900a2d3f6 + languageName: node + linkType: hard + "@tanstack/query-core@npm:4.19.1": version: 4.19.1 resolution: "@tanstack/query-core@npm:4.19.1" @@ -1960,6 +1969,21 @@ __metadata: languageName: node linkType: hard +"@tanstack/react-query-devtools@npm:^4.24.4": + version: 4.24.4 + resolution: "@tanstack/react-query-devtools@npm:4.24.4" + dependencies: + "@tanstack/match-sorter-utils": ^8.7.0 + superjson: ^1.10.0 + use-sync-external-store: ^1.2.0 + peerDependencies: + "@tanstack/react-query": 4.24.4 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 8b8f1ae8e55f016f25b383baae0000f0b608ec0327ee4eccb0a7b3b1c596b12f68c848e429be84c8a6039bd0a7d5bd36a7232fd7818868f1a3ae3d0462898e26 + languageName: node + linkType: hard + "@tanstack/react-query@npm:^4.2.1": version: 4.19.1 resolution: "@tanstack/react-query@npm:4.19.1" @@ -3239,6 +3263,15 @@ __metadata: languageName: node linkType: hard +"copy-anything@npm:^3.0.2": + version: 3.0.3 + resolution: "copy-anything@npm:3.0.3" + dependencies: + is-what: ^4.1.8 + checksum: d456dc5ec98dee7c7cf87d809eac30dc2ac942acd4cf970fab394e280ceb6dd7a8a7a5a44fcbcc50e0206658de3cc20b92863562f5797930bb2619f164f4c182 + languageName: node + linkType: hard + "core-js-pure@npm:^3.25.1": version: 3.26.1 resolution: "core-js-pure@npm:3.26.1" @@ -4916,6 +4949,7 @@ __metadata: "@nivo/line": ^0.79.1 "@tabler/icons": ^1.106.0 "@tanstack/react-query": ^4.2.1 + "@tanstack/react-query-devtools": ^4.24.4 "@types/dockerode": ^3.3.9 "@types/node": 17.0.1 "@types/ping": ^0.4.1 @@ -5447,6 +5481,13 @@ __metadata: languageName: node linkType: hard +"is-what@npm:^4.1.8": + version: 4.1.8 + resolution: "is-what@npm:4.1.8" + checksum: b9bec3acff102d14ad467f4c74c9886af310fa160e07a63292c8c181e6768c7c4c1054644e13d67185b963644e4a513bce8c6b8ce3d3ca6f9488a69fccad5f97 + languageName: node + linkType: hard + "isarray@npm:0.0.1": version: 0.0.1 resolution: "isarray@npm:0.0.1" @@ -7410,6 +7451,13 @@ __metadata: languageName: node linkType: hard +"remove-accents@npm:0.4.2": + version: 0.4.2 + resolution: "remove-accents@npm:0.4.2" + checksum: 84a6988555dea24115e2d1954db99509588d43fe55a1590f0b5894802776f7b488b3151c37ceb9e4f4b646f26b80b7325dcea2fae58bc3865df146e1fa606711 + languageName: node + linkType: hard + "require-directory@npm:^2.1.1": version: 2.1.1 resolution: "require-directory@npm:2.1.1" @@ -8044,6 +8092,15 @@ __metadata: languageName: node linkType: hard +"superjson@npm:^1.10.0": + version: 1.12.2 + resolution: "superjson@npm:1.12.2" + dependencies: + copy-anything: ^3.0.2 + checksum: cf7735e172811ed87476a7c2f1bb0e83725a0e3c2d7a50a71303a973060b3c710288767fb767a7a7eee8e5625d3ccaee1176a93e27f43841627512c15c4cdf84 + languageName: node + linkType: hard + "supports-color@npm:^5.3.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" From 87909c27c0c37633c29e2796222369d4f16b359a Mon Sep 17 00:00:00 2001 From: ajnart <ajnart@pm.me> Date: Tue, 31 Jan 2023 10:35:17 +0900 Subject: [PATCH 09/41] =?UTF-8?q?=F0=9F=9A=B8=20Improve=20tv=20show=20epis?= =?UTF-8?q?ode=20badge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/common/MediaDisplay.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/common/MediaDisplay.tsx b/src/modules/common/MediaDisplay.tsx index 7fea14508..61344cf34 100644 --- a/src/modules/common/MediaDisplay.tsx +++ b/src/modules/common/MediaDisplay.tsx @@ -194,7 +194,7 @@ export function MediaDisplay({ media }: { media: IMedia }) { }} > {media.type === 'tvshow' && ( - <Badge variant="dot" size="xs" radius="md" color="blue"> + <Badge variant="dot" size="xs" radius="md" color="blue" style={{ maxWidth: 200 }}> s{media.seasonNumber}e{media.episodeNumber} - {media.episodetitle} </Badge> )} From 6586cdd060e81556673c15ed4aa762c9d246bda4 Mon Sep 17 00:00:00 2001 From: ajnart <ajnart@pm.me> Date: Tue, 31 Jan 2023 11:45:52 +0900 Subject: [PATCH 10/41] =?UTF-8?q?=E2=9C=A8=20Add=20cache=20and=20stale=20t?= =?UTF-8?q?imers=20for=20most=20react=20query?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Tabs/AppereanceTab/AppereanceTab.tsx | 2 +- .../Dashboard/Tiles/Apps/AppPing.tsx | 2 +- src/components/layout/header/Header.tsx | 33 +++++++++---------- src/components/layout/header/Search.tsx | 11 ++++--- src/tools/queryClient.ts | 9 ++++- src/widgets/calendar/CalendarTile.tsx | 1 + src/widgets/weather/WeatherTile.tsx | 2 +- src/widgets/weather/useWeatherForCity.ts | 16 ++++++--- 8 files changed, 45 insertions(+), 31 deletions(-) diff --git a/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/AppereanceTab.tsx b/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/AppereanceTab.tsx index f74ab7d60..d8795d25f 100644 --- a/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/AppereanceTab.tsx +++ b/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/AppereanceTab.tsx @@ -1,4 +1,4 @@ -import { Autocomplete, createStyles, Flex, Tabs, TextInput } from '@mantine/core'; +import { Autocomplete, createStyles, Flex, Tabs } from '@mantine/core'; import { UseFormReturnType } from '@mantine/form'; import { useQuery } from '@tanstack/react-query'; import { useTranslation } from 'next-i18next'; diff --git a/src/components/Dashboard/Tiles/Apps/AppPing.tsx b/src/components/Dashboard/Tiles/Apps/AppPing.tsx index 3f2a8c925..d94cdc676 100644 --- a/src/components/Dashboard/Tiles/Apps/AppPing.tsx +++ b/src/components/Dashboard/Tiles/Apps/AppPing.tsx @@ -16,7 +16,7 @@ export const AppPing = ({ app }: AppPingProps) => { (config?.settings.customization.layout.enabledPing && app.network.enabledStatusChecker) ?? false; const { data, isLoading } = useQuery({ - queryKey: [`ping/${app.id}`], + queryKey: ['ping', { id: app.id, name: app.name }], queryFn: async () => { const response = await fetch(`/api/modules/ping?url=${encodeURI(app.url)}`); const isOk = app.network.okStatus.includes(response.status); diff --git a/src/components/layout/header/Header.tsx b/src/components/layout/header/Header.tsx index c70fa7128..4559c2d80 100644 --- a/src/components/layout/header/Header.tsx +++ b/src/components/layout/header/Header.tsx @@ -1,7 +1,6 @@ import { Box, createStyles, Group, Header as MantineHeader, Indicator } from '@mantine/core'; -import { useEffect, useState } from 'react'; +import { useQuery } from '@tanstack/react-query'; import { CURRENT_VERSION, REPO_URL } from '../../../../data/constants'; -import { useConfigContext } from '../../../config/provider'; import { Logo } from '../Logo'; import { useCardStyles } from '../useCardStyles'; import DockerMenuButton from '../../../modules/Docker/DockerModule'; @@ -14,20 +13,14 @@ export const HeaderHeight = 64; export function Header(props: any) { const { classes } = useStyles(); const { classes: cardClasses } = useCardStyles(false); - - const { config } = useConfigContext(); - - const [newVersionAvailable, setNewVersionAvailable] = useState<string>(''); - useEffect(() => { - // Fetch Data here when component first mounted - fetch(`https://api.github.com/repos/${REPO_URL}/releases/latest`).then((res) => { - res.json().then((data) => { - if (data.tag_name > CURRENT_VERSION) { - setNewVersionAvailable(data.tag_name); - } - }); - }); - }, [CURRENT_VERSION]); + const { isLoading, error, data } = useQuery({ + queryKey: ['github/latest'], + cacheTime: 1000 * 60 * 60 * 24, + staleTime: 1000 * 60 * 60 * 5, + queryFn: () => + fetch(`https://api.github.com/repos/${REPO_URL}/releases/latest`).then((res) => res.json()), + }); + const newVersionAvailable = data?.tag_name !== CURRENT_VERSION ? data?.tag_name : undefined; return ( <MantineHeader height={HeaderHeight} className={cardClasses.card}> @@ -39,7 +32,13 @@ export function Header(props: any) { <Search /> <ToggleEditModeAction /> <DockerMenuButton /> - <Indicator size={15} color="blue" withBorder processing disabled={!newVersionAvailable}> + <Indicator + size={15} + color="blue" + withBorder + processing + disabled={newVersionAvailable === undefined} + > <SettingsMenu newVersionAvailable={newVersionAvailable} /> </Indicator> </Group> diff --git a/src/components/layout/header/Search.tsx b/src/components/layout/header/Search.tsx index ba8d799d0..add4cb184 100644 --- a/src/components/layout/header/Search.tsx +++ b/src/components/layout/header/Search.tsx @@ -148,13 +148,14 @@ export function Search() { } = useQuery( ['overseerr', debounced], async () => { - if (debounced !== '' && selectedSearchEngine.value === 'overseerr' && debounced.length > 3) { - const res = await axios.get(`/api/modules/overseerr?query=${debounced}`); - return res.data.results ?? []; - } - return []; + const res = await axios.get(`/api/modules/overseerr?query=${debounced}`); + return res.data.results ?? []; }, { + enabled: + isOverseerrEnabled === true && + selectedSearchEngine.value === 'overseerr' && + debounced.length > 3, refetchOnWindowFocus: false, refetchOnMount: false, refetchInterval: false, diff --git a/src/tools/queryClient.ts b/src/tools/queryClient.ts index 6d46de591..b4d80818c 100644 --- a/src/tools/queryClient.ts +++ b/src/tools/queryClient.ts @@ -1,3 +1,10 @@ import { QueryClient } from '@tanstack/react-query'; -export const queryClient = new QueryClient(); +export const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 5 * 60 * 1000, // 5 mins + cacheTime: 10 * 60 * 1000, // 10 mins + }, + }, +}); diff --git a/src/widgets/calendar/CalendarTile.tsx b/src/widgets/calendar/CalendarTile.tsx index 7518a038b..6398dd3f7 100644 --- a/src/widgets/calendar/CalendarTile.tsx +++ b/src/widgets/calendar/CalendarTile.tsx @@ -53,6 +53,7 @@ function CalendarTile({ widget }: CalendarTileProps) { const { data: medias } = useQuery({ queryKey: ['calendar/medias', { month: month.getMonth(), year: month.getFullYear() }], + staleTime: 1000 * 60 * 60 * 5, queryFn: async () => (await ( await fetch( diff --git a/src/widgets/weather/WeatherTile.tsx b/src/widgets/weather/WeatherTile.tsx index ad8efde4d..ba7b9eff7 100644 --- a/src/widgets/weather/WeatherTile.tsx +++ b/src/widgets/weather/WeatherTile.tsx @@ -76,7 +76,7 @@ function WeatherTile({ widget }: WeatherTileProps) { align="center" style={{ height: '100%', width: '100%' }} > - <Group align={'center'} position="center" spacing="xs"> + <Group align="center" position="center" spacing="xs"> <WeatherIcon code={weather!.current_weather.weathercode} /> <Title> {getPerferedUnit( diff --git a/src/widgets/weather/useWeatherForCity.ts b/src/widgets/weather/useWeatherForCity.ts index a785c92eb..62312f017 100644 --- a/src/widgets/weather/useWeatherForCity.ts +++ b/src/widgets/weather/useWeatherForCity.ts @@ -11,12 +11,18 @@ export const useWeatherForCity = (cityName: string) => { data: city, isLoading, isError, - } = useQuery({ queryKey: ['weatherCity', { cityName }], queryFn: () => fetchCity(cityName) }); + } = useQuery({ + queryKey: ['weatherCity', { cityName }], + queryFn: () => fetchCity(cityName), + cacheTime: 1000 * 60 * 60 * 24, // the city is cached for 24 hours + staleTime: Infinity, // the city is never considered stale + }); const weatherQuery = useQuery({ queryKey: ['weather', { cityName }], queryFn: () => fetchWeather(city?.results[0]), enabled: !!city, - refetchInterval: 1000 * 60 * 5, // requests the weather every 5 minutes + cacheTime: 1000 * 60 * 60 * 6, // the weather is cached for 6 hours + staleTime: 1000 * 60 * 5, // the weather is considered stale after 5 minutes }); return { @@ -41,13 +47,13 @@ const fetchCity = async (cityName: string) => { * @param coordinates of the location the weather should be fetched * @returns weather of specified coordinates */ -const fetchWeather = async (coordinates?: Coordinates) => { - if (!coordinates) return; +async function fetchWeather(coordinates?: Coordinates) { + if (!coordinates) return null; const { longitude, latitude } = coordinates; const res = await fetch( `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&daily=weathercode,temperature_2m_max,temperature_2m_min¤t_weather=true&timezone=Europe%2FLondon` ); return (await res.json()) as WeatherResponse; -}; +} type Coordinates = { latitude: number; longitude: number }; From dfa623d98fcd00cc4e1397780c2396521334ec5e Mon Sep 17 00:00:00 2001 From: ajnart <ajnart@pm.me> Date: Tue, 31 Jan 2023 12:53:32 +0900 Subject: [PATCH 11/41] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Upgrade=20to=20Next?= =?UTF-8?q?=20v13.1.6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- src/widgets/calendar/CalendarTile.tsx | 3 +- yarn.lock | 163 +++++++++++++++----------- 3 files changed, 96 insertions(+), 72 deletions(-) diff --git a/package.json b/package.json index adc842f74..05047d4d4 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "i18next-browser-languagedetector": "^6.1.5", "i18next-http-backend": "^1.4.1", "js-file-download": "^0.4.12", - "next": "^13.1.4", + "next": "^13.1.6", "next-i18next": "^11.3.0", "nzbget-api": "^0.0.3", "ping": "^0.4.2", diff --git a/src/widgets/calendar/CalendarTile.tsx b/src/widgets/calendar/CalendarTile.tsx index 4c904a90f..fab26359a 100644 --- a/src/widgets/calendar/CalendarTile.tsx +++ b/src/widgets/calendar/CalendarTile.tsx @@ -2,6 +2,7 @@ import { createStyles, Group, MantineThemeColors, useMantineTheme } from '@manti import { Calendar } from '@mantine/dates'; import { IconCalendarTime } from '@tabler/icons'; import { useQuery } from '@tanstack/react-query'; +import { i18n } from 'next-i18next'; import { useState } from 'react'; import { useConfigContext } from '../../config/provider'; import { useColorTheme } from '../../tools/color'; @@ -73,7 +74,7 @@ function CalendarTile({ widget }: CalendarTileProps) { style={{ position: 'relative', top: -15 }} onMonthChange={setMonth} size="xs" - locale={i18n.resolvedLanguage} + locale={i18n?.resolvedLanguage ?? 'en'} fullWidth onChange={() => {}} firstDayOfWeek={widget.properties.sundayStart ? 'sunday' : 'monday'} diff --git a/yarn.lock b/yarn.lock index 72a52cb27..629a864cf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1114,20 +1114,20 @@ __metadata: languageName: node linkType: hard -"@mantine/core@npm:^5.9.3": - version: 5.10.1 - resolution: "@mantine/core@npm:5.10.1" +"@mantine/core@npm:^5.10.1": + version: 5.10.2 + resolution: "@mantine/core@npm:5.10.2" dependencies: "@floating-ui/react-dom-interactions": ^0.10.1 - "@mantine/styles": 5.10.1 - "@mantine/utils": 5.10.1 + "@mantine/styles": 5.10.2 + "@mantine/utils": 5.10.2 "@radix-ui/react-scroll-area": 1.0.2 react-textarea-autosize: 8.3.4 peerDependencies: - "@mantine/hooks": 5.10.1 + "@mantine/hooks": 5.10.2 react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 8b4ecd6951e6c0c618064ca0ca130c81835887ec69a4534a8315aa5fd9c2c1d7e9bbda44ba5942d47c0c65490f1edd2436320dc37f24261844705db68ef7e6a1 + checksum: 25b385a37106119283a11d79e26578c2e6b7625b2e26bf121926aeaaaa6b74e96c24354c12ead3604881db97be3c7dea9b121b3cb4df673cc08d6e1cd435ee2a languageName: node linkType: hard @@ -1268,6 +1268,20 @@ __metadata: languageName: node linkType: hard +"@mantine/styles@npm:5.10.2": + version: 5.10.2 + resolution: "@mantine/styles@npm:5.10.2" + dependencies: + clsx: 1.1.1 + csstype: 3.0.9 + peerDependencies: + "@emotion/react": ">=11.9.0" + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: c7c6b785399b9414bf3933673266bbff205884639639d2e2e44ad0152af4d7e40e82c10d27043aac80d426144fc73876b877146218de8a970e2f729b4fa66276 + languageName: node + linkType: hard + "@mantine/utils@npm:5.10.1": version: 5.10.1 resolution: "@mantine/utils@npm:5.10.1" @@ -1277,6 +1291,15 @@ __metadata: languageName: node linkType: hard +"@mantine/utils@npm:5.10.2": + version: 5.10.2 + resolution: "@mantine/utils@npm:5.10.2" + peerDependencies: + react: ">=16.8.0" + checksum: 6d1acdcd58020c26b646adfacd8082a03d17348ab921d741c6dae48312264bfd03afe9b91cb827c48cbc2f0480054a7b3aae808ec39240d0e4067b413cf19ea8 + languageName: node + linkType: hard + "@mantine/utils@npm:5.9.3": version: 5.9.3 resolution: "@mantine/utils@npm:5.9.3" @@ -1360,10 +1383,10 @@ __metadata: languageName: node linkType: hard -"@next/env@npm:13.1.4": - version: 13.1.4 - resolution: "@next/env@npm:13.1.4" - checksum: 68e222462d35959f183aa17829e3b9045830d0a1030a64bc40fe556bde70584cab7f4bde3bf53abbdbf110fefb2c00a0c573180a50536676919016cded945e09 +"@next/env@npm:13.1.6": + version: 13.1.6 + resolution: "@next/env@npm:13.1.6" + checksum: 0f911a18f0b3372007632fffa87f5d7f802c00d07b3bf757d2d09574735ae43f60000ecdf64b6f06e195971c508c2bcee82dd1e3aab27a08a4300eb0317652bb languageName: node linkType: hard @@ -1376,93 +1399,93 @@ __metadata: languageName: node linkType: hard -"@next/swc-android-arm-eabi@npm:13.1.4": - version: 13.1.4 - resolution: "@next/swc-android-arm-eabi@npm:13.1.4" +"@next/swc-android-arm-eabi@npm:13.1.6": + version: 13.1.6 + resolution: "@next/swc-android-arm-eabi@npm:13.1.6" conditions: os=android & cpu=arm languageName: node linkType: hard -"@next/swc-android-arm64@npm:13.1.4": - version: 13.1.4 - resolution: "@next/swc-android-arm64@npm:13.1.4" +"@next/swc-android-arm64@npm:13.1.6": + version: 13.1.6 + resolution: "@next/swc-android-arm64@npm:13.1.6" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@next/swc-darwin-arm64@npm:13.1.4": - version: 13.1.4 - resolution: "@next/swc-darwin-arm64@npm:13.1.4" +"@next/swc-darwin-arm64@npm:13.1.6": + version: 13.1.6 + resolution: "@next/swc-darwin-arm64@npm:13.1.6" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@next/swc-darwin-x64@npm:13.1.4": - version: 13.1.4 - resolution: "@next/swc-darwin-x64@npm:13.1.4" +"@next/swc-darwin-x64@npm:13.1.6": + version: 13.1.6 + resolution: "@next/swc-darwin-x64@npm:13.1.6" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@next/swc-freebsd-x64@npm:13.1.4": - version: 13.1.4 - resolution: "@next/swc-freebsd-x64@npm:13.1.4" +"@next/swc-freebsd-x64@npm:13.1.6": + version: 13.1.6 + resolution: "@next/swc-freebsd-x64@npm:13.1.6" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@next/swc-linux-arm-gnueabihf@npm:13.1.4": - version: 13.1.4 - resolution: "@next/swc-linux-arm-gnueabihf@npm:13.1.4" +"@next/swc-linux-arm-gnueabihf@npm:13.1.6": + version: 13.1.6 + resolution: "@next/swc-linux-arm-gnueabihf@npm:13.1.6" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@next/swc-linux-arm64-gnu@npm:13.1.4": - version: 13.1.4 - resolution: "@next/swc-linux-arm64-gnu@npm:13.1.4" +"@next/swc-linux-arm64-gnu@npm:13.1.6": + version: 13.1.6 + resolution: "@next/swc-linux-arm64-gnu@npm:13.1.6" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@next/swc-linux-arm64-musl@npm:13.1.4": - version: 13.1.4 - resolution: "@next/swc-linux-arm64-musl@npm:13.1.4" +"@next/swc-linux-arm64-musl@npm:13.1.6": + version: 13.1.6 + resolution: "@next/swc-linux-arm64-musl@npm:13.1.6" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@next/swc-linux-x64-gnu@npm:13.1.4": - version: 13.1.4 - resolution: "@next/swc-linux-x64-gnu@npm:13.1.4" +"@next/swc-linux-x64-gnu@npm:13.1.6": + version: 13.1.6 + resolution: "@next/swc-linux-x64-gnu@npm:13.1.6" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@next/swc-linux-x64-musl@npm:13.1.4": - version: 13.1.4 - resolution: "@next/swc-linux-x64-musl@npm:13.1.4" +"@next/swc-linux-x64-musl@npm:13.1.6": + version: 13.1.6 + resolution: "@next/swc-linux-x64-musl@npm:13.1.6" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@next/swc-win32-arm64-msvc@npm:13.1.4": - version: 13.1.4 - resolution: "@next/swc-win32-arm64-msvc@npm:13.1.4" +"@next/swc-win32-arm64-msvc@npm:13.1.6": + version: 13.1.6 + resolution: "@next/swc-win32-arm64-msvc@npm:13.1.6" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@next/swc-win32-ia32-msvc@npm:13.1.4": - version: 13.1.4 - resolution: "@next/swc-win32-ia32-msvc@npm:13.1.4" +"@next/swc-win32-ia32-msvc@npm:13.1.6": + version: 13.1.6 + resolution: "@next/swc-win32-ia32-msvc@npm:13.1.6" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@next/swc-win32-x64-msvc@npm:13.1.4": - version: 13.1.4 - resolution: "@next/swc-win32-x64-msvc@npm:13.1.4" +"@next/swc-win32-x64-msvc@npm:13.1.6": + version: 13.1.6 + resolution: "@next/swc-win32-x64-msvc@npm:13.1.6" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -4909,7 +4932,7 @@ __metadata: "@dnd-kit/utilities": ^3.2.0 "@emotion/react": ^11.10.5 "@emotion/server": ^11.10.0 - "@mantine/core": ^5.9.3 + "@mantine/core": ^5.10.1 "@mantine/dates": ^5.10.1 "@mantine/dropzone": ^5.10.1 "@mantine/form": ^5.10.1 @@ -4955,7 +4978,7 @@ __metadata: i18next-http-backend: ^1.4.1 jest: ^28.1.3 js-file-download: ^0.4.12 - next: ^13.1.4 + next: ^13.1.6 next-i18next: ^11.3.0 nzbget-api: ^0.0.3 ping: ^0.4.2 @@ -6546,24 +6569,24 @@ __metadata: languageName: node linkType: hard -"next@npm:^13.1.4": - version: 13.1.4 - resolution: "next@npm:13.1.4" +"next@npm:^13.1.6": + version: 13.1.6 + resolution: "next@npm:13.1.6" dependencies: - "@next/env": 13.1.4 - "@next/swc-android-arm-eabi": 13.1.4 - "@next/swc-android-arm64": 13.1.4 - "@next/swc-darwin-arm64": 13.1.4 - "@next/swc-darwin-x64": 13.1.4 - "@next/swc-freebsd-x64": 13.1.4 - "@next/swc-linux-arm-gnueabihf": 13.1.4 - "@next/swc-linux-arm64-gnu": 13.1.4 - "@next/swc-linux-arm64-musl": 13.1.4 - "@next/swc-linux-x64-gnu": 13.1.4 - "@next/swc-linux-x64-musl": 13.1.4 - "@next/swc-win32-arm64-msvc": 13.1.4 - "@next/swc-win32-ia32-msvc": 13.1.4 - "@next/swc-win32-x64-msvc": 13.1.4 + "@next/env": 13.1.6 + "@next/swc-android-arm-eabi": 13.1.6 + "@next/swc-android-arm64": 13.1.6 + "@next/swc-darwin-arm64": 13.1.6 + "@next/swc-darwin-x64": 13.1.6 + "@next/swc-freebsd-x64": 13.1.6 + "@next/swc-linux-arm-gnueabihf": 13.1.6 + "@next/swc-linux-arm64-gnu": 13.1.6 + "@next/swc-linux-arm64-musl": 13.1.6 + "@next/swc-linux-x64-gnu": 13.1.6 + "@next/swc-linux-x64-musl": 13.1.6 + "@next/swc-win32-arm64-msvc": 13.1.6 + "@next/swc-win32-ia32-msvc": 13.1.6 + "@next/swc-win32-x64-msvc": 13.1.6 "@swc/helpers": 0.4.14 caniuse-lite: ^1.0.30001406 postcss: 8.4.14 @@ -6610,7 +6633,7 @@ __metadata: optional: true bin: next: dist/bin/next - checksum: cc399e2245c5bf27ac2101672e8ce591d8258bce777de788b812f3e5d346f99349c74be8567b7340270c314bd1522e90de9a36187f9b74dc759aa114a3fb5036 + checksum: 584977e382bd826c21e7fc5f67bca50e4d95741a854b1686394d45331404479c7266569671227421975fc18e5cf70769a4ad7edede7450d4497213205bba77c8 languageName: node linkType: hard From 2b76ae83b11b9907bd0339a78c15f00394e262f8 Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Mon, 30 Jan 2023 21:17:46 +0100 Subject: [PATCH 12/41] =?UTF-8?q?=F0=9F=92=84=20Fix=20overflowing=20text?= =?UTF-8?q?=20wrap=20in=20header=20for=20page=20title=20#606?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout/header/Header.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/layout/header/Header.tsx b/src/components/layout/header/Header.tsx index c70fa7128..45f432f34 100644 --- a/src/components/layout/header/Header.tsx +++ b/src/components/layout/header/Header.tsx @@ -30,7 +30,7 @@ export function Header(props: any) { }, [CURRENT_VERSION]); return ( - <MantineHeader height={HeaderHeight} className={cardClasses.card}> + <MantineHeader height="auto" className={cardClasses.card}> <Group p="xs" noWrap grow> <Box className={classes.hide}> <Logo /> From e20149a93c83ae4bd93a5d773b59b8384b79a8ea Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Tue, 31 Jan 2023 18:06:49 +0100 Subject: [PATCH 13/41] =?UTF-8?q?=F0=9F=92=84=20Fix=20overflow=20in=20abou?= =?UTF-8?q?t=20modal=20and=20docker=20drawer=20#674?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modals/AboutModal}/AboutModal.tsx | 79 +++++++++++-------- src/components/layout/header/SettingsMenu.tsx | 2 +- src/modules/Docker/DockerModule.tsx | 9 +++ src/modules/Docker/DockerTable.tsx | 2 +- 4 files changed, 56 insertions(+), 36 deletions(-) rename src/components/{About => Dashboard/Modals/AboutModal}/AboutModal.tsx (80%) diff --git a/src/components/About/AboutModal.tsx b/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx similarity index 80% rename from src/components/About/AboutModal.tsx rename to src/components/Dashboard/Modals/AboutModal/AboutModal.tsx index 8d258ebd3..6ec6b6536 100644 --- a/src/components/About/AboutModal.tsx +++ b/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx @@ -5,6 +5,7 @@ import { Button, createStyles, Divider, + Grid, Group, HoverCard, Modal, @@ -27,11 +28,11 @@ import { InitOptions } from 'i18next'; import { i18n, Trans, useTranslation } from 'next-i18next'; import Image from 'next/image'; import { ReactNode } from 'react'; -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'; +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; @@ -85,35 +86,45 @@ export const AboutModal = ({ opened, closeModal, newVersionAvailable }: AboutMod {t('layout/modals/about:contact')} - - - - - + + + + + + + + + + + + ); diff --git a/src/components/layout/header/SettingsMenu.tsx b/src/components/layout/header/SettingsMenu.tsx index c525d1afd..e02fe5571 100644 --- a/src/components/layout/header/SettingsMenu.tsx +++ b/src/components/layout/header/SettingsMenu.tsx @@ -2,7 +2,7 @@ import { Badge, Button, Menu } from '@mantine/core'; import { useDisclosure } from '@mantine/hooks'; import { IconInfoCircle, IconMenu2, IconSettings } from '@tabler/icons'; import { useTranslation } from 'next-i18next'; -import { AboutModal } from '../../About/AboutModal'; +import { AboutModal } from '../../Dashboard/Modals/AboutModal/AboutModal'; import { SettingsDrawer } from '../../Settings/SettingsDrawer'; import { useCardStyles } from '../useCardStyles'; import { ColorSchemeSwitch } from './SettingsMenu/ColorSchemeSwitch'; diff --git a/src/modules/Docker/DockerModule.tsx b/src/modules/Docker/DockerModule.tsx index 684178dd9..91de2247b 100644 --- a/src/modules/Docker/DockerModule.tsx +++ b/src/modules/Docker/DockerModule.tsx @@ -68,6 +68,15 @@ export default function DockerMenuButton(props: any) { position="right" size="full" title={} + styles={{ + drawer: { + display: 'flex', + flexDirection: 'column', + }, + body: { + minHeight: 0, + }, + }} > diff --git a/src/modules/Docker/DockerTable.tsx b/src/modules/Docker/DockerTable.tsx index 70f597d23..b2ba3c3c2 100644 --- a/src/modules/Docker/DockerTable.tsx +++ b/src/modules/Docker/DockerTable.tsx @@ -120,7 +120,7 @@ export default function DockerTable({ }); return ( - + Date: Tue, 31 Jan 2023 21:17:37 +0100 Subject: [PATCH 14/41] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Add=20version=20read?= =?UTF-8?q?ing=20by=20package=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/constants.ts | 1 - src/components/About/AboutModal.tsx | 17 ++++++++++++++--- .../Tabs/AppereanceTab/AppereanceTab.tsx | 2 +- .../Wrappers/Category/useCategoryActions.tsx | 1 - src/components/layout/header/Header.tsx | 13 ++++++------- src/pages/[slug].tsx | 4 ++-- src/pages/_app.tsx | 19 +++++++++++++++++-- src/pages/index.tsx | 15 +++++++++------ src/pages/login.tsx | 2 +- src/tools/addToHomarr.ts | 11 +---------- src/tools/{ => client}/calculateEta.ts | 0 src/tools/{ => client}/parseDuration.ts | 0 .../zustands/usePackageAttributesStore.ts | 15 +++++++++++++++ src/tools/server/getPackageVersion.ts | 14 ++++++++++++++ .../{ => server}/getServerSideTranslations.ts | 0 .../{ => server}/translation-namespaces.ts | 0 src/types/dashboardPageType.ts | 1 + src/widgets/torrent/TorrentQueueItem.tsx | 2 +- src/widgets/useNet/UsenetHistoryList.tsx | 2 +- 19 files changed, 83 insertions(+), 36 deletions(-) rename src/tools/{ => client}/calculateEta.ts (100%) rename src/tools/{ => client}/parseDuration.ts (100%) create mode 100644 src/tools/client/zustands/usePackageAttributesStore.ts create mode 100644 src/tools/server/getPackageVersion.ts rename src/tools/{ => server}/getServerSideTranslations.ts (100%) rename src/tools/{ => server}/translation-namespaces.ts (100%) diff --git a/data/constants.ts b/data/constants.ts index b1ff95a49..b10f63d7e 100644 --- a/data/constants.ts +++ b/data/constants.ts @@ -1,3 +1,2 @@ export const REPO_URL = 'ajnart/homarr'; -export const CURRENT_VERSION = 'v0.11.3'; export const ICON_PICKER_SLICE_LIMIT = 36; diff --git a/src/components/About/AboutModal.tsx b/src/components/About/AboutModal.tsx index 8d258ebd3..56b6954a8 100644 --- a/src/components/About/AboutModal.tsx +++ b/src/components/About/AboutModal.tsx @@ -13,6 +13,7 @@ import { Title, } from '@mantine/core'; import { + IconAnchor, IconBrandDiscord, IconBrandGithub, IconFile, @@ -27,9 +28,9 @@ import { InitOptions } from 'i18next'; import { i18n, Trans, useTranslation } from 'next-i18next'; import Image from 'next/image'; import { ReactNode } from 'react'; -import { CURRENT_VERSION } from '../../../data/constants'; import { useConfigContext } from '../../config/provider'; import { useConfigStore } from '../../config/store'; +import { usePackageAttributesStore } from '../../tools/client/zustands/usePackageAttributesStore'; import { usePrimaryGradient } from '../layout/useGradient'; import Credits from '../Settings/Common/Credits'; @@ -132,6 +133,7 @@ interface ExtendedInitOptions extends InitOptions { const useInformationTableItems = (newVersionAvailable?: string): InformationTableItem[] => { // TODO: Fix this to not request. Pass it as a prop. const colorGradiant = usePrimaryGradient(); + const { attributes } = usePackageAttributesStore(); const { configVersion } = useConfigContext(); const { configs } = useConfigStore(); @@ -190,7 +192,7 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl content: ( - {CURRENT_VERSION} + {attributes.packageVersion ?? 'Unknown'} {newVersionAvailable && ( @@ -218,13 +220,22 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl {newVersionAvailable} {' '} - is available ! Current version: {CURRENT_VERSION} + is available ! Current version: {attributes.packageVersion} )} ), }, + { + icon: , + label: 'Node environment', + content: ( + + {attributes.environment} + + ), + }, ...items, ]; diff --git a/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/AppereanceTab.tsx b/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/AppereanceTab.tsx index f74ab7d60..d8795d25f 100644 --- a/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/AppereanceTab.tsx +++ b/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/AppereanceTab.tsx @@ -1,4 +1,4 @@ -import { Autocomplete, createStyles, Flex, Tabs, TextInput } from '@mantine/core'; +import { Autocomplete, createStyles, Flex, Tabs } from '@mantine/core'; import { UseFormReturnType } from '@mantine/form'; import { useQuery } from '@tanstack/react-query'; import { useTranslation } from 'next-i18next'; diff --git a/src/components/Dashboard/Wrappers/Category/useCategoryActions.tsx b/src/components/Dashboard/Wrappers/Category/useCategoryActions.tsx index 7225f3b5d..6be7e99a4 100644 --- a/src/components/Dashboard/Wrappers/Category/useCategoryActions.tsx +++ b/src/components/Dashboard/Wrappers/Category/useCategoryActions.tsx @@ -3,7 +3,6 @@ import { useConfigStore } from '../../../../config/store'; import { openContextModalGeneric } from '../../../../tools/mantineModalManagerExtensions'; import { AppType } from '../../../../types/app'; import { CategoryType } from '../../../../types/category'; -import { ConfigType } from '../../../../types/config'; import { WrapperType } from '../../../../types/wrapper'; import { IWidget } from '../../../../widgets/widgets'; import { CategoryEditModalInnerProps } from './CategoryEditModal'; diff --git a/src/components/layout/header/Header.tsx b/src/components/layout/header/Header.tsx index 45f432f34..ec7a9f33d 100644 --- a/src/components/layout/header/Header.tsx +++ b/src/components/layout/header/Header.tsx @@ -1,10 +1,10 @@ import { Box, createStyles, Group, Header as MantineHeader, Indicator } from '@mantine/core'; import { useEffect, useState } from 'react'; -import { CURRENT_VERSION, REPO_URL } from '../../../../data/constants'; -import { useConfigContext } from '../../../config/provider'; +import { REPO_URL } from '../../../../data/constants'; +import DockerMenuButton from '../../../modules/Docker/DockerModule'; +import { usePackageAttributesStore } from '../../../tools/client/zustands/usePackageAttributesStore'; import { Logo } from '../Logo'; import { useCardStyles } from '../useCardStyles'; -import DockerMenuButton from '../../../modules/Docker/DockerModule'; import { ToggleEditModeAction } from './Actions/ToggleEditMode/ToggleEditMode'; import { Search } from './Search'; import { SettingsMenu } from './SettingsMenu'; @@ -14,20 +14,19 @@ export const HeaderHeight = 64; export function Header(props: any) { const { classes } = useStyles(); const { classes: cardClasses } = useCardStyles(false); - - const { config } = useConfigContext(); + const { attributes } = usePackageAttributesStore(); const [newVersionAvailable, setNewVersionAvailable] = useState(''); useEffect(() => { // Fetch Data here when component first mounted fetch(`https://api.github.com/repos/${REPO_URL}/releases/latest`).then((res) => { res.json().then((data) => { - if (data.tag_name > CURRENT_VERSION) { + if (data.tag_name > `v${attributes.packageVersion}`) { setNewVersionAvailable(data.tag_name); } }); }); - }, [CURRENT_VERSION]); + }, []); return ( diff --git a/src/pages/[slug].tsx b/src/pages/[slug].tsx index 5a0876847..c3ac1b272 100644 --- a/src/pages/[slug].tsx +++ b/src/pages/[slug].tsx @@ -8,8 +8,8 @@ import Layout from '../components/layout/Layout'; import { useInitConfig } from '../config/init'; import { getFallbackConfig } from '../tools/config/getFallbackConfig'; import { getFrontendConfig } from '../tools/config/getFrontendConfig'; -import { getServerSideTranslations } from '../tools/getServerSideTranslations'; -import { dashboardNamespaces } from '../tools/translation-namespaces'; +import { getServerSideTranslations } from '../tools/server/getServerSideTranslations'; +import { dashboardNamespaces } from '../tools/server/translation-namespaces'; import { ConfigType } from '../types/config'; import { DashboardServerSideProps } from '../types/dashboardPageType'; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 088141e6c..b372436a0 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -8,7 +8,7 @@ import { GetServerSidePropsContext } from 'next'; import { appWithTranslation } from 'next-i18next'; import { AppProps } from 'next/app'; import Head from 'next/head'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { ChangeAppPositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeAppPositionModal'; import { ChangeWidgetPositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal'; @@ -22,8 +22,16 @@ import '../styles/global.scss'; import { ColorTheme } from '../tools/color'; import { queryClient } from '../tools/queryClient'; import { theme } from '../tools/theme'; +import { + getServiceSidePackageAttributes, + ServerSidePackageAttributesType, +} from '../tools/server/getPackageVersion'; +import { usePackageAttributesStore } from '../tools/client/zustands/usePackageAttributesStore'; -function App(this: any, props: AppProps & { colorScheme: ColorScheme }) { +function App( + this: any, + props: AppProps & { colorScheme: ColorScheme; packageAttributes: ServerSidePackageAttributesType } +) { const { Component, pageProps } = props; const [primaryColor, setPrimaryColor] = useState('red'); const [secondaryColor, setSecondaryColor] = useState('orange'); @@ -46,6 +54,12 @@ function App(this: any, props: AppProps & { colorScheme: ColorScheme }) { getInitialValueInEffect: true, }); + const { setInitialPackageAttributes } = usePackageAttributesStore(); + + useEffect(() => { + setInitialPackageAttributes(props.packageAttributes); + }, []); + const toggleColorScheme = (value?: ColorScheme) => setColorScheme(value || (colorScheme === 'dark' ? 'light' : 'dark')); @@ -111,6 +125,7 @@ function App(this: any, props: AppProps & { colorScheme: ColorScheme }) { App.getInitialProps = ({ ctx }: { ctx: GetServerSidePropsContext }) => ({ colorScheme: getCookie('color-scheme', ctx) || 'light', + packageAttributes: getServiceSidePackageAttributes(), }); export default appWithTranslation(App); diff --git a/src/pages/index.tsx b/src/pages/index.tsx index dd416158b..5b1f60d8a 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,15 +1,15 @@ import { getCookie, setCookie } from 'cookies-next'; +import fs from 'fs'; import { GetServerSidePropsContext } from 'next'; -import fs from 'fs'; +import { LoadConfigComponent } from '../components/Config/LoadConfig'; import { Dashboard } from '../components/Dashboard/Dashboard'; import Layout from '../components/layout/Layout'; import { useInitConfig } from '../config/init'; import { getFrontendConfig } from '../tools/config/getFrontendConfig'; -import { getServerSideTranslations } from '../tools/getServerSideTranslations'; -import { dashboardNamespaces } from '../tools/translation-namespaces'; +import { getServerSideTranslations } from '../tools/server/getServerSideTranslations'; +import { dashboardNamespaces } from '../tools/server/translation-namespaces'; import { DashboardServerSideProps } from '../types/dashboardPageType'; -import { LoadConfigComponent } from '../components/Config/LoadConfig'; export async function getServerSideProps({ req, @@ -47,11 +47,14 @@ export async function getServerSideProps({ } const translations = await getServerSideTranslations(req, res, dashboardNamespaces, locale); - const config = getFrontendConfig(configName as string); return { - props: { configName: configName as string, config, ...translations }, + props: { + configName: configName as string, + config, + ...translations, + }, }; } diff --git a/src/pages/login.tsx b/src/pages/login.tsx index 7587bbde0..bf414ed83 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -8,7 +8,7 @@ import { useRouter } from 'next/router'; import { useTranslation } from 'next-i18next'; import { useForm } from '@mantine/form'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; -import { loginNamespaces } from '../tools/translation-namespaces'; +import { loginNamespaces } from '../tools/server/translation-namespaces'; // TODO: Add links to the wiki articles about the login process. export default function AuthenticationTitle() { diff --git a/src/tools/addToHomarr.ts b/src/tools/addToHomarr.ts index 5f97bf49d..bb340ba4e 100644 --- a/src/tools/addToHomarr.ts +++ b/src/tools/addToHomarr.ts @@ -1,14 +1,5 @@ import Dockerode from 'dockerode'; -import { Config, MatchingImages, ServiceType, tryMatchPort } from './types'; - -async function MatchIcon(name: string) { - const res = await fetch( - `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${name - .replace(/\s+/g, '-') - .toLowerCase()}.png` - ); - return res.ok ? res.url : '/imgs/favicon/favicon.png'; -} +import { MatchingImages, ServiceType, tryMatchPort } from './types'; function tryMatchType(imageName: string): ServiceType { // Try to find imageName inside MatchingImages diff --git a/src/tools/calculateEta.ts b/src/tools/client/calculateEta.ts similarity index 100% rename from src/tools/calculateEta.ts rename to src/tools/client/calculateEta.ts diff --git a/src/tools/parseDuration.ts b/src/tools/client/parseDuration.ts similarity index 100% rename from src/tools/parseDuration.ts rename to src/tools/client/parseDuration.ts diff --git a/src/tools/client/zustands/usePackageAttributesStore.ts b/src/tools/client/zustands/usePackageAttributesStore.ts new file mode 100644 index 000000000..6d710997d --- /dev/null +++ b/src/tools/client/zustands/usePackageAttributesStore.ts @@ -0,0 +1,15 @@ +import create from 'zustand'; + +import { ServerSidePackageAttributesType } from '../../server/getPackageVersion'; + +interface PackageAttributesState { + attributes: ServerSidePackageAttributesType; + setInitialPackageAttributes: (attributes: ServerSidePackageAttributesType) => void; +} + +export const usePackageAttributesStore = create((set) => ({ + attributes: { packageVersion: undefined, environment: 'test' }, + setInitialPackageAttributes(attributes) { + set((state) => ({ ...state, attributes })); + }, +})); diff --git a/src/tools/server/getPackageVersion.ts b/src/tools/server/getPackageVersion.ts new file mode 100644 index 000000000..0c74d56a7 --- /dev/null +++ b/src/tools/server/getPackageVersion.ts @@ -0,0 +1,14 @@ +const getServerPackageVersion = (): string | undefined => process.env.npm_package_version; + +const getServerNodeEnvironment = (): 'development' | 'production' | 'test' => + process.env.NODE_ENV; + +export const getServiceSidePackageAttributes = (): ServerSidePackageAttributesType => ({ + packageVersion: getServerPackageVersion(), + environment: getServerNodeEnvironment(), +}); + +export type ServerSidePackageAttributesType = { + packageVersion: string | undefined; + environment: 'development' | 'production' | 'test'; +}; diff --git a/src/tools/getServerSideTranslations.ts b/src/tools/server/getServerSideTranslations.ts similarity index 100% rename from src/tools/getServerSideTranslations.ts rename to src/tools/server/getServerSideTranslations.ts diff --git a/src/tools/translation-namespaces.ts b/src/tools/server/translation-namespaces.ts similarity index 100% rename from src/tools/translation-namespaces.ts rename to src/tools/server/translation-namespaces.ts diff --git a/src/types/dashboardPageType.ts b/src/types/dashboardPageType.ts index 416c3b1bf..f361e449f 100644 --- a/src/types/dashboardPageType.ts +++ b/src/types/dashboardPageType.ts @@ -1,4 +1,5 @@ import { SSRConfig } from 'next-i18next'; + import { ConfigType } from './config'; export type DashboardServerSideProps = { diff --git a/src/widgets/torrent/TorrentQueueItem.tsx b/src/widgets/torrent/TorrentQueueItem.tsx index 6ec0c1a71..6d7afd891 100644 --- a/src/widgets/torrent/TorrentQueueItem.tsx +++ b/src/widgets/torrent/TorrentQueueItem.tsx @@ -24,7 +24,7 @@ import { IconUpload, } from '@tabler/icons'; import { useTranslation } from 'next-i18next'; -import { calculateETA } from '../../tools/calculateEta'; +import { calculateETA } from '../../tools/client/calculateEta'; import { humanFileSize } from '../../tools/humanFileSize'; import { AppType } from '../../types/app'; diff --git a/src/widgets/useNet/UsenetHistoryList.tsx b/src/widgets/useNet/UsenetHistoryList.tsx index 28ee10ccf..2578fdec6 100644 --- a/src/widgets/useNet/UsenetHistoryList.tsx +++ b/src/widgets/useNet/UsenetHistoryList.tsx @@ -20,7 +20,7 @@ import { useTranslation } from 'next-i18next'; import { FunctionComponent, useState } from 'react'; import { useGetUsenetHistory } from '../../hooks/widgets/dashDot/api'; import { humanFileSize } from '../../tools/humanFileSize'; -import { parseDuration } from '../../tools/parseDuration'; +import { parseDuration } from '../../tools/client/parseDuration'; dayjs.extend(duration); From b459b0fe8958294e11feab5bacf9dd70a9edc8bd Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Tue, 31 Jan 2023 22:03:34 +0100 Subject: [PATCH 15/41] =?UTF-8?q?=F0=9F=90=9B=20Re-add=20missing=20filters?= =?UTF-8?q?=20for=20torrent=20widget?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/widgets/torrent/TorrentTile.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/widgets/torrent/TorrentTile.tsx b/src/widgets/torrent/TorrentTile.tsx index 41c0a3996..c55287ef2 100644 --- a/src/widgets/torrent/TorrentTile.tsx +++ b/src/widgets/torrent/TorrentTile.tsx @@ -1,3 +1,4 @@ +import { TorrentState } from '@ctrl/shared-torrent'; import { Badge, Center, @@ -122,7 +123,14 @@ function TorrentTile({ widget }: TorrentTileProps) { ); } - const torrents = data.apps.flatMap((app) => (app.type === 'torrent' ? app.torrents : [])); + const torrents = data.apps + .flatMap((app) => (app.type === 'torrent' ? app.torrents : [])) + .filter((torrent) => (widget.properties.displayCompletedTorrents ? true : !torrent.isCompleted)) + .filter((torrent) => + widget.properties.displayStaleTorrents + ? true + : torrent.isCompleted || torrent.downloadSpeed > 0 + ); const difference = new Date().getTime() - dataUpdatedAt; const duration = dayjs.duration(difference, 'ms'); From e89a65c8dcefb17a0096ca1ff869e0fc9632dff8 Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Tue, 31 Jan 2023 22:10:27 +0100 Subject: [PATCH 16/41] =?UTF-8?q?=F0=9F=9A=A8=20Fix=20build=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Tabs/NetworkTab/NetworkTab.tsx | 2 +- .../Dashboard/Tiles/Apps/AppTile.tsx | 3 +- src/pages/404.tsx | 11 +++-- src/pages/index.tsx | 4 +- src/tools/acceptableStatusCodes.ts | 42 ++++++++++--------- src/types/app.ts | 1 + yarn.lock | 8 ++-- 7 files changed, 35 insertions(+), 36 deletions(-) diff --git a/src/components/Dashboard/Modals/EditAppModal/Tabs/NetworkTab/NetworkTab.tsx b/src/components/Dashboard/Modals/EditAppModal/Tabs/NetworkTab/NetworkTab.tsx index 4d735a19f..6b3cc6a81 100644 --- a/src/components/Dashboard/Modals/EditAppModal/Tabs/NetworkTab/NetworkTab.tsx +++ b/src/components/Dashboard/Modals/EditAppModal/Tabs/NetworkTab/NetworkTab.tsx @@ -27,7 +27,7 @@ export const NetworkTab = ({ form }: NetworkTabProps) => { data={StatusCodes} clearable searchable - defaultValue={form.values.network.statusCodes} + defaultValue={form.values.network.okStatus.map((x) => `${x}`)} variant="default" {...form.getInputProps('network.statusCodes')} /> diff --git a/src/components/Dashboard/Tiles/Apps/AppTile.tsx b/src/components/Dashboard/Tiles/Apps/AppTile.tsx index fff6d200c..b11604187 100644 --- a/src/components/Dashboard/Tiles/Apps/AppTile.tsx +++ b/src/components/Dashboard/Tiles/Apps/AppTile.tsx @@ -1,5 +1,4 @@ -import { Anchor, Box, Stack, Title, UnstyledButton } from '@mantine/core'; -import { NextLink } from '@mantine/next'; +import { Box, Stack, Title, UnstyledButton } from '@mantine/core'; import { createStyles } from '@mantine/styles'; import { motion } from 'framer-motion'; import Link from 'next/link'; diff --git a/src/pages/404.tsx b/src/pages/404.tsx index ea1ea65ec..aadf66175 100644 --- a/src/pages/404.tsx +++ b/src/pages/404.tsx @@ -1,14 +1,13 @@ -import React from 'react'; import { - createStyles, - Container, - Title, - Text, Button, + Container, + createStyles, Group, + Text, + Title, useMantineTheme, } from '@mantine/core'; -import { NextLink } from '@mantine/next'; +import React from 'react'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; import Link from 'next/link'; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index c20dd8456..9ccf16c8d 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -2,6 +2,7 @@ import { getCookie, setCookie } from 'cookies-next'; import { GetServerSidePropsContext } from 'next'; import fs from 'fs'; +import { LoadConfigComponent } from '../components/Config/LoadConfig'; import { Dashboard } from '../components/Dashboard/Dashboard'; import Layout from '../components/layout/Layout'; import { useInitConfig } from '../config/init'; @@ -9,9 +10,6 @@ import { getFrontendConfig } from '../tools/config/getFrontendConfig'; import { getServerSideTranslations } from '../tools/getServerSideTranslations'; import { dashboardNamespaces } from '../tools/translation-namespaces'; import { DashboardServerSideProps } from '../types/dashboardPageType'; -import { LoadConfigComponent } from '../components/Config/LoadConfig'; -import dayjs from 'dayjs'; -import { useTranslation } from 'next-i18next'; export async function getServerSideProps({ req, diff --git a/src/tools/acceptableStatusCodes.ts b/src/tools/acceptableStatusCodes.ts index 3d942fd8d..12707ad65 100644 --- a/src/tools/acceptableStatusCodes.ts +++ b/src/tools/acceptableStatusCodes.ts @@ -1,21 +1,23 @@ -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: 504, label: '504 - Gateway Timeout Error', group: 'Server error responses' }, +import { SelectItem } from '@mantine/core'; + +export const StatusCodes: SelectItem[] = [ + { 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' }, ]; diff --git a/src/types/app.ts b/src/types/app.ts index 7edeadee9..0f6284628 100644 --- a/src/types/app.ts +++ b/src/types/app.ts @@ -1,4 +1,5 @@ import { IconKey, IconPassword, IconUser, TablerIcon } from '@tabler/icons'; + import { TileBaseType } from './tile'; export interface AppType extends TileBaseType { diff --git a/yarn.lock b/yarn.lock index 629a864cf..398c6e544 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4568,7 +4568,7 @@ __metadata: "fsevents@patch:fsevents@^2.3.2#~builtin, fsevents@patch:fsevents@~2.3.2#~builtin": version: 2.3.2 - resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=18f3a7" + resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=df0bf1" dependencies: node-gyp: latest conditions: os=darwin @@ -7519,7 +7519,7 @@ __metadata: "resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.0#~builtin": version: 1.22.1 - resolution: "resolve@patch:resolve@npm%3A1.22.1#~builtin::version=1.22.1&hash=07638b" + resolution: "resolve@patch:resolve@npm%3A1.22.1#~builtin::version=1.22.1&hash=c3c19d" dependencies: is-core-module: ^2.9.0 path-parse: ^1.0.7 @@ -7532,7 +7532,7 @@ __metadata: "resolve@patch:resolve@^2.0.0-next.3#~builtin": version: 2.0.0-next.4 - resolution: "resolve@patch:resolve@npm%3A2.0.0-next.4#~builtin::version=2.0.0-next.4&hash=07638b" + resolution: "resolve@patch:resolve@npm%3A2.0.0-next.4#~builtin::version=2.0.0-next.4&hash=c3c19d" dependencies: is-core-module: ^2.9.0 path-parse: ^1.0.7 @@ -8370,7 +8370,7 @@ __metadata: "typescript@patch:typescript@^4.7.4#~builtin": version: 4.9.4 - resolution: "typescript@patch:typescript@npm%3A4.9.4#~builtin::version=4.9.4&hash=7ad353" + resolution: "typescript@patch:typescript@npm%3A4.9.4#~builtin::version=4.9.4&hash=d73830" bin: tsc: bin/tsc tsserver: bin/tsserver From 37672e4b15839756cb9a049926bbe5d818c278ff Mon Sep 17 00:00:00 2001 From: Thomas Camlong <49837342+ajnart@users.noreply.github.com> Date: Wed, 1 Feb 2023 11:04:15 +0900 Subject: [PATCH 17/41] Update _app.tsx --- src/pages/_app.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index b372436a0..822817dc5 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -68,7 +68,7 @@ function App( return ( <> - + From 24973c5bbb4f7ed8421e0b0870905fdfdec91374 Mon Sep 17 00:00:00 2001 From: Thomas Camlong <49837342+ajnart@users.noreply.github.com> Date: Wed, 1 Feb 2023 11:06:24 +0900 Subject: [PATCH 18/41] Update _app.tsx --- src/pages/_app.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 822817dc5..204db7734 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -68,7 +68,7 @@ function App( return ( <> - + From 49b17c2fd76fd3501852cb24e97b348985685199 Mon Sep 17 00:00:00 2001 From: ajnart Date: Wed, 1 Feb 2023 11:22:57 +0900 Subject: [PATCH 19/41] =?UTF-8?q?=F0=9F=9A=A8=20Fix=20compilation=20by=20d?= =?UTF-8?q?owngrading=20Mantine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 17 +++--- yarn.lock | 170 ++++++++++++++++++--------------------------------- 2 files changed, 69 insertions(+), 118 deletions(-) diff --git a/package.json b/package.json index 0ce42e768..4fbd03992 100644 --- a/package.json +++ b/package.json @@ -32,15 +32,14 @@ "@dnd-kit/utilities": "^3.2.0", "@emotion/react": "^11.10.5", "@emotion/server": "^11.10.0", - "@mantine/core": "^5.10.1", - "@mantine/dates": "^5.10.1", - "@mantine/dropzone": "^5.10.1", - "@mantine/form": "^5.10.1", - "@mantine/hooks": "^5.10.1", - "@mantine/modals": "^5.10.1", - "@mantine/next": "^5.10.1", - "@mantine/notifications": "^5.10.1", - "@mantine/prism": "^5.9.3", + "@mantine/core": "^5.9.3", + "@mantine/dates": "^5.9.3", + "@mantine/dropzone": "^5.9.3", + "@mantine/form": "^5.9.3", + "@mantine/hooks": "^5.9.3", + "@mantine/modals": "^5.9.3", + "@mantine/next": "^5.9.3", + "@mantine/notifications": "^5.9.3", "@nivo/core": "^0.79.0", "@nivo/line": "^0.79.1", "@tabler/icons": "^1.106.0", diff --git a/yarn.lock b/yarn.lock index 399583df6..cf1f50c6d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1114,7 +1114,7 @@ __metadata: languageName: node linkType: hard -"@mantine/core@npm:^5.10.1": +"@mantine/core@npm:^5.9.3": version: 5.10.2 resolution: "@mantine/core@npm:5.10.2" dependencies: @@ -1131,140 +1131,111 @@ __metadata: languageName: node linkType: hard -"@mantine/dates@npm:^5.10.1": - version: 5.10.1 - resolution: "@mantine/dates@npm:5.10.1" +"@mantine/dates@npm:^5.9.3": + version: 5.10.2 + resolution: "@mantine/dates@npm:5.10.2" dependencies: - "@mantine/utils": 5.10.1 + "@mantine/utils": 5.10.2 peerDependencies: - "@mantine/core": 5.10.1 - "@mantine/hooks": 5.10.1 + "@mantine/core": 5.10.2 + "@mantine/hooks": 5.10.2 dayjs: ">=1.0.0" react: ">=16.8.0" - checksum: 68f2d26e7dc91429c2281bab4af8ba895ac26fe08527484467b2bc891ce213f5548de61f0d5afe144927b0a2cd4131dd2117657171ab5c3720bd3cfeadf3fe39 + checksum: ffa1d1677d6f9f0722816169a09b879e3087d67e4ea4855616f6e6a456816f851f0ce7262bff25e968e86178a4b0621a8f99587e5d85ff1f299963f0432159ea languageName: node linkType: hard -"@mantine/dropzone@npm:^5.10.1": - version: 5.10.1 - resolution: "@mantine/dropzone@npm:5.10.1" +"@mantine/dropzone@npm:^5.9.3": + version: 5.10.2 + resolution: "@mantine/dropzone@npm:5.10.2" dependencies: - "@mantine/utils": 5.10.1 + "@mantine/utils": 5.10.2 react-dropzone: 14.2.3 peerDependencies: - "@mantine/core": 5.10.1 - "@mantine/hooks": 5.10.1 + "@mantine/core": 5.10.2 + "@mantine/hooks": 5.10.2 react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: e5e7b35e775117fb3e5d4e74798d53bbfb82f846aae173b06ae17762c79b4b114deec7c4dccb858c30a38dee09d6c34add939f171ec343cbb70ce7bd3ee56ad9 + checksum: 103feac1ba8481591382ced6f954e3f98ae2da7134090fa9ef977bb3f1442c06a7ec2f55eddfd645e1d79020d1610ada0b4ade5492d61e1a2b68cd659ee43741 languageName: node linkType: hard -"@mantine/form@npm:^5.10.1": - version: 5.10.1 - resolution: "@mantine/form@npm:5.10.1" +"@mantine/form@npm:^5.9.3": + version: 5.10.2 + resolution: "@mantine/form@npm:5.10.2" dependencies: fast-deep-equal: ^3.1.3 klona: ^2.0.5 peerDependencies: react: ">=16.8.0" - checksum: 2d6b19894f48b95f45bfdc525e456897581be178f4577715c4bec9ddac90d135e74edf94860334a0d43cee75d8c79fb093a7328c7eef2c70361bcbcb8cd17354 + checksum: 4e94d603b2530278eb340d38c8296d7ec5b73391040ec83766c4afe613b18ca486bb5b3064e3fec8ef47ed40302b102702ea0cc4d68c7b59c62bff822857c8f0 languageName: node linkType: hard -"@mantine/hooks@npm:^5.10.1": - version: 5.10.1 - resolution: "@mantine/hooks@npm:5.10.1" +"@mantine/hooks@npm:^5.9.3": + version: 5.10.2 + resolution: "@mantine/hooks@npm:5.10.2" peerDependencies: react: ">=16.8.0" - checksum: 9b01c87ba8709bfd3bc7990b3bca5beb6d82ce66c089a39fcd63a4abf9dd64a5e85caee98ac22a68e0755a9ae815491561f5bc4fa5693d011a04bb22d8f211d1 + checksum: 247f5cdce90e4ecabc81a899361091d0c9b4da57485d30ca74dccdb99053b7d5a53d2fb57df18907ca3676a8328f9a94d69d72aa38426efd17dd60d1b1f645a9 languageName: node linkType: hard -"@mantine/modals@npm:^5.10.1": - version: 5.10.1 - resolution: "@mantine/modals@npm:5.10.1" +"@mantine/modals@npm:^5.9.3": + version: 5.10.2 + resolution: "@mantine/modals@npm:5.10.2" dependencies: - "@mantine/utils": 5.10.1 + "@mantine/utils": 5.10.2 peerDependencies: - "@mantine/core": 5.10.1 - "@mantine/hooks": 5.10.1 + "@mantine/core": 5.10.2 + "@mantine/hooks": 5.10.2 react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 143c51476c4acb8de8b99f308199fee125e2a774b60427baa282e015910a24945fe75bca48baa199592f42b89b1a37de15ca77c6e0a3ea7b7824b36d422cf4fd + checksum: 65310c532eb5d56fa4ee5f2613fb500eb6245361321cd87f2b3e8e0f9bbfcb12e91877ca0618ef8898d2d58be5091e98fa078b9750ff17ec5a53c60d9803ba6d languageName: node linkType: hard -"@mantine/next@npm:^5.10.1": - version: 5.10.1 - resolution: "@mantine/next@npm:5.10.1" +"@mantine/next@npm:^5.9.3": + version: 5.10.2 + resolution: "@mantine/next@npm:5.10.2" dependencies: - "@mantine/ssr": 5.10.1 - "@mantine/styles": 5.10.1 + "@mantine/ssr": 5.10.2 + "@mantine/styles": 5.10.2 peerDependencies: next: "*" react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 511a51e18d62d6afec25bc496b36e51d245c070ef7e0879b906a30f5fd114b574f5a4f5cef441a5751bf600812339981add649b9dba5a17b05877a88afb6b81f + checksum: 1cbc55b5daeb632d9f0f418eaf41c5213840e95f4d50aaaae895cbe2091cd10685e2af7d97a8d0c9da8d46d3998ac1b2ad5cfc3c9798ba434baf386e8814f61d languageName: node linkType: hard -"@mantine/notifications@npm:^5.10.1": - version: 5.10.1 - resolution: "@mantine/notifications@npm:5.10.1" +"@mantine/notifications@npm:^5.9.3": + version: 5.10.2 + resolution: "@mantine/notifications@npm:5.10.2" dependencies: - "@mantine/utils": 5.10.1 + "@mantine/utils": 5.10.2 react-transition-group: 4.4.2 peerDependencies: - "@mantine/core": 5.10.1 - "@mantine/hooks": 5.10.1 + "@mantine/core": 5.10.2 + "@mantine/hooks": 5.10.2 react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 80e065db28757af2bdf50093529238e91fb85d936087e6e40219d530f51b860f5f510c52526b42e1ca58a4cb3d939cea080e549a4659e9cdcba3c045e3bfee00 + checksum: 36289997284e326863c3560f7538c92a3fe8304560e9052a2555c77f8467e29a91a8a06b4e42acd8ef346cde46e02fd652a23c883f07d645b8328502ab21fe0d languageName: node linkType: hard -"@mantine/prism@npm:^5.9.3": - version: 5.9.3 - resolution: "@mantine/prism@npm:5.9.3" +"@mantine/ssr@npm:5.10.2": + version: 5.10.2 + resolution: "@mantine/ssr@npm:5.10.2" dependencies: - "@mantine/utils": 5.9.3 - prism-react-renderer: ^1.2.1 - peerDependencies: - "@mantine/core": 5.9.3 - "@mantine/hooks": 5.9.3 - react: ">=16.8.0" - react-dom: ">=16.8.0" - checksum: 5d5b66a13ff2260cc150f7252eb0a8c581e062df347ebb842ddcdb0bbddc255373861e9e824697aaf6414dc0062b849db7d2dd9088a20719a420e24bc1a8ef7c - languageName: node - linkType: hard - -"@mantine/ssr@npm:5.10.1": - version: 5.10.1 - resolution: "@mantine/ssr@npm:5.10.1" - dependencies: - "@mantine/styles": 5.10.1 + "@mantine/styles": 5.10.2 html-react-parser: 1.4.12 peerDependencies: "@emotion/react": ">=11.9.0" "@emotion/server": ">=11.4.0" react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 3a4cf7516661e96d5b736f67aeed07217c0808453f079e2a1361e9cc86cd71a1749e8aba3d94d21099f7a27d1a1dc14b14701138cf57d196169786300cedf845 - languageName: node - linkType: hard - -"@mantine/styles@npm:5.10.1": - version: 5.10.1 - resolution: "@mantine/styles@npm:5.10.1" - dependencies: - clsx: 1.1.1 - csstype: 3.0.9 - peerDependencies: - "@emotion/react": ">=11.9.0" - react: ">=16.8.0" - react-dom: ">=16.8.0" - checksum: 877d28258a7f1a7e1ff9681e90be7ccdfe5bf75638f9ec9a2f9ad13153d1f27610b5f9bc30867ae211cad6533ed02ee816b36654b5c00ca0c2a47d5ee6287e4f + checksum: e37f5bafc3700e97ea5b89aa2f974d13a154437eaef2f84a22166739821f1337dd657f9d3771143e30e63077b2b69127a98944beb9cc0a8f25448529fabffea4 languageName: node linkType: hard @@ -1282,15 +1253,6 @@ __metadata: languageName: node linkType: hard -"@mantine/utils@npm:5.10.1": - version: 5.10.1 - resolution: "@mantine/utils@npm:5.10.1" - peerDependencies: - react: ">=16.8.0" - checksum: a5ab487a0b24cc12e0b4ef1e5c49439dfa03e29f8f631eb45565fb6991a31aa762816d520904f22a36eff849b7fa2c0996f35af7e1581e4a069c5a1c81b231d4 - languageName: node - linkType: hard - "@mantine/utils@npm:5.10.2": version: 5.10.2 resolution: "@mantine/utils@npm:5.10.2" @@ -1300,15 +1262,6 @@ __metadata: languageName: node linkType: hard -"@mantine/utils@npm:5.9.3": - version: 5.9.3 - resolution: "@mantine/utils@npm:5.9.3" - peerDependencies: - react: ">=16.8.0" - checksum: 5cdb34ce05213636f396fa85d0a03fcd13a24e5e87a85e87719a213745ca5cd98d9e143630d9b8d3b5029de3a9694df16ee6c28c3d668f8f6012815600f42b44 - languageName: node - linkType: hard - "@motionone/animation@npm:^10.12.0": version: 10.15.1 resolution: "@motionone/animation@npm:10.15.1" @@ -4601,7 +4554,7 @@ __metadata: "fsevents@patch:fsevents@^2.3.2#~builtin, fsevents@patch:fsevents@~2.3.2#~builtin": version: 2.3.2 - resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=df0bf1" + resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=18f3a7" dependencies: node-gyp: latest conditions: os=darwin @@ -4965,15 +4918,14 @@ __metadata: "@dnd-kit/utilities": ^3.2.0 "@emotion/react": ^11.10.5 "@emotion/server": ^11.10.0 - "@mantine/core": ^5.10.1 - "@mantine/dates": ^5.10.1 - "@mantine/dropzone": ^5.10.1 - "@mantine/form": ^5.10.1 - "@mantine/hooks": ^5.10.1 - "@mantine/modals": ^5.10.1 - "@mantine/next": ^5.10.1 - "@mantine/notifications": ^5.10.1 - "@mantine/prism": ^5.9.3 + "@mantine/core": ^5.9.3 + "@mantine/dates": ^5.9.3 + "@mantine/dropzone": ^5.9.3 + "@mantine/form": ^5.9.3 + "@mantine/hooks": ^5.9.3 + "@mantine/modals": ^5.9.3 + "@mantine/next": ^5.9.3 + "@mantine/notifications": ^5.9.3 "@next/bundle-analyzer": ^12.1.4 "@next/eslint-plugin-next": ^12.1.4 "@nivo/core": ^0.79.0 @@ -7181,7 +7133,7 @@ __metadata: languageName: node linkType: hard -"prism-react-renderer@npm:^1.2.1, prism-react-renderer@npm:^1.3.5": +"prism-react-renderer@npm:^1.3.5": version: 1.3.5 resolution: "prism-react-renderer@npm:1.3.5" peerDependencies: @@ -7567,7 +7519,7 @@ __metadata: "resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.0#~builtin": version: 1.22.1 - resolution: "resolve@patch:resolve@npm%3A1.22.1#~builtin::version=1.22.1&hash=c3c19d" + resolution: "resolve@patch:resolve@npm%3A1.22.1#~builtin::version=1.22.1&hash=07638b" dependencies: is-core-module: ^2.9.0 path-parse: ^1.0.7 @@ -7580,7 +7532,7 @@ __metadata: "resolve@patch:resolve@^2.0.0-next.3#~builtin": version: 2.0.0-next.4 - resolution: "resolve@patch:resolve@npm%3A2.0.0-next.4#~builtin::version=2.0.0-next.4&hash=c3c19d" + resolution: "resolve@patch:resolve@npm%3A2.0.0-next.4#~builtin::version=2.0.0-next.4&hash=07638b" dependencies: is-core-module: ^2.9.0 path-parse: ^1.0.7 @@ -8427,7 +8379,7 @@ __metadata: "typescript@patch:typescript@^4.7.4#~builtin": version: 4.9.4 - resolution: "typescript@patch:typescript@npm%3A4.9.4#~builtin::version=4.9.4&hash=d73830" + resolution: "typescript@patch:typescript@npm%3A4.9.4#~builtin::version=4.9.4&hash=7ad353" bin: tsc: bin/tsc tsserver: bin/tsserver From a533aad7b95335d3a36b8341925ad6ac619b5b77 Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Tue, 31 Jan 2023 20:12:03 +0100 Subject: [PATCH 20/41] =?UTF-8?q?=E2=9C=A8=20Add=20switch=20for=20percenta?= =?UTF-8?q?ges=20in=20dash.=20widget=20#641?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/modules/dashdot.json | 3 +++ src/widgets/dashDot/DashDotGraph.tsx | 20 ++++++++++++++++---- src/widgets/dashDot/DashDotTile.tsx | 7 +++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/public/locales/en/modules/dashdot.json b/public/locales/en/modules/dashdot.json index 9268364f7..5d1e3e623 100644 --- a/public/locales/en/modules/dashdot.json +++ b/public/locales/en/modules/dashdot.json @@ -18,6 +18,9 @@ }, "url": { "label": "Dash. URL" + }, + "usePercentages": { + "label": "Display percentages" } } }, diff --git a/src/widgets/dashDot/DashDotGraph.tsx b/src/widgets/dashDot/DashDotGraph.tsx index 571a021c7..f64ba8140 100644 --- a/src/widgets/dashDot/DashDotGraph.tsx +++ b/src/widgets/dashDot/DashDotGraph.tsx @@ -5,9 +5,15 @@ interface DashDotGraphProps { graph: GraphType; isCompact: boolean; dashDotUrl: string; + usePercentages: boolean; } -export const DashDotGraph = ({ graph, isCompact, dashDotUrl }: DashDotGraphProps) => { +export const DashDotGraph = ({ + graph, + isCompact, + dashDotUrl, + usePercentages, +}: DashDotGraphProps) => { const { classes } = useStyles(); return ( ); }; -const useIframeSrc = (dashDotUrl: string, graph: GraphType, isCompact: boolean) => { +const useIframeSrc = ( + dashDotUrl: string, + graph: GraphType, + isCompact: boolean, + usePercentages: boolean +) => { const { colorScheme, colors, radius } = useMantineTheme(); const surface = (colorScheme === 'dark' ? colors.dark[7] : colors.gray[0]).substring(1); // removes # from hex value @@ -45,7 +56,8 @@ const useIframeSrc = (dashDotUrl: string, graph: GraphType, isCompact: boolean) `&surface=${surface}` + `&gap=${isCompact ? 10 : 5}` + `&innerRadius=${radius.lg}` + - `&multiView=${graph.isMultiView}` + `&multiView=${graph.isMultiView}` + + `&showPercentage=${usePercentages ? 'true' : 'false'}` ); }; diff --git a/src/widgets/dashDot/DashDotTile.tsx b/src/widgets/dashDot/DashDotTile.tsx index 89f83d353..07ff508fc 100644 --- a/src/widgets/dashDot/DashDotTile.tsx +++ b/src/widgets/dashDot/DashDotTile.tsx @@ -25,6 +25,10 @@ const definition = defineWidget({ type: 'switch', defaultValue: true, }, + usePercentages: { + type: 'switch', + defaultValue: false, + }, graphs: { type: 'multi-select', defaultValue: ['cpu', 'memory'], @@ -88,6 +92,8 @@ function DashDotTile({ widget }: DashDotTileProps) { const isCompactNetworkVisible = graphs?.some((g) => g.id === 'network' && isCompact); + const usePercentages = widget?.properties.usePercentages ?? false; + const displayedGraphs = graphs?.filter( (g) => !isCompact || !['network', 'storage'].includes(g.id) ); @@ -109,6 +115,7 @@ function DashDotTile({ widget }: DashDotTileProps) { graph={graph} dashDotUrl={dashDotUrl} isCompact={isCompact} + usePercentages={usePercentages} /> ))} From 789bfd6f06ac0d42898af2834b504805b6dcab0a Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Wed, 1 Feb 2023 12:55:31 +0100 Subject: [PATCH 21/41] =?UTF-8?q?=F0=9F=94=96=20Increment=20version=20numb?= =?UTF-8?q?er?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4cf737dd8..e1738d601 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homarr", - "version": "0.11.3", + "version": "0.11.4", "description": "Homarr - A homepage for your server.", "license": "MIT", "repository": { From 766418ce53c19278ee1696cd2034c9f7add2d652 Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Tue, 31 Jan 2023 21:17:37 +0100 Subject: [PATCH 22/41] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Add=20version=20read?= =?UTF-8?q?ing=20by=20package=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/constants.ts | 1 - .../Modals/AboutModal/AboutModal.tsx | 25 +++++++++++++------ .../Tabs/AppereanceTab/AppereanceTab.tsx | 2 +- .../Wrappers/Category/useCategoryActions.tsx | 1 - src/components/layout/header/Header.tsx | 13 +++++----- src/pages/[slug].tsx | 4 +-- src/pages/_app.tsx | 19 ++++++++++++-- src/pages/index.tsx | 15 ++++++----- src/pages/login.tsx | 2 +- src/tools/addToHomarr.ts | 11 +------- src/tools/{ => client}/calculateEta.ts | 0 src/tools/{ => client}/parseDuration.ts | 0 .../zustands/usePackageAttributesStore.ts | 15 +++++++++++ src/tools/server/getPackageVersion.ts | 14 +++++++++++ .../{ => server}/getServerSideTranslations.ts | 0 .../{ => server}/translation-namespaces.ts | 0 src/types/dashboardPageType.ts | 1 + src/widgets/torrent/TorrentQueueItem.tsx | 2 +- src/widgets/useNet/UsenetHistoryList.tsx | 2 +- 19 files changed, 87 insertions(+), 40 deletions(-) rename src/tools/{ => client}/calculateEta.ts (100%) rename src/tools/{ => client}/parseDuration.ts (100%) create mode 100644 src/tools/client/zustands/usePackageAttributesStore.ts create mode 100644 src/tools/server/getPackageVersion.ts rename src/tools/{ => server}/getServerSideTranslations.ts (100%) rename src/tools/{ => server}/translation-namespaces.ts (100%) diff --git a/data/constants.ts b/data/constants.ts index b1ff95a49..b10f63d7e 100644 --- a/data/constants.ts +++ b/data/constants.ts @@ -1,3 +1,2 @@ export const REPO_URL = 'ajnart/homarr'; -export const CURRENT_VERSION = 'v0.11.3'; export const ICON_PICKER_SLICE_LIMIT = 36; diff --git a/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx b/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx index 6ec6b6536..905085e47 100644 --- a/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx +++ b/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx @@ -14,6 +14,7 @@ import { Title, } from '@mantine/core'; import { + IconAnchor, IconBrandDiscord, IconBrandGithub, IconFile, @@ -28,11 +29,11 @@ import { InitOptions } from 'i18next'; import { i18n, Trans, useTranslation } from 'next-i18next'; import Image from 'next/image'; import { ReactNode } from 'react'; -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'; +import { useConfigContext } from '../../config/provider'; +import { useConfigStore } from '../../config/store'; +import { usePackageAttributesStore } from '../../tools/client/zustands/usePackageAttributesStore'; +import { usePrimaryGradient } from '../layout/useGradient'; +import Credits from '../Settings/Common/Credits'; interface AboutModalProps { opened: boolean; @@ -143,6 +144,7 @@ interface ExtendedInitOptions extends InitOptions { const useInformationTableItems = (newVersionAvailable?: string): InformationTableItem[] => { // TODO: Fix this to not request. Pass it as a prop. const colorGradiant = usePrimaryGradient(); + const { attributes } = usePackageAttributesStore(); const { configVersion } = useConfigContext(); const { configs } = useConfigStore(); @@ -201,7 +203,7 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl content: ( - {CURRENT_VERSION} + {attributes.packageVersion ?? 'Unknown'} {newVersionAvailable && ( @@ -229,13 +231,22 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl {newVersionAvailable} {' '} - is available ! Current version: {CURRENT_VERSION} + is available ! Current version: {attributes.packageVersion} )} ), }, + { + icon: , + label: 'Node environment', + content: ( + + {attributes.environment} + + ), + }, ...items, ]; diff --git a/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/AppereanceTab.tsx b/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/AppereanceTab.tsx index f74ab7d60..d8795d25f 100644 --- a/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/AppereanceTab.tsx +++ b/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/AppereanceTab.tsx @@ -1,4 +1,4 @@ -import { Autocomplete, createStyles, Flex, Tabs, TextInput } from '@mantine/core'; +import { Autocomplete, createStyles, Flex, Tabs } from '@mantine/core'; import { UseFormReturnType } from '@mantine/form'; import { useQuery } from '@tanstack/react-query'; import { useTranslation } from 'next-i18next'; diff --git a/src/components/Dashboard/Wrappers/Category/useCategoryActions.tsx b/src/components/Dashboard/Wrappers/Category/useCategoryActions.tsx index 7225f3b5d..6be7e99a4 100644 --- a/src/components/Dashboard/Wrappers/Category/useCategoryActions.tsx +++ b/src/components/Dashboard/Wrappers/Category/useCategoryActions.tsx @@ -3,7 +3,6 @@ import { useConfigStore } from '../../../../config/store'; import { openContextModalGeneric } from '../../../../tools/mantineModalManagerExtensions'; import { AppType } from '../../../../types/app'; import { CategoryType } from '../../../../types/category'; -import { ConfigType } from '../../../../types/config'; import { WrapperType } from '../../../../types/wrapper'; import { IWidget } from '../../../../widgets/widgets'; import { CategoryEditModalInnerProps } from './CategoryEditModal'; diff --git a/src/components/layout/header/Header.tsx b/src/components/layout/header/Header.tsx index 45f432f34..ec7a9f33d 100644 --- a/src/components/layout/header/Header.tsx +++ b/src/components/layout/header/Header.tsx @@ -1,10 +1,10 @@ import { Box, createStyles, Group, Header as MantineHeader, Indicator } from '@mantine/core'; import { useEffect, useState } from 'react'; -import { CURRENT_VERSION, REPO_URL } from '../../../../data/constants'; -import { useConfigContext } from '../../../config/provider'; +import { REPO_URL } from '../../../../data/constants'; +import DockerMenuButton from '../../../modules/Docker/DockerModule'; +import { usePackageAttributesStore } from '../../../tools/client/zustands/usePackageAttributesStore'; import { Logo } from '../Logo'; import { useCardStyles } from '../useCardStyles'; -import DockerMenuButton from '../../../modules/Docker/DockerModule'; import { ToggleEditModeAction } from './Actions/ToggleEditMode/ToggleEditMode'; import { Search } from './Search'; import { SettingsMenu } from './SettingsMenu'; @@ -14,20 +14,19 @@ export const HeaderHeight = 64; export function Header(props: any) { const { classes } = useStyles(); const { classes: cardClasses } = useCardStyles(false); - - const { config } = useConfigContext(); + const { attributes } = usePackageAttributesStore(); const [newVersionAvailable, setNewVersionAvailable] = useState(''); useEffect(() => { // Fetch Data here when component first mounted fetch(`https://api.github.com/repos/${REPO_URL}/releases/latest`).then((res) => { res.json().then((data) => { - if (data.tag_name > CURRENT_VERSION) { + if (data.tag_name > `v${attributes.packageVersion}`) { setNewVersionAvailable(data.tag_name); } }); }); - }, [CURRENT_VERSION]); + }, []); return ( diff --git a/src/pages/[slug].tsx b/src/pages/[slug].tsx index 5a0876847..c3ac1b272 100644 --- a/src/pages/[slug].tsx +++ b/src/pages/[slug].tsx @@ -8,8 +8,8 @@ import Layout from '../components/layout/Layout'; import { useInitConfig } from '../config/init'; import { getFallbackConfig } from '../tools/config/getFallbackConfig'; import { getFrontendConfig } from '../tools/config/getFrontendConfig'; -import { getServerSideTranslations } from '../tools/getServerSideTranslations'; -import { dashboardNamespaces } from '../tools/translation-namespaces'; +import { getServerSideTranslations } from '../tools/server/getServerSideTranslations'; +import { dashboardNamespaces } from '../tools/server/translation-namespaces'; import { ConfigType } from '../types/config'; import { DashboardServerSideProps } from '../types/dashboardPageType'; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 088141e6c..b372436a0 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -8,7 +8,7 @@ import { GetServerSidePropsContext } from 'next'; import { appWithTranslation } from 'next-i18next'; import { AppProps } from 'next/app'; import Head from 'next/head'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { ChangeAppPositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeAppPositionModal'; import { ChangeWidgetPositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal'; @@ -22,8 +22,16 @@ import '../styles/global.scss'; import { ColorTheme } from '../tools/color'; import { queryClient } from '../tools/queryClient'; import { theme } from '../tools/theme'; +import { + getServiceSidePackageAttributes, + ServerSidePackageAttributesType, +} from '../tools/server/getPackageVersion'; +import { usePackageAttributesStore } from '../tools/client/zustands/usePackageAttributesStore'; -function App(this: any, props: AppProps & { colorScheme: ColorScheme }) { +function App( + this: any, + props: AppProps & { colorScheme: ColorScheme; packageAttributes: ServerSidePackageAttributesType } +) { const { Component, pageProps } = props; const [primaryColor, setPrimaryColor] = useState('red'); const [secondaryColor, setSecondaryColor] = useState('orange'); @@ -46,6 +54,12 @@ function App(this: any, props: AppProps & { colorScheme: ColorScheme }) { getInitialValueInEffect: true, }); + const { setInitialPackageAttributes } = usePackageAttributesStore(); + + useEffect(() => { + setInitialPackageAttributes(props.packageAttributes); + }, []); + const toggleColorScheme = (value?: ColorScheme) => setColorScheme(value || (colorScheme === 'dark' ? 'light' : 'dark')); @@ -111,6 +125,7 @@ function App(this: any, props: AppProps & { colorScheme: ColorScheme }) { App.getInitialProps = ({ ctx }: { ctx: GetServerSidePropsContext }) => ({ colorScheme: getCookie('color-scheme', ctx) || 'light', + packageAttributes: getServiceSidePackageAttributes(), }); export default appWithTranslation(App); diff --git a/src/pages/index.tsx b/src/pages/index.tsx index dd416158b..5b1f60d8a 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,15 +1,15 @@ import { getCookie, setCookie } from 'cookies-next'; +import fs from 'fs'; import { GetServerSidePropsContext } from 'next'; -import fs from 'fs'; +import { LoadConfigComponent } from '../components/Config/LoadConfig'; import { Dashboard } from '../components/Dashboard/Dashboard'; import Layout from '../components/layout/Layout'; import { useInitConfig } from '../config/init'; import { getFrontendConfig } from '../tools/config/getFrontendConfig'; -import { getServerSideTranslations } from '../tools/getServerSideTranslations'; -import { dashboardNamespaces } from '../tools/translation-namespaces'; +import { getServerSideTranslations } from '../tools/server/getServerSideTranslations'; +import { dashboardNamespaces } from '../tools/server/translation-namespaces'; import { DashboardServerSideProps } from '../types/dashboardPageType'; -import { LoadConfigComponent } from '../components/Config/LoadConfig'; export async function getServerSideProps({ req, @@ -47,11 +47,14 @@ export async function getServerSideProps({ } const translations = await getServerSideTranslations(req, res, dashboardNamespaces, locale); - const config = getFrontendConfig(configName as string); return { - props: { configName: configName as string, config, ...translations }, + props: { + configName: configName as string, + config, + ...translations, + }, }; } diff --git a/src/pages/login.tsx b/src/pages/login.tsx index 7587bbde0..bf414ed83 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -8,7 +8,7 @@ import { useRouter } from 'next/router'; import { useTranslation } from 'next-i18next'; import { useForm } from '@mantine/form'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; -import { loginNamespaces } from '../tools/translation-namespaces'; +import { loginNamespaces } from '../tools/server/translation-namespaces'; // TODO: Add links to the wiki articles about the login process. export default function AuthenticationTitle() { diff --git a/src/tools/addToHomarr.ts b/src/tools/addToHomarr.ts index 5f97bf49d..bb340ba4e 100644 --- a/src/tools/addToHomarr.ts +++ b/src/tools/addToHomarr.ts @@ -1,14 +1,5 @@ import Dockerode from 'dockerode'; -import { Config, MatchingImages, ServiceType, tryMatchPort } from './types'; - -async function MatchIcon(name: string) { - const res = await fetch( - `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${name - .replace(/\s+/g, '-') - .toLowerCase()}.png` - ); - return res.ok ? res.url : '/imgs/favicon/favicon.png'; -} +import { MatchingImages, ServiceType, tryMatchPort } from './types'; function tryMatchType(imageName: string): ServiceType { // Try to find imageName inside MatchingImages diff --git a/src/tools/calculateEta.ts b/src/tools/client/calculateEta.ts similarity index 100% rename from src/tools/calculateEta.ts rename to src/tools/client/calculateEta.ts diff --git a/src/tools/parseDuration.ts b/src/tools/client/parseDuration.ts similarity index 100% rename from src/tools/parseDuration.ts rename to src/tools/client/parseDuration.ts diff --git a/src/tools/client/zustands/usePackageAttributesStore.ts b/src/tools/client/zustands/usePackageAttributesStore.ts new file mode 100644 index 000000000..6d710997d --- /dev/null +++ b/src/tools/client/zustands/usePackageAttributesStore.ts @@ -0,0 +1,15 @@ +import create from 'zustand'; + +import { ServerSidePackageAttributesType } from '../../server/getPackageVersion'; + +interface PackageAttributesState { + attributes: ServerSidePackageAttributesType; + setInitialPackageAttributes: (attributes: ServerSidePackageAttributesType) => void; +} + +export const usePackageAttributesStore = create((set) => ({ + attributes: { packageVersion: undefined, environment: 'test' }, + setInitialPackageAttributes(attributes) { + set((state) => ({ ...state, attributes })); + }, +})); diff --git a/src/tools/server/getPackageVersion.ts b/src/tools/server/getPackageVersion.ts new file mode 100644 index 000000000..0c74d56a7 --- /dev/null +++ b/src/tools/server/getPackageVersion.ts @@ -0,0 +1,14 @@ +const getServerPackageVersion = (): string | undefined => process.env.npm_package_version; + +const getServerNodeEnvironment = (): 'development' | 'production' | 'test' => + process.env.NODE_ENV; + +export const getServiceSidePackageAttributes = (): ServerSidePackageAttributesType => ({ + packageVersion: getServerPackageVersion(), + environment: getServerNodeEnvironment(), +}); + +export type ServerSidePackageAttributesType = { + packageVersion: string | undefined; + environment: 'development' | 'production' | 'test'; +}; diff --git a/src/tools/getServerSideTranslations.ts b/src/tools/server/getServerSideTranslations.ts similarity index 100% rename from src/tools/getServerSideTranslations.ts rename to src/tools/server/getServerSideTranslations.ts diff --git a/src/tools/translation-namespaces.ts b/src/tools/server/translation-namespaces.ts similarity index 100% rename from src/tools/translation-namespaces.ts rename to src/tools/server/translation-namespaces.ts diff --git a/src/types/dashboardPageType.ts b/src/types/dashboardPageType.ts index 416c3b1bf..f361e449f 100644 --- a/src/types/dashboardPageType.ts +++ b/src/types/dashboardPageType.ts @@ -1,4 +1,5 @@ import { SSRConfig } from 'next-i18next'; + import { ConfigType } from './config'; export type DashboardServerSideProps = { diff --git a/src/widgets/torrent/TorrentQueueItem.tsx b/src/widgets/torrent/TorrentQueueItem.tsx index 6ec0c1a71..6d7afd891 100644 --- a/src/widgets/torrent/TorrentQueueItem.tsx +++ b/src/widgets/torrent/TorrentQueueItem.tsx @@ -24,7 +24,7 @@ import { IconUpload, } from '@tabler/icons'; import { useTranslation } from 'next-i18next'; -import { calculateETA } from '../../tools/calculateEta'; +import { calculateETA } from '../../tools/client/calculateEta'; import { humanFileSize } from '../../tools/humanFileSize'; import { AppType } from '../../types/app'; diff --git a/src/widgets/useNet/UsenetHistoryList.tsx b/src/widgets/useNet/UsenetHistoryList.tsx index 28ee10ccf..2578fdec6 100644 --- a/src/widgets/useNet/UsenetHistoryList.tsx +++ b/src/widgets/useNet/UsenetHistoryList.tsx @@ -20,7 +20,7 @@ import { useTranslation } from 'next-i18next'; import { FunctionComponent, useState } from 'react'; import { useGetUsenetHistory } from '../../hooks/widgets/dashDot/api'; import { humanFileSize } from '../../tools/humanFileSize'; -import { parseDuration } from '../../tools/parseDuration'; +import { parseDuration } from '../../tools/client/parseDuration'; dayjs.extend(duration); From 868b0017b9b00a30b4ba529dd7397a977be28a89 Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Tue, 31 Jan 2023 22:03:34 +0100 Subject: [PATCH 23/41] =?UTF-8?q?=F0=9F=90=9B=20Re-add=20missing=20filters?= =?UTF-8?q?=20for=20torrent=20widget?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/widgets/torrent/TorrentTile.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/widgets/torrent/TorrentTile.tsx b/src/widgets/torrent/TorrentTile.tsx index 41c0a3996..c55287ef2 100644 --- a/src/widgets/torrent/TorrentTile.tsx +++ b/src/widgets/torrent/TorrentTile.tsx @@ -1,3 +1,4 @@ +import { TorrentState } from '@ctrl/shared-torrent'; import { Badge, Center, @@ -122,7 +123,14 @@ function TorrentTile({ widget }: TorrentTileProps) { ); } - const torrents = data.apps.flatMap((app) => (app.type === 'torrent' ? app.torrents : [])); + const torrents = data.apps + .flatMap((app) => (app.type === 'torrent' ? app.torrents : [])) + .filter((torrent) => (widget.properties.displayCompletedTorrents ? true : !torrent.isCompleted)) + .filter((torrent) => + widget.properties.displayStaleTorrents + ? true + : torrent.isCompleted || torrent.downloadSpeed > 0 + ); const difference = new Date().getTime() - dataUpdatedAt; const duration = dayjs.duration(difference, 'ms'); From 6995396af4f8fb4055677300e5d05a9698cbfddf Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Tue, 31 Jan 2023 20:12:03 +0100 Subject: [PATCH 24/41] =?UTF-8?q?=E2=9C=A8=20Add=20switch=20for=20percenta?= =?UTF-8?q?ges=20in=20dash.=20widget=20#641?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/modules/dashdot.json | 3 +++ src/widgets/dashDot/DashDotGraph.tsx | 20 ++++++++++++++++---- src/widgets/dashDot/DashDotTile.tsx | 7 +++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/public/locales/en/modules/dashdot.json b/public/locales/en/modules/dashdot.json index 9268364f7..5d1e3e623 100644 --- a/public/locales/en/modules/dashdot.json +++ b/public/locales/en/modules/dashdot.json @@ -18,6 +18,9 @@ }, "url": { "label": "Dash. URL" + }, + "usePercentages": { + "label": "Display percentages" } } }, diff --git a/src/widgets/dashDot/DashDotGraph.tsx b/src/widgets/dashDot/DashDotGraph.tsx index 571a021c7..f64ba8140 100644 --- a/src/widgets/dashDot/DashDotGraph.tsx +++ b/src/widgets/dashDot/DashDotGraph.tsx @@ -5,9 +5,15 @@ interface DashDotGraphProps { graph: GraphType; isCompact: boolean; dashDotUrl: string; + usePercentages: boolean; } -export const DashDotGraph = ({ graph, isCompact, dashDotUrl }: DashDotGraphProps) => { +export const DashDotGraph = ({ + graph, + isCompact, + dashDotUrl, + usePercentages, +}: DashDotGraphProps) => { const { classes } = useStyles(); return ( ); }; -const useIframeSrc = (dashDotUrl: string, graph: GraphType, isCompact: boolean) => { +const useIframeSrc = ( + dashDotUrl: string, + graph: GraphType, + isCompact: boolean, + usePercentages: boolean +) => { const { colorScheme, colors, radius } = useMantineTheme(); const surface = (colorScheme === 'dark' ? colors.dark[7] : colors.gray[0]).substring(1); // removes # from hex value @@ -45,7 +56,8 @@ const useIframeSrc = (dashDotUrl: string, graph: GraphType, isCompact: boolean) `&surface=${surface}` + `&gap=${isCompact ? 10 : 5}` + `&innerRadius=${radius.lg}` + - `&multiView=${graph.isMultiView}` + `&multiView=${graph.isMultiView}` + + `&showPercentage=${usePercentages ? 'true' : 'false'}` ); }; diff --git a/src/widgets/dashDot/DashDotTile.tsx b/src/widgets/dashDot/DashDotTile.tsx index 89f83d353..07ff508fc 100644 --- a/src/widgets/dashDot/DashDotTile.tsx +++ b/src/widgets/dashDot/DashDotTile.tsx @@ -25,6 +25,10 @@ const definition = defineWidget({ type: 'switch', defaultValue: true, }, + usePercentages: { + type: 'switch', + defaultValue: false, + }, graphs: { type: 'multi-select', defaultValue: ['cpu', 'memory'], @@ -88,6 +92,8 @@ function DashDotTile({ widget }: DashDotTileProps) { const isCompactNetworkVisible = graphs?.some((g) => g.id === 'network' && isCompact); + const usePercentages = widget?.properties.usePercentages ?? false; + const displayedGraphs = graphs?.filter( (g) => !isCompact || !['network', 'storage'].includes(g.id) ); @@ -109,6 +115,7 @@ function DashDotTile({ widget }: DashDotTileProps) { graph={graph} dashDotUrl={dashDotUrl} isCompact={isCompact} + usePercentages={usePercentages} /> ))} From 5382da6998c492c38ca9f36ddb0ffe7b5cf40364 Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Wed, 1 Feb 2023 12:55:31 +0100 Subject: [PATCH 25/41] =?UTF-8?q?=F0=9F=94=96=20Increment=20version=20numb?= =?UTF-8?q?er?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4cf737dd8..e1738d601 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homarr", - "version": "0.11.3", + "version": "0.11.4", "description": "Homarr - A homepage for your server.", "license": "MIT", "repository": { From 32462ccdea6731990a969cb3f8a9722eda475c47 Mon Sep 17 00:00:00 2001 From: ajnart Date: Thu, 2 Feb 2023 19:13:12 +0900 Subject: [PATCH 26/41] =?UTF-8?q?=F0=9F=9A=A8=20Fix=20imports?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dashboard/Modals/AboutModal/AboutModal.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx b/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx index 13b290a3f..0ea10387c 100644 --- a/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx +++ b/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx @@ -29,11 +29,11 @@ import { InitOptions } from 'i18next'; import { i18n, Trans, useTranslation } from 'next-i18next'; import Image from 'next/image'; import { ReactNode } from 'react'; -import { useConfigContext } from '../../config/provider'; -import { useConfigStore } from '../../config/store'; -import { usePackageAttributesStore } from '../../tools/client/zustands/usePackageAttributesStore'; -import { usePrimaryGradient } from '../layout/useGradient'; -import Credits from '../Settings/Common/Credits'; +import { useConfigContext } from '../../../../config/provider'; +import { useConfigStore } from '../../../../config/store'; +import { usePackageAttributesStore } from '../../../../tools/client/zustands/usePackageAttributesStore'; +import { usePrimaryGradient } from '../../../layout/useGradient'; +import Credits from '../../../Settings/Common/Credits'; interface AboutModalProps { opened: boolean; From 61333d6c3debf9938957220431f65f5deff87d30 Mon Sep 17 00:00:00 2001 From: ajnart Date: Thu, 2 Feb 2023 19:14:17 +0900 Subject: [PATCH 27/41] =?UTF-8?q?=F0=9F=9A=A8=20Fix=20imports?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout/header/Header.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/layout/header/Header.tsx b/src/components/layout/header/Header.tsx index f16d61209..187674d80 100644 --- a/src/components/layout/header/Header.tsx +++ b/src/components/layout/header/Header.tsx @@ -1,4 +1,5 @@ import { Box, createStyles, Group, Header as MantineHeader, Indicator } from '@mantine/core'; +import { useQuery } from '@tanstack/react-query'; import { useEffect, useState } from 'react'; import { REPO_URL } from '../../../../data/constants'; import DockerMenuButton from '../../../modules/Docker/DockerModule'; From eff73d233acfdff34ce7da8500e10bd00833dea7 Mon Sep 17 00:00:00 2001 From: ajnart Date: Thu, 2 Feb 2023 20:48:15 +0900 Subject: [PATCH 28/41] =?UTF-8?q?=F0=9F=9A=91=20Hotfix=20media=20popover?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout/header/Search.tsx | 5 ++++- src/widgets/calendar/MediaList.tsx | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/layout/header/Search.tsx b/src/components/layout/header/Search.tsx index add4cb184..00d9bd38d 100644 --- a/src/components/layout/header/Search.tsx +++ b/src/components/layout/header/Search.tsx @@ -172,7 +172,10 @@ export function Search() { return ( 0 && opened && searchQuery.length > 3} + opened={ + (OverseerrResults && OverseerrResults.length > 0 && opened && searchQuery.length > 3) ?? + false + } position="bottom" withinPortal shadow="md" diff --git a/src/widgets/calendar/MediaList.tsx b/src/widgets/calendar/MediaList.tsx index 153e8608c..c77a413d9 100644 --- a/src/widgets/calendar/MediaList.tsx +++ b/src/widgets/calendar/MediaList.tsx @@ -18,7 +18,7 @@ export const MediaList = ({ medias }: MediaListProps) => { return ( Date: Fri, 3 Feb 2023 21:08:27 +0900 Subject: [PATCH 29/41] =?UTF-8?q?=F0=9F=9A=91=20Hotfix=20preview=20height?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/widgets/calendar/MediaList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/calendar/MediaList.tsx b/src/widgets/calendar/MediaList.tsx index c77a413d9..53b2c5133 100644 --- a/src/widgets/calendar/MediaList.tsx +++ b/src/widgets/calendar/MediaList.tsx @@ -18,7 +18,7 @@ export const MediaList = ({ medias }: MediaListProps) => { return ( Date: Fri, 3 Feb 2023 21:17:23 +0900 Subject: [PATCH 30/41] =?UTF-8?q?=F0=9F=9A=91=20Hotfix=20password=20not=20?= =?UTF-8?q?working?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/middleware.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middleware.ts b/src/middleware.ts index 15b770e8b..c0106cfab 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -2,7 +2,7 @@ import { NextFetchEvent, NextRequest, NextResponse } from 'next/server'; // eslint-disable-next-line consistent-return export function middleware(req: NextRequest, ev: NextFetchEvent) { - const isCorrectPassword = req.cookies.get('password') === process.env.PASSWORD; + const isCorrectPassword = req.cookies.get('password')?.value === process.env.PASSWORD; const url = req.nextUrl.clone(); const skipURL = url.pathname && From e27aa51b4d981ace847b960c28d09470c11f0a6b Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Sat, 4 Feb 2023 20:48:55 +0100 Subject: [PATCH 31/41] =?UTF-8?q?=F0=9F=90=9B=20Fix=20package=20attributes?= =?UTF-8?q?=20readout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/settings/common.json | 7 ++- .../Modals/AboutModal/AboutModal.tsx | 1 + src/components/Settings/Common/Credits.tsx | 51 +++++++++++++++++-- .../zustands/usePackageAttributesStore.ts | 2 +- src/tools/server/getPackageVersion.ts | 24 ++++++--- 5 files changed, 73 insertions(+), 12 deletions(-) diff --git a/public/locales/en/settings/common.json b/public/locales/en/settings/common.json index 193f5195b..7908b2be8 100644 --- a/public/locales/en/settings/common.json +++ b/public/locales/en/settings/common.json @@ -9,7 +9,12 @@ "configTip": "Upload your config file by drag and dropping it onto the page!" }, "credits": { - "madeWithLove": "Made with ❤️ by @" + "madeWithLove": "Made with ❤️ by @", + "thirdPartyContent": "See third party content", + "thirdPartyContentTable": { + "dependencyName": "Dependency", + "dependencyVersion": "Version" + } }, "grow": "Grow grid (take all space)", "layout": { diff --git a/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx b/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx index 0ea10387c..b703f82a6 100644 --- a/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx +++ b/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx @@ -90,6 +90,7 @@ export const AboutModal = ({ opened, closeModal, newVersionAvailable }: AboutMod ))} + {t('layout/modals/about:contact')} diff --git a/src/components/Settings/Common/Credits.tsx b/src/components/Settings/Common/Credits.tsx index e3867d710..e1fc8bd15 100644 --- a/src/components/Settings/Common/Credits.tsx +++ b/src/components/Settings/Common/Credits.tsx @@ -1,11 +1,13 @@ -import { Group, Anchor, Text } from '@mantine/core'; +import { Anchor, Box, Collapse, Flex, Table, Text } from '@mantine/core'; +import { useDisclosure } from '@mantine/hooks'; import { useTranslation } from 'next-i18next'; +import { usePackageAttributesStore } from '../../../tools/client/zustands/usePackageAttributesStore'; export default function Credits() { const { t } = useTranslation('settings/common'); return ( - <Group position="center" mt="xs"> + <Flex justify="center" direction="column" mt="xs"> <Text style={{ fontSize: '0.90rem', @@ -22,6 +24,49 @@ export default function Credits() { </Anchor>{' '} and you! </Text> - </Group> + <DependencyTable /> + </Flex> ); } + +const DependencyTable = () => { + const { t } = useTranslation('settings/common'); + const [opened, { toggle }] = useDisclosure(false); + const { attributes } = usePackageAttributesStore(); + return ( + <> + <Text variant="link" mx="auto" size="xs" opacity={0.3} onClick={toggle}> + {t('credits.thirdPartyContent')} + </Text> + + <Collapse in={opened}> + <Box + sx={(theme) => ({ + backgroundColor: + theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0], + padding: theme.spacing.xl, + borderRadius: theme.radius.md, + })} + mt="md" + > + <Table> + <thead> + <tr> + <th>{t('credits.thirdPartyContentTable.dependencyName')}</th> + <th>{t('credits.thirdPartyContentTable.dependencyVersion')}</th> + </tr> + </thead> + {Object.keys(attributes.dependencies).map((key, index) => ( + <tbody key={`dependency-${index}`}> + <tr> + <td>{key}</td> + <td>{attributes.dependencies[key]}</td> + </tr> + </tbody> + ))} + </Table> + </Box> + </Collapse> + </> + ); +}; diff --git a/src/tools/client/zustands/usePackageAttributesStore.ts b/src/tools/client/zustands/usePackageAttributesStore.ts index 6d710997d..1949cacfd 100644 --- a/src/tools/client/zustands/usePackageAttributesStore.ts +++ b/src/tools/client/zustands/usePackageAttributesStore.ts @@ -8,7 +8,7 @@ interface PackageAttributesState { } export const usePackageAttributesStore = create<PackageAttributesState>((set) => ({ - attributes: { packageVersion: undefined, environment: 'test' }, + attributes: { packageVersion: undefined, environment: 'test', dependencies: {} }, setInitialPackageAttributes(attributes) { set((state) => ({ ...state, attributes })); }, diff --git a/src/tools/server/getPackageVersion.ts b/src/tools/server/getPackageVersion.ts index 0c74d56a7..dfbed1606 100644 --- a/src/tools/server/getPackageVersion.ts +++ b/src/tools/server/getPackageVersion.ts @@ -1,14 +1,24 @@ -const getServerPackageVersion = (): string | undefined => process.env.npm_package_version; +import packageJson from '../../../package.json'; -const getServerNodeEnvironment = (): 'development' | 'production' | 'test' => - process.env.NODE_ENV; +const getServerPackageVersion = (): string | undefined => packageJson.version; -export const getServiceSidePackageAttributes = (): ServerSidePackageAttributesType => ({ - packageVersion: getServerPackageVersion(), - environment: getServerNodeEnvironment(), -}); +const getServerNodeEnvironment = (): 'development' | 'production' | 'test' => process.env.NODE_ENV; + +const getDependencies = (): PackageJsonDependencies => packageJson.dependencies; + +export const getServiceSidePackageAttributes = (): ServerSidePackageAttributesType => { + const result = { + packageVersion: getServerPackageVersion(), + environment: getServerNodeEnvironment(), + dependencies: getDependencies(), + } as ServerSidePackageAttributesType; + return result; +}; export type ServerSidePackageAttributesType = { packageVersion: string | undefined; environment: 'development' | 'production' | 'test'; + dependencies: PackageJsonDependencies; }; + +type PackageJsonDependencies = { [key in string]: string }; From 2588134b58a99dc84b26215d322aa90d5ba78ea2 Mon Sep 17 00:00:00 2001 From: ajnart <ajnart@pm.me> Date: Sun, 5 Feb 2023 19:37:21 +0900 Subject: [PATCH 32/41] Try fixing previews --- src/widgets/calendar/MediaList.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/widgets/calendar/MediaList.tsx b/src/widgets/calendar/MediaList.tsx index 53b2c5133..464ee9cff 100644 --- a/src/widgets/calendar/MediaList.tsx +++ b/src/widgets/calendar/MediaList.tsx @@ -1,4 +1,5 @@ import { createStyles, Divider, ScrollArea } from '@mantine/core'; +import { useViewportSize } from '@mantine/hooks'; import React from 'react'; import { LidarrMediaDisplay, @@ -14,11 +15,19 @@ interface MediaListProps { export const MediaList = ({ medias }: MediaListProps) => { const { classes } = useStyles(); + const { height } = useViewportSize(); const lastMediaType = getLastMediaType(medias); + const MEDIA_HEIGHT = 250; + // Euclidean division to get the number of media displayed + const maxMediaDisplayed = Math.floor(height / MEDIA_HEIGHT); return ( <ScrollArea - style={{ height: 450, minHeight: 210, maxWidth: '90vw' }} + style={{ + height: maxMediaDisplayed <= 5 ? maxMediaDisplayed * MEDIA_HEIGHT : 5 * MEDIA_HEIGHT, + minHeight: 210, + maxWidth: '90vw', + }} offsetScrollbars pt={5} className={classes.scrollArea} From cc9317b31c6f8236dfc131feeac108f028a8049f Mon Sep 17 00:00:00 2001 From: ajnart <ajnart@pm.me> Date: Sun, 5 Feb 2023 21:09:21 +0900 Subject: [PATCH 33/41] Change calendar media style --- src/widgets/calendar/MediaList.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/widgets/calendar/MediaList.tsx b/src/widgets/calendar/MediaList.tsx index 464ee9cff..2aea1f9bb 100644 --- a/src/widgets/calendar/MediaList.tsx +++ b/src/widgets/calendar/MediaList.tsx @@ -17,15 +17,11 @@ export const MediaList = ({ medias }: MediaListProps) => { const { classes } = useStyles(); const { height } = useViewportSize(); const lastMediaType = getLastMediaType(medias); - const MEDIA_HEIGHT = 250; - // Euclidean division to get the number of media displayed - const maxMediaDisplayed = Math.floor(height / MEDIA_HEIGHT); return ( <ScrollArea style={{ - height: maxMediaDisplayed <= 5 ? maxMediaDisplayed * MEDIA_HEIGHT : 5 * MEDIA_HEIGHT, - minHeight: 210, + height: 230, maxWidth: '90vw', }} offsetScrollbars From 5296ce88d2a5e55390a983a3334afce74034aa01 Mon Sep 17 00:00:00 2001 From: Thomas Camlong <49837342+ajnart@users.noreply.github.com> Date: Mon, 6 Feb 2023 01:09:11 +0900 Subject: [PATCH 34/41] =?UTF-8?q?=E2=9C=A8=20=20Add=20sonarr-v4=20compatib?= =?UTF-8?q?ility=20(#689)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/modules/calendar.json | 3 +++ src/modules/common/MediaDisplay.tsx | 6 +++++- src/pages/api/modules/calendar.ts | 22 +++++++++++++--------- src/widgets/calendar/CalendarTile.tsx | 4 ++++ 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/public/locales/en/modules/calendar.json b/public/locales/en/modules/calendar.json index fe7fc32ce..5b3aefe0f 100644 --- a/public/locales/en/modules/calendar.json +++ b/public/locales/en/modules/calendar.json @@ -4,6 +4,9 @@ "description": "Displays a calendar with upcoming releases, from supported integrations.", "settings": { "title": "Settings for Calendar widget", + "useSonarrv4": { + "label": "Use Sonarr v4 API" + }, "sundayStart": { "label": "Start the week on Sunday" }, diff --git a/src/modules/common/MediaDisplay.tsx b/src/modules/common/MediaDisplay.tsx index 61344cf34..453a262e3 100644 --- a/src/modules/common/MediaDisplay.tsx +++ b/src/modules/common/MediaDisplay.tsx @@ -152,6 +152,10 @@ export function RadarrMediaDisplay(props: any) { export function SonarrMediaDisplay(props: any) { const { media }: { media: any } = props; + const { config } = useConfigContext(); + const calendar = config?.widgets.find((w) => w.id === 'calendar'); + const useSonarrv4 = calendar?.properties.useSonarrv4 ?? false; + // Find a poster CoverType const poster = media.series.images.find((image: any) => image.coverType === 'poster'); // Return a movie poster containting the title and the description @@ -162,7 +166,7 @@ export function SonarrMediaDisplay(props: any) { genres: media.series.genres ?? [], overview: media.overview ?? media.series.overview ?? '', title: media.series.title, - poster: poster ? poster.url : undefined, + poster: useSonarrv4 ? poster.remoteUrl : poster.url, episodeNumber: media.episodeNumber, seasonNumber: media.seasonNumber, episodetitle: media.title, diff --git a/src/pages/api/modules/calendar.ts b/src/pages/api/modules/calendar.ts index d454497a9..dd90fc8c8 100644 --- a/src/pages/api/modules/calendar.ts +++ b/src/pages/api/modules/calendar.ts @@ -35,6 +35,10 @@ async function Get(req: NextApiRequest, res: NextApiResponse) { const config = getConfig(configName); + // Find the calendar widget in the config + const calendar = config.widgets.find((w) => w.id === 'calendar'); + const useSonarrv4 = calendar?.properties.useSonarrv4 ?? false; + const mediaAppIntegrationTypes: AppIntegrationType['type'][] = [ 'sonarr', 'radarr', @@ -45,6 +49,13 @@ async function Get(req: NextApiRequest, res: NextApiResponse) { (app) => app.integration && mediaAppIntegrationTypes.includes(app.integration.type) ); + const IntegrationTypeEndpointMap = new Map<AppIntegrationType['type'], string>([ + ['sonarr', useSonarrv4 ? '/api/v3/calendar' : '/api/calendar'], + ['radarr', '/api/v3/calendar'], + ['lidarr', '/api/v1/calendar'], + ['readarr', '/api/v1/calendar'], + ]); + try { const medias = await Promise.all( await mediaApps.map(async (app) => { @@ -71,7 +82,7 @@ async function Get(req: NextApiRequest, res: NextApiResponse) { if (!apiKey) return { type: integration.type, items: [], success: false }; return axios .get( - `${origin}${endpoint}?apiKey=${apiKey}&end=${end.toISOString()}&start=${start.toISOString()}` + `${origin}${endpoint}?apiKey=${apiKey}&end=${end.toISOString()}&start=${start.toISOString()}&includeSeries=true&includeEpisodeFile=true&includeEpisodeImages=true` ) .then((x) => ({ type: integration.type, items: x.data as any[], success: true })) .catch((err) => { @@ -87,7 +98,7 @@ async function Get(req: NextApiRequest, res: NextApiResponse) { }) ); - const countFailed = medias.filter(x => !x.success).length; + const countFailed = medias.filter((x) => !x.success).length; if (countFailed > 0) { Consola.warn(`A total of ${countFailed} apps for the calendar widget failed`); } @@ -111,10 +122,3 @@ async function Get(req: NextApiRequest, res: NextApiResponse) { }); } } - -const IntegrationTypeEndpointMap = new Map<AppIntegrationType['type'], string>([ - ['sonarr', '/api/calendar'], - ['radarr', '/api/v3/calendar'], - ['lidarr', '/api/v1/calendar'], - ['readarr', '/api/v1/calendar'], -]); diff --git a/src/widgets/calendar/CalendarTile.tsx b/src/widgets/calendar/CalendarTile.tsx index f076160e3..f06ca88d2 100644 --- a/src/widgets/calendar/CalendarTile.tsx +++ b/src/widgets/calendar/CalendarTile.tsx @@ -16,6 +16,10 @@ const definition = defineWidget({ id: 'calendar', icon: IconCalendarTime, options: { + useSonarrv4: { + type: 'switch', + defaultValue: false, + }, sundayStart: { type: 'switch', defaultValue: false, From 2539e8cec17f9dce02d0d79eba0d004ff6bd7677 Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Sun, 5 Feb 2023 17:16:03 +0100 Subject: [PATCH 35/41] =?UTF-8?q?=E2=9C=A8=20Custom=20column=20counts=20fo?= =?UTF-8?q?r=20gridstack=20#613=20#660?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next.config.js | 21 +- package.json | 4 + public/locales/en/common.json | 6 + .../header/actions/toggle-edit-mode.json | 5 - public/locales/en/layout/modals/about.json | 14 +- public/locales/en/settings/common.json | 6 +- .../customization/color-selector.json | 1 + .../en/settings/customization/general.json | 21 + .../en/settings/customization/gridstack.json | 10 + .../customization/page-appearance.json | 16 +- .../Modals/AboutModal/AboutModal.tsx | 13 +- .../Dashboard/Wrappers/gridstack/store.tsx | 20 +- .../Customization/CustomizationAccordeon.tsx | 117 + .../Customization/CustomizationSettings.tsx | 43 +- .../Layout/GridstackConfiguration.tsx | 112 + .../Customization/Layout/LayoutSelector.tsx | 233 +- .../Customization/Meta/BackgroundChanger.tsx | 12 +- .../Customization/Meta/FaviconChanger.tsx | 15 +- .../Customization/Meta/LogoImageChanger.tsx | 14 +- .../Customization/Meta/MetaTitleChanger.tsx | 15 +- .../Customization/Meta/PageTitleChanger.tsx | 15 +- .../Customization/Theme/ColorSelector.tsx | 2 +- .../Customization/Theme/CustomCssChanger.tsx | 103 +- .../Customization/Theme/OpacitySelector.tsx | 10 +- .../Customization/Theme/ShadeSelector.tsx | 10 +- .../Actions/ToggleEditMode/ToggleEditMode.tsx | 7 +- src/components/layout/header/Header.tsx | 1 - src/constants/gridstack-breakpoints.ts | 4 + src/pages/_app.tsx | 4 +- src/styles/global.scss | 8 +- src/tools/client/arrays.ts | 1 + src/tools/client/time.ts | 4 + src/tools/server/translation-namespaces.ts | 2 + src/types/settings.ts | 7 + .../TorrentNetworkTrafficTile.tsx | 3 +- src/widgets/torrent/TorrentTile.tsx | 3 +- yarn.lock | 1927 ++++++++++++----- 37 files changed, 2064 insertions(+), 745 deletions(-) create mode 100644 public/locales/en/settings/customization/general.json create mode 100644 public/locales/en/settings/customization/gridstack.json create mode 100644 src/components/Settings/Customization/CustomizationAccordeon.tsx create mode 100644 src/components/Settings/Customization/Layout/GridstackConfiguration.tsx create mode 100644 src/constants/gridstack-breakpoints.ts create mode 100644 src/tools/client/arrays.ts create mode 100644 src/tools/client/time.ts diff --git a/next.config.js b/next.config.js index dde3ec977..0abbde49c 100644 --- a/next.config.js +++ b/next.config.js @@ -1,14 +1,19 @@ const { i18n } = require('./next-i18next.config'); +const removeImports = require('next-remove-imports')(); + const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true', }); -module.exports = withBundleAnalyzer({ - images: { - domains: ['cdn.jsdelivr.net'], - }, - reactStrictMode: true, - output: 'standalone', - i18n, -}); +module.exports = withBundleAnalyzer( + removeImports({ + experimental: { esmExternals: true }, + images: { + domains: ['cdn.jsdelivr.net'], + }, + reactStrictMode: true, + output: 'standalone', + i18n, + }) +); diff --git a/package.json b/package.json index 23e874fde..6a5469457 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@tabler/icons": "^1.106.0", "@tanstack/react-query": "^4.2.1", "@tanstack/react-query-devtools": "^4.24.4", + "@uiw/react-textarea-code-editor": "v1.4.4", "axios": "^0.27.2", "consola": "^2.15.3", "cookies-next": "^2.1.1", @@ -59,6 +60,7 @@ "js-file-download": "^0.4.12", "next": "^13.1.6", "next-i18next": "^11.3.0", + "next-remove-imports": "^1.0.8", "nzbget-api": "^0.0.3", "ping": "^0.4.2", "prism-react-renderer": "^1.3.5", @@ -81,6 +83,8 @@ "@types/uuid": "^8.3.4", "@typescript-eslint/eslint-plugin": "^5.30.7", "@typescript-eslint/parser": "^5.30.7", + "babel-loader": "^9.1.2", + "babel-plugin-transform-remove-imports": "^1.7.0", "eslint": "^8.20.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb-typescript": "^17.0.0", diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 5c14a9ca6..1b78b10a3 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -24,5 +24,11 @@ "seconds": "seconds", "minutes": "minutes", "hours": "hours" + }, + "loading": "Loading...", + "breakPoints": { + "small": "small", + "medium": "medium", + "large": "large" } } \ No newline at end of file diff --git a/public/locales/en/layout/header/actions/toggle-edit-mode.json b/public/locales/en/layout/header/actions/toggle-edit-mode.json index 965eb3b8a..e99d62915 100644 --- a/public/locales/en/layout/header/actions/toggle-edit-mode.json +++ b/public/locales/en/layout/header/actions/toggle-edit-mode.json @@ -7,10 +7,5 @@ "popover": { "title": "Edit mode is enabled for <1>{{size}}</1> size", "text": "You can adjust and configure your apps now. Changes are <strong>not saved</strong> until you exit edit mode" - }, - "screenSizes": { - "small": "small", - "medium": "medium", - "large": "large" } } diff --git a/public/locales/en/layout/modals/about.json b/public/locales/en/layout/modals/about.json index fa0044a91..2263dc21f 100644 --- a/public/locales/en/layout/modals/about.json +++ b/public/locales/en/layout/modals/about.json @@ -1,7 +1,13 @@ { "description": "Homarr is a <strong>sleek</strong>, <strong>modern</strong> dashboard that puts all of your apps and services at your fingertips. With Homarr, you can access and control everything in one convenient location. Homarr seamlessly integrates with the apps you've added, providing you with valuable information and giving you complete control. Installation is a breeze, and Homarr supports a wide range of deployment methods.", - "i18n": "Loaded I18n translation namespaces", - "locales": "Configured I18n locales", "contact": "Having trouble or questions? Connect with us!", - "addToDashboard": "Add to Dashboard" -} + "addToDashboard": "Add to Dashboard", + "metrics": { + "configurationSchemaVersion": "Configuration schema version", + "configurationsCount": "Available configurations", + "version": "Version", + "nodeEnvironment": "Node environment", + "i18n": "Loaded I18n translation namespaces", + "locales": "Configured I18n locales" + } +} \ No newline at end of file diff --git a/public/locales/en/settings/common.json b/public/locales/en/settings/common.json index 7908b2be8..b5b0a488c 100644 --- a/public/locales/en/settings/common.json +++ b/public/locales/en/settings/common.json @@ -18,7 +18,11 @@ }, "grow": "Grow grid (take all space)", "layout": { - "title": "Dashboard layout", + "preview": { + "title": "Preview", + "subtitle": "Changes will be saved automatically" + }, + "divider": "Layout options", "main": "Main", "sidebar": "Sidebar", "cannotturnoff": "Cannot be turned off", diff --git a/public/locales/en/settings/customization/color-selector.json b/public/locales/en/settings/customization/color-selector.json index d66bbfe6e..19f42e95b 100644 --- a/public/locales/en/settings/customization/color-selector.json +++ b/public/locales/en/settings/customization/color-selector.json @@ -1,3 +1,4 @@ { + "colors": "Colors", "suffix": "{{color}} color" } \ No newline at end of file diff --git a/public/locales/en/settings/customization/general.json b/public/locales/en/settings/customization/general.json new file mode 100644 index 000000000..0215ba5e8 --- /dev/null +++ b/public/locales/en/settings/customization/general.json @@ -0,0 +1,21 @@ +{ + "text": "Customizations allow you to configure and adjust your experience with Homarr to your preferences.", + "accordeon": { + "layout": { + "name": "Layout", + "description": "Enable and disable elements on your header and dashboard tiles" + }, + "gridstack": { + "name": "Gridstack", + "description": "Customize the behaviour and columns of your dashboard area" + }, + "pageMetadata": { + "name": "Page Metadata", + "description": "Adjust titles, logo and PWA" + }, + "appereance": { + "name": "Appereance", + "description": "Customize the background, colors and apps appereance" + } + } +} \ No newline at end of file diff --git a/public/locales/en/settings/customization/gridstack.json b/public/locales/en/settings/customization/gridstack.json new file mode 100644 index 000000000..b98d62960 --- /dev/null +++ b/public/locales/en/settings/customization/gridstack.json @@ -0,0 +1,10 @@ +{ + "columnsCount": { + "labelPreset": "Columns in {{size}} size", + "descriptionPreset": "Number of columns when the screen is less than {{pixels}} pixels wide", + "descriptionExceedsPreset": "Number of columns when the screen size exceeds {{pixels}} pixels" + }, + "unsavedChanges": "You have unsaved changes. Click the Apply changes button below to apply and save.", + "applyChanges": "Apply changes", + "defaultValues": "Default values" +} \ No newline at end of file diff --git a/public/locales/en/settings/customization/page-appearance.json b/public/locales/en/settings/customization/page-appearance.json index 459154ceb..92c9dc632 100644 --- a/public/locales/en/settings/customization/page-appearance.json +++ b/public/locales/en/settings/customization/page-appearance.json @@ -1,22 +1,28 @@ { "pageTitle": { - "label": "Page Title" + "label": "Page Title", + "description": "The dashboard title at the top left" }, "metaTitle": { - "label": "Meta Title" + "label": "Meta Title", + "description": "The title, that is being displayed as your tab name" }, "logo": { - "label": "Logo" + "label": "Logo", + "description": "The dashboard logo at the top left" }, "favicon": { - "label": "Favicon" + "label": "Favicon", + "description": "The icon, that is being used in front of your tab name" }, "background": { "label": "Background" }, "customCSS": { "label": "Custom CSS", - "placeholder": "Custom CSS will be applied last" + "description": "Customize all elements on your dashboard, only recommended for experienced users", + "placeholder": "Custom CSS will be applied last", + "applying": "Applying CSS..." }, "buttons": { "submit": "Submit" diff --git a/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx b/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx index b703f82a6..3d36ca722 100644 --- a/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx +++ b/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx @@ -82,7 +82,7 @@ export const AboutModal = ({ opened, closeModal, newVersionAvailable }: AboutMod <ActionIcon className={classes.informationIcon} variant="default"> {item.icon} </ActionIcon> - {t(item.label)} + {t(`layout/modals/about:metrics.${item.label}`)} </Group> </td> <td className={classes.informationTableColumn}>{item.content}</td> @@ -151,7 +151,6 @@ interface ExtendedInitOptions extends InitOptions { } const useInformationTableItems = (newVersionAvailable?: string): InformationTableItem[] => { - // TODO: Fix this to not request. Pass it as a prop. const colorGradiant = usePrimaryGradient(); const { attributes } = usePackageAttributesStore(); @@ -168,7 +167,7 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl ...items, { icon: <IconLanguage size={20} />, - label: 'layout/modals/about:i18n', + label: 'i18n', content: ( <Badge variant="gradient" gradient={colorGradiant}> {usedI18nNamespaces.length} @@ -177,7 +176,7 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl }, { icon: <IconVocabulary size={20} />, - label: 'layout/modals/about:locales', + label: 'locales', content: ( <Badge variant="gradient" gradient={colorGradiant}> {initOptions.locales.length} @@ -190,7 +189,7 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl items = [ { icon: <IconSchema size={20} />, - label: 'Configuration schema version', + label: 'configurationSchemaVersion', content: ( <Badge variant="gradient" gradient={colorGradiant}> {configVersion} @@ -199,7 +198,7 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl }, { icon: <IconFile size={20} />, - label: 'Available configurations', + label: 'configurationsCount', content: ( <Badge variant="gradient" gradient={colorGradiant}> {configs.length} @@ -249,7 +248,7 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl }, { icon: <IconAnchor size={20} />, - label: 'Node environment', + label: 'nodeEnvironment', content: ( <Badge variant="gradient" gradient={colorGradiant}> {attributes.environment} diff --git a/src/components/Dashboard/Wrappers/gridstack/store.tsx b/src/components/Dashboard/Wrappers/gridstack/store.tsx index 6df8c2f75..084d9de75 100644 --- a/src/components/Dashboard/Wrappers/gridstack/store.tsx +++ b/src/components/Dashboard/Wrappers/gridstack/store.tsx @@ -1,5 +1,7 @@ import { useMantineTheme } from '@mantine/core'; import create from 'zustand'; +import { useConfigContext } from '../../../../config/provider'; +import { GridstackBreakpoints } from '../../../../constants/gridstack-breakpoints'; export const useGridstackStore = create<GridstackStoreType>((set, get) => ({ mainAreaWidth: null, @@ -27,18 +29,28 @@ export const useNamedWrapperColumnCount = (): 'small' | 'medium' | 'large' | nul }; export const useWrapperColumnCount = () => { + const { config } = useConfigContext(); + + if (!config) { + return null; + } + switch (useNamedWrapperColumnCount()) { case 'large': - return 12; + return config.settings.customization.gridstack?.columnCountLarge ?? 12; case 'medium': - return 6; + return config.settings.customization.gridstack?.columnCountMedium ?? 6; case 'small': - return 3; + return config.settings.customization.gridstack?.columnCountSmall ?? 3; default: return null; } }; function getCurrentShapeSize(size: number) { - return size >= 1400 ? 'lg' : size >= 768 ? 'md' : 'sm'; + return size >= GridstackBreakpoints.large + ? 'lg' + : size >= GridstackBreakpoints.medium + ? 'md' + : 'sm'; } diff --git a/src/components/Settings/Customization/CustomizationAccordeon.tsx b/src/components/Settings/Customization/CustomizationAccordeon.tsx new file mode 100644 index 000000000..5aab48bdc --- /dev/null +++ b/src/components/Settings/Customization/CustomizationAccordeon.tsx @@ -0,0 +1,117 @@ +import { Accordion, Grid, Group, Stack, Text } from '@mantine/core'; +import { IconBrush, IconChartCandle, IconDragDrop, IconLayout } from '@tabler/icons'; +import { useTranslation } from 'next-i18next'; +import { ReactNode } from 'react'; +import { GridstackConfiguration } from './Layout/GridstackConfiguration'; +import { LayoutSelector } from './Layout/LayoutSelector'; +import { BackgroundChanger } from './Meta/BackgroundChanger'; +import { FaviconChanger } from './Meta/FaviconChanger'; +import { LogoImageChanger } from './Meta/LogoImageChanger'; +import { BrowserTabTitle } from './Meta/MetaTitleChanger'; +import { DashboardTitleChanger } from './Meta/PageTitleChanger'; +import { ColorSelector } from './Theme/ColorSelector'; +import { CustomCssChanger } from './Theme/CustomCssChanger'; +import { DashboardTilesOpacitySelector } from './Theme/OpacitySelector'; +import { ShadeSelector } from './Theme/ShadeSelector'; + +export const CustomizationSettingsAccordeon = () => { + const items = getItems().map((item) => ( + <Accordion.Item value={item.id} key={item.label}> + <Accordion.Control> + <AccordionLabel {...item} /> + </Accordion.Control> + <Accordion.Panel> + <Text size="sm">{item.content}</Text> + </Accordion.Panel> + </Accordion.Item> + )); + return ( + <Accordion variant="contained" chevronPosition="right"> + {items} + </Accordion> + ); +}; + +interface AccordionLabelProps { + label: string; + image: ReactNode; + description: string; +} + +const AccordionLabel = ({ label, image, description }: AccordionLabelProps) => ( + <Group noWrap> + {image} + <div> + <Text>{label}</Text> + <Text size="sm" color="dimmed" weight={400}> + {description} + </Text> + </div> + </Group> +); + +const getItems = () => { + const { t } = useTranslation([ + 'settings/customization/general', + 'settings/customization/color-selector', + ]); + return [ + { + id: 'layout', + image: <IconLayout />, + label: t('accordeon.layout.name'), + description: t('accordeon.layout.description'), + content: <LayoutSelector />, + }, + { + id: 'gridstack', + image: <IconDragDrop />, + label: t('accordeon.gridstack.name'), + description: t('accordeon.gridstack.description'), + content: <GridstackConfiguration />, + }, + { + id: 'page_metadata', + image: <IconChartCandle />, + label: t('accordeon.pageMetadata.name'), + description: t('accordeon.pageMetadata.description'), + content: ( + <> + <DashboardTitleChanger /> + <BrowserTabTitle /> + <LogoImageChanger /> + <FaviconChanger /> + </> + ), + }, + { + id: 'appereance', + image: <IconBrush />, + label: t('accordeon.appereance.name'), + description: t('accordeon.appereance.description'), + content: ( + <> + <BackgroundChanger /> + + <Stack spacing="xs" my="md"> + <Text>{t('settings/customization/color-selector:colors')}</Text> + <Grid> + <Grid.Col sm={12} md={6}> + <ColorSelector type="primary" defaultValue="red" /> + </Grid.Col> + <Grid.Col sm={12} md={6}> + <ColorSelector type="secondary" defaultValue="orange" /> + </Grid.Col> + <Grid.Col sm={12} md={6}> + <ShadeSelector /> + </Grid.Col> + </Grid> + </Stack> + + <DashboardTilesOpacitySelector /> + <CustomCssChanger /> + </> + ), + }, + ]; +}; diff --git a/src/components/Settings/Customization/CustomizationSettings.tsx b/src/components/Settings/Customization/CustomizationSettings.tsx index 8265ac4a6..f7dbc327d 100644 --- a/src/components/Settings/Customization/CustomizationSettings.tsx +++ b/src/components/Settings/Customization/CustomizationSettings.tsx @@ -1,46 +1,19 @@ -import { ScrollArea, Stack } from '@mantine/core'; +import { ScrollArea, Stack, Text } from '@mantine/core'; import { useViewportSize } from '@mantine/hooks'; import { useTranslation } from 'next-i18next'; -import { useConfigContext } from '../../../config/provider'; -import { useConfigStore } from '../../../config/store'; -import { LayoutSelector } from './Layout/LayoutSelector'; -import { BackgroundChanger } from './Meta/BackgroundChanger'; -import { FaviconChanger } from './Meta/FaviconChanger'; -import { LogoImageChanger } from './Meta/LogoImageChanger'; -import { MetaTitleChanger } from './Meta/MetaTitleChanger'; -import { PageTitleChanger } from './Meta/PageTitleChanger'; -import { ColorSelector } from './Theme/ColorSelector'; -import { CustomCssChanger } from './Theme/CustomCssChanger'; -import { OpacitySelector } from './Theme/OpacitySelector'; -import { ShadeSelector } from './Theme/ShadeSelector'; +import { CustomizationSettingsAccordeon } from './CustomizationAccordeon'; export default function CustomizationSettings() { - const { config, name: configName } = useConfigContext(); - const { t } = useTranslation('common'); - const { height, width } = useViewportSize(); - - const { updateConfig } = useConfigStore(); + const { height } = useViewportSize(); + const { t } = useTranslation('settings/customization/general'); return ( <ScrollArea style={{ height: height - 100 }} offsetScrollbars> <Stack mt="xs" mb="md" spacing="xs"> - <LayoutSelector defaultLayout={config?.settings.customization.layout} /> - <PageTitleChanger defaultValue={config?.settings.customization.pageTitle} /> - <MetaTitleChanger defaultValue={config?.settings.customization.metaTitle} /> - <LogoImageChanger defaultValue={config?.settings.customization.logoImageUrl} /> - <FaviconChanger defaultValue={config?.settings.customization.faviconUrl} /> - <BackgroundChanger defaultValue={config?.settings.customization.backgroundImageUrl} /> - <CustomCssChanger defaultValue={config?.settings.customization.customCss} /> - <ColorSelector - type="primary" - defaultValue={config?.settings.customization.colors.primary} - /> - <ColorSelector - type="secondary" - defaultValue={config?.settings.customization.colors.secondary} - /> - <ShadeSelector defaultValue={config?.settings.customization.colors.shade} /> - <OpacitySelector defaultValue={config?.settings.customization.appOpacity} /> + <Text color="dimmed"> + {t('text')} + </Text> + <CustomizationSettingsAccordeon /> </Stack> </ScrollArea> ); diff --git a/src/components/Settings/Customization/Layout/GridstackConfiguration.tsx b/src/components/Settings/Customization/Layout/GridstackConfiguration.tsx new file mode 100644 index 000000000..8fe1d1b46 --- /dev/null +++ b/src/components/Settings/Customization/Layout/GridstackConfiguration.tsx @@ -0,0 +1,112 @@ +import { Alert, Button, Grid, Input, LoadingOverlay, Slider } from '@mantine/core'; +import { useForm } from '@mantine/form'; +import { IconCheck, IconReload } from '@tabler/icons'; +import { useTranslation } from 'next-i18next'; +import { useState } from 'react'; +import { useConfigContext } from '../../../../config/provider'; +import { useConfigStore } from '../../../../config/store'; +import { GridstackBreakpoints } from '../../../../constants/gridstack-breakpoints'; +import { sleep } from '../../../../tools/client/time'; +import { GridstackSettingsType } from '../../../../types/settings'; + +export const GridstackConfiguration = () => { + const { t } = useTranslation(['settings/customization/gridstack', 'common']); + const { config, name: configName } = useConfigContext(); + const updateConfig = useConfigStore((x) => x.updateConfig); + + if (!config || !configName) { + return null; + } + + const initialValue = config.settings.customization?.gridstack ?? { + columnCountSmall: 3, + columnCountMedium: 6, + columnCountLarge: 12, + }; + + const form = useForm({ + initialValues: initialValue, + }); + + const [isSaving, setIsSaving] = useState(false); + + const handleSubmit = async (values: GridstackSettingsType) => { + setIsSaving(true); + + await sleep(250); + await updateConfig( + configName, + (previousConfig) => ({ + ...previousConfig, + settings: { + ...previousConfig.settings, + customization: { + ...previousConfig.settings.customization, + gridstack: values, + }, + }, + }), + true, + true + ); + + form.resetDirty(); + setIsSaving(false); + }; + + return ( + <form onSubmit={form.onSubmit(handleSubmit)} style={{ position: 'relative' }}> + <LoadingOverlay overlayBlur={2} visible={isSaving} radius="md" /> + <Input.Wrapper + label={t('columnsCount.labelPreset', { size: t('common:breakPoints.small') })} + description={t('columnsCount.descriptionPreset', { pixels: GridstackBreakpoints.medium })} + mb="md" + > + <Slider min={1} max={8} mt="xs" {...form.getInputProps('columnCountSmall')} /> + </Input.Wrapper> + <Input.Wrapper + label={t('columnsCount.labelPreset', { size: t('common:breakPoints.medium') })} + description={t('columnsCount.descriptionPreset', { pixels: GridstackBreakpoints.large })} + mb="md" + > + <Slider min={3} max={16} mt="xs" {...form.getInputProps('columnCountMedium')} /> + </Input.Wrapper> + <Input.Wrapper + label={t('columnsCount.labelPreset', { size: t('common:breakPoints.large') })} + description={t('columnsCount.descriptionExceedsPreset', { + pixels: GridstackBreakpoints.large, + })} + > + <Slider min={5} max={20} mt="xs" {...form.getInputProps('columnCountLarge')} /> + </Input.Wrapper> + {form.isDirty() && ( + <Alert variant="light" color="yellow" title="Unsaved changes" my="md"> + {t('unsavedChanges')} + </Alert> + )} + <Grid mt="md"> + <Grid.Col md={6} xs={12}> + <Button variant="light" leftIcon={<IconCheck size={18} />} type="submit" fullWidth> + {t('applyChanges')} + </Button> + </Grid.Col> + <Grid.Col md={6} xs={12}> + <Button + variant="light" + leftIcon={<IconReload size={18} />} + onClick={() => + form.setValues({ + columnCountSmall: 3, + columnCountMedium: 6, + columnCountLarge: 12, + }) + } + fullWidth + > + {t('defaultValues')} + </Button> + </Grid.Col> + </Grid> + </form> + ); +}; diff --git a/src/components/Settings/Customization/Layout/LayoutSelector.tsx b/src/components/Settings/Customization/Layout/LayoutSelector.tsx index 94e380827..3b05524eb 100644 --- a/src/components/Settings/Customization/Layout/LayoutSelector.tsx +++ b/src/components/Settings/Customization/Layout/LayoutSelector.tsx @@ -1,43 +1,39 @@ import { - ActionIcon, Checkbox, createStyles, + Divider, Flex, Group, + Indicator, Paper, Stack, Text, Title, - useMantineTheme, } from '@mantine/core'; import { useTranslation } from 'next-i18next'; import { ChangeEvent, Dispatch, SetStateAction, useState } from 'react'; import { useConfigContext } from '../../../../config/provider'; import { useConfigStore } from '../../../../config/store'; +import { createDummyArray } from '../../../../tools/client/arrays'; import { CustomizationSettingsType } from '../../../../types/settings'; import { Logo } from '../../../layout/Logo'; -interface LayoutSelectorProps { - defaultLayout: CustomizationSettingsType['layout'] | undefined; -} - -// TODO: add translations -export const LayoutSelector = ({ defaultLayout }: LayoutSelectorProps) => { +export const LayoutSelector = () => { const { classes } = useStyles(); - const { name: configName } = useConfigContext(); + const { config, name: configName } = useConfigContext(); const updateConfig = useConfigStore((x) => x.updateConfig); - const [leftSidebar, setLeftSidebar] = useState(defaultLayout?.enabledLeftSidebar ?? true); - const [rightSidebar, setRightSidebar] = useState(defaultLayout?.enabledRightSidebar ?? true); - const [docker, setDocker] = useState(defaultLayout?.enabledDocker ?? false); - const [ping, setPing] = useState(defaultLayout?.enabledPing ?? false); - const [searchBar, setSearchBar] = useState(defaultLayout?.enabledSearchbar ?? false); + const layoutSettings = config?.settings.customization.layout; - const { colors, colorScheme } = useMantineTheme(); + const [leftSidebar, setLeftSidebar] = useState(layoutSettings?.enabledLeftSidebar ?? true); + const [rightSidebar, setRightSidebar] = useState(layoutSettings?.enabledRightSidebar ?? true); + const [docker, setDocker] = useState(layoutSettings?.enabledDocker ?? false); + const [ping, setPing] = useState(layoutSettings?.enabledPing ?? false); + const [searchBar, setSearchBar] = useState(layoutSettings?.enabledSearchbar ?? false); const { t } = useTranslation('settings/common'); - if (!configName) return null; + if (!configName || !config) return null; const handleChange = ( key: keyof CustomizationSettingsType['layout'], @@ -68,99 +64,140 @@ export const LayoutSelector = ({ defaultLayout }: LayoutSelectorProps) => { ); }; + const enabledPing = layoutSettings?.enabledPing ?? false; + return ( - <Stack spacing="xs"> - <Title order={6}>{t('layout.title')} - - - - - - {searchBar ? ( - - ) : null} - {docker ? : null} + <> + + {t('layout.preview.title')} + + {t('layout.preview.subtitle')} + + + + + + + + {searchBar && } + {docker && } + - - - - - {leftSidebar && ( - - - {t('layout.sidebar')} - - Only for -
- apps &
- integrations -
-
-
- )} - - - {t('layout.main')} - - {t('layout.cannotturnoff')} - - {rightSidebar && ( - - - {t('layout.sidebar')} - - Only for -
- apps &
- integrations -
+ + {leftSidebar && ( + + + {createDummyArray(5).map((item, index) => ( + + ))} + + + )} + + + + {createDummyArray(10).map((item, index) => ( + + ))} - )} -
- - handleChange('enabledLeftSidebar', ev, setLeftSidebar)} - /> - handleChange('enabledRightSidebar', ev, setRightSidebar)} - /> - handleChange('enabledSearchbar', ev, setSearchBar)} - /> - handleChange('enabledDocker', ev, setDocker)} - /> - handleChange('enabledPing', ev, setPing)} - /> + {rightSidebar && ( + + + {createDummyArray(5).map((item, index) => ( + + ))} + + + )} + + + + + handleChange('enabledLeftSidebar', ev, setLeftSidebar)} + /> + handleChange('enabledRightSidebar', ev, setRightSidebar)} + /> + handleChange('enabledSearchbar', ev, setSearchBar)} + /> + handleChange('enabledDocker', ev, setDocker)} + /> + handleChange('enabledPing', ev, setPing)} + /> + - + ); }; +const BaseElement = ({ height, width }: { height: number; width: number }) => ( + ({ + backgroundColor: theme.colorScheme === 'dark' ? theme.colors.gray[8] : theme.colors.gray[1], + })} + h={height} + p={2} + w={width} + /> +); + +const PlaceholderElement = (props: any) => { + const { height, width, hasPing, index } = props; + + if (hasPing) { + return ( + + + + ); + } + + return ; +}; + const useStyles = createStyles((theme) => ({ primaryWrapper: { flexGrow: 2, diff --git a/src/components/Settings/Customization/Meta/BackgroundChanger.tsx b/src/components/Settings/Customization/Meta/BackgroundChanger.tsx index 5c3efabd2..3bbad0908 100644 --- a/src/components/Settings/Customization/Meta/BackgroundChanger.tsx +++ b/src/components/Settings/Customization/Meta/BackgroundChanger.tsx @@ -4,15 +4,13 @@ import { ChangeEventHandler, useState } from 'react'; import { useConfigContext } from '../../../../config/provider'; import { useConfigStore } from '../../../../config/store'; -interface BackgroundChangerProps { - defaultValue: string | undefined; -} - -export const BackgroundChanger = ({ defaultValue }: BackgroundChangerProps) => { +export const BackgroundChanger = () => { const { t } = useTranslation('settings/customization/page-appearance'); const updateConfig = useConfigStore((x) => x.updateConfig); - const { name: configName } = useConfigContext(); - const [backgroundImageUrl, setBackgroundImageUrl] = useState(defaultValue); + const { config, name: configName } = useConfigContext(); + const [backgroundImageUrl, setBackgroundImageUrl] = useState( + config?.settings.customization.backgroundImageUrl + ); if (!configName) return null; diff --git a/src/components/Settings/Customization/Meta/FaviconChanger.tsx b/src/components/Settings/Customization/Meta/FaviconChanger.tsx index 7bb71f815..f92252244 100644 --- a/src/components/Settings/Customization/Meta/FaviconChanger.tsx +++ b/src/components/Settings/Customization/Meta/FaviconChanger.tsx @@ -4,21 +4,19 @@ import { ChangeEventHandler, useState } from 'react'; import { useConfigContext } from '../../../../config/provider'; import { useConfigStore } from '../../../../config/store'; -interface FaviconChangerProps { - defaultValue: string | undefined; -} - -export const FaviconChanger = ({ defaultValue }: FaviconChangerProps) => { +export const FaviconChanger = () => { const { t } = useTranslation('settings/customization/page-appearance'); const updateConfig = useConfigStore((x) => x.updateConfig); - const { name: configName } = useConfigContext(); - const [faviconUrl, setFaviconUrl] = useState(defaultValue); + const { config, name: configName } = useConfigContext(); + const [faviconUrl, setFaviconUrl] = useState( + config?.settings.customization.faviconUrl ?? '/imgs/favicon/favicon-squared.png' + ); if (!configName) return null; const handleChange: ChangeEventHandler = (ev) => { const { value } = ev.currentTarget; - const faviconUrl = value.trim().length === 0 ? undefined : value; + const faviconUrl = value.trim(); setFaviconUrl(faviconUrl); updateConfig(configName, (prev) => ({ ...prev, @@ -35,6 +33,7 @@ export const FaviconChanger = ({ defaultValue }: FaviconChangerProps) => { return ( { +export const LogoImageChanger = () => { const { t } = useTranslation('settings/customization/page-appearance'); const updateConfig = useConfigStore((x) => x.updateConfig); - const { name: configName } = useConfigContext(); - const [logoImageSrc, setLogoImageSrc] = useState(defaultValue); + const { config, name: configName } = useConfigContext(); + const [logoImageSrc, setLogoImageSrc] = useState(config?.settings.customization.logoImageUrl ?? '/imgs/logo/logo.png'); if (!configName) return null; const handleChange: ChangeEventHandler = (ev) => { const { value } = ev.currentTarget; - const logoImageSrc = value.trim().length === 0 ? undefined : value; + const logoImageSrc = value.trim(); setLogoImageSrc(logoImageSrc); updateConfig(configName, (prev) => ({ ...prev, @@ -35,9 +31,11 @@ export const LogoImageChanger = ({ defaultValue }: LogoImageChangerProps) => { return ( ); }; diff --git a/src/components/Settings/Customization/Meta/MetaTitleChanger.tsx b/src/components/Settings/Customization/Meta/MetaTitleChanger.tsx index 3e871a9b8..d9e0b440f 100644 --- a/src/components/Settings/Customization/Meta/MetaTitleChanger.tsx +++ b/src/components/Settings/Customization/Meta/MetaTitleChanger.tsx @@ -4,22 +4,17 @@ import { ChangeEventHandler, useState } from 'react'; import { useConfigContext } from '../../../../config/provider'; import { useConfigStore } from '../../../../config/store'; -interface MetaTitleChangerProps { - defaultValue: string | undefined; -} - -// TODO: change to pageTitle -export const MetaTitleChanger = ({ defaultValue }: MetaTitleChangerProps) => { +export const BrowserTabTitle = () => { const { t } = useTranslation('settings/customization/page-appearance'); const updateConfig = useConfigStore((x) => x.updateConfig); - const { name: configName } = useConfigContext(); - const [metaTitle, setMetaTitle] = useState(defaultValue); + const { config, name: configName } = useConfigContext(); + const [metaTitle, setMetaTitle] = useState(config?.settings.customization.metaTitle ?? ''); if (!configName) return null; const handleChange: ChangeEventHandler = (ev) => { const { value } = ev.currentTarget; - const metaTitle = value.trim().length === 0 ? undefined : value; + const metaTitle = value.trim(); setMetaTitle(metaTitle); updateConfig(configName, (prev) => ({ ...prev, @@ -36,9 +31,11 @@ export const MetaTitleChanger = ({ defaultValue }: MetaTitleChangerProps) => { return ( ); }; diff --git a/src/components/Settings/Customization/Meta/PageTitleChanger.tsx b/src/components/Settings/Customization/Meta/PageTitleChanger.tsx index c2febd0ed..eb1609a99 100644 --- a/src/components/Settings/Customization/Meta/PageTitleChanger.tsx +++ b/src/components/Settings/Customization/Meta/PageTitleChanger.tsx @@ -4,22 +4,17 @@ import { ChangeEventHandler, useState } from 'react'; import { useConfigContext } from '../../../../config/provider'; import { useConfigStore } from '../../../../config/store'; -interface PageTitleChangerProps { - defaultValue: string | undefined; -} - -// TODO: change to dashboard title -export const PageTitleChanger = ({ defaultValue }: PageTitleChangerProps) => { +export const DashboardTitleChanger = () => { const { t } = useTranslation('settings/customization/page-appearance'); const updateConfig = useConfigStore((x) => x.updateConfig); - const { name: configName } = useConfigContext(); - const [pageTitle, setPageTitle] = useState(defaultValue); + const { config, name: configName } = useConfigContext(); + const [pageTitle, setPageTitle] = useState(config?.settings.customization.pageTitle ?? ''); if (!configName) return null; const handleChange: ChangeEventHandler = (ev) => { const { value } = ev.currentTarget; - const pageTitle = value.trim().length === 0 ? undefined : value; + const pageTitle = value.trim(); setPageTitle(pageTitle); updateConfig(configName, (prev) => ({ ...prev, @@ -36,9 +31,11 @@ export const PageTitleChanger = ({ defaultValue }: PageTitleChangerProps) => { return ( ); }; diff --git a/src/components/Settings/Customization/Theme/ColorSelector.tsx b/src/components/Settings/Customization/Theme/ColorSelector.tsx index 053a7d386..a8725e9c7 100644 --- a/src/components/Settings/Customization/Theme/ColorSelector.tsx +++ b/src/components/Settings/Customization/Theme/ColorSelector.tsx @@ -24,7 +24,7 @@ export function ColorSelector({ type, defaultValue }: ColorControlProps) { const [color, setColor] = useState(defaultValue); const [popoverOpened, popover] = useDisclosure(false); const { setPrimaryColor, setSecondaryColor } = useColorTheme(); - const { name: configName } = useConfigContext(); + const { config, name: configName } = useConfigContext(); const updateConfig = useConfigStore((x) => x.updateConfig); const theme = useMantineTheme(); diff --git a/src/components/Settings/Customization/Theme/CustomCssChanger.tsx b/src/components/Settings/Customization/Theme/CustomCssChanger.tsx index 5b8db8de1..97155a688 100644 --- a/src/components/Settings/Customization/Theme/CustomCssChanger.tsx +++ b/src/components/Settings/Customization/Theme/CustomCssChanger.tsx @@ -1,44 +1,105 @@ -import { Textarea } from '@mantine/core'; +import { + Box, + createStyles, + Group, + Loader, + ScrollArea, + Stack, + Text, + useMantineTheme, +} from '@mantine/core'; +import { useDebouncedValue } from '@mantine/hooks'; import { useTranslation } from 'next-i18next'; -import { ChangeEventHandler, useState } from 'react'; +import dynamic from 'next/dynamic'; +import { ChangeEvent, useEffect, useState } from 'react'; import { useConfigContext } from '../../../../config/provider'; import { useConfigStore } from '../../../../config/store'; -interface CustomCssChangerProps { - defaultValue: string | undefined; -} +const CodeEditor = dynamic( + () => import('@uiw/react-textarea-code-editor').then((mod) => mod.default), + { ssr: false } +); -export const CustomCssChanger = ({ defaultValue }: CustomCssChangerProps) => { +export const CustomCssChanger = () => { const { t } = useTranslation('settings/customization/page-appearance'); const updateConfig = useConfigStore((x) => x.updateConfig); - const { name: configName } = useConfigContext(); - const [customCss, setCustomCss] = useState(defaultValue); + const { colorScheme, colors } = useMantineTheme(); + const { config, name: configName } = useConfigContext(); + const [nonDebouncedCustomCSS, setNonDebouncedCustomCSS] = useState( + config?.settings.customization.customCss ?? '' + ); + const [debouncedCustomCSS] = useDebouncedValue(nonDebouncedCustomCSS, 696); + const { classes } = useStyles(); if (!configName) return null; - const handleChange: ChangeEventHandler = (ev) => { - const { value } = ev.currentTarget; - const customCss = value.trim().length === 0 ? undefined : value; - setCustomCss(customCss); + useEffect(() => { updateConfig(configName, (prev) => ({ ...prev, settings: { ...prev.settings, customization: { ...prev.settings.customization, - customCss, + customCss: debouncedCustomCSS, }, }, })); - }; + }, [debouncedCustomCSS]); + + const codeIsDirty = nonDebouncedCustomCSS !== debouncedCustomCSS; + const codeEditorHeight = codeIsDirty ? 250 - 42 : 250; return ( -