mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-03 03:55:56 +01:00
Use Config provider everywhere in app
We can now load as much data as we want in the services and settings values. This solves the issue of using multiple localStorages boxes
This commit is contained in:
@@ -17,7 +17,7 @@ import { useForm } from '@mantine/hooks';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useState } from 'react';
|
||||
import { Apps } from 'tabler-icons-react';
|
||||
import { ServiceTypes } from '../../tools/types';
|
||||
import { ServiceType, ServiceTypeList } from '../../tools/types';
|
||||
|
||||
export default function AddItemShelfItem(props: any) {
|
||||
const { additem: addItem } = props;
|
||||
@@ -94,7 +94,7 @@ export default function AddItemShelfItem(props: any) {
|
||||
required
|
||||
searchable
|
||||
onChange={(value) => form.setFieldValue('type', value ?? 'Other')}
|
||||
data={ServiceTypes}
|
||||
data={ServiceTypeList}
|
||||
/>
|
||||
</Group>
|
||||
|
||||
|
||||
@@ -15,8 +15,9 @@ import { showNotification } from '@mantine/notifications';
|
||||
import { AlertCircle, Cross, X } from 'tabler-icons-react';
|
||||
import AppShelfMenu from './AppShelfMenu';
|
||||
import AddItemShelfItem from './AddAppShelfItem';
|
||||
import { useServices } from '../../tools/state';
|
||||
import { useConfig } from '../../tools/state';
|
||||
import { pingQbittorrent } from '../../tools/api';
|
||||
import { Config } from '../../tools/types';
|
||||
|
||||
const useStyles = createStyles((theme) => ({
|
||||
main: {
|
||||
@@ -32,52 +33,57 @@ const useStyles = createStyles((theme) => ({
|
||||
}));
|
||||
|
||||
const AppShelf = (props: any) => {
|
||||
const { services, addService, removeService, setServicesState } = useServices();
|
||||
const { config, addService, removeService, setConfig } = useConfig();
|
||||
const { classes } = useStyles();
|
||||
const [hovering, setHovering] = useState('none');
|
||||
|
||||
/* A hook that is used to load the config from local storage. */
|
||||
useEffect(() => {
|
||||
const localServices = localStorage.getItem('services');
|
||||
if (localServices) {
|
||||
setServicesState(JSON.parse(localServices));
|
||||
const localConfig = localStorage.getItem('config');
|
||||
if (localConfig) {
|
||||
setConfig(JSON.parse(localConfig));
|
||||
}
|
||||
}, []);
|
||||
services.forEach((service) => {
|
||||
if (service.type === 'qBittorrent') {
|
||||
pingQbittorrent(service);
|
||||
}
|
||||
});
|
||||
if (config.services && config.services.length === 0) {
|
||||
config.services.forEach((service) => {
|
||||
if (service.type === 'qBittorrent') {
|
||||
pingQbittorrent(service);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid m="xl" gutter="xl">
|
||||
{services.map((service, i) => (
|
||||
<Grid.Col lg={2} sm={3} key={i}>
|
||||
<motion.div
|
||||
onHoverStart={(e) => {
|
||||
setHovering(service.name);
|
||||
}}
|
||||
onHoverEnd={(e) => {
|
||||
setHovering('none');
|
||||
}}
|
||||
>
|
||||
<AspectRatio ratio={4 / 3}>
|
||||
<Box className={classes.main}>
|
||||
<motion.div animate={{ opacity: hovering == service.name ? 1 : 0 }}>
|
||||
<AppShelfMenu removeitem={removeService} name={service.name} />
|
||||
</motion.div>
|
||||
<Group direction="column" position="center">
|
||||
<Anchor href={service.url} target="_blank">
|
||||
<motion.div whileHover={{ scale: 1.2 }}>
|
||||
<Image style={{ maxWidth: 60 }} src={service.icon} alt={service.name} />
|
||||
{config.services
|
||||
? config.services.map((service, i) => (
|
||||
<Grid.Col lg={2} sm={3} key={i}>
|
||||
<motion.div
|
||||
onHoverStart={(e) => {
|
||||
setHovering(service.name);
|
||||
}}
|
||||
onHoverEnd={(e) => {
|
||||
setHovering('none');
|
||||
}}
|
||||
>
|
||||
<AspectRatio ratio={4 / 3}>
|
||||
<Box className={classes.main}>
|
||||
<motion.div animate={{ opacity: hovering == service.name ? 1 : 0 }}>
|
||||
<AppShelfMenu removeitem={removeService} name={service.name} />
|
||||
</motion.div>
|
||||
</Anchor>
|
||||
<Text>{service.name}</Text>
|
||||
</Group>
|
||||
</Box>
|
||||
</AspectRatio>
|
||||
</motion.div>
|
||||
</Grid.Col>
|
||||
))}
|
||||
<Group direction="column" position="center">
|
||||
<Anchor href={service.url} target="_blank">
|
||||
<motion.div whileHover={{ scale: 1.2 }}>
|
||||
<Image style={{ maxWidth: 60 }} src={service.icon} alt={service.name} />
|
||||
</motion.div>
|
||||
</Anchor>
|
||||
<Text>{service.name}</Text>
|
||||
</Group>
|
||||
</Box>
|
||||
</AspectRatio>
|
||||
</motion.div>
|
||||
</Grid.Col>
|
||||
))
|
||||
: null}
|
||||
<AddItemShelfItem additem={addService} />
|
||||
</Grid>
|
||||
);
|
||||
|
||||
@@ -4,8 +4,8 @@ import { Dropzone, DropzoneStatus, FullScreenDropzone, IMAGE_MIME_TYPE } from '@
|
||||
import { showNotification } from '@mantine/notifications';
|
||||
import { useRef } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useServices } from '../../tools/state';
|
||||
import { serviceItem } from '../../tools/types';
|
||||
import { useConfig } from '../../tools/state';
|
||||
import { Config, serviceItem } from '../../tools/types';
|
||||
|
||||
function getIconColor(status: DropzoneStatus, theme: MantineTheme) {
|
||||
return status.accepted
|
||||
@@ -48,7 +48,7 @@ export const dropzoneChildren = (status: DropzoneStatus, theme: MantineTheme) =>
|
||||
);
|
||||
|
||||
export default function LoadConfigComponent(props: any) {
|
||||
const { services, addService, removeService, setServicesState } = useServices();
|
||||
const { saveConfig, setConfig } = useConfig();
|
||||
const theme = useMantineTheme();
|
||||
const router = useRouter();
|
||||
const openRef = useRef<() => void>();
|
||||
@@ -58,7 +58,7 @@ export default function LoadConfigComponent(props: any) {
|
||||
onDrop={(files) => {
|
||||
files[0].text().then((e) => {
|
||||
try {
|
||||
JSON.parse(e) as serviceItem[];
|
||||
JSON.parse(e) as Config;
|
||||
} catch (e) {
|
||||
showNotification({
|
||||
autoClose: 5000,
|
||||
@@ -77,7 +77,7 @@ export default function LoadConfigComponent(props: any) {
|
||||
icon: <Check />,
|
||||
message: undefined,
|
||||
});
|
||||
setServicesState(JSON.parse(e));
|
||||
setConfig(JSON.parse(e));
|
||||
});
|
||||
}}
|
||||
accept={['application/json']}
|
||||
|
||||
@@ -2,12 +2,14 @@ import { Anchor, Button, ThemeIcon, Tooltip } from '@mantine/core';
|
||||
import fileDownload from 'js-file-download';
|
||||
import { Dropzone, DropzoneStatus, IMAGE_MIME_TYPE } from '@mantine/dropzone';
|
||||
import { Download } from 'tabler-icons-react';
|
||||
import { useConfig } from '../../tools/state';
|
||||
|
||||
export default function SaveConfigComponent(props: any) {
|
||||
|
||||
const { config } = useConfig();
|
||||
function onClick(e: any) {
|
||||
const services = localStorage.getItem('services');
|
||||
if (services) {
|
||||
fileDownload(JSON.stringify(JSON.parse(services), null, '\t'), 'services.json');
|
||||
if (config) {
|
||||
fileDownload(JSON.stringify(config, null, '\t'), 'services.json');
|
||||
}
|
||||
}
|
||||
return (
|
||||
|
||||
@@ -19,38 +19,22 @@ import {
|
||||
InfoCircle,
|
||||
FileX,
|
||||
} from 'tabler-icons-react';
|
||||
import { loadSettings } from '../../tools/settings';
|
||||
import { useConfig } from '../../tools/state';
|
||||
import { Settings } from '../../tools/types';
|
||||
|
||||
export default function SearchBar(props: any) {
|
||||
const { config, setConfig } = useConfig();
|
||||
const [opened, setOpened] = useState(false);
|
||||
const [icon, setIcon] = useState(<Search />);
|
||||
const theme = useMantineTheme();
|
||||
const [config, setConfig] = useState<Settings>({
|
||||
searchBar: true,
|
||||
searchUrl: 'https://www.google.com/search?q=',
|
||||
});
|
||||
|
||||
const querryUrl = config.searchUrl || 'https://www.google.com/search?q=';
|
||||
const querryUrl = config.settings.searchUrl || 'https://www.google.com/search?q=';
|
||||
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
querry: '',
|
||||
},
|
||||
});
|
||||
useEffect(() => {
|
||||
const config = loadSettings('settings');
|
||||
if (config) {
|
||||
showNotification({
|
||||
autoClose: 1000,
|
||||
title: <Text>Config loaded</Text>,
|
||||
message: undefined,
|
||||
});
|
||||
setConfig(config);
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (!config.searchBar) {
|
||||
if (config.settings.searchBar == false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,57 +3,39 @@ import { showNotification } from '@mantine/notifications';
|
||||
import { motion } from 'framer-motion';
|
||||
import { CSSProperties, useEffect, useState } from 'react';
|
||||
import { Mail, Settings as SettingsIcon, X } from 'tabler-icons-react';
|
||||
import { loadSettings } from '../../tools/settings';
|
||||
import { Settings } from '../../tools/types';
|
||||
import { useConfig } from '../../tools/state';
|
||||
import SaveConfigComponent from '../Config/SaveConfig';
|
||||
|
||||
function SettingsMenu(props: any) {
|
||||
const [config, setConfig] = useState<Settings>({
|
||||
searchUrl: 'https://www.google.com/search?q=',
|
||||
searchBar: true,
|
||||
});
|
||||
const { config, setConfig } = useConfig();
|
||||
|
||||
useEffect(() => {
|
||||
const config = loadSettings('settings');
|
||||
if (config) {
|
||||
setConfig(config);
|
||||
}
|
||||
}, []);
|
||||
return (
|
||||
<Group direction="column" grow>
|
||||
<TextInput
|
||||
label="Search bar querry url"
|
||||
defaultValue={config.searchUrl}
|
||||
onChange={(e) => {
|
||||
defaultValue={config.settings.searchUrl}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
searchUrl: e.target.value,
|
||||
});
|
||||
localStorage.setItem(
|
||||
'settings',
|
||||
JSON.stringify({
|
||||
...config,
|
||||
settings: {
|
||||
...config.settings,
|
||||
searchUrl: e.target.value,
|
||||
})
|
||||
);
|
||||
}}
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Group direction="column">
|
||||
<Switch
|
||||
onChange={(e) => {
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
searchBar: e.target.checked,
|
||||
});
|
||||
localStorage.setItem(
|
||||
'settings',
|
||||
JSON.stringify({
|
||||
...config,
|
||||
searchBar: e.target.checked,
|
||||
})
|
||||
);
|
||||
}}
|
||||
checked={config.searchBar}
|
||||
settings: {
|
||||
...config.settings,
|
||||
searchBar: e.currentTarget.checked,
|
||||
},
|
||||
})
|
||||
}
|
||||
checked={config.settings.searchBar}
|
||||
label="Enable search bar"
|
||||
/>
|
||||
</Group>
|
||||
|
||||
@@ -4,14 +4,14 @@ import { Calendar } from '@mantine/dates';
|
||||
import dayjs from 'dayjs';
|
||||
import MediaDisplay from './MediaDisplay';
|
||||
import { medias } from './mediaExample';
|
||||
import { useServices } from '../../tools/state';
|
||||
import { useConfig } from '../../tools/state';
|
||||
|
||||
async function GetCalendars(endDate: Date) {
|
||||
// Load context
|
||||
const { services, addService, removeService, setServicesState } = useServices();
|
||||
const { config, addService, removeService, setConfig } = useConfig();
|
||||
// Load services that have the type to "Sonarr" or "Radarr"
|
||||
const sonarrServices = services.filter((service) => service.type === 'Sonarr');
|
||||
const radarrServices = services.filter((service) => service.type === 'Radarr');
|
||||
const sonarrServices = config.services.filter((service) => service.type === 'Sonarr');
|
||||
const radarrServices = config.services.filter((service) => service.type === 'Radarr');
|
||||
// Merge the two arrays
|
||||
const allServices = [...sonarrServices, ...radarrServices];
|
||||
// Load the calendars for each service
|
||||
|
||||
@@ -6,7 +6,7 @@ import Head from 'next/head';
|
||||
import { MantineProvider, ColorScheme, ColorSchemeProvider } from '@mantine/core';
|
||||
import { NotificationsProvider } from '@mantine/notifications';
|
||||
import Layout from '../components/layout/Layout';
|
||||
import { ServicesProvider } from '../tools/state';
|
||||
import { ConfigProvider } from '../tools/state';
|
||||
|
||||
export default function App(props: AppProps & { colorScheme: ColorScheme }) {
|
||||
const { Component, pageProps } = props;
|
||||
@@ -29,11 +29,11 @@ export default function App(props: AppProps & { colorScheme: ColorScheme }) {
|
||||
<ColorSchemeProvider colorScheme={colorScheme} toggleColorScheme={toggleColorScheme}>
|
||||
<MantineProvider theme={{ colorScheme }} withGlobalStyles withNormalizeCSS>
|
||||
<NotificationsProvider position="top-right">
|
||||
<ServicesProvider>
|
||||
<ConfigProvider>
|
||||
<Layout>
|
||||
<Component {...pageProps} />
|
||||
</Layout>
|
||||
</ServicesProvider>
|
||||
</ConfigProvider>
|
||||
</NotificationsProvider>
|
||||
</MantineProvider>
|
||||
</ColorSchemeProvider>
|
||||
|
||||
Reference in New Issue
Block a user