Compare commits

...

15 Commits

Author SHA1 Message Date
Aj - Thomas
a0d86e2914 Linting and formatting
CRLF > LF is REALLY annoying
2022-05-10 19:03:41 +02:00
Aj - Thomas
5d80d36be3 Fix AppShelf circular imports 2022-05-10 19:02:16 +02:00
Aj - Thomas
72f19d450c Basic layout styling 2022-05-10 18:58:21 +02:00
Aj - Thomas
6024391414 AppShelf styling 2022-05-10 18:58:13 +02:00
Aj - Thomas
905f445641 Use custom theme
This is a step thowards personalisation for Changeable wallpaper feature. #32
2022-05-10 18:57:41 +02:00
Aj - Thomas
2462671700 Add Module wrapper 2022-05-10 18:57:04 +02:00
Aj - Thomas
0c0f8247d8 Update Calendar Module styling 2022-05-10 18:56:50 +02:00
Aj - Thomas
fa45eb3b3b Add Date module 2022-05-10 18:56:29 +02:00
Aj - Thomas
b8ce86d783 Update module interface 2022-05-10 18:56:16 +02:00
Aj - Thomas
cc58fbe263 Add GitHub Action auto deploy to Github containers
Ajnart/issue18
2022-05-10 00:21:39 +02:00
Aj - Thomas
50448c9fb6 Merge pull request #26 from ajnart/dependabot/npm_and_yarn/minimist-1.2.6
Bump minimist from 1.2.5 to 1.2.6
2022-05-09 22:10:38 +02:00
dependabot[bot]
0f1a948682 Bump minimist from 1.2.5 to 1.2.6
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-09 20:09:19 +00:00
Aj - Thomas
8f71c898a2 Merge pull request #25 from ajnart/dev
Merge dev into master
2022-05-09 22:08:16 +02:00
Aj - Thomas
7be22833b0 Merge pull request #22 from ajnart/ajnart/issue21
Add darkmode switch in settings menu #21
2022-05-09 22:06:26 +02:00
Aj - Thomas
43c0465e52 Add darkmode switch in settings menu #21 2022-05-09 01:35:11 +02:00
17 changed files with 249 additions and 94 deletions

View File

@@ -18,6 +18,7 @@ import { useState } from 'react';
import { Apps } from 'tabler-icons-react'; import { Apps } from 'tabler-icons-react';
import { useConfig } from '../../tools/state'; import { useConfig } from '../../tools/state';
import { ServiceTypeList } from '../../tools/types'; import { ServiceTypeList } from '../../tools/types';
import { AppShelfItemWrapper } from './AppShelfItemWrapper';
export default function AddItemShelfItem(props: any) { export default function AddItemShelfItem(props: any) {
const { addService } = useConfig(); const { addService } = useConfig();
@@ -34,30 +35,39 @@ export default function AddItemShelfItem(props: any) {
> >
<AddAppShelfItemForm setOpened={setOpened} /> <AddAppShelfItemForm setOpened={setOpened} />
</Modal> </Modal>
<AspectRatio <AppShelfItemWrapper>
style={{ <Card.Section>
minHeight: 120, <Group position="center" mx="lg">
minWidth: 120, <Text
}} // TODO: #1 Remove this hack to get the text to be centered.
ratio={4 / 3} ml={15}
> style={{
<Card alignSelf: 'center',
style={{ alignContent: 'center',
backgroundColor: alignItems: 'center',
theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[1], justifyContent: 'center',
width: 200, justifyItems: 'center',
height: 180, }}
}} mt="sm"
radius="md" weight={500}
> >
<Group direction="column" position="center"> Add a service
<motion.div whileHover={{ scale: 1.2 }}> </Text>
<Apps style={{ cursor: 'pointer' }} onClick={() => setOpened(true)} size={60} />
</motion.div>
<Text>Add Service</Text>
</Group> </Group>
</Card> </Card.Section>
</AspectRatio> <Card.Section>
<AspectRatio ratio={5 / 3} m="xl">
<motion.i
whileHover={{
cursor: 'pointer',
scale: 1.1,
}}
>
<Apps style={{ cursor: 'pointer' }} onClick={() => setOpened(true)} size={60} />
</motion.i>
</AspectRatio>
</Card.Section>
</AppShelfItemWrapper>
</> </>
); );
} }

View File

@@ -3,7 +3,6 @@ import { motion } from 'framer-motion';
import { import {
Text, Text,
AspectRatio, AspectRatio,
createStyles,
SimpleGrid, SimpleGrid,
Card, Card,
useMantineTheme, useMantineTheme,
@@ -11,20 +10,12 @@ import {
Group, Group,
Space, Space,
} from '@mantine/core'; } from '@mantine/core';
import AppShelfMenu from './AppShelfMenu';
import AddItemShelfItem from './AddAppShelfItem';
import { useConfig } from '../../tools/state'; import { useConfig } from '../../tools/state';
import { pingQbittorrent } from '../../tools/api'; import { pingQbittorrent } from '../../tools/api';
import { serviceItem } from '../../tools/types'; import { serviceItem } from '../../tools/types';
import AddItemShelfItem from './AddAppShelfItem';
const useStyles = createStyles((theme) => ({ import { AppShelfItemWrapper } from './AppShelfItemWrapper';
main: { import AppShelfMenu from './AppShelfMenu';
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[1],
//TODO: #3 Fix this temporary fix and make the width and height dynamic / responsive
width: 200,
height: 180,
},
}));
const AppShelf = (props: any) => { const AppShelf = (props: any) => {
const { config, addService, removeService, setConfig } = useConfig(); const { config, addService, removeService, setConfig } = useConfig();
@@ -58,7 +49,6 @@ export function AppShelfItem(props: any) {
const { service }: { service: serviceItem } = props; const { service }: { service: serviceItem } = props;
const theme = useMantineTheme(); const theme = useMantineTheme();
const { removeService } = useConfig(); const { removeService } = useConfig();
const { classes } = useStyles();
const [hovering, setHovering] = useState(false); const [hovering, setHovering] = useState(false);
return ( return (
<motion.div <motion.div
@@ -70,13 +60,7 @@ export function AppShelfItem(props: any) {
setHovering(false); setHovering(false);
}} }}
> >
<Card <AppShelfItemWrapper hovering={hovering}>
className={classes.main}
style={{
boxShadow: hovering ? '0px 0px 3px rgba(0, 0, 0, 0.5)' : '0px 0px 1px rgba(0, 0, 0, 0.5)',
}}
radius="md"
>
<Card.Section> <Card.Section>
<Group position="apart" mx="lg"> <Group position="apart" mx="lg">
<Space /> <Space />
@@ -128,7 +112,7 @@ export function AppShelfItem(props: any) {
</motion.i> </motion.i>
</AspectRatio> </AspectRatio>
</Card.Section> </Card.Section>
</Card> </AppShelfItemWrapper>
</motion.div> </motion.div>
); );
} }

View File

@@ -0,0 +1,21 @@
import { useMantineTheme, Card } from '@mantine/core';
export function AppShelfItemWrapper(props: any) {
const { children, hovering } = props;
const theme = useMantineTheme();
return (
<Card
style={{
boxShadow: hovering ? '0px 0px 3px rgba(0, 0, 0, 0.5)' : '0px 0px 1px rgba(0, 0, 0, 0.5)',
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[1],
//TODO: #3 Fix this temporary fix and make the width and height dynamic / responsive
width: 200,
height: 180,
}}
radius="md"
>
{children}
</Card>
);
}

View File

@@ -0,0 +1,45 @@
import React from 'react';
import { createStyles, Switch, Group, useMantineColorScheme } from '@mantine/core';
import { Sun, MoonStars } from 'tabler-icons-react';
const useStyles = createStyles((theme) => ({
root: {
position: 'relative',
'& *': {
cursor: 'pointer',
},
},
icon: {
pointerEvents: 'none',
position: 'absolute',
zIndex: 1,
top: 3,
},
iconLight: {
left: 4,
color: theme.white,
},
iconDark: {
right: 4,
color: theme.colors.gray[6],
},
}));
export function ColorSchemeSwitch() {
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
const { classes, cx } = useStyles();
return (
<Group>
<div className={classes.root}>
<Sun className={cx(classes.icon, classes.iconLight)} size={18} />
<MoonStars className={cx(classes.icon, classes.iconDark)} size={18} />
<Switch checked={colorScheme === 'dark'} onChange={() => toggleColorScheme()} size="md" />
</div>
Switch to {colorScheme === 'dark' ? 'light' : 'dark'} mode
</Group>
);
}

View File

@@ -8,13 +8,16 @@ import {
Tooltip, Tooltip,
SegmentedControl, SegmentedControl,
} from '@mantine/core'; } from '@mantine/core';
import { useColorScheme } from '@mantine/hooks';
import { useState } from 'react'; import { useState } from 'react';
import { Settings as SettingsIcon } from 'tabler-icons-react'; import { Settings as SettingsIcon } from 'tabler-icons-react';
import { useConfig } from '../../tools/state'; import { useConfig } from '../../tools/state';
import { ColorSchemeSwitch } from '../ColorSchemeToggle/ColorSchemeSwitch';
import SaveConfigComponent from '../Config/SaveConfig'; import SaveConfigComponent from '../Config/SaveConfig';
function SettingsMenu(props: any) { function SettingsMenu(props: any) {
const { config, setConfig } = useConfig(); const { config, setConfig } = useConfig();
const colorScheme = useColorScheme();
const matches = [ const matches = [
{ label: 'Google', value: 'https://google.com/search?q=' }, { label: 'Google', value: 'https://google.com/search?q=' },
{ label: 'DuckDuckGo', value: 'https://duckduckgo.com/?q=' }, { label: 'DuckDuckGo', value: 'https://duckduckgo.com/?q=' },
@@ -46,6 +49,7 @@ function SettingsMenu(props: any) {
</Group> </Group>
<Group direction="column"> <Group direction="column">
<Switch <Switch
size="md"
onChange={(e) => onChange={(e) =>
setConfig({ setConfig({
...config, ...config,
@@ -59,6 +63,7 @@ function SettingsMenu(props: any) {
label="Enable search bar" label="Enable search bar"
/> />
</Group> </Group>
<ColorSchemeSwitch />
<SaveConfigComponent /> <SaveConfigComponent />
<Text <Text
style={{ style={{

View File

@@ -0,0 +1,18 @@
import { Aside as MantineAside } from '@mantine/core';
import { CalendarModule } from '../modules/calendar/CalendarModule';
import ModuleWrapper from '../modules/moduleWrapper';
export default function Aside() {
return (
<MantineAside
height="100%"
hiddenBreakpoint="md"
hidden
width={{
base: 'auto',
}}
>
<ModuleWrapper module={CalendarModule} />
</MantineAside>
);
}

View File

@@ -11,7 +11,6 @@ import {
import { useBooleanToggle } from '@mantine/hooks'; import { useBooleanToggle } from '@mantine/hooks';
import { NextLink } from '@mantine/next'; import { NextLink } from '@mantine/next';
import { Logo } from './Logo'; import { Logo } from './Logo';
import { ColorSchemeToggle } from '../ColorSchemeToggle/ColorSchemeToggle';
import { SettingsMenuButton } from '../Settings/SettingsMenu'; import { SettingsMenuButton } from '../Settings/SettingsMenu';
import CalendarComponent from '../modules/calendar/CalendarModule'; import CalendarComponent from '../modules/calendar/CalendarModule';
@@ -119,7 +118,6 @@ export function Header({ links }: HeaderResponsiveProps) {
<Head height={HEADER_HEIGHT} mb={10} className={classes.root}> <Head height={HEADER_HEIGHT} mb={10} className={classes.root}>
<Container className={classes.header}> <Container className={classes.header}>
<Group> <Group>
<ColorSchemeToggle />
<NextLink style={{ textDecoration: 'none' }} href="/"> <NextLink style={{ textDecoration: 'none' }} href="/">
<Logo style={{ fontSize: 22 }} /> <Logo style={{ fontSize: 22 }} />
</NextLink> </NextLink>

View File

@@ -1,7 +1,8 @@
import { AppShell, Aside, Center, createStyles } from '@mantine/core'; import { AppShell, Center, createStyles } from '@mantine/core';
import { Header } from './Header'; import { Header } from './Header';
import { Footer } from './Footer'; import { Footer } from './Footer';
import CalendarComponent from '../modules/calendar/CalendarModule'; import Aside from './Aside';
import Navbar from './Navbar';
const useStyles = createStyles((theme) => ({ const useStyles = createStyles((theme) => ({
main: { main: {
@@ -15,18 +16,8 @@ export default function Layout({ children, style }: any) {
const { classes, cx } = useStyles(); const { classes, cx } = useStyles();
return ( return (
<AppShell <AppShell
aside={ navbar={<Navbar />}
<Aside aside={<Aside />}
height="auto"
hiddenBreakpoint="md"
hidden
width={{
base: 'auto',
}}
>
<CalendarComponent />
</Aside>
}
header={<Header links={[]} />} header={<Header links={[]} />}
footer={<Footer links={[]} />} footer={<Footer links={[]} />}
> >

View File

@@ -0,0 +1,18 @@
import { Navbar as MantineNavbar } from '@mantine/core';
import { DateModule } from '../modules/date/DateModule';
import ModuleWrapper from '../modules/moduleWrapper';
export default function Navbar() {
return (
<MantineNavbar
height="100%"
hiddenBreakpoint="md"
hidden
width={{
base: 'auto',
}}
>
<ModuleWrapper module={DateModule} />
</MantineNavbar>
);
}

View File

@@ -1,14 +1,13 @@
/* eslint-disable react/no-children-prop */ /* eslint-disable react/no-children-prop */
import { Popover, Box, ScrollArea, Divider, Indicator } from '@mantine/core'; import { Popover, Box, ScrollArea, Divider, Indicator } from '@mantine/core';
import { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Calendar } from '@mantine/dates'; import { Calendar } from '@mantine/dates';
import { CalendarIcon } from '@modulz/radix-icons'; import { CalendarIcon } from '@modulz/radix-icons';
import { RadarrMediaDisplay, SonarrMediaDisplay } from './MediaDisplay'; import { RadarrMediaDisplay, SonarrMediaDisplay } from './MediaDisplay';
import { useConfig } from '../../../tools/state'; import { useConfig } from '../../../tools/state';
import { MHPModule } from '../modules'; import { IModule } from '../modules';
import React from 'react';
export const CalendarModule: MHPModule = { export const CalendarModule: IModule = {
title: 'Calendar', title: 'Calendar',
description: description:
'A calendar module for displaying upcoming releases. It interacts with the Sonarr and Radarr API.', 'A calendar module for displaying upcoming releases. It interacts with the Sonarr and Radarr API.',
@@ -94,12 +93,8 @@ function DayComponent(props: any) {
setOpened(true); setOpened(true);
}} }}
> >
{radarrFiltered.length > 0 && ( {radarrFiltered.length > 0 && <Indicator size={7} color="yellow" children={null} />}
<Indicator size={7} color="yellow" children={null} /> {sonarrFiltered.length > 0 && <Indicator size={7} offset={8} color="blue" children={null} />}
)}
{sonarrFiltered.length > 0 && (
<Indicator size={7} offset={8} color="blue" children={null} />
)}
<Popover <Popover
position="left" position="left"
width={700} width={700}
@@ -109,25 +104,21 @@ function DayComponent(props: any) {
target={` ${day}`} target={` ${day}`}
> >
<ScrollArea style={{ height: 400 }}> <ScrollArea style={{ height: 400 }}>
{sonarrFiltered.map((media: any, index: number) => { {sonarrFiltered.map((media: any, index: number) => (
return ( <React.Fragment key={index}>
<React.Fragment key={index}> <SonarrMediaDisplay media={media} />
<SonarrMediaDisplay media={media} /> {index < sonarrFiltered.length - 1 && <Divider variant="dashed" my="xl" />}
{index < sonarrFiltered.length - 1 && <Divider variant="dashed" my="xl" />} </React.Fragment>
</React.Fragment> ))}
);
})}
{radarrFiltered.length > 0 && sonarrFiltered.length > 0 && ( {radarrFiltered.length > 0 && sonarrFiltered.length > 0 && (
<Divider variant="dashed" my="xl" /> <Divider variant="dashed" my="xl" />
)} )}
{radarrFiltered.map((media: any, index: number) => { {radarrFiltered.map((media: any, index: number) => (
return ( <React.Fragment key={index}>
<React.Fragment key={index}> <RadarrMediaDisplay media={media} />
<RadarrMediaDisplay media={media} /> {index < radarrFiltered.length - 1 && <Divider variant="dashed" my="xl" />}
{index < radarrFiltered.length - 1 && <Divider variant="dashed" my="xl" />} </React.Fragment>
</React.Fragment> ))}
);
})}
</ScrollArea> </ScrollArea>
</Popover> </Popover>
</Box> </Box>

View File

@@ -0,0 +1,7 @@
import DateComponent from './DateModule';
export default {
title: 'Date module',
};
export const Default = (args: any) => <DateComponent {...args} />;

View File

@@ -0,0 +1,41 @@
import { Group, Text, Title } from '@mantine/core';
import dayjs from 'dayjs';
import { useEffect, useState } from 'react';
import { Clock } from 'tabler-icons-react';
import { IModule } from '../modules';
export const DateModule: IModule = {
title: 'Date',
description: 'Show the current time and date in a card',
icon: Clock,
component: DateComponent,
};
export default function DateComponent(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 (
<Group p="sm" direction="column">
<Title>
{hours < 10 ? `0${hours}` : hours}:{minutes < 10 ? `0${minutes}` : minutes}
</Title>
<Text size="xl">
{
// Use dayjs to format the date
// https://day.js.org/en/getting-started/installation/
dayjs(date).format('dddd, MMMM D YYYY')
}
</Text>
</Group>
);
}

View File

@@ -0,0 +1,20 @@
import { Card, useMantineTheme } from '@mantine/core';
import { IModule } from './modules';
export default function ModuleWrapper(props: any) {
const { module }: { module: IModule } = props;
const theme = useMantineTheme();
return (
<Card
mx="sm"
radius="lg"
shadow="sm"
style={{
// Make background color of the card depend on the theme
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[6] : 'white',
}}
>
<module.component />
</Card>
);
}

View File

@@ -2,7 +2,7 @@
// Each module should have its own interface and call the following function: // Each module should have its own interface and call the following function:
// TODO: Add a function to register a module // TODO: Add a function to register a module
// Note: Maybe use context to keep track of the modules // Note: Maybe use context to keep track of the modules
export interface MHPModule { export interface IModule {
title: string; title: string;
description: string; description: string;
icon: React.ReactNode; icon: React.ReactNode;

View File

@@ -7,6 +7,7 @@ import { MantineProvider, ColorScheme, ColorSchemeProvider } from '@mantine/core
import { NotificationsProvider } from '@mantine/notifications'; import { NotificationsProvider } from '@mantine/notifications';
import Layout from '../components/layout/Layout'; import Layout from '../components/layout/Layout';
import { ConfigProvider } from '../tools/state'; import { ConfigProvider } from '../tools/state';
import { theme } from '../tools/theme';
export default function App(props: AppProps & { colorScheme: ColorScheme }) { export default function App(props: AppProps & { colorScheme: ColorScheme }) {
const { Component, pageProps } = props; const { Component, pageProps } = props;
@@ -27,7 +28,14 @@ export default function App(props: AppProps & { colorScheme: ColorScheme }) {
</Head> </Head>
<ColorSchemeProvider colorScheme={colorScheme} toggleColorScheme={toggleColorScheme}> <ColorSchemeProvider colorScheme={colorScheme} toggleColorScheme={toggleColorScheme}>
<MantineProvider theme={{ colorScheme }} withGlobalStyles withNormalizeCSS> <MantineProvider
theme={{
...theme,
colorScheme,
}}
withGlobalStyles
withNormalizeCSS
>
<NotificationsProvider position="top-right"> <NotificationsProvider position="top-right">
<ConfigProvider> <ConfigProvider>
<Layout> <Layout>

3
tools/theme.ts Normal file
View File

@@ -0,0 +1,3 @@
import { MantineProviderProps } from '@mantine/core';
export const theme: MantineProviderProps['theme'] = {};

View File

@@ -8853,16 +8853,11 @@ minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.2:
dependencies: dependencies:
brace-expansion "^1.1.7" brace-expansion "^1.1.7"
minimist@^1.1.1, minimist@^1.2.5, minimist@^1.2.6: minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@~1.2.5:
version "1.2.6" version "1.2.6"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
minimist@^1.2.0, minimist@~1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
minipass-collect@^1.0.2: minipass-collect@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617"