From fd7a73c1e716b16adfb931184fd731ba106de77d Mon Sep 17 00:00:00 2001 From: ajnart Date: Tue, 31 Jan 2023 10:06:24 +0900 Subject: [PATCH 01/12] 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 ( {mapMedias(medias.tvShows, SonarrMediaDisplay, lastMediaType === 'tv-show')} {mapMedias(medias.movies, RadarrMediaDisplay, lastMediaType === 'movie')} From 87909c27c0c37633c29e2796222369d4f16b359a Mon Sep 17 00:00:00 2001 From: ajnart Date: Tue, 31 Jan 2023 10:35:17 +0900 Subject: [PATCH 02/12] =?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' && ( - + s{media.seasonNumber}e{media.episodeNumber} - {media.episodetitle} )} From 6586cdd060e81556673c15ed4aa762c9d246bda4 Mon Sep 17 00:00:00 2001 From: ajnart Date: Tue, 31 Jan 2023 11:45:52 +0900 Subject: [PATCH 03/12] =?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(''); - 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 ( @@ -39,7 +32,13 @@ export function Header(props: any) { - + 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%' }} > - + {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 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 04/12] =?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: Wed, 1 Feb 2023 11:04:15 +0900 Subject: [PATCH 05/12] 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 06/12] 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 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 07/12] =?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 08/12] =?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 09/12] =?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 10/12] =?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 11/12] =?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 12/12] =?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": {