From 2c707e86aa861d304104adb4c00661a3cf4de863 Mon Sep 17 00:00:00 2001 From: ajnart Date: Tue, 17 May 2022 00:18:22 +0200 Subject: [PATCH 01/47] =?UTF-8?q?=F0=9F=93=A6=20Upgrade=20package?= 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 6c6ee0354..10d16e8a5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homarr", - "version": "0.3.0", + "version": "0.3.1", "private": "false", "description": "Homarr - A homepage for your server.", "repository": { From e60db9f57a52f37a5c06e4fa9ae777801740bfff Mon Sep 17 00:00:00 2001 From: ajnart Date: Tue, 17 May 2022 00:19:24 +0200 Subject: [PATCH 02/47] =?UTF-8?q?=F0=9F=92=84=20Better=20style=20events=20?= =?UTF-8?q?in=20the=20calendar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modules/calendar/CalendarModule.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/modules/calendar/CalendarModule.tsx b/src/components/modules/calendar/CalendarModule.tsx index dd84050fa..8dcbdae9e 100644 --- a/src/components/modules/calendar/CalendarModule.tsx +++ b/src/components/modules/calendar/CalendarModule.tsx @@ -1,5 +1,5 @@ /* eslint-disable react/no-children-prop */ -import { Popover, Box, ScrollArea, Divider, Indicator } from '@mantine/core'; +import { Popover, Box, ScrollArea, Divider, Indicator, useMantineTheme } from '@mantine/core'; import React, { useEffect, useState } from 'react'; import { Calendar } from '@mantine/dates'; import { showNotification } from '@mantine/notifications'; @@ -93,6 +93,7 @@ function DayComponent(props: any) { radarrmedias, }: { renderdate: Date; sonarrmedias: []; radarrmedias: [] } = props; const [opened, setOpened] = useState(false); + const theme = useMantineTheme(); const day = renderdate.getDate(); // Itterate over the medias and filter the ones that are on the same day @@ -126,8 +127,7 @@ function DayComponent(props: any) { width={700} onClose={() => setOpened(false)} opened={opened} - // TODO: Fix this !! WTF ? - target={`‏ ${day}`} + target={day} > {sonarrFiltered.map((media: any, index: number) => ( From b7e8c51b29533760eec04b41899f0e438e7bdead Mon Sep 17 00:00:00 2001 From: ajnart Date: Tue, 17 May 2022 00:19:41 +0200 Subject: [PATCH 03/47] =?UTF-8?q?=F0=9F=8E=A8=20Use=20user=20prefered=20th?= =?UTF-8?q?eme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/_app.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 3b2271307..767597a45 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -5,14 +5,15 @@ import { getCookie, setCookies } from 'cookies-next'; import Head from 'next/head'; import { MantineProvider, ColorScheme, ColorSchemeProvider } from '@mantine/core'; import { NotificationsProvider } from '@mantine/notifications'; -import { useHotkeys } from '@mantine/hooks'; +import { useColorScheme, useHotkeys } from '@mantine/hooks'; import Layout from '../components/layout/Layout'; import { ConfigProvider } from '../tools/state'; import { theme } from '../tools/theme'; export default function App(props: AppProps & { colorScheme: ColorScheme }) { const { Component, pageProps } = props; - const [colorScheme, setColorScheme] = useState(props.colorScheme); + const preferredColorScheme = useColorScheme(); + const [colorScheme, setColorScheme] = useState(preferredColorScheme); const toggleColorScheme = (value?: ColorScheme) => { const nextColorScheme = value || (colorScheme === 'dark' ? 'light' : 'dark'); From 0cb3db6b89eb82c01f632c41378811efe88d0311 Mon Sep 17 00:00:00 2001 From: ajnart Date: Tue, 17 May 2022 00:18:22 +0200 Subject: [PATCH 04/47] =?UTF-8?q?=F0=9F=93=A6=20Upgrade=20package?= 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 6c6ee0354..10d16e8a5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homarr", - "version": "0.3.0", + "version": "0.3.1", "private": "false", "description": "Homarr - A homepage for your server.", "repository": { From 3e31a4d38e82bf11fb9f52a7e6feee31dfd79343 Mon Sep 17 00:00:00 2001 From: ajnart Date: Tue, 17 May 2022 00:19:24 +0200 Subject: [PATCH 05/47] =?UTF-8?q?=F0=9F=92=84=20Better=20style=20events=20?= =?UTF-8?q?in=20the=20calendar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modules/calendar/CalendarModule.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/modules/calendar/CalendarModule.tsx b/src/components/modules/calendar/CalendarModule.tsx index dd84050fa..8dcbdae9e 100644 --- a/src/components/modules/calendar/CalendarModule.tsx +++ b/src/components/modules/calendar/CalendarModule.tsx @@ -1,5 +1,5 @@ /* eslint-disable react/no-children-prop */ -import { Popover, Box, ScrollArea, Divider, Indicator } from '@mantine/core'; +import { Popover, Box, ScrollArea, Divider, Indicator, useMantineTheme } from '@mantine/core'; import React, { useEffect, useState } from 'react'; import { Calendar } from '@mantine/dates'; import { showNotification } from '@mantine/notifications'; @@ -93,6 +93,7 @@ function DayComponent(props: any) { radarrmedias, }: { renderdate: Date; sonarrmedias: []; radarrmedias: [] } = props; const [opened, setOpened] = useState(false); + const theme = useMantineTheme(); const day = renderdate.getDate(); // Itterate over the medias and filter the ones that are on the same day @@ -126,8 +127,7 @@ function DayComponent(props: any) { width={700} onClose={() => setOpened(false)} opened={opened} - // TODO: Fix this !! WTF ? - target={`‏ ${day}`} + target={day} > {sonarrFiltered.map((media: any, index: number) => ( From 8cdc9c3e293fcbe0c392a9159c6de39eea83a1c3 Mon Sep 17 00:00:00 2001 From: ajnart Date: Tue, 17 May 2022 00:19:41 +0200 Subject: [PATCH 06/47] =?UTF-8?q?=F0=9F=8E=A8=20Use=20user=20prefered=20th?= =?UTF-8?q?eme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/_app.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 3b2271307..767597a45 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -5,14 +5,15 @@ import { getCookie, setCookies } from 'cookies-next'; import Head from 'next/head'; import { MantineProvider, ColorScheme, ColorSchemeProvider } from '@mantine/core'; import { NotificationsProvider } from '@mantine/notifications'; -import { useHotkeys } from '@mantine/hooks'; +import { useColorScheme, useHotkeys } from '@mantine/hooks'; import Layout from '../components/layout/Layout'; import { ConfigProvider } from '../tools/state'; import { theme } from '../tools/theme'; export default function App(props: AppProps & { colorScheme: ColorScheme }) { const { Component, pageProps } = props; - const [colorScheme, setColorScheme] = useState(props.colorScheme); + const preferredColorScheme = useColorScheme(); + const [colorScheme, setColorScheme] = useState(preferredColorScheme); const toggleColorScheme = (value?: ColorScheme) => { const nextColorScheme = value || (colorScheme === 'dark' ? 'light' : 'dark'); From 13aeeefb225684d6ca7ab827618d098909812f9c Mon Sep 17 00:00:00 2001 From: ajnart Date: Tue, 17 May 2022 00:55:24 +0200 Subject: [PATCH 07/47] :bug: Fix AddAppShelfItem image fit not properly set Resolves #117 --- src/components/AppShelf/AddAppShelfItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AppShelf/AddAppShelfItem.tsx b/src/components/AppShelf/AddAppShelfItem.tsx index cd6b7ff68..d6101b223 100644 --- a/src/components/AppShelf/AddAppShelfItem.tsx +++ b/src/components/AppShelf/AddAppShelfItem.tsx @@ -154,7 +154,7 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } & return ( <>
- Placeholder + Placeholder
{ From d62189f0863bf5c48ef1e1580602b960e6c963c3 Mon Sep 17 00:00:00 2001 From: ajnart Date: Tue, 17 May 2022 01:01:26 +0200 Subject: [PATCH 08/47] :lipstick: Remove version from logo and add it in footer resolves #116 --- src/components/layout/Footer.tsx | 26 +++++++++++++++++++++----- src/components/layout/Logo.tsx | 18 +----------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx index 697f02123..e93a4ff7f 100644 --- a/src/components/layout/Footer.tsx +++ b/src/components/layout/Footer.tsx @@ -8,6 +8,7 @@ import { Footer as FooterComponent, } from '@mantine/core'; import { BrandGithub } from 'tabler-icons-react'; +import { CURRENT_VERSION } from '../../../data/constants'; const useStyles = createStyles((theme) => ({ footer: { @@ -54,12 +55,27 @@ export function Footer({ links }: FooterCenteredProps) { )); - return ( - + return ( + - component="a" href="https://github.com/ajnart/homarr" size="lg"> - - + + component="a" href="https://github.com/ajnart/homarr" size="lg"> + + + + {CURRENT_VERSION} + + + Homarr - - {CURRENT_VERSION} - ); } From e3d47d78e0d1cc3c0ca62cf170205540d4923113 Mon Sep 17 00:00:00 2001 From: ajnart Date: Tue, 17 May 2022 01:23:19 +0200 Subject: [PATCH 09/47] :bug: Add a delay before opening search results Resolves #115 --- src/components/SearchBar/SearchBar.tsx | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/components/SearchBar/SearchBar.tsx b/src/components/SearchBar/SearchBar.tsx index e10c8b79b..b9151e8f4 100644 --- a/src/components/SearchBar/SearchBar.tsx +++ b/src/components/SearchBar/SearchBar.tsx @@ -1,6 +1,6 @@ import { TextInput, Kbd, createStyles, useMantineTheme, Text, Popover } from '@mantine/core'; import { useForm, useHotkeys } from '@mantine/hooks'; -import { useRef, useState } from 'react'; +import { MutableRefObject, useRef, useState } from 'react'; import { Search, BrandYoutube, Download } from 'tabler-icons-react'; import { useConfig } from '../../tools/state'; @@ -19,7 +19,7 @@ export default function SearchBar(props: any) { const [opened, setOpened] = useState(false); const [icon, setIcon] = useState(); const queryUrl = config.settings.searchUrl || 'https://www.google.com/search?q='; - const textInput: any = useRef(null); + const textInput = useRef(); useHotkeys([['ctrl+K', () => textInput.current.focus()]]); const { classes, cx } = useStyles(); @@ -58,17 +58,18 @@ export default function SearchBar(props: any) { } }} onSubmit={form.onSubmit((values) => { - // Find if query is prefixed by !yt or !t const query = values.query.trim(); const isYoutube = query.startsWith('!yt'); const isTorrent = query.startsWith('!t'); - if (isYoutube) { - window.open(`https://www.youtube.com/results?search_query=${query.substring(3)}`); - } else if (isTorrent) { - window.open(`https://bitsearch.to/search?q=${query.substring(3)}`); - } else { - window.open(`${queryUrl}${values.query}`); - } + setTimeout(() => { + if (isYoutube) { + window.open(`https://www.youtube.com/results?search_query=${query.substring(3)}`); + } else if (isTorrent) { + window.open(`https://bitsearch.to/search?q=${query.substring(3)}`); + } else { + window.open(`${queryUrl}${values.query}`); + } + }, 20); })} > Date: Tue, 17 May 2022 01:43:40 +0200 Subject: [PATCH 10/47] :lipstick: Update AppShelf UI --- src/components/AppShelf/AppShelf.tsx | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/components/AppShelf/AppShelf.tsx b/src/components/AppShelf/AppShelf.tsx index 76ec5df39..bcc3024d5 100644 --- a/src/components/AppShelf/AppShelf.tsx +++ b/src/components/AppShelf/AppShelf.tsx @@ -1,13 +1,32 @@ import React, { useState } from 'react'; import { motion } from 'framer-motion'; -import { Text, AspectRatio, Card, Image, useMantineTheme, Center, Grid } from '@mantine/core'; +import { + Text, + AspectRatio, + Card, + Image, + useMantineTheme, + Center, + Grid, + createStyles, +} from '@mantine/core'; import { useConfig } from '../../tools/state'; import { serviceItem } from '../../tools/types'; import AppShelfMenu from './AppShelfMenu'; +const useStyles = createStyles((theme) => ({ + item: { + transition: 'box-shadow 150ms ease, transform 100ms ease', + + '&:hover': { + boxShadow: `${theme.shadows.md} !important`, + transform: 'scale(1.05)', + }, + }, +})); + const AppShelf = (props: any) => { const { config } = useConfig(); - return ( {config.services.map((service) => ( @@ -22,7 +41,7 @@ const AppShelf = (props: any) => { export function AppShelfItem(props: any) { const { service }: { service: serviceItem } = props; const [hovering, setHovering] = useState(false); - const theme = useMantineTheme(); + const { classes, theme } = useStyles(); return ( - + {service.name} From a474f3e4ee687374018da7fdd5c70a85cada31cf Mon Sep 17 00:00:00 2001 From: ajnart Date: Tue, 17 May 2022 01:44:26 +0200 Subject: [PATCH 11/47] :goal_net: Add 404 to catch errors Reduce the ammount of visible errors by adding a 404 page. --- src/pages/404.tsx | 95 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 src/pages/404.tsx diff --git a/src/pages/404.tsx b/src/pages/404.tsx new file mode 100644 index 000000000..8dd04f06e --- /dev/null +++ b/src/pages/404.tsx @@ -0,0 +1,95 @@ +import React from 'react'; +import { + createStyles, + Container, + Title, + Text, + Button, + Group, + useMantineTheme, +} from '@mantine/core'; +import { NextLink } from '@mantine/next'; + +const useStyles = createStyles((theme) => ({ + root: { + paddingTop: 80, + paddingBottom: 80, + }, + + inner: { + position: 'relative', + }, + + image: { + position: 'absolute', + top: 0, + right: 0, + left: 0, + zIndex: 0, + opacity: 0.75, + }, + + content: { + paddingTop: 220, + position: 'relative', + zIndex: 1, + + [theme.fn.smallerThan('sm')]: { + paddingTop: 120, + }, + }, + + title: { + fontFamily: `Greycliff CF, ${theme.fontFamily}`, + textAlign: 'center', + fontWeight: 900, + fontSize: 38, + + [theme.fn.smallerThan('sm')]: { + fontSize: 32, + }, + }, + + description: { + maxWidth: 540, + margin: 'auto', + marginTop: theme.spacing.xl, + marginBottom: theme.spacing.xl * 1.5, + }, +})); + +export function Illustration(props: React.ComponentPropsWithoutRef<'svg'>) { + const theme = useMantineTheme(); + return ( + + + + ); +} + +export default function NothingFoundBackground() { + const { classes } = useStyles(); + + return ( + +
+ +
+ Nothing to see here + + Page you are trying to open does not exist. You may have mistyped the address, or the + page has been moved to another URL. If you think this is an error contact support. + + + + + + +
+
+
+ ); +} From d4ce2a3ed6be90d9bb77d162b352d9020d27d1af Mon Sep 17 00:00:00 2001 From: ajnart Date: Tue, 17 May 2022 01:52:43 +0200 Subject: [PATCH 12/47] :label: Update types for the SearchBar --- src/components/SearchBar/SearchBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/SearchBar/SearchBar.tsx b/src/components/SearchBar/SearchBar.tsx index b9151e8f4..7018dc18f 100644 --- a/src/components/SearchBar/SearchBar.tsx +++ b/src/components/SearchBar/SearchBar.tsx @@ -1,6 +1,6 @@ import { TextInput, Kbd, createStyles, useMantineTheme, Text, Popover } from '@mantine/core'; import { useForm, useHotkeys } from '@mantine/hooks'; -import { MutableRefObject, useRef, useState } from 'react'; +import { useRef, useState } from 'react'; import { Search, BrandYoutube, Download } from 'tabler-icons-react'; import { useConfig } from '../../tools/state'; @@ -20,7 +20,7 @@ export default function SearchBar(props: any) { const [icon, setIcon] = useState(); const queryUrl = config.settings.searchUrl || 'https://www.google.com/search?q='; const textInput = useRef(); - useHotkeys([['ctrl+K', () => textInput.current.focus()]]); + useHotkeys([['ctrl+K', () => textInput.current && textInput.current.focus()]]); const { classes, cx } = useStyles(); const theme = useMantineTheme(); From 16b86870c4135bbc832d7a574ab5a33f6cc12319 Mon Sep 17 00:00:00 2001 From: ajnart Date: Tue, 17 May 2022 02:03:52 +0200 Subject: [PATCH 13/47] :building_construction: Fix small bug in code arch, forgot the key --- src/components/AppShelf/AppShelf.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AppShelf/AppShelf.tsx b/src/components/AppShelf/AppShelf.tsx index bcc3024d5..ccf7dea5c 100644 --- a/src/components/AppShelf/AppShelf.tsx +++ b/src/components/AppShelf/AppShelf.tsx @@ -30,7 +30,7 @@ const AppShelf = (props: any) => { return ( {config.services.map((service) => ( - + ))} From 4cb85391432443cdb160979362c30ff0b54095f9 Mon Sep 17 00:00:00 2001 From: ajnart Date: Tue, 17 May 2022 02:04:44 +0200 Subject: [PATCH 14/47] :sparkles: Make the search bar a module Resolves #118 --- src/components/AppShelf/AppShelf.tsx | 11 +---------- src/components/Settings/SettingsMenu.tsx | 16 ---------------- src/components/layout/Header.tsx | 2 +- src/components/modules/index.ts | 1 + .../search/SearchModule.story.tsx} | 2 +- .../search/SearchModule.tsx} | 16 ++++++++++++---- src/components/modules/search/index.ts | 1 + 7 files changed, 17 insertions(+), 32 deletions(-) rename src/components/{SearchBar/SearchBar.story.tsx => modules/search/SearchModule.story.tsx} (78%) rename src/components/{SearchBar/SearchBar.tsx => modules/search/SearchModule.tsx} (86%) create mode 100644 src/components/modules/search/index.ts diff --git a/src/components/AppShelf/AppShelf.tsx b/src/components/AppShelf/AppShelf.tsx index ccf7dea5c..28fa7bf64 100644 --- a/src/components/AppShelf/AppShelf.tsx +++ b/src/components/AppShelf/AppShelf.tsx @@ -1,15 +1,6 @@ import React, { useState } from 'react'; import { motion } from 'framer-motion'; -import { - Text, - AspectRatio, - Card, - Image, - useMantineTheme, - Center, - Grid, - createStyles, -} from '@mantine/core'; +import { Text, AspectRatio, Card, Image, Center, Grid, createStyles } from '@mantine/core'; import { useConfig } from '../../tools/state'; import { serviceItem } from '../../tools/types'; import AppShelfMenu from './AppShelfMenu'; diff --git a/src/components/Settings/SettingsMenu.tsx b/src/components/Settings/SettingsMenu.tsx index 2f821a4ef..cbd30c987 100644 --- a/src/components/Settings/SettingsMenu.tsx +++ b/src/components/Settings/SettingsMenu.tsx @@ -90,22 +90,6 @@ function SettingsMenu(props: any) { /> )}
- - - setConfig({ - ...config, - settings: { - ...config.settings, - searchBar: e.currentTarget.checked, - }, - }) - } - checked={config.settings.searchBar} - label="Enable search bar" - /> - diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx index f319c4c0e..28ed95f46 100644 --- a/src/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { createStyles, Header as Head, Group, Box } from '@mantine/core'; import { Logo } from './Logo'; -import SearchBar from '../SearchBar/SearchBar'; +import SearchBar from '../modules/search/SearchModule'; import { AddItemShelfButton } from '../AppShelf/AddAppShelfItem'; import { SettingsMenuButton } from '../Settings/SettingsMenu'; diff --git a/src/components/modules/index.ts b/src/components/modules/index.ts index b8817cb83..a3dce87be 100644 --- a/src/components/modules/index.ts +++ b/src/components/modules/index.ts @@ -1,2 +1,3 @@ export * from './date'; export * from './calendar'; +export * from './search'; diff --git a/src/components/SearchBar/SearchBar.story.tsx b/src/components/modules/search/SearchModule.story.tsx similarity index 78% rename from src/components/SearchBar/SearchBar.story.tsx rename to src/components/modules/search/SearchModule.story.tsx index 3e19c4e2d..cbd7b64c2 100644 --- a/src/components/SearchBar/SearchBar.story.tsx +++ b/src/components/modules/search/SearchModule.story.tsx @@ -1,4 +1,4 @@ -import SearchBar from './SearchBar'; +import SearchBar from './SearchModule'; export default { title: 'Search bar', diff --git a/src/components/SearchBar/SearchBar.tsx b/src/components/modules/search/SearchModule.tsx similarity index 86% rename from src/components/SearchBar/SearchBar.tsx rename to src/components/modules/search/SearchModule.tsx index 7018dc18f..03c230d0c 100644 --- a/src/components/SearchBar/SearchBar.tsx +++ b/src/components/modules/search/SearchModule.tsx @@ -1,8 +1,9 @@ -import { TextInput, Kbd, createStyles, useMantineTheme, Text, Popover } from '@mantine/core'; +import { TextInput, Kbd, createStyles, Text, Popover } from '@mantine/core'; import { useForm, useHotkeys } from '@mantine/hooks'; import { useRef, useState } from 'react'; import { Search, BrandYoutube, Download } from 'tabler-icons-react'; -import { useConfig } from '../../tools/state'; +import { useConfig } from '../../../tools/state'; +import { IModule } from '../modules'; const useStyles = createStyles((theme) => ({ hide: { @@ -14,6 +15,13 @@ const useStyles = createStyles((theme) => ({ }, })); +export const SearchModule: IModule = { + title: 'Search Bar', + description: 'Show the current time and date in a card', + icon: Search, + component: SearchBar, +}; + export default function SearchBar(props: any) { const { config, setConfig } = useConfig(); const [opened, setOpened] = useState(false); @@ -23,7 +31,6 @@ export default function SearchBar(props: any) { useHotkeys([['ctrl+K', () => textInput.current && textInput.current.focus()]]); const { classes, cx } = useStyles(); - const theme = useMantineTheme(); const rightSection = (
Ctrl @@ -38,7 +45,8 @@ export default function SearchBar(props: any) { }, }); - if (config.settings.searchBar === false) { + // If enabled modules doesn't contain the module, return null + if (!config.settings.enabledModules.includes(SearchModule.title)) { return null; } diff --git a/src/components/modules/search/index.ts b/src/components/modules/search/index.ts new file mode 100644 index 000000000..eda41643a --- /dev/null +++ b/src/components/modules/search/index.ts @@ -0,0 +1 @@ +export { SearchModule } from './SearchModule'; From b8fe799ac6b03d6baa4f97cedc304bfeebbd4588 Mon Sep 17 00:00:00 2001 From: ajnart Date: Tue, 17 May 2022 02:07:38 +0200 Subject: [PATCH 15/47] :coffin: Remove dead code for the settings I turned the settings into a module in 4cb85391432443cdb160979362c30ff0b54095f9 --- src/tools/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/types.ts b/src/tools/types.ts index 1b3d9437b..6465fc295 100644 --- a/src/tools/types.ts +++ b/src/tools/types.ts @@ -1,6 +1,5 @@ export interface Settings { searchUrl: string; - searchBar: boolean; enabledModules: string[]; [key: string]: any; } From c9c6f2b0c9fec7ff59e6f3a499559137bc402ccd Mon Sep 17 00:00:00 2001 From: ajnart Date: Tue, 17 May 2022 04:02:14 +0200 Subject: [PATCH 16/47] :sparkles: Add ping service module Resolves #78 --- src/components/AppShelf/AppShelf.tsx | 2 + src/components/modules/index.ts | 1 + .../modules/ping/PingModule.story.tsx | 15 ++++++ src/components/modules/ping/PingModule.tsx | 51 +++++++++++++++++++ src/components/modules/ping/index.ts | 1 + src/pages/[slug].tsx | 8 +++ src/pages/api/modules/ping.ts | 29 +++++++++++ 7 files changed, 107 insertions(+) create mode 100644 src/components/modules/ping/PingModule.story.tsx create mode 100644 src/components/modules/ping/PingModule.tsx create mode 100644 src/components/modules/ping/index.ts create mode 100644 src/pages/[slug].tsx create mode 100644 src/pages/api/modules/ping.ts diff --git a/src/components/AppShelf/AppShelf.tsx b/src/components/AppShelf/AppShelf.tsx index 28fa7bf64..761320294 100644 --- a/src/components/AppShelf/AppShelf.tsx +++ b/src/components/AppShelf/AppShelf.tsx @@ -4,6 +4,7 @@ import { Text, AspectRatio, Card, Image, Center, Grid, createStyles } from '@man import { useConfig } from '../../tools/state'; import { serviceItem } from '../../tools/types'; import AppShelfMenu from './AppShelfMenu'; +import PingComponent from '../modules/ping/PingModule'; const useStyles = createStyles((theme) => ({ item: { @@ -89,6 +90,7 @@ export function AppShelfItem(props: any) { /> + diff --git a/src/components/modules/index.ts b/src/components/modules/index.ts index a3dce87be..134be155c 100644 --- a/src/components/modules/index.ts +++ b/src/components/modules/index.ts @@ -1,3 +1,4 @@ export * from './date'; export * from './calendar'; export * from './search'; +export * from './ping'; diff --git a/src/components/modules/ping/PingModule.story.tsx b/src/components/modules/ping/PingModule.story.tsx new file mode 100644 index 000000000..c781563e0 --- /dev/null +++ b/src/components/modules/ping/PingModule.story.tsx @@ -0,0 +1,15 @@ +import { serviceItem } from '../../../tools/types'; +import PingComponent from './PingModule'; + +export default { + title: 'Modules/Search bar', +}; + +const service: serviceItem = { + type: 'Other', + name: 'YouTube', + icon: 'https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/youtube.png', + url: 'https://youtube.com/', +}; + +export const Default = (args: any) => ; diff --git a/src/components/modules/ping/PingModule.tsx b/src/components/modules/ping/PingModule.tsx new file mode 100644 index 000000000..3a27bc73c --- /dev/null +++ b/src/components/modules/ping/PingModule.tsx @@ -0,0 +1,51 @@ +import { Indicator, Tooltip } from '@mantine/core'; +import axios from 'axios'; +import { useEffect, useState } from 'react'; +import { Plug } from 'tabler-icons-react'; +import { useConfig } from '../../../tools/state'; +import { IModule } from '../modules'; + +export const PingModule: IModule = { + title: 'Ping Services', + description: 'Pings your services and shows their status as an indicator', + icon: Plug, + component: PingComponent, +}; + +export default function PingComponent(props: any) { + type State = 'loading' | 'down' | 'online'; + const { config } = useConfig(); + + const { url }: { url: string } = props; + const [isOnline, setOnline] = useState('loading'); + useEffect(() => { + if (!config.settings.enabledModules.includes('Ping Services')) { + return; + } + axios + .get('/api/modules/ping', { params: { url } }) + .then(() => { + setOnline('online'); + }) + .catch(() => { + setOnline('down'); + }); + }, []); + if (!config.settings.enabledModules.includes('Ping Services')) { + return null; + } + return ( + + + {null} + + + ); +} diff --git a/src/components/modules/ping/index.ts b/src/components/modules/ping/index.ts new file mode 100644 index 000000000..f4de8c000 --- /dev/null +++ b/src/components/modules/ping/index.ts @@ -0,0 +1 @@ +export { PingModule } from './PingModule'; diff --git a/src/pages/[slug].tsx b/src/pages/[slug].tsx new file mode 100644 index 000000000..06fe31f8a --- /dev/null +++ b/src/pages/[slug].tsx @@ -0,0 +1,8 @@ +import { Title } from '@mantine/core'; +import { useRouter } from 'next/router'; + +export default function SlugPage(props: any) { + const router = useRouter(); + const { slug } = router.query; + return ok; +} diff --git a/src/pages/api/modules/ping.ts b/src/pages/api/modules/ping.ts new file mode 100644 index 000000000..b0a095d53 --- /dev/null +++ b/src/pages/api/modules/ping.ts @@ -0,0 +1,29 @@ +import axios from 'axios'; +import { NextApiRequest, NextApiResponse } from 'next'; + +async function Get(req: NextApiRequest, res: NextApiResponse) { + // Parse req.body as a ServiceItem + const { url } = req.query; + await axios + .get(url as string) + .then((response) => { + res.status(200).json(response.data); + }) + .catch((error) => { + res.status(500).json(error); + }); + // // Make a request to the URL + // const response = await axios.get(url); + // // Return the response +} + +export default async (req: NextApiRequest, res: NextApiResponse) => { + // Filter out if the reuqest is a POST or a GET + if (req.method === 'GET') { + return Get(req, res); + } + return res.status(405).json({ + statusCode: 405, + message: 'Method not allowed', + }); +}; From e94cae620a68f8aee9501ce62649188b8704a7ed Mon Sep 17 00:00:00 2001 From: ajnart Date: Tue, 17 May 2022 04:19:59 +0200 Subject: [PATCH 17/47] :rewind: Rever b7e8c51b29533760eec04b41899f0e438e7bdead Does not work. Apparently --- src/pages/_app.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 767597a45..3b2271307 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -5,15 +5,14 @@ import { getCookie, setCookies } from 'cookies-next'; import Head from 'next/head'; import { MantineProvider, ColorScheme, ColorSchemeProvider } from '@mantine/core'; import { NotificationsProvider } from '@mantine/notifications'; -import { useColorScheme, useHotkeys } from '@mantine/hooks'; +import { useHotkeys } from '@mantine/hooks'; import Layout from '../components/layout/Layout'; import { ConfigProvider } from '../tools/state'; import { theme } from '../tools/theme'; export default function App(props: AppProps & { colorScheme: ColorScheme }) { const { Component, pageProps } = props; - const preferredColorScheme = useColorScheme(); - const [colorScheme, setColorScheme] = useState(preferredColorScheme); + const [colorScheme, setColorScheme] = useState(props.colorScheme); const toggleColorScheme = (value?: ColorScheme) => { const nextColorScheme = value || (colorScheme === 'dark' ? 'light' : 'dark'); From 696d0c582d2c6dd48e2a7149c1c4ef6e9dafbab3 Mon Sep 17 00:00:00 2001 From: "Thomas \"ajnart\" Camlong" Date: Tue, 17 May 2022 20:58:55 +0200 Subject: [PATCH 18/47] :bug: Clear the search input on search Resolves #125 --- src/components/modules/search/SearchModule.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/modules/search/SearchModule.tsx b/src/components/modules/search/SearchModule.tsx index 03c230d0c..aa7698e61 100644 --- a/src/components/modules/search/SearchModule.tsx +++ b/src/components/modules/search/SearchModule.tsx @@ -69,6 +69,7 @@ export default function SearchBar(props: any) { const query = values.query.trim(); const isYoutube = query.startsWith('!yt'); const isTorrent = query.startsWith('!t'); + form.setValues({ query: '' }); setTimeout(() => { if (isYoutube) { window.open(`https://www.youtube.com/results?search_query=${query.substring(3)}`); From e86eb7798fccc396f0a3f1de1bd9d8534157e65b Mon Sep 17 00:00:00 2001 From: "Thomas \"ajnart\" Camlong" Date: Sun, 15 May 2022 13:53:47 +0200 Subject: [PATCH 19/47] :construction: Set up the structure for the weather module --- src/components/modules/index.ts | 1 + .../modules/weather/WeatherModule.tsx | 41 +++++++++++++++++++ src/components/modules/weather/index.ts | 1 + 3 files changed, 43 insertions(+) create mode 100644 src/components/modules/weather/WeatherModule.tsx create mode 100644 src/components/modules/weather/index.ts diff --git a/src/components/modules/index.ts b/src/components/modules/index.ts index 134be155c..4b85fa930 100644 --- a/src/components/modules/index.ts +++ b/src/components/modules/index.ts @@ -2,3 +2,4 @@ export * from './date'; export * from './calendar'; export * from './search'; export * from './ping'; +export * from './weather'; diff --git a/src/components/modules/weather/WeatherModule.tsx b/src/components/modules/weather/WeatherModule.tsx new file mode 100644 index 000000000..bb157fda9 --- /dev/null +++ b/src/components/modules/weather/WeatherModule.tsx @@ -0,0 +1,41 @@ +import { Group, Text, Title } from '@mantine/core'; +import dayjs from 'dayjs'; +import { useEffect, useState } from 'react'; +import { Clock, Cloud } from 'tabler-icons-react'; +import { IModule } from '../modules'; + +export const WeatherModule: IModule = { + title: 'Weather', + description: 'Look up the current weather in your location', + icon: Cloud, + component: WeatherComponent, +}; + +export default function WeatherComponent(props: any) { + const [date, setDate] = useState(new Date()); + const hours = date.getHours(); + const minutes = date.getMinutes(); + + // Change date on minute change + // Note: Using 10 000ms instead of 1000ms to chill a little :) + useEffect(() => { + setInterval(() => { + setDate(new Date()); + }, 10000); + }, []); + + return ( + + + {hours < 10 ? `0${hours}` : hours}:{minutes < 10 ? `0${minutes}` : minutes} + + + { + // Use dayjs to format the date + // https://day.js.org/en/getting-started/installation/ + dayjs(date).format('dddd, MMMM D') + } + + + ); +} diff --git a/src/components/modules/weather/index.ts b/src/components/modules/weather/index.ts new file mode 100644 index 000000000..e8817394b --- /dev/null +++ b/src/components/modules/weather/index.ts @@ -0,0 +1 @@ +export { WeatherModule } from './WeatherModule'; From 31deb5010f4fb3a845f7aed1502ecd4e8a9448e0 Mon Sep 17 00:00:00 2001 From: "Thomas \"ajnart\" Camlong" Date: Sun, 15 May 2022 13:54:25 +0200 Subject: [PATCH 20/47] :lipstick: Improve styling of modules --- src/components/layout/Navbar.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/layout/Navbar.tsx b/src/components/layout/Navbar.tsx index fba3ecec1..5e243c8dc 100644 --- a/src/components/layout/Navbar.tsx +++ b/src/components/layout/Navbar.tsx @@ -1,4 +1,5 @@ import { Group, Navbar as MantineNavbar } from '@mantine/core'; +import { WeatherModule } from '../modules'; import { DateModule } from '../modules/date/DateModule'; import ModuleWrapper from '../modules/moduleWrapper'; @@ -16,6 +17,7 @@ export default function Navbar() { > + ); From 49d57024b944cce2cc52d9703071573d0e091685 Mon Sep 17 00:00:00 2001 From: "Thomas \"ajnart\" Camlong" Date: Sun, 15 May 2022 18:52:29 +0200 Subject: [PATCH 21/47] Advancement on the weather widget --- .../modules/weather/WeatherInterface.ts | 236 ++++++++++++++++++ .../modules/weather/WeatherModule.story.tsx | 22 ++ .../modules/weather/WeatherModule.tsx | 141 +++++++++-- src/components/modules/weather/mockdata.json | 58 +++++ 4 files changed, 442 insertions(+), 15 deletions(-) create mode 100644 src/components/modules/weather/WeatherInterface.ts create mode 100644 src/components/modules/weather/WeatherModule.story.tsx create mode 100644 src/components/modules/weather/mockdata.json diff --git a/src/components/modules/weather/WeatherInterface.ts b/src/components/modules/weather/WeatherInterface.ts new file mode 100644 index 000000000..d6b728a75 --- /dev/null +++ b/src/components/modules/weather/WeatherInterface.ts @@ -0,0 +1,236 @@ +// To parse this data: +// +// import { Convert, WeatherResponse } from "./file"; +// +// const weatherResponse = Convert.toWeatherResponse(json); +// +// These functions will throw an error if the JSON doesn't +// match the expected interface, even if the JSON is valid. + +export interface WeatherResponse { + current_weather: CurrentWeather; + utc_offset_seconds: number; + latitude: number; + elevation: number; + longitude: number; + generationtime_ms: number; + daily_units: DailyUnits; + daily: Daily; +} + +export interface CurrentWeather { + winddirection: number; + windspeed: number; + time: string; + weathercode: number; + temperature: number; +} + +export interface Daily { + temperature_2m_max: number[]; + time: Date[]; + temperature_2m_min: number[]; + weathercode: number[]; +} + +export interface DailyUnits { + temperature_2m_max: string; + temperature_2m_min: string; + time: string; + weathercode: string; +} + +// Converts JSON strings to/from your types +// and asserts the results of JSON.parse at runtime +export class Convert { + public static toWeatherResponse(json: string): WeatherResponse { + return cast(JSON.parse(json), r('WeatherResponse')); + } + + public static weatherResponseToJson(value: WeatherResponse): string { + return JSON.stringify(uncast(value, r('WeatherResponse')), null, 2); + } +} + +function invalidValue(typ: any, val: any, key: any = ''): never { + if (key) { + throw Error( + `Invalid value for key "${key}". Expected type ${JSON.stringify( + typ + )} but got ${JSON.stringify(val)}` + ); + } + throw Error(`Invalid value ${JSON.stringify(val)} for type ${JSON.stringify(typ)}`); +} + +function jsonToJSProps(typ: any): any { + if (typ.jsonToJS === undefined) { + const map: any = {}; + typ.props.forEach((p: any) => (map[p.json] = { key: p.js, typ: p.typ })); + typ.jsonToJS = map; + } + return typ.jsonToJS; +} + +function jsToJSONProps(typ: any): any { + if (typ.jsToJSON === undefined) { + const map: any = {}; + typ.props.forEach((p: any) => (map[p.js] = { key: p.json, typ: p.typ })); + typ.jsToJSON = map; + } + return typ.jsToJSON; +} + +function transform(val: any, typ: any, getProps: any, key: any = ''): any { + function transformPrimitive(typ: string, val: any): any { + if (typeof typ === typeof val) return val; + return invalidValue(typ, val, key); + } + + function transformUnion(typs: any[], val: any): any { + // val must validate against one typ in typs + const l = typs.length; + for (let i = 0; i < l; i++) { + const typ = typs[i]; + try { + return transform(val, typ, getProps); + } catch (_) {} + } + return invalidValue(typs, val); + } + + function transformEnum(cases: string[], val: any): any { + if (cases.indexOf(val) !== -1) return val; + return invalidValue(cases, val); + } + + function transformArray(typ: any, val: any): any { + // val must be an array with no invalid elements + if (!Array.isArray(val)) return invalidValue('array', val); + return val.map((el) => transform(el, typ, getProps)); + } + + function transformDate(val: any): any { + if (val === null) { + return null; + } + const d = new Date(val); + if (isNaN(d.valueOf())) { + return invalidValue('Date', val); + } + return d; + } + + function transformObject(props: { [k: string]: any }, additional: any, val: any): any { + if (val === null || typeof val !== 'object' || Array.isArray(val)) { + return invalidValue('object', val); + } + const result: any = {}; + Object.getOwnPropertyNames(props).forEach((key) => { + const prop = props[key]; + const v = Object.prototype.hasOwnProperty.call(val, key) ? val[key] : undefined; + result[prop.key] = transform(v, prop.typ, getProps, prop.key); + }); + Object.getOwnPropertyNames(val).forEach((key) => { + if (!Object.prototype.hasOwnProperty.call(props, key)) { + result[key] = transform(val[key], additional, getProps, key); + } + }); + return result; + } + + if (typ === 'any') return val; + if (typ === null) { + if (val === null) return val; + return invalidValue(typ, val); + } + if (typ === false) return invalidValue(typ, val); + while (typeof typ === 'object' && typ.ref !== undefined) { + typ = typeMap[typ.ref]; + } + if (Array.isArray(typ)) return transformEnum(typ, val); + if (typeof typ === 'object') { + return typ.hasOwnProperty('unionMembers') + ? transformUnion(typ.unionMembers, val) + : typ.hasOwnProperty('arrayItems') + ? transformArray(typ.arrayItems, val) + : typ.hasOwnProperty('props') + ? transformObject(getProps(typ), typ.additional, val) + : invalidValue(typ, val); + } + // Numbers can be parsed by Date but shouldn't be. + if (typ === Date && typeof val !== 'number') return transformDate(val); + return transformPrimitive(typ, val); +} + +function cast(val: any, typ: any): T { + return transform(val, typ, jsonToJSProps); +} + +function uncast(val: T, typ: any): any { + return transform(val, typ, jsToJSONProps); +} + +function a(typ: any) { + return { arrayItems: typ }; +} + +function u(...typs: any[]) { + return { unionMembers: typs }; +} + +function o(props: any[], additional: any) { + return { props, additional }; +} + +function m(additional: any) { + return { props: [], additional }; +} + +function r(name: string) { + return { ref: name }; +} + +const typeMap: any = { + WeatherResponse: o( + [ + { json: 'current_weather', js: 'current_weather', typ: r('CurrentWeather') }, + { json: 'utc_offset_seconds', js: 'utc_offset_seconds', typ: 0 }, + { json: 'latitude', js: 'latitude', typ: 3.14 }, + { json: 'elevation', js: 'elevation', typ: 3.14 }, + { json: 'longitude', js: 'longitude', typ: 3.14 }, + { json: 'generationtime_ms', js: 'generationtime_ms', typ: 3.14 }, + { json: 'daily_units', js: 'daily_units', typ: r('DailyUnits') }, + { json: 'daily', js: 'daily', typ: r('Daily') }, + ], + false + ), + CurrentWeather: o( + [ + { json: 'winddirection', js: 'winddirection', typ: 0 }, + { json: 'windspeed', js: 'windspeed', typ: 3.14 }, + { json: 'time', js: 'time', typ: '' }, + { json: 'weathercode', js: 'weathercode', typ: 0 }, + { json: 'temperature', js: 'temperature', typ: 3.14 }, + ], + false + ), + Daily: o( + [ + { json: 'temperature_2m_max', js: 'temperature_2m_max', typ: a(3.14) }, + { json: 'time', js: 'time', typ: a(Date) }, + { json: 'temperature_2m_min', js: 'temperature_2m_min', typ: a(3.14) }, + { json: 'weathercode', js: 'weathercode', typ: a(0) }, + ], + false + ), + DailyUnits: o( + [ + { json: 'temperature_2m_max', js: 'temperature_2m_max', typ: '' }, + { json: 'temperature_2m_min', js: 'temperature_2m_min', typ: '' }, + { json: 'time', js: 'time', typ: '' }, + { json: 'weathercode', js: 'weathercode', typ: '' }, + ], + false + ), +}; diff --git a/src/components/modules/weather/WeatherModule.story.tsx b/src/components/modules/weather/WeatherModule.story.tsx new file mode 100644 index 000000000..2681443ec --- /dev/null +++ b/src/components/modules/weather/WeatherModule.story.tsx @@ -0,0 +1,22 @@ +import withMock from 'storybook-addon-mock'; +import WeatherComponent from './WeatherModule'; +import mockdata from './mockdata.json'; + +export default { + title: 'Weather module', + decorators: [withMock], +}; + +export const Default = (args: any) => ; +Default.parameters = { + mockData: [ + { + url: 'https://api.open-meteo.com/v1/forecast', + method: 'GET', + status: 200, + response: { + data: mockdata, + }, + }, + ], +}; diff --git a/src/components/modules/weather/WeatherModule.tsx b/src/components/modules/weather/WeatherModule.tsx index bb157fda9..536f33387 100644 --- a/src/components/modules/weather/WeatherModule.tsx +++ b/src/components/modules/weather/WeatherModule.tsx @@ -1,8 +1,19 @@ -import { Group, Text, Title } from '@mantine/core'; +import { Group, Text, Title, Tooltip } from '@mantine/core'; +import axios from 'axios'; import dayjs from 'dayjs'; import { useEffect, useState } from 'react'; -import { Clock, Cloud } from 'tabler-icons-react'; +import { + Cloud, + CloudFog, + CloudRain, + CloudSnow, + CloudStorm, + QuestionMark, + Snowflake, + Sun, +} from 'tabler-icons-react'; import { IModule } from '../modules'; +import { WeatherResponse, Convert } from './WeatherInterface'; export const WeatherModule: IModule = { title: 'Weather', @@ -11,29 +22,129 @@ export const WeatherModule: IModule = { component: WeatherComponent, }; +// 0 Clear sky +// 1, 2, 3 Mainly clear, partly cloudy, and overcast +// 45, 48 Fog and depositing rime fog +// 51, 53, 55 Drizzle: Light, moderate, and dense intensity +// 56, 57 Freezing Drizzle: Light and dense intensity +// 61, 63, 65 Rain: Slight, moderate and heavy intensity +// 66, 67 Freezing Rain: Light and heavy intensity +// 71, 73, 75 Snow fall: Slight, moderate, and heavy intensity +// 77 Snow grains +// 80, 81, 82 Rain showers: Slight, moderate, and violent +// 85, 86 Snow showers slight and heavy +// 95 * Thunderstorm: Slight or moderate +// 96, 99 * Thunderstorm with slight and heavy hail +export function WeatherIcon(props: any) { + const { code } = props; + let data: { icon: any; name: string }; + switch (code) { + case 0: { + data = { icon: , name: 'Clear' }; + break; + } + case 1: + case 2: + case 3: { + data = { icon: , name: 'Mainly clear' }; + break; + } + case 45: + case 48: { + data = { icon: , name: 'Fog' }; + break; + } + case 51: + case 53: + case 55: { + data = { icon: , name: 'Drizzle' }; + break; + } + case 56: + case 57: { + data = { icon: , name: 'Freezing drizzle' }; + break; + } + case 61: + case 63: + case 65: { + data = { icon: , name: 'Rain' }; + break; + } + case 66: + case 67: { + data = { icon: , name: 'Freezing rain' }; + break; + } + case 71: + case 73: + case 75: { + data = { icon: , name: 'Snow fall' }; + break; + } + case 77: { + data = { icon: , name: 'Snow grains' }; + break; + } + case 80: + case 81: + case 82: { + data = { icon: , name: 'Rain showers' }; + + break; + } + case 85: + case 86: { + data = { icon: , name: 'Snow showers' }; + break; + } + case 95: { + data = { icon: , name: 'Thunderstorm' }; + break; + } + case 96: + case 99: { + data = { icon: , name: 'Thunderstorm with hail' }; + break; + } + default: { + data = { icon: , name: 'Unknown' }; + } + } + return {data.icon}; +} + export default function WeatherComponent(props: any) { - const [date, setDate] = useState(new Date()); - const hours = date.getHours(); - const minutes = date.getMinutes(); + // Get location from browser + const [location, setLocation] = useState({ lat: 0, lng: 0 }); + const [weather, setWeather] = useState({} as WeatherResponse); + if ('geolocation' in navigator && location.lat === 0 && location.lng === 0) { + navigator.geolocation.getCurrentPosition((position) => { + setLocation({ lat: position.coords.latitude, lng: position.coords.longitude }); + }); + } - // Change date on minute change - // Note: Using 10 000ms instead of 1000ms to chill a little :) useEffect(() => { - setInterval(() => { - setDate(new Date()); - }, 10000); + axios + .get( + `https://api.open-meteo.com/v1/forecast?latitude=${location.lat}&longitude=${location.lng}&daily=weathercode,temperature_2m_max,temperature_2m_min¤t_weather=true&timezone=Europe%2FLondon` + ) + .then((res) => { + setWeather(res.data); + }); }, []); - + if (!weather.current_weather) { + return null; + } + console.log(weather.current_weather); return ( - - {hours < 10 ? `0${hours}` : hours}:{minutes < 10 ? `0${minutes}` : minutes} - + {weather.current_weather.temperature}°C + { // Use dayjs to format the date // https://day.js.org/en/getting-started/installation/ - dayjs(date).format('dddd, MMMM D') } diff --git a/src/components/modules/weather/mockdata.json b/src/components/modules/weather/mockdata.json new file mode 100644 index 000000000..b9e3795b1 --- /dev/null +++ b/src/components/modules/weather/mockdata.json @@ -0,0 +1,58 @@ +{ + "current_weather": { + "winddirection": 121, + "windspeed": 12.7, + "time": "2022-05-15T14:00", + "weathercode": 3, + "temperature": 28.7 + }, + "utc_offset_seconds": 3600, + "latitude": 48.86, + "elevation": 46.1875, + "longitude": 2.3599997, + "generationtime_ms": 0.36406517028808594, + "daily_units": { + "temperature_2m_max": "°C", + "temperature_2m_min": "°C", + "time": "iso8601", + "weathercode": "wmo code" + }, + "daily": { + "temperature_2m_max": [ + 29.1, + 25.4, + 28.2, + 29.7, + 24.6, + 27.1, + 22.9 + ], + "time": [ + "2022-05-15", + "2022-05-16", + "2022-05-17", + "2022-05-18", + "2022-05-19", + "2022-05-20", + "2022-05-21" + ], + "temperature_2m_min": [ + 14.3, + 16.9, + 17.2, + 17.7, + 19.2, + 19.1, + 14 + ], + "weathercode": [ + 95, + 3, + 3, + 3, + 3, + 80, + 3 + ] + } +} \ No newline at end of file From 73d06e15fbf6b9997f2b2330f3486e0903c934ad Mon Sep 17 00:00:00 2001 From: "Thomas \"ajnart\" Camlong" Date: Sun, 15 May 2022 18:53:41 +0200 Subject: [PATCH 22/47] :white_check_mark: Update tests for storybook --- .storybook/main.js | 1 + 1 file changed, 1 insertion(+) diff --git a/.storybook/main.js b/.storybook/main.js index 6449361d8..80bc56834 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -3,6 +3,7 @@ module.exports = { addons: [ 'storybook-dark-mode', '@storybook/addon-links', + 'storybook-addon-mock/register', '@storybook/addon-essentials', { name: 'storybook-addon-turbo-build', From 50d760f3b82b7a2672322ded88cf30f1a973a4e0 Mon Sep 17 00:00:00 2001 From: "Thomas \"ajnart\" Camlong" Date: Tue, 17 May 2022 21:24:10 +0200 Subject: [PATCH 23/47] Prepare for v0.3.2 --- data/constants.ts | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/constants.ts b/data/constants.ts index 11dbe4c3b..c24bbfb74 100644 --- a/data/constants.ts +++ b/data/constants.ts @@ -1,2 +1,2 @@ export const REPO_URL = 'ajnart/homarr'; -export const CURRENT_VERSION = 'v0.3.1'; +export const CURRENT_VERSION = 'v0.3.2'; diff --git a/package.json b/package.json index 10d16e8a5..57b60a969 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homarr", - "version": "0.3.1", + "version": "0.3.2", "private": "false", "description": "Homarr - A homepage for your server.", "repository": { From ab860eeea17cab64898813528a39402bdb0b3a34 Mon Sep 17 00:00:00 2001 From: Aj - Thomas Date: Mon, 16 May 2022 00:23:49 +0200 Subject: [PATCH 24/47] =?UTF-8?q?=EF=BF=BD=20Weather=20module=20improvemen?= =?UTF-8?q?ts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modules/date/DateModule.tsx | 2 +- .../modules/weather/WeatherModule.tsx | 74 +++++++++---------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/components/modules/date/DateModule.tsx b/src/components/modules/date/DateModule.tsx index ba653f862..d53c160ee 100644 --- a/src/components/modules/date/DateModule.tsx +++ b/src/components/modules/date/DateModule.tsx @@ -25,7 +25,7 @@ export default function DateComponent(props: any) { }, []); return ( - + {hours < 10 ? `0${hours}` : hours}:{minutes < 10 ? `0${minutes}` : minutes} diff --git a/src/components/modules/weather/WeatherModule.tsx b/src/components/modules/weather/WeatherModule.tsx index 536f33387..8a3dcba2c 100644 --- a/src/components/modules/weather/WeatherModule.tsx +++ b/src/components/modules/weather/WeatherModule.tsx @@ -1,6 +1,5 @@ -import { Group, Text, Title, Tooltip } from '@mantine/core'; +import { Center, Group, Text, Title, Tooltip } from '@mantine/core'; import axios from 'axios'; -import dayjs from 'dayjs'; import { useEffect, useState } from 'react'; import { Cloud, @@ -13,105 +12,109 @@ import { Sun, } from 'tabler-icons-react'; import { IModule } from '../modules'; -import { WeatherResponse, Convert } from './WeatherInterface'; +import { WeatherResponse } from './WeatherInterface'; export const WeatherModule: IModule = { title: 'Weather', description: 'Look up the current weather in your location', - icon: Cloud, + icon: Sun, component: WeatherComponent, }; // 0 Clear sky // 1, 2, 3 Mainly clear, partly cloudy, and overcast // 45, 48 Fog and depositing rime fog -// 51, 53, 55 Drizzle: Light, moderate, and dense intensity -// 56, 57 Freezing Drizzle: Light and dense intensity -// 61, 63, 65 Rain: Slight, moderate and heavy intensity -// 66, 67 Freezing Rain: Light and heavy intensity -// 71, 73, 75 Snow fall: Slight, moderate, and heavy intensity +// 51, 53, 55 Drizzle: Light, moderate, and dense intensity +// 56, 57 Freezing Drizzle: Light and dense intensity +// 61, 63, 65 Rain: Slight, moderate and heavy intensity +// 66, 67 Freezing Rain: Light and heavy intensity +// 71, 73, 75 Snow fall: Slight, moderate, and heavy intensity // 77 Snow grains -// 80, 81, 82 Rain showers: Slight, moderate, and violent -// 85, 86 Snow showers slight and heavy -// 95 * Thunderstorm: Slight or moderate -// 96, 99 * Thunderstorm with slight and heavy hail +// 80, 81, 82 Rain showers: Slight, moderate, and violent +// 85, 86Snow showers slight and heavy +// 95 *Thunderstorm: Slight or moderate +// 96, 99 *Thunderstorm with slight and heavy hail export function WeatherIcon(props: any) { const { code } = props; let data: { icon: any; name: string }; switch (code) { case 0: { - data = { icon: , name: 'Clear' }; + data = { icon: Sun, name: 'Clear' }; break; } case 1: case 2: case 3: { - data = { icon: , name: 'Mainly clear' }; + data = { icon: Cloud, name: 'Mainly clear' }; break; } case 45: case 48: { - data = { icon: , name: 'Fog' }; + data = { icon: CloudFog, name: 'Fog' }; break; } case 51: case 53: case 55: { - data = { icon: , name: 'Drizzle' }; + data = { icon: Cloud, name: 'Drizzle' }; break; } case 56: case 57: { - data = { icon: , name: 'Freezing drizzle' }; + data = { icon: Snowflake, name: 'Freezing drizzle' }; break; } case 61: case 63: case 65: { - data = { icon: , name: 'Rain' }; + data = { icon: CloudRain, name: 'Rain' }; break; } case 66: case 67: { - data = { icon: , name: 'Freezing rain' }; + data = { icon: CloudRain, name: 'Freezing rain' }; break; } case 71: case 73: case 75: { - data = { icon: , name: 'Snow fall' }; + data = { icon: CloudSnow, name: 'Snow fall' }; break; } case 77: { - data = { icon: , name: 'Snow grains' }; + data = { icon: CloudSnow, name: 'Snow grains' }; break; } case 80: case 81: case 82: { - data = { icon: , name: 'Rain showers' }; + data = { icon: CloudRain, name: 'Rain showers' }; break; } case 85: case 86: { - data = { icon: , name: 'Snow showers' }; + data = { icon: CloudSnow, name: 'Snow showers' }; break; } case 95: { - data = { icon: , name: 'Thunderstorm' }; + data = { icon: CloudStorm, name: 'Thunderstorm' }; break; } case 96: case 99: { - data = { icon: , name: 'Thunderstorm with hail' }; + data = { icon: CloudStorm, name: 'Thunderstorm with hail' }; break; } default: { - data = { icon: , name: 'Unknown' }; + data = { icon: QuestionMark, name: 'Unknown' }; } } - return {data.icon}; + return ( + + + + ); } export default function WeatherComponent(props: any) { @@ -136,17 +139,14 @@ export default function WeatherComponent(props: any) { if (!weather.current_weather) { return null; } - console.log(weather.current_weather); + return ( - + {weather.current_weather.temperature}°C - - - { - // Use dayjs to format the date - // https://day.js.org/en/getting-started/installation/ - } - + + + {weather.daily.temperature_2m_max[0]}°C / {weather.daily.temperature_2m_min[0]}°C + ); } From bdf871b4768d3ea2fa288c0b44b786cd76580a2e Mon Sep 17 00:00:00 2001 From: Aj - Thomas Date: Mon, 16 May 2022 12:36:35 +0200 Subject: [PATCH 25/47] =?UTF-8?q?=F0=9F=92=84=20=EF=BF=BD=20Update=20weath?= =?UTF-8?q?er=20module=20styling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modules/weather/WeatherModule.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/modules/weather/WeatherModule.tsx b/src/components/modules/weather/WeatherModule.tsx index 8a3dcba2c..3c3b218e7 100644 --- a/src/components/modules/weather/WeatherModule.tsx +++ b/src/components/modules/weather/WeatherModule.tsx @@ -1,7 +1,9 @@ -import { Center, Group, Text, Title, Tooltip } from '@mantine/core'; +import { Group, Space, Title, Tooltip } from '@mantine/core'; import axios from 'axios'; import { useEffect, useState } from 'react'; import { + ArrowDownRight, + ArrowUpRight, Cloud, CloudFog, CloudRain, @@ -139,13 +141,17 @@ export default function WeatherComponent(props: any) { if (!weather.current_weather) { return null; } - return ( {weather.current_weather.temperature}°C - + - {weather.daily.temperature_2m_max[0]}°C / {weather.daily.temperature_2m_min[0]}°C + + {weather.daily.temperature_2m_max[0]}°C + + + {weather.daily.temperature_2m_min[0]}°C + ); From 471a9f740743c32e2e8377360536c613958a1b7a Mon Sep 17 00:00:00 2001 From: Aj - Thomas Date: Mon, 16 May 2022 12:46:27 +0200 Subject: [PATCH 26/47] Update page title --- 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 3b2271307..610e21ad4 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -24,7 +24,7 @@ export default function App(props: AppProps & { colorScheme: ColorScheme }) { return ( <> - Homarr - A homepage for your server! + Homarr 🦞 From 2ba9d517a85bffd802547fdd912321c37ea9c274 Mon Sep 17 00:00:00 2001 From: "Thomas \"ajnart\" Camlong" Date: Tue, 17 May 2022 21:22:14 +0200 Subject: [PATCH 27/47] :zap: Improve weather module --- src/components/layout/Aside.tsx | 5 ++--- src/components/layout/Navbar.tsx | 7 +++---- src/components/modules/index.ts | 1 + src/components/modules/moduleWrapper.tsx | 2 +- src/components/modules/weather/WeatherModule.tsx | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/components/layout/Aside.tsx b/src/components/layout/Aside.tsx index 912b231ac..c68c36a78 100644 --- a/src/components/layout/Aside.tsx +++ b/src/components/layout/Aside.tsx @@ -1,7 +1,5 @@ import { Aside as MantineAside, Group } from '@mantine/core'; -import { DateModule } from '../modules'; -import { CalendarModule } from '../modules/calendar/CalendarModule'; -import ModuleWrapper from '../modules/moduleWrapper'; +import { WeatherModule, DateModule, ModuleWrapper, CalendarModule } from '../modules'; export default function Aside(props: any) { return ( @@ -18,6 +16,7 @@ export default function Aside(props: any) { + ); diff --git a/src/components/layout/Navbar.tsx b/src/components/layout/Navbar.tsx index 5e243c8dc..f9fe7b28f 100644 --- a/src/components/layout/Navbar.tsx +++ b/src/components/layout/Navbar.tsx @@ -1,7 +1,5 @@ import { Group, Navbar as MantineNavbar } from '@mantine/core'; -import { WeatherModule } from '../modules'; -import { DateModule } from '../modules/date/DateModule'; -import ModuleWrapper from '../modules/moduleWrapper'; +import { WeatherModule, DateModule, ModuleWrapper } from '../modules'; export default function Navbar() { return ( @@ -17,8 +15,9 @@ export default function Navbar() { > - + + ); } diff --git a/src/components/modules/index.ts b/src/components/modules/index.ts index 4b85fa930..65051904a 100644 --- a/src/components/modules/index.ts +++ b/src/components/modules/index.ts @@ -1,3 +1,4 @@ +export * from './moduleWrapper'; export * from './date'; export * from './calendar'; export * from './search'; diff --git a/src/components/modules/moduleWrapper.tsx b/src/components/modules/moduleWrapper.tsx index 55b421a5e..64a648eef 100644 --- a/src/components/modules/moduleWrapper.tsx +++ b/src/components/modules/moduleWrapper.tsx @@ -2,7 +2,7 @@ import { Card, useMantineTheme } from '@mantine/core'; import { useConfig } from '../../tools/state'; import { IModule } from './modules'; -export default function ModuleWrapper(props: any) { +export function ModuleWrapper(props: any) { const { module }: { module: IModule } = props; const { config } = useConfig(); const enabledModules = config.settings.enabledModules ?? []; diff --git a/src/components/modules/weather/WeatherModule.tsx b/src/components/modules/weather/WeatherModule.tsx index 3c3b218e7..3a54a37b2 100644 --- a/src/components/modules/weather/WeatherModule.tsx +++ b/src/components/modules/weather/WeatherModule.tsx @@ -17,7 +17,7 @@ import { IModule } from '../modules'; import { WeatherResponse } from './WeatherInterface'; export const WeatherModule: IModule = { - title: 'Weather', + title: 'Weather (beta)', description: 'Look up the current weather in your location', icon: Sun, component: WeatherComponent, From 64923b03d9fdeb61e305340e71cd7379ca92f2f4 Mon Sep 17 00:00:00 2001 From: "Thomas \"ajnart\" Camlong" Date: Tue, 17 May 2022 21:36:07 +0200 Subject: [PATCH 28/47] :art: Fix architecture for CI --- src/components/AppShelf/AddAppShelfItem.tsx | 9 +- src/components/Settings/SettingsMenu.tsx | 1 - src/components/layout/Aside.tsx | 3 +- src/components/layout/Layout.tsx | 6 +- src/components/layout/Navbar.tsx | 7 +- src/components/modules/index.ts | 1 - .../modules/weather/WeatherInterface.ts | 195 ------------------ 7 files changed, 15 insertions(+), 207 deletions(-) diff --git a/src/components/AppShelf/AddAppShelfItem.tsx b/src/components/AppShelf/AddAppShelfItem.tsx index d6101b223..ccc6569f2 100644 --- a/src/components/AppShelf/AddAppShelfItem.tsx +++ b/src/components/AppShelf/AddAppShelfItem.tsx @@ -154,7 +154,14 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } & return ( <>
- Placeholder + Placeholder
{ diff --git a/src/components/Settings/SettingsMenu.tsx b/src/components/Settings/SettingsMenu.tsx index cbd30c987..9e9853e3a 100644 --- a/src/components/Settings/SettingsMenu.tsx +++ b/src/components/Settings/SettingsMenu.tsx @@ -2,7 +2,6 @@ import { ActionIcon, Group, Modal, - Switch, Title, Text, Tooltip, diff --git a/src/components/layout/Aside.tsx b/src/components/layout/Aside.tsx index c68c36a78..6cef6eb4e 100644 --- a/src/components/layout/Aside.tsx +++ b/src/components/layout/Aside.tsx @@ -1,5 +1,6 @@ import { Aside as MantineAside, Group } from '@mantine/core'; -import { WeatherModule, DateModule, ModuleWrapper, CalendarModule } from '../modules'; +import { WeatherModule, DateModule, CalendarModule } from '../modules'; +import { ModuleWrapper } from '../modules/moduleWrapper'; export default function Aside(props: any) { return ( diff --git a/src/components/layout/Layout.tsx b/src/components/layout/Layout.tsx index 8b82b4a74..ac2c3c742 100644 --- a/src/components/layout/Layout.tsx +++ b/src/components/layout/Layout.tsx @@ -10,11 +10,7 @@ const useStyles = createStyles((theme) => ({ export default function Layout({ children, style }: any) { const { classes, cx } = useStyles(); return ( - } - header={
} - footer={