✨ Add translation for module, fix language changer
This commit is contained in:
42 files changed
+138
-117
No files matched your search
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"states": {
|
|
||||||
"online": "Online {{response}}",
|
|
||||||
"offline": "Offline {{response}}",
|
|
||||||
"loading": "Loading..."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"descriptor": {
|
||||||
|
"name": "Ping",
|
||||||
|
"description": "Allows you to check if the service is up or returns a specific HTTP status code."
|
||||||
|
},
|
||||||
|
"states": {
|
||||||
|
"online": "Online {{response}}",
|
||||||
|
"offline": "Offline {{response}}",
|
||||||
|
"loading": "Loading..."
|
||||||
|
}
|
||||||
|
}
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -126,7 +126,7 @@ const AppShelf = (props: any) => {
|
|||||||
const noCategory = config.services.filter(
|
const noCategory = config.services.filter(
|
||||||
(e) => e.category === undefined || e.category === null
|
(e) => e.category === undefined || e.category === null
|
||||||
);
|
);
|
||||||
const downloadEnabled = config.modules?.[DownloadsModule.title]?.enabled ?? false;
|
const downloadEnabled = config.modules?.[DownloadsModule.id]?.enabled ?? false;
|
||||||
// Create an item with 0: true, 1: true, 2: true... For each category
|
// Create an item with 0: true, 1: true, 2: true... For each category
|
||||||
return (
|
return (
|
||||||
// TODO: Style accordion so that the bar is transparent to the user settings
|
// TODO: Style accordion so that the bar is transparent to the user settings
|
||||||
|
|||||||
@@ -4,14 +4,17 @@ import { showNotification } from '@mantine/notifications';
|
|||||||
import { forwardRef, useState } from 'react';
|
import { forwardRef, useState } from 'react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
|
import { getCookie, setCookie } from 'cookies-next';
|
||||||
import { getLanguageByCode, Language } from '../../languages/language';
|
import { getLanguageByCode, Language } from '../../languages/language';
|
||||||
|
|
||||||
export default function LanguageSwitch() {
|
export default function LanguageSwitch() {
|
||||||
const { t, i18n } = useTranslation('settings/general/internationalization');
|
const { t, i18n } = useTranslation('settings/general/internationalization');
|
||||||
const { changeLanguage } = i18n;
|
const { changeLanguage } = i18n;
|
||||||
|
const configLocale = getCookie('config-locale');
|
||||||
const { locale, locales } = useRouter();
|
const { locale, locales } = useRouter();
|
||||||
const [selectedLanguage, setSelectedLanguage] = useState<string | null | undefined>(locale);
|
const [selectedLanguage, setSelectedLanguage] = useState<string | undefined>(
|
||||||
|
(configLocale as string) ?? locale
|
||||||
|
);
|
||||||
|
|
||||||
const data = locales
|
const data = locales
|
||||||
? locales.map((localeItem) => ({
|
? locales.map((localeItem) => ({
|
||||||
@@ -26,9 +29,13 @@ export default function LanguageSwitch() {
|
|||||||
setSelectedLanguage(value);
|
setSelectedLanguage(value);
|
||||||
|
|
||||||
const newLanguage = getLanguageByCode(value);
|
const newLanguage = getLanguageByCode(value);
|
||||||
|
|
||||||
changeLanguage(value)
|
changeLanguage(value)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
setCookie('config-locale', value, {
|
||||||
|
maxAge: 60 * 60 * 24 * 30,
|
||||||
|
sameSite: 'strict',
|
||||||
|
});
|
||||||
|
|
||||||
showNotification({
|
showNotification({
|
||||||
title: 'Language changed',
|
title: 'Language changed',
|
||||||
message: `You changed the language to '${newLanguage.originalName}'`,
|
message: `You changed the language to '${newLanguage.originalName}'`,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Checkbox, Popover, SimpleGrid, Stack, Text, Title } from '@mantine/core';
|
import { Checkbox, HoverCard, SimpleGrid, Stack, Text, Title } from '@mantine/core';
|
||||||
import { useDisclosure } from '@mantine/hooks';
|
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import * as Modules from '../../modules';
|
import * as Modules from '../../modules';
|
||||||
import { IModule } from '../../modules/ModuleTypes';
|
import { IModule } from '../../modules/ModuleTypes';
|
||||||
@@ -11,9 +10,9 @@ export default function ModuleEnabler(props: any) {
|
|||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={4}>{t('title')}</Title>
|
<Title order={4}>{t('title')}</Title>
|
||||||
<SimpleGrid cols={3} spacing="xs">
|
<SimpleGrid cols={3} spacing="sm">
|
||||||
{modules.map((module) => (
|
{modules.map((module) => (
|
||||||
<ModuleToggle module={module} />
|
<ModuleToggle key={module.id} module={module} />
|
||||||
))}
|
))}
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -22,39 +21,36 @@ export default function ModuleEnabler(props: any) {
|
|||||||
|
|
||||||
const ModuleToggle = ({ module }: { module: IModule }) => {
|
const ModuleToggle = ({ module }: { module: IModule }) => {
|
||||||
const { config, setConfig } = useConfig();
|
const { config, setConfig } = useConfig();
|
||||||
const { t: translationModules } = useTranslation(module.translationNamespace);
|
const { t } = useTranslation(`modules/${module.id}`);
|
||||||
const [opened, { close, open }] = useDisclosure(false);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover opened={opened} withArrow withinPortal width={200}>
|
<HoverCard withArrow withinPortal width={200} shadow="md" openDelay={200}>
|
||||||
<Popover.Target>
|
<HoverCard.Target>
|
||||||
<div onMouseEnter={open} onMouseLeave={close}>
|
<Checkbox
|
||||||
<Checkbox
|
key={module.id}
|
||||||
key={module.title}
|
size="md"
|
||||||
size="md"
|
checked={config.modules?.[module.id]?.enabled ?? false}
|
||||||
checked={config.modules?.[module.title]?.enabled ?? false}
|
label={t('descriptor.name', {
|
||||||
label={translationModules('descriptor.name', {
|
defaultValue: 'Unknown',
|
||||||
defaultValue: 'Unknown',
|
})}
|
||||||
})}
|
onChange={(e) => {
|
||||||
onChange={(e) => {
|
setConfig({
|
||||||
setConfig({
|
...config,
|
||||||
...config,
|
modules: {
|
||||||
modules: {
|
...config.modules,
|
||||||
...config.modules,
|
[module.id]: {
|
||||||
[module.title]: {
|
...config.modules?.[module.id],
|
||||||
...config.modules?.[module.title],
|
enabled: e.currentTarget.checked,
|
||||||
enabled: e.currentTarget.checked,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
}}
|
});
|
||||||
/>
|
}}
|
||||||
</div>
|
/>
|
||||||
</Popover.Target>
|
</HoverCard.Target>
|
||||||
<Popover.Dropdown>
|
<HoverCard.Dropdown>
|
||||||
<Text weight="bold">{translationModules('descriptor.name')}</Text>
|
<Title order={4}>{t('descriptor.name')}</Title>
|
||||||
<Text>{translationModules('descriptor.description')}</Text>
|
<Text size="sm">{t('descriptor.description')}</Text>
|
||||||
</Popover.Dropdown>
|
</HoverCard.Dropdown>
|
||||||
</Popover>
|
</HoverCard>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
Vendored
+1
-1
@@ -6,11 +6,11 @@ import { TablerIcon } from '@tabler/icons';
|
|||||||
|
|
||||||
// Note: Maybe use context to keep track of the modules
|
// Note: Maybe use context to keep track of the modules
|
||||||
export interface IModule {
|
export interface IModule {
|
||||||
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
icon: TablerIcon;
|
icon: TablerIcon;
|
||||||
component: React.ComponentType;
|
component: React.ComponentType;
|
||||||
options?: Option;
|
options?: Option;
|
||||||
translationNamespace: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Option {
|
interface Option {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export const CalendarModule: IModule = {
|
|||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
translationNamespace: 'modules/calendar-module',
|
id: 'calendar',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function CalendarComponent(props: any) {
|
export default function CalendarComponent(props: any) {
|
||||||
@@ -127,7 +127,7 @@ export default function CalendarComponent(props: any) {
|
|||||||
}, [config.services]);
|
}, [config.services]);
|
||||||
|
|
||||||
const weekStartsAtSunday =
|
const weekStartsAtSunday =
|
||||||
(config?.modules?.[CalendarModule.title]?.options?.sundaystart?.value as boolean) ?? false;
|
(config?.modules?.[CalendarModule.id]?.options?.sundaystart?.value as boolean) ?? false;
|
||||||
return (
|
return (
|
||||||
<Calendar
|
<Calendar
|
||||||
firstDayOfWeek={weekStartsAtSunday ? 'sunday' : 'monday'}
|
firstDayOfWeek={weekStartsAtSunday ? 'sunday' : 'monday'}
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ export function SonarrMediaDisplay(props: any) {
|
|||||||
export function MediaDisplay({ media }: { media: IMedia }) {
|
export function MediaDisplay({ media }: { media: IMedia }) {
|
||||||
const [opened, setOpened] = useState(false);
|
const [opened, setOpened] = useState(false);
|
||||||
const { secondaryColor } = useColorTheme();
|
const { secondaryColor } = useColorTheme();
|
||||||
const { t } = useTranslation('modules/common-media-cards-module');
|
const { t } = useTranslation('modules/common-media-cards');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group mr="xs" align="stretch" noWrap style={{ maxHeight: 200 }}>
|
<Group mr="xs" align="stretch" noWrap style={{ maxHeight: 200 }}>
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export const DashdotModule = asModule({
|
|||||||
value: '',
|
value: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
translationNamespace: 'modules/dashdot-module',
|
id: 'dashdot',
|
||||||
});
|
});
|
||||||
|
|
||||||
const useStyles = createStyles((theme, _params) => ({
|
const useStyles = createStyles((theme, _params) => ({
|
||||||
@@ -126,7 +126,7 @@ export function DashdotComponent() {
|
|||||||
const { classes } = useStyles();
|
const { classes } = useStyles();
|
||||||
const { colorScheme } = useMantineColorScheme();
|
const { colorScheme } = useMantineColorScheme();
|
||||||
|
|
||||||
const dashConfig = config.modules?.[DashdotModule.title]
|
const dashConfig = config.modules?.[DashdotModule.id]
|
||||||
.options as typeof DashdotModule['options'];
|
.options as typeof DashdotModule['options'];
|
||||||
const isCompact = dashConfig?.useCompactView?.value ?? false;
|
const isCompact = dashConfig?.useCompactView?.value ?? false;
|
||||||
const dashdotService: serviceItem | undefined = config.services.filter(
|
const dashdotService: serviceItem | undefined = config.services.filter(
|
||||||
@@ -148,7 +148,7 @@ export function DashdotComponent() {
|
|||||||
const totalSize =
|
const totalSize =
|
||||||
(info?.storage?.layout as any[])?.reduce((acc, curr) => (curr.size ?? 0) + acc, 0) ?? 0;
|
(info?.storage?.layout as any[])?.reduce((acc, curr) => (curr.size ?? 0) + acc, 0) ?? 0;
|
||||||
|
|
||||||
const { t } = useTranslation('modules/dashdot-module');
|
const { t } = useTranslation('modules/dashdot');
|
||||||
|
|
||||||
const graphs = [
|
const graphs = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ export const DateModule: IModule = {
|
|||||||
value: true,
|
value: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
translationNamespace: 'modules/date-module',
|
id: 'date',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function DateComponent(props: any) {
|
export default function DateComponent(props: any) {
|
||||||
const [date, setDate] = useState(new Date());
|
const [date, setDate] = useState(new Date());
|
||||||
const setSafeInterval = useSetSafeInterval();
|
const setSafeInterval = useSetSafeInterval();
|
||||||
const { config } = useConfig();
|
const { config } = useConfig();
|
||||||
const isFullTime = config?.modules?.[DateModule.title]?.options?.full?.value ?? true;
|
const isFullTime = config?.modules?.[DateModule.id]?.options?.full?.value ?? true;
|
||||||
const formatString = isFullTime ? 'HH:mm' : 'h:mm A';
|
const formatString = isFullTime ? 'HH:mm' : 'h:mm A';
|
||||||
// Change date on minute change
|
// Change date on minute change
|
||||||
// Note: Using 10 000ms instead of 1000ms to chill a little :)
|
// Note: Using 10 000ms instead of 1000ms to chill a little :)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ function sendDockerCommand(
|
|||||||
containerName: string,
|
containerName: string,
|
||||||
reload: () => void
|
reload: () => void
|
||||||
) {
|
) {
|
||||||
const { t } = useTranslation('modules/docker-module');
|
const { t } = useTranslation('modules/docker');
|
||||||
|
|
||||||
showNotification({
|
showNotification({
|
||||||
id: containerId,
|
id: containerId,
|
||||||
@@ -64,7 +64,7 @@ export interface ContainerActionBarProps {
|
|||||||
|
|
||||||
export default function ContainerActionBar({ selected, reload }: ContainerActionBarProps) {
|
export default function ContainerActionBar({ selected, reload }: ContainerActionBarProps) {
|
||||||
const [opened, setOpened] = useState<boolean>(false);
|
const [opened, setOpened] = useState<boolean>(false);
|
||||||
const { t } = useTranslation('modules/docker-module');
|
const { t } = useTranslation('modules/docker');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group>
|
<Group>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export interface ContainerStateProps {
|
|||||||
export default function ContainerState(props: ContainerStateProps) {
|
export default function ContainerState(props: ContainerStateProps) {
|
||||||
const { state } = props;
|
const { state } = props;
|
||||||
|
|
||||||
const { t } = useTranslation('modules/docker-module');
|
const { t } = useTranslation('modules/docker');
|
||||||
|
|
||||||
const options: {
|
const options: {
|
||||||
size: MantineSize;
|
size: MantineSize;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const DockerModule: IModule = {
|
|||||||
title: 'Docker',
|
title: 'Docker',
|
||||||
icon: IconBrandDocker,
|
icon: IconBrandDocker,
|
||||||
component: DockerMenuButton,
|
component: DockerMenuButton,
|
||||||
translationNamespace: 'modules/docker-module',
|
id: 'docker',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function DockerMenuButton(props: any) {
|
export default function DockerMenuButton(props: any) {
|
||||||
@@ -23,9 +23,9 @@ export default function DockerMenuButton(props: any) {
|
|||||||
const [containers, setContainers] = useState<Docker.ContainerInfo[]>([]);
|
const [containers, setContainers] = useState<Docker.ContainerInfo[]>([]);
|
||||||
const [selection, setSelection] = useState<Docker.ContainerInfo[]>([]);
|
const [selection, setSelection] = useState<Docker.ContainerInfo[]>([]);
|
||||||
const { config } = useConfig();
|
const { config } = useConfig();
|
||||||
const moduleEnabled = config.modules?.[DockerModule.title]?.enabled ?? false;
|
const moduleEnabled = config.modules?.[DockerModule.id]?.enabled ?? false;
|
||||||
|
|
||||||
const { t } = useTranslation('modules/docker-module');
|
const { t } = useTranslation('modules/docker');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
reload();
|
reload();
|
||||||
@@ -54,7 +54,7 @@ export default function DockerMenuButton(props: any) {
|
|||||||
);
|
);
|
||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
const exists = config.modules?.[DockerModule.title]?.enabled ?? false;
|
const exists = config.modules?.[DockerModule.id]?.enabled ?? false;
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export default function DockerTable({
|
|||||||
const { classes, cx } = useStyles();
|
const { classes, cx } = useStyles();
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
|
|
||||||
const { t } = useTranslation('modules/docker-module');
|
const { t } = useTranslation('modules/docker');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setContainers(containers);
|
setContainers(containers);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
Skeleton,
|
Skeleton,
|
||||||
ScrollArea,
|
ScrollArea,
|
||||||
Center,
|
Center,
|
||||||
|
Stack,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { IconDownload as Download } from '@tabler/icons';
|
import { IconDownload as Download } from '@tabler/icons';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
@@ -32,7 +33,7 @@ export const DownloadsModule: IModule = {
|
|||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
translationNamespace: 'modules/downloads-module',
|
id: 'torrents-status',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function DownloadComponent() {
|
export default function DownloadComponent() {
|
||||||
@@ -46,12 +47,12 @@ export default function DownloadComponent() {
|
|||||||
service.type === 'Deluge'
|
service.type === 'Deluge'
|
||||||
) ?? [];
|
) ?? [];
|
||||||
const hideComplete: boolean =
|
const hideComplete: boolean =
|
||||||
(config?.modules?.[DownloadsModule.title]?.options?.hidecomplete?.value as boolean) ?? false;
|
(config?.modules?.[DownloadsModule.id]?.options?.hidecomplete?.value as boolean) ?? false;
|
||||||
const [torrents, setTorrents] = useState<NormalizedTorrent[]>([]);
|
const [torrents, setTorrents] = useState<NormalizedTorrent[]>([]);
|
||||||
const setSafeInterval = useSetSafeInterval();
|
const setSafeInterval = useSetSafeInterval();
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
|
||||||
const { t } = useTranslation('modules/downloads-module');
|
const { t } = useTranslation(`modules/${DownloadsModule.id}`);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
@@ -85,13 +86,13 @@ export default function DownloadComponent() {
|
|||||||
|
|
||||||
if (downloadServices.length === 0) {
|
if (downloadServices.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Group>
|
<Stack>
|
||||||
<Title order={3}>{t('card.errors.noDownloadClients.title')}</Title>
|
<Title order={3}>{t('card.errors.noDownloadClients.title')}</Title>
|
||||||
<Group>
|
<Group>
|
||||||
<Text>{t('card.errors.noDownloadClients.text')}</Text>
|
<Text>{t('card.errors.noDownloadClients.text')}</Text>
|
||||||
<AddItemShelfButton />
|
<AddItemShelfButton />
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export const TotalDownloadsModule: IModule = {
|
|||||||
title: 'Download Speed',
|
title: 'Download Speed',
|
||||||
icon: Download,
|
icon: Download,
|
||||||
component: TotalDownloadsComponent,
|
component: TotalDownloadsComponent,
|
||||||
translationNamespace: 'modules/total-downloads-module',
|
id: 'dlspeed',
|
||||||
};
|
};
|
||||||
|
|
||||||
interface torrentHistory {
|
interface torrentHistory {
|
||||||
@@ -37,7 +37,7 @@ export default function TotalDownloadsComponent() {
|
|||||||
service.type === 'Transmission' ||
|
service.type === 'Transmission' ||
|
||||||
service.type === 'Deluge'
|
service.type === 'Deluge'
|
||||||
) ?? [];
|
) ?? [];
|
||||||
const { t } = useTranslation('modules/downloads-module');
|
const { t } = useTranslation(`modules/${TotalDownloadsModule.id}`);
|
||||||
|
|
||||||
const [torrentHistory, torrentHistoryHandlers] = useListState<torrentHistory>([]);
|
const [torrentHistory, torrentHistoryHandlers] = useListState<torrentHistory>([]);
|
||||||
const [torrents, setTorrents] = useState<NormalizedTorrent[]>([]);
|
const [torrents, setTorrents] = useState<NormalizedTorrent[]>([]);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { IModule } from './ModuleTypes';
|
|||||||
|
|
||||||
function getItems(module: IModule) {
|
function getItems(module: IModule) {
|
||||||
const { config, setConfig } = useConfig();
|
const { config, setConfig } = useConfig();
|
||||||
const { t } = useTranslation(module.translationNamespace);
|
const { t } = useTranslation([module.id, 'common']);
|
||||||
|
|
||||||
const items: JSX.Element[] = [];
|
const items: JSX.Element[] = [];
|
||||||
if (module.options) {
|
if (module.options) {
|
||||||
@@ -28,8 +28,8 @@ function getItems(module: IModule) {
|
|||||||
const types = values.map((v) => typeof v.value);
|
const types = values.map((v) => typeof v.value);
|
||||||
// Loop over all the types with a for each loop
|
// Loop over all the types with a for each loop
|
||||||
types.forEach((type, index) => {
|
types.forEach((type, index) => {
|
||||||
const optionName = `${module.title}.${keys[index]}`;
|
const optionName = `${module.id}.${keys[index]}`;
|
||||||
const moduleInConfig = config.modules?.[module.title];
|
const moduleInConfig = config.modules?.[module.id];
|
||||||
if (type === 'object') {
|
if (type === 'object') {
|
||||||
items.push(
|
items.push(
|
||||||
<MultiSelect
|
<MultiSelect
|
||||||
@@ -46,7 +46,7 @@ function getItems(module: IModule) {
|
|||||||
...config,
|
...config,
|
||||||
modules: {
|
modules: {
|
||||||
...config.modules,
|
...config.modules,
|
||||||
[module.title]: {
|
[module.id]: {
|
||||||
...moduleInConfig,
|
...moduleInConfig,
|
||||||
options: {
|
options: {
|
||||||
...moduleInConfig?.options,
|
...moduleInConfig?.options,
|
||||||
@@ -71,12 +71,12 @@ function getItems(module: IModule) {
|
|||||||
...config,
|
...config,
|
||||||
modules: {
|
modules: {
|
||||||
...config.modules,
|
...config.modules,
|
||||||
[module.title]: {
|
[module.id]: {
|
||||||
...config.modules[module.title],
|
...config.modules[module.id],
|
||||||
options: {
|
options: {
|
||||||
...config.modules[module.title].options,
|
...config.modules[module.id].options,
|
||||||
[keys[index]]: {
|
[keys[index]]: {
|
||||||
...config.modules[module.title].options?.[keys[index]],
|
...config.modules[module.id].options?.[keys[index]],
|
||||||
value: (e.target as any)[0].value,
|
value: (e.target as any)[0].value,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -99,7 +99,7 @@ function getItems(module: IModule) {
|
|||||||
onChange={(e) => {}}
|
onChange={(e) => {}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button type="submit">Save</Button>
|
<Button type="submit">{t('actions.save')}</Button>
|
||||||
</Group>
|
</Group>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
@@ -120,12 +120,12 @@ function getItems(module: IModule) {
|
|||||||
...config,
|
...config,
|
||||||
modules: {
|
modules: {
|
||||||
...config.modules,
|
...config.modules,
|
||||||
[module.title]: {
|
[module.id]: {
|
||||||
...config.modules[module.title],
|
...config.modules[module.id],
|
||||||
options: {
|
options: {
|
||||||
...config.modules[module.title].options,
|
...config.modules[module.id].options,
|
||||||
[keys[index]]: {
|
[keys[index]]: {
|
||||||
...config.modules[module.title].options?.[keys[index]],
|
...config.modules[module.id].options?.[keys[index]],
|
||||||
value: e.currentTarget.checked,
|
value: e.currentTarget.checked,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -148,7 +148,7 @@ export function ModuleWrapper(props: any) {
|
|||||||
const { config, setConfig } = useConfig();
|
const { config, setConfig } = useConfig();
|
||||||
const enabledModules = config.modules ?? {};
|
const enabledModules = config.modules ?? {};
|
||||||
// Remove 'Module' from enabled modules titles
|
// Remove 'Module' from enabled modules titles
|
||||||
const isShown = enabledModules[module.title]?.enabled ?? false;
|
const isShown = enabledModules[module.id]?.enabled ?? false;
|
||||||
//TODO: fix the hover problem
|
//TODO: fix the hover problem
|
||||||
const [hovering, setHovering] = useState(false);
|
const [hovering, setHovering] = useState(false);
|
||||||
const { t } = useTranslation('modules');
|
const { t } = useTranslation('modules');
|
||||||
@@ -160,7 +160,7 @@ export function ModuleWrapper(props: any) {
|
|||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
{...props}
|
{...props}
|
||||||
key={module.title}
|
key={module.id}
|
||||||
hidden={!isShown}
|
hidden={!isShown}
|
||||||
withBorder
|
withBorder
|
||||||
radius="lg"
|
radius="lg"
|
||||||
@@ -195,7 +195,7 @@ export function ModuleMenu(props: any) {
|
|||||||
<>
|
<>
|
||||||
{module.options && (
|
{module.options && (
|
||||||
<Menu
|
<Menu
|
||||||
key={module.title}
|
key={module.id}
|
||||||
withinPortal
|
withinPortal
|
||||||
width="lg"
|
width="lg"
|
||||||
shadow="xl"
|
shadow="xl"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export const OverseerrModule: IModule = {
|
|||||||
title: 'Overseerr',
|
title: 'Overseerr',
|
||||||
icon: IconEyeglass,
|
icon: IconEyeglass,
|
||||||
component: OverseerrMediaDisplay,
|
component: OverseerrMediaDisplay,
|
||||||
translationNamespace: 'modules/overseerr-module',
|
id: 'overseerr',
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface OverseerSearchProps {
|
export interface OverseerSearchProps {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { useColorTheme } from '../../tools/color';
|
|||||||
import { MovieResult } from './Movie.d';
|
import { MovieResult } from './Movie.d';
|
||||||
import { MediaType, Result } from './SearchResult.d';
|
import { MediaType, Result } from './SearchResult.d';
|
||||||
import { TvShowResult, TvShowResultSeason } from './TvShow.d';
|
import { TvShowResult, TvShowResultSeason } from './TvShow.d';
|
||||||
|
|
||||||
interface RequestModalProps {
|
interface RequestModalProps {
|
||||||
base: Result;
|
base: Result;
|
||||||
opened: boolean;
|
opened: boolean;
|
||||||
@@ -57,7 +58,7 @@ export function MovieRequestModal({
|
|||||||
setOpened: (opened: boolean) => void;
|
setOpened: (opened: boolean) => void;
|
||||||
}) {
|
}) {
|
||||||
const { secondaryColor } = useColorTheme();
|
const { secondaryColor } = useColorTheme();
|
||||||
const { t } = useTranslation('modules/overseerr-module');
|
const { t } = useTranslation('modules/overseerr');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@@ -114,7 +115,7 @@ export function TvRequestModal({
|
|||||||
}) {
|
}) {
|
||||||
const [selection, setSelection] = useState<TvShowResultSeason[]>(result.seasons);
|
const [selection, setSelection] = useState<TvShowResultSeason[]>(result.seasons);
|
||||||
const { classes, cx } = useStyles();
|
const { classes, cx } = useStyles();
|
||||||
const { t } = useTranslation('modules/overseerr-module');
|
const { t } = useTranslation('modules/overseerr');
|
||||||
|
|
||||||
const toggleRow = (container: TvShowResultSeason) =>
|
const toggleRow = (container: TvShowResultSeason) =>
|
||||||
setSelection((current: TvShowResultSeason[]) =>
|
setSelection((current: TvShowResultSeason[]) =>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const PingModule: IModule = {
|
|||||||
title: 'Ping Services',
|
title: 'Ping Services',
|
||||||
icon: Plug,
|
icon: Plug,
|
||||||
component: PingComponent,
|
component: PingComponent,
|
||||||
translationNamespace: 'modules/ping-module',
|
id: 'ping',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function PingComponent(props: any) {
|
export default function PingComponent(props: any) {
|
||||||
@@ -21,9 +21,9 @@ export default function PingComponent(props: any) {
|
|||||||
const { url }: { url: string } = props;
|
const { url }: { url: string } = props;
|
||||||
const [isOnline, setOnline] = useState<State>('loading');
|
const [isOnline, setOnline] = useState<State>('loading');
|
||||||
const [response, setResponse] = useState(500);
|
const [response, setResponse] = useState(500);
|
||||||
const exists = config.modules?.[PingModule.title]?.enabled ?? false;
|
const exists = config.modules?.[PingModule.id]?.enabled ?? false;
|
||||||
|
|
||||||
const { t } = useTranslation('modules/ping-module');
|
const { t } = useTranslation('modules/ping');
|
||||||
|
|
||||||
function statusCheck(response: AxiosResponse) {
|
function statusCheck(response: AxiosResponse) {
|
||||||
const { status }: { status: string[] } = props;
|
const { status }: { status: string[] } = props;
|
||||||
@@ -54,7 +54,7 @@ export default function PingComponent(props: any) {
|
|||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
statusCheck(error.response);
|
statusCheck(error.response);
|
||||||
});
|
});
|
||||||
}, [config.modules?.[PingModule.title]?.enabled]);
|
}, [config.modules?.[PingModule.id]?.enabled]);
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,15 +30,15 @@ export const SearchModule: IModule = {
|
|||||||
title: 'Search',
|
title: 'Search',
|
||||||
icon: Search,
|
icon: Search,
|
||||||
component: SearchBar,
|
component: SearchBar,
|
||||||
translationNamespace: 'modules/search-module',
|
id: 'search',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function SearchBar(props: any) {
|
export default function SearchBar(props: any) {
|
||||||
const { classes, cx } = useStyles();
|
const { classes, cx } = useStyles();
|
||||||
// Config
|
// Config
|
||||||
const { config } = useConfig();
|
const { config } = useConfig();
|
||||||
const isModuleEnabled = config.modules?.[SearchModule.title]?.enabled ?? false;
|
const isModuleEnabled = config.modules?.[SearchModule.id]?.enabled ?? false;
|
||||||
const isOverseerrEnabled = config.modules?.[OverseerrModule.title]?.enabled ?? false;
|
const isOverseerrEnabled = config.modules?.[OverseerrModule.id]?.enabled ?? false;
|
||||||
const OverseerrService = config.services.find(
|
const OverseerrService = config.services.find(
|
||||||
(service) => service.type === 'Overseerr' || service.type === 'Jellyseerr'
|
(service) => service.type === 'Overseerr' || service.type === 'Jellyseerr'
|
||||||
);
|
);
|
||||||
@@ -60,7 +60,7 @@ export default function SearchBar(props: any) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const [debounced, cancel] = useDebouncedValue(form.values.query, 250);
|
const [debounced, cancel] = useDebouncedValue(form.values.query, 250);
|
||||||
const { t } = useTranslation('modules/search-module');
|
const { t } = useTranslation('modules/search');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (OverseerrService === undefined && isOverseerrEnabled) {
|
if (OverseerrService === undefined && isOverseerrEnabled) {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export const WeatherModule: IModule = {
|
|||||||
value: 'Paris',
|
value: 'Paris',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
translationNamespace: 'modules/weather-module',
|
id: 'weather',
|
||||||
};
|
};
|
||||||
|
|
||||||
// 0 Clear sky
|
// 0 Clear sky
|
||||||
@@ -49,7 +49,7 @@ export const WeatherModule: IModule = {
|
|||||||
// 95 *Thunderstorm: Slight or moderate
|
// 95 *Thunderstorm: Slight or moderate
|
||||||
// 96, 99 *Thunderstorm with slight and heavy hail
|
// 96, 99 *Thunderstorm with slight and heavy hail
|
||||||
export function WeatherIcon(props: any) {
|
export function WeatherIcon(props: any) {
|
||||||
const { t } = useTranslation('modules/weather-module');
|
const { t } = useTranslation('modules/weather');
|
||||||
|
|
||||||
const { code } = props;
|
const { code } = props;
|
||||||
let data: { icon: any; name: string };
|
let data: { icon: any; name: string };
|
||||||
@@ -146,9 +146,9 @@ export default function WeatherComponent(props: any) {
|
|||||||
const { config } = useConfig();
|
const { config } = useConfig();
|
||||||
const [weather, setWeather] = useState({} as WeatherResponse);
|
const [weather, setWeather] = useState({} as WeatherResponse);
|
||||||
const cityInput: string =
|
const cityInput: string =
|
||||||
(config?.modules?.[WeatherModule.title]?.options?.location?.value as string) ?? 'Paris';
|
(config?.modules?.[WeatherModule.id]?.options?.location?.value as string) ?? 'Paris';
|
||||||
const isFahrenheit: boolean =
|
const isFahrenheit: boolean =
|
||||||
(config?.modules?.[WeatherModule.title]?.options?.freedomunit?.value as boolean) ?? false;
|
(config?.modules?.[WeatherModule.id]?.options?.freedomunit?.value as boolean) ?? false;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
axios
|
axios
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import {
|
|||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { NextLink } from '@mantine/next';
|
import { NextLink } from '@mantine/next';
|
||||||
|
|
||||||
|
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||||
|
|
||||||
const useStyles = createStyles((theme) => ({
|
const useStyles = createStyles((theme) => ({
|
||||||
root: {
|
root: {
|
||||||
paddingTop: 80,
|
paddingTop: 80,
|
||||||
@@ -92,3 +94,12 @@ export default function Custom404() {
|
|||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getStaticProps({ locale }: { locale: string }) {
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
...(await serverSideTranslations(locale, ['404'])),
|
||||||
|
// Will be passed to the page component as props
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
+17
-16
@@ -17,18 +17,19 @@ export async function getServerSideProps({
|
|||||||
res,
|
res,
|
||||||
locale,
|
locale,
|
||||||
}: GetServerSidePropsContext): Promise<{ props: { config: Config } }> {
|
}: GetServerSidePropsContext): Promise<{ props: { config: Config } }> {
|
||||||
let cookie = getCookie('config-name', { req, res });
|
let configName = getCookie('config-name', { req, res });
|
||||||
if (!cookie) {
|
const configLocale = getCookie('config-locale', { req, res });
|
||||||
|
if (!configName) {
|
||||||
setCookie('config-name', 'default', {
|
setCookie('config-name', 'default', {
|
||||||
req,
|
req,
|
||||||
res,
|
res,
|
||||||
maxAge: 60 * 60 * 24 * 30,
|
maxAge: 60 * 60 * 24 * 30,
|
||||||
sameSite: 'strict',
|
sameSite: 'strict',
|
||||||
});
|
});
|
||||||
cookie = 'default';
|
configName = 'default';
|
||||||
}
|
}
|
||||||
|
|
||||||
const translations = await serverSideTranslations(locale as string, [
|
const translations = await serverSideTranslations((configLocale ?? locale) as string, [
|
||||||
'common',
|
'common',
|
||||||
'layout/app-shelf',
|
'layout/app-shelf',
|
||||||
'layout/add-service-app-shelf',
|
'layout/add-service-app-shelf',
|
||||||
@@ -46,19 +47,19 @@ export async function getServerSideProps({
|
|||||||
'settings/customization/app-width',
|
'settings/customization/app-width',
|
||||||
'settings/customization/opacity-selector',
|
'settings/customization/opacity-selector',
|
||||||
'modules/common',
|
'modules/common',
|
||||||
'modules/date-module',
|
'modules/date',
|
||||||
'modules/calendar-module',
|
'modules/calendar',
|
||||||
'modules/total-downloads-module',
|
'modules/dlspeed',
|
||||||
'modules/search-module',
|
'modules/search',
|
||||||
'modules/downloads-module',
|
'modules/torrents-status',
|
||||||
'modules/weather-module',
|
'modules/weather',
|
||||||
'modules/ping-module',
|
'modules/ping',
|
||||||
'modules/docker-module',
|
'modules/docker',
|
||||||
'modules/dashdot-module',
|
'modules/dashdot',
|
||||||
'modules/overseerr-module',
|
'modules/overseerr',
|
||||||
'modules/common-media-cards-module',
|
'modules/common-media-cards',
|
||||||
]);
|
]);
|
||||||
return getConfig(cookie as string, translations);
|
return getConfig(configName as string, translations);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function HomePage(props: any) {
|
export default function HomePage(props: any) {
|
||||||
|
|||||||
Reference in new issue
Block a user