Files
Homarr/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx

341 lines
9.2 KiB
TypeScript
Raw Normal View History

2022-12-04 18:45:14 +01:00
import {
Accordion,
2022-12-04 18:45:14 +01:00
ActionIcon,
Anchor,
2022-12-04 18:45:14 +01:00
Badge,
Button,
createStyles,
Grid,
2022-12-04 18:45:14 +01:00
Group,
HoverCard,
Kbd,
2022-12-04 18:45:14 +01:00
Modal,
Stack,
2022-12-04 18:45:14 +01:00
Table,
Text,
Title,
} from '@mantine/core';
import {
IconAnchor,
2022-12-04 18:45:14 +01:00
IconBrandDiscord,
IconBrandGithub,
IconFile,
IconKey,
2022-12-04 18:45:14 +01:00
IconLanguage,
IconSchema,
2022-12-04 18:45:14 +01:00
IconVersions,
IconVocabulary,
IconWorldWww,
} from '@tabler/icons';
import { motion } from 'framer-motion';
2022-12-06 21:41:16 +01:00
import { InitOptions } from 'i18next';
2022-12-20 16:54:22 +09:00
import { i18n, Trans, useTranslation } from 'next-i18next';
2022-12-06 21:41:16 +01:00
import Image from 'next/image';
2022-12-04 18:45:14 +01:00
import { ReactNode } from 'react';
2023-02-02 19:13:12 +09:00
import { useConfigContext } from '../../../../config/provider';
import { useConfigStore } from '../../../../config/store';
import { useEditModeInformationStore } from '../../../../hooks/useEditModeInformation';
2023-02-02 19:13:12 +09:00
import { usePackageAttributesStore } from '../../../../tools/client/zustands/usePackageAttributesStore';
import Tip from '../../../layout/Tip';
2023-02-02 19:13:12 +09:00
import { usePrimaryGradient } from '../../../layout/useGradient';
import Credits from '../../../Settings/Common/Credits';
2022-12-04 18:45:14 +01:00
interface AboutModalProps {
opened: boolean;
closeModal: () => void;
newVersionAvailable?: string;
2022-12-04 18:45:14 +01:00
}
export const AboutModal = ({ opened, closeModal, newVersionAvailable }: AboutModalProps) => {
2022-12-04 18:45:14 +01:00
const { classes } = useStyles();
const colorGradiant = usePrimaryGradient();
const informations = useInformationTableItems(newVersionAvailable);
2022-12-20 16:54:22 +09:00
const { t } = useTranslation(['common', 'layout/modals/about']);
2022-12-04 18:45:14 +01:00
const keybinds = [
{ key: 'Mod + J', shortcut: 'Toggle light/dark mode' },
{ key: 'Mod + K', shortcut: 'Focus on search bar' },
{ key: 'Mod + B', shortcut: 'Open docker widget' },
{ key: 'Mod + E', shortcut: 'Toggle Edit mode' },
];
const rows = keybinds.map((element) => (
<tr key={element.key}>
<td>
<Kbd>{element.key}</Kbd>
</td>
2023-03-21 11:43:10 +08:00
<td>
<Text>{element.shortcut}</Text>
</td>
</tr>
));
2022-12-04 18:45:14 +01:00
return (
<Modal
onClose={() => closeModal()}
opened={opened}
title={
<Group spacing="sm">
2023-01-23 01:34:36 +09:00
<Image
alt="Homarr logo"
src="/imgs/logo/logo.png"
width={30}
height={30}
style={{
objectFit: 'contain',
}}
/>
2022-12-04 18:45:14 +01:00
<Title order={3} variant="gradient" gradient={colorGradiant}>
2022-12-20 16:54:22 +09:00
{t('about')} Homarr
2022-12-04 18:45:14 +01:00
</Title>
</Group>
}
size="xl"
>
<Text mb="lg">
2022-12-20 16:54:22 +09:00
<Trans i18nKey="layout/modals/about:description" />
2022-12-04 18:45:14 +01:00
</Text>
<Table mb="lg" highlightOnHover withBorder>
2022-12-04 18:45:14 +01:00
<tbody>
{informations.map((item, index) => (
<tr key={index}>
<td>
<Group spacing="xs">
<ActionIcon className={classes.informationIcon} variant="default">
{item.icon}
</ActionIcon>
<Text>
<Trans
i18nKey={`layout/modals/about:metrics.${item.label}`}
components={{ b: <b /> }}
/>
</Text>
2022-12-04 18:45:14 +01:00
</Group>
</td>
<td className={classes.informationTableColumn} style={{ maxWidth: 200 }}>
{item.content}
</td>
2022-12-04 18:45:14 +01:00
</tr>
))}
</tbody>
</Table>
<Accordion mb={5} variant="contained" radius="md">
<Accordion.Item value="keybinds">
2023-03-21 11:43:10 +08:00
<Accordion.Control icon={<IconKey size={20} />}>
{t('layout/modals/about:keybinds')}
</Accordion.Control>
<Accordion.Panel>
<Table mb={5}>
<thead>
<tr>
2023-03-21 11:43:10 +08:00
<th>{t('layout/modals/about:key')}</th>
<th>{t('layout/modals/about:action')}</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</Table>
2023-03-21 11:18:19 +08:00
<Tip>{t('layout/modals/about:tip')}</Tip>
</Accordion.Panel>
</Accordion.Item>
</Accordion>
2023-02-04 20:48:55 +01:00
2022-12-04 18:45:14 +01:00
<Title order={6} mb="xs" align="center">
2022-12-20 16:54:22 +09:00
{t('layout/modals/about:contact')}
2022-12-04 18:45:14 +01:00
</Title>
<Grid grow>
<Grid.Col md={4} xs={12}>
<Button
component="a"
href="https://github.com/ajnart/homarr"
target="_blank"
leftIcon={<IconBrandGithub size={20} />}
variant="default"
fullWidth
>
GitHub
</Button>
</Grid.Col>
<Grid.Col md={4} xs={12}>
<Button
component="a"
href="https://homarr.dev/"
target="_blank"
leftIcon={<IconWorldWww size={20} />}
variant="default"
fullWidth
>
Documentation
</Button>
</Grid.Col>
<Grid.Col md={4} xs={12}>
<Button
component="a"
href="https://discord.gg/aCsmEV5RgA"
target="_blank"
leftIcon={<IconBrandDiscord size={20} />}
variant="default"
fullWidth
>
Discord
</Button>
</Grid.Col>
</Grid>
2023-01-04 22:39:58 +09:00
<Credits />
2022-12-04 18:45:14 +01:00
</Modal>
);
};
interface InformationTableItem {
icon: ReactNode;
label: string;
content: ReactNode;
}
2022-12-06 21:41:16 +01:00
interface ExtendedInitOptions extends InitOptions {
locales: string[];
}
const useInformationTableItems = (newVersionAvailable?: string): InformationTableItem[] => {
2022-12-04 18:45:14 +01:00
const colorGradiant = usePrimaryGradient();
const { attributes } = usePackageAttributesStore();
const { editModeEnabled } = useEditModeInformationStore();
2022-12-04 18:45:14 +01:00
const { configVersion } = useConfigContext();
const { configs } = useConfigStore();
2022-12-06 21:41:16 +01:00
let items: InformationTableItem[] = [];
if (editModeEnabled) {
items = [
...items,
{
icon: <IconKey size={20} />,
label: 'experimental_disableEditMode',
content: (
<Stack>
<Badge color="red">WARNING</Badge>
<Text color="red" size="xs">
This is an experimental feature, where the edit mode is disabled entirely - no config
modifications are possbile anymore. All update requests for the config will be dropped
on the API. This will be removed in future versions, as Homarr will receive a proper
authentication system, which will make this obsolete.
</Text>
</Stack>
),
},
];
}
2022-12-06 21:41:16 +01:00
if (i18n !== null) {
const usedI18nNamespaces = i18n.reportNamespaces.getUsedNamespaces();
const initOptions = i18n.options as ExtendedInitOptions;
2022-12-04 18:45:14 +01:00
2022-12-06 21:41:16 +01:00
items = [
...items,
{
icon: <IconLanguage size={20} />,
label: 'i18n',
2022-12-06 21:41:16 +01:00
content: (
<Badge variant="gradient" gradient={colorGradiant}>
{usedI18nNamespaces.length}
</Badge>
),
},
{
icon: <IconVocabulary size={20} />,
label: 'locales',
2022-12-06 21:41:16 +01:00
content: (
<Badge variant="gradient" gradient={colorGradiant}>
{initOptions.locales.length}
</Badge>
),
},
];
}
items = [
{
icon: <IconSchema size={20} />,
label: 'configurationSchemaVersion',
content: (
<Badge variant="gradient" gradient={colorGradiant}>
{configVersion}
</Badge>
),
},
{
icon: <IconFile size={20} />,
label: 'configurationsCount',
content: (
<Badge variant="gradient" gradient={colorGradiant}>
{configs.length}
</Badge>
),
},
2022-12-04 18:45:14 +01:00
{
icon: <IconVersions size={20} />,
2022-12-20 16:54:22 +09:00
label: 'version',
2022-12-04 18:45:14 +01:00
content: (
<Group position="right">
<Badge variant="gradient" gradient={colorGradiant}>
{attributes.packageVersion ?? 'Unknown'}
</Badge>
{newVersionAvailable && (
<HoverCard shadow="md" position="top" withArrow>
<HoverCard.Target>
<motion.div
initial={{ scale: 1.2 }}
animate={{
2022-12-20 16:54:22 +09:00
scale: [0.8, 1.1, 1],
rotate: [0, 10, 0],
}}
transition={{ duration: 0.8, ease: 'easeInOut' }}
>
<Badge color="green" variant="filled">
new: {newVersionAvailable}
</Badge>
</motion.div>
</HoverCard.Target>
<HoverCard.Dropdown>
Version{' '}
<b>
<Anchor
target="_blank"
href={`https://github.com/ajnart/homarr/releases/tag/${newVersionAvailable}`}
>
{newVersionAvailable}
</Anchor>
</b>{' '}
is available ! Current version: {attributes.packageVersion}
</HoverCard.Dropdown>
</HoverCard>
)}
</Group>
2022-12-04 18:45:14 +01:00
),
},
{
icon: <IconAnchor size={20} />,
label: 'nodeEnvironment',
content: (
<Badge variant="gradient" gradient={colorGradiant}>
{attributes.environment}
</Badge>
),
},
...items,
2022-12-04 18:45:14 +01:00
];
2022-12-06 21:41:16 +01:00
return items;
2022-12-04 18:45:14 +01:00
};
const useStyles = createStyles(() => ({
informationTableColumn: {
textAlign: 'right',
},
informationIcon: {
cursor: 'default',
},
}));