Merge branch 'master' into devel

This commit is contained in:
Walkx
2022-05-12 13:15:39 +02:00
committed by GitHub
18 changed files with 430 additions and 224 deletions

View File

@@ -10,9 +10,9 @@ import {
AspectRatio,
Text,
Card,
LoadingOverlay,
} from '@mantine/core';
import { useForm } from '@mantine/hooks';
import { UseForm } from '@mantine/hooks/lib/use-form/use-form';
import { useForm } from '@mantine/form';
import { motion } from 'framer-motion';
import { useState } from 'react';
import { Apps } from 'tabler-icons-react';
@@ -94,10 +94,12 @@ function MatchIcon(
return false;
}
export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } & any) {
const { setOpened } = props;
const { addService, config, setConfig } = useConfig();
const [isLoading, setLoading] = useState(false);
const form = useForm({
initialValues: {
type: props.type ?? 'Other',
@@ -106,6 +108,23 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
url: props.url ?? '',
apiKey: props.apiKey ?? (undefined as unknown as string),
},
validate: {
apiKey: (value: string) => null,
// Validate icon with a regex
icon: (value: string) => {
if (!value.match(/^https?:\/\/.+\.(png|jpg|jpeg|gif)$/)) {
return 'Please enter a valid icon URL';
}
return null;
},
// Validate url with a regex http/https
url: (value: string) => {
if (!value.match(/^https?:\/\/.+\/$/)) {
return 'Please enter a valid URL (that ends with a /)';
}
return null;
},
},
});
return (
@@ -148,44 +167,40 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
form.setFieldValue('icon', match);
}
}}
error={form.errors.name && 'Invalid name'}
error={form.errors.name && 'Invalid icon url'}
/>
<TextInput
required
label="Icon url"
placeholder="https://i.gifer.com/ANPC.gif"
value={form.values.icon}
onChange={(event) => {
form.setFieldValue('icon', event.currentTarget.value);
}}
error={form.errors.icon && 'Icon url is invalid'}
{...form.getInputProps('icon')}
/>
<TextInput
required
label="Service url"
placeholder="http://localhost:8989"
value={form.values.url}
onChange={(event) => form.setFieldValue('url', event.currentTarget.value)}
error={form.errors.url && 'Service url is invalid'}
{...form.getInputProps('url')}
/>
<Select
label="Select the type of service (used for API calls)"
defaultValue="Other"
placeholder="Pick one"
value={form.values.type}
required
searchable
onChange={(value) => form.setFieldValue('type', value ?? 'Other')}
data={ServiceTypeList}
{...form.getInputProps('type')}
/>
<LoadingOverlay visible={isLoading} />
{(form.values.type === 'Sonarr' || form.values.type === 'Radarr') && (
<TextInput
required
label="API key"
placeholder="Your API key"
value={form.values.apiKey}
onChange={(event) => form.setFieldValue('apiKey', event.currentTarget.value)}
onChange={(event) => {
form.setFieldValue('apiKey', event.currentTarget.value);
}}
error={form.errors.apiKey && 'Invalid API key'}
/>
)}

View File

@@ -11,7 +11,6 @@ import {
Space,
} from '@mantine/core';
import { useConfig } from '../../tools/state';
import { pingQbittorrent } from '../../tools/api';
import { serviceItem } from '../../tools/types';
import AddItemShelfItem from './AddAppShelfItem';
import { AppShelfItemWrapper } from './AppShelfItemWrapper';
@@ -27,13 +26,6 @@ const AppShelf = (props: any) => {
setConfig(JSON.parse(localConfig));
}
}, []);
if (config.services && config.services.length === 0) {
config.services.forEach((service) => {
if (service.type === 'qBittorrent') {
pingQbittorrent(service);
}
});
}
return (
<SimpleGrid m="xl" cols={5} spacing="xl">

View File

@@ -0,0 +1,41 @@
import { Group, Switch } from '@mantine/core';
import * as Modules from '../modules';
import { useConfig } from '../../tools/state';
export default function ModuleEnabler(props: any) {
const { config, setConfig } = useConfig();
const modules = Object.values(Modules).map((module) => module);
const enabledModules = config.settings.enabledModules ?? [];
modules.filter((module) => enabledModules.includes(module.title));
return (
<Group direction="column">
{modules.map((module) => (
<Switch
key={module.title}
size="md"
checked={enabledModules.includes(module.title)}
label={`Enable ${module.title} module`}
onChange={(e) => {
if (e.currentTarget.checked) {
setConfig({
...config,
settings: {
...config.settings,
enabledModules: [...enabledModules, module.title],
},
});
} else {
setConfig({
...config,
settings: {
...config.settings,
enabledModules: enabledModules.filter((m) => m !== module.title),
},
});
}
}}
/>
))}
</Group>
);
}

View File

@@ -14,6 +14,7 @@ import { Settings as SettingsIcon } from 'tabler-icons-react';
import { useConfig } from '../../tools/state';
import { ColorSchemeSwitch } from '../ColorSchemeToggle/ColorSchemeSwitch';
import SaveConfigComponent from '../Config/SaveConfig';
import ModuleEnabler from './ModuleEnabler';
function SettingsMenu(props: any) {
const { config, setConfig } = useConfig();
@@ -63,6 +64,7 @@ function SettingsMenu(props: any) {
label="Enable search bar"
/>
</Group>
<ModuleEnabler />
<ColorSchemeSwitch />
<SaveConfigComponent />
<Text

View File

@@ -3,6 +3,8 @@ import { Popover, Box, ScrollArea, Divider, Indicator } from '@mantine/core';
import React, { useEffect, useState } from 'react';
import { Calendar } from '@mantine/dates';
import { CalendarIcon } from '@modulz/radix-icons';
import { showNotification } from '@mantine/notifications';
import { Check } from 'tabler-icons-react';
import { RadarrMediaDisplay, SonarrMediaDisplay } from './MediaDisplay';
import { useConfig } from '../../../tools/state';
import { IModule } from '../modules';
@@ -34,14 +36,36 @@ export default function CalendarComponent(props: any) {
fetch(
`${sonarrService?.url}api/calendar?apikey=${sonarrService?.apiKey}&end=${nextMonth}`
).then((response) => {
response.ok && response.json().then((data) => setSonarrMedias(data));
response.ok &&
response.json().then((data) => {
setSonarrMedias(data);
showNotification({
title: 'Sonarr',
icon: <Check />,
color: 'green',
autoClose: 1500,
radius: 'md',
message: `Loaded ${data.length} releases`,
});
});
});
}
if (radarrService && radarrService.apiKey) {
fetch(
`${radarrService?.url}api/v3/calendar?apikey=${radarrService?.apiKey}&end=${nextMonth}`
).then((response) => {
response.ok && response.json().then((data) => setRadarrMedias(data));
response.ok &&
response.json().then((data) => {
setRadarrMedias(data);
showNotification({
title: 'Radarr',
icon: <Check />,
color: 'green',
autoClose: 1500,
radius: 'md',
message: `Loaded ${data.length} releases`,
});
});
});
}
}, [config.services]);

View File

@@ -0,0 +1 @@
export { CalendarModule } from './CalendarModule';

View File

@@ -33,7 +33,7 @@ export default function DateComponent(props: any) {
{
// Use dayjs to format the date
// https://day.js.org/en/getting-started/installation/
dayjs(date).format('dddd, MMMM D YYYY')
dayjs(date).format('dddd, MMMM D')
}
</Text>
</Group>

View File

@@ -0,0 +1 @@
export { DateModule } from './DateModule';

View File

@@ -0,0 +1,2 @@
export * from './date';
export * from './calendar';

View File

@@ -1,11 +1,20 @@
import { Card, useMantineTheme } from '@mantine/core';
import { useConfig } from '../../tools/state';
import { IModule } from './modules';
export default function ModuleWrapper(props: any) {
const { module }: { module: IModule } = props;
const { config } = useConfig();
const enabledModules = config.settings.enabledModules ?? [];
// Remove 'Module' from enabled modules titles
const isShown = enabledModules.includes(module.title);
const theme = useMantineTheme();
if (!isShown) {
return null;
}
return (
<Card
hidden={!isShown}
mx="sm"
radius="lg"
shadow="sm"