mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-11 07:55:52 +01:00
- 6fd23cf6a0 changed how items for the calendar are acquired, making it so every series gets updated when a new one gets added, or one gets moved. This causes the entries in the calendar to duplicate due to old code being left in.
307 lines
9.0 KiB
TypeScript
307 lines
9.0 KiB
TypeScript
/* eslint-disable react/no-children-prop */
|
|
import {
|
|
Box,
|
|
Divider,
|
|
Indicator,
|
|
Popover,
|
|
ScrollArea,
|
|
createStyles,
|
|
useMantineTheme,
|
|
} from '@mantine/core';
|
|
import React, { useEffect, useState } from 'react';
|
|
import { Calendar } from '@mantine/dates';
|
|
import { IconCalendar as CalendarIcon } from '@tabler/icons';
|
|
import axios from 'axios';
|
|
import { useConfig } from '../../../tools/state';
|
|
import { IModule } from '../modules';
|
|
import {
|
|
SonarrMediaDisplay,
|
|
RadarrMediaDisplay,
|
|
LidarrMediaDisplay,
|
|
ReadarrMediaDisplay,
|
|
} from '../common';
|
|
import { serviceItem } from '../../../tools/types';
|
|
import { useColorTheme } from '../../../tools/color';
|
|
|
|
export const CalendarModule: IModule = {
|
|
title: 'Calendar',
|
|
description:
|
|
'A calendar module for displaying upcoming releases. It interacts with the Sonarr and Radarr API.',
|
|
icon: CalendarIcon,
|
|
component: CalendarComponent,
|
|
options: {
|
|
sundaystart: {
|
|
name: 'Start the week on Sunday',
|
|
value: false,
|
|
},
|
|
},
|
|
};
|
|
|
|
export default function CalendarComponent(props: any) {
|
|
const { config } = useConfig();
|
|
const theme = useMantineTheme();
|
|
const { secondaryColor } = useColorTheme();
|
|
const useStyles = createStyles((theme) => ({
|
|
weekend: {
|
|
color: `${secondaryColor} !important`,
|
|
},
|
|
}));
|
|
|
|
const [sonarrMedias, setSonarrMedias] = useState([] as any);
|
|
const [lidarrMedias, setLidarrMedias] = useState([] as any);
|
|
const [radarrMedias, setRadarrMedias] = useState([] as any);
|
|
const [readarrMedias, setReadarrMedias] = useState([] as any);
|
|
const sonarrServices = config.services.filter((service) => service.type === 'Sonarr');
|
|
const radarrServices = config.services.filter((service) => service.type === 'Radarr');
|
|
const lidarrServices = config.services.filter((service) => service.type === 'Lidarr');
|
|
const readarrServices = config.services.filter((service) => service.type === 'Readarr');
|
|
const today = new Date();
|
|
|
|
const { classes, cx } = useStyles();
|
|
|
|
function getMedias(service: serviceItem | undefined, type: string) {
|
|
if (!service || !service.apiKey) {
|
|
return Promise.resolve({ data: [] });
|
|
}
|
|
return axios.post(`/api/modules/calendar?type=${type}`, { ...service });
|
|
}
|
|
|
|
useEffect(() => {
|
|
// Create each Sonarr service and get the medias
|
|
const currentSonarrMedias: any[] = [];
|
|
Promise.all(
|
|
sonarrServices.map((service) =>
|
|
getMedias(service, 'sonarr').then((res) => {
|
|
currentSonarrMedias.push(...res.data);
|
|
}).catch(() => {
|
|
currentSonarrMedias.push([]);
|
|
})
|
|
)
|
|
).then(() => {
|
|
setSonarrMedias(currentSonarrMedias);
|
|
});
|
|
const currentRadarrMedias: any[] = [];
|
|
Promise.all(
|
|
radarrServices.map((service) =>
|
|
getMedias(service, 'radarr').then((res) => {
|
|
currentRadarrMedias.push(...res.data);
|
|
}).catch(() => {
|
|
currentRadarrMedias.push([]);
|
|
})
|
|
)
|
|
).then(() => {
|
|
setRadarrMedias(currentRadarrMedias);
|
|
});
|
|
const currentLidarrMedias: any[] = [];
|
|
Promise.all(
|
|
lidarrServices.map((service) =>
|
|
getMedias(service, 'lidarr').then((res) => {
|
|
currentLidarrMedias.push(...res.data);
|
|
}).catch(() => {
|
|
currentLidarrMedias.push([]);
|
|
})
|
|
)
|
|
).then(() => {
|
|
setLidarrMedias(currentLidarrMedias);
|
|
});
|
|
const currentReadarrMedias: any[] = [];
|
|
Promise.all(
|
|
readarrServices.map((service) =>
|
|
getMedias(service, 'readarr').then((res) => {
|
|
currentReadarrMedias.push(...res.data);
|
|
}).catch(() => {
|
|
currentReadarrMedias.push([]);
|
|
})
|
|
)
|
|
).then(() => {
|
|
setReadarrMedias(currentReadarrMedias);
|
|
});
|
|
}, [config.services]);
|
|
|
|
const weekStartsAtSunday =
|
|
(config?.modules?.[CalendarModule.title]?.options?.sundaystart?.value as boolean) ?? false;
|
|
return (
|
|
<Calendar
|
|
firstDayOfWeek={weekStartsAtSunday ? 'sunday' : 'monday'}
|
|
onChange={(day: any) => {}}
|
|
dayStyle={(date) =>
|
|
date.getDay() === today.getDay() && date.getDate() === today.getDate()
|
|
? {
|
|
backgroundColor:
|
|
theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[0],
|
|
}
|
|
: {}
|
|
}
|
|
styles={{
|
|
calendarHeader: {
|
|
marginRight: 15,
|
|
marginLeft: 15,
|
|
},
|
|
}}
|
|
allowLevelChange={false}
|
|
dayClassName={(date, modifiers) => cx({ [classes.weekend]: modifiers.weekend })}
|
|
renderDay={(renderdate) => (
|
|
<DayComponent
|
|
renderdate={renderdate}
|
|
sonarrmedias={sonarrMedias}
|
|
radarrmedias={radarrMedias}
|
|
lidarrmedias={lidarrMedias}
|
|
readarrmedias={readarrMedias}
|
|
/>
|
|
)}
|
|
/>
|
|
);
|
|
}
|
|
|
|
function DayComponent(props: any) {
|
|
const {
|
|
renderdate,
|
|
sonarrmedias,
|
|
radarrmedias,
|
|
lidarrmedias,
|
|
readarrmedias,
|
|
}: { renderdate: Date; sonarrmedias: []; radarrmedias: []; lidarrmedias: []; readarrmedias: [] } =
|
|
props;
|
|
const [opened, setOpened] = useState(false);
|
|
|
|
const day = renderdate.getDate();
|
|
|
|
const readarrFiltered = readarrmedias.filter((media: any) => {
|
|
const date = new Date(media.releaseDate);
|
|
return date.toDateString() === renderdate.toDateString();
|
|
});
|
|
|
|
const lidarrFiltered = lidarrmedias.filter((media: any) => {
|
|
const date = new Date(media.releaseDate);
|
|
return date.toDateString() === renderdate.toDateString();
|
|
});
|
|
const sonarrFiltered = sonarrmedias.filter((media: any) => {
|
|
const date = new Date(media.airDateUtc);
|
|
return date.toDateString() === renderdate.toDateString();
|
|
});
|
|
const radarrFiltered = radarrmedias.filter((media: any) => {
|
|
const date = new Date(media.inCinemas);
|
|
return date.toDateString() === renderdate.toDateString();
|
|
});
|
|
if (
|
|
sonarrFiltered.length === 0 &&
|
|
radarrFiltered.length === 0 &&
|
|
lidarrFiltered.length === 0 &&
|
|
readarrFiltered.length === 0
|
|
) {
|
|
return <div>{day}</div>;
|
|
}
|
|
|
|
return (
|
|
<Box
|
|
onClick={() => {
|
|
setOpened(true);
|
|
}}
|
|
>
|
|
{readarrFiltered.length > 0 && (
|
|
<Indicator
|
|
size={10}
|
|
withBorder
|
|
style={{
|
|
position: 'absolute',
|
|
bottom: 8,
|
|
left: 8,
|
|
}}
|
|
color="red"
|
|
children={null}
|
|
/>
|
|
)}
|
|
{radarrFiltered.length > 0 && (
|
|
<Indicator
|
|
size={10}
|
|
withBorder
|
|
style={{
|
|
position: 'absolute',
|
|
top: 8,
|
|
left: 8,
|
|
}}
|
|
color="yellow"
|
|
children={null}
|
|
/>
|
|
)}
|
|
{sonarrFiltered.length > 0 && (
|
|
<Indicator
|
|
size={10}
|
|
withBorder
|
|
style={{
|
|
position: 'absolute',
|
|
top: 8,
|
|
right: 8,
|
|
}}
|
|
color="blue"
|
|
children={null}
|
|
/>
|
|
)}
|
|
{lidarrFiltered.length > 0 && (
|
|
<Indicator
|
|
size={10}
|
|
withBorder
|
|
style={{
|
|
position: 'absolute',
|
|
bottom: 8,
|
|
right: 8,
|
|
}}
|
|
color="green"
|
|
children={undefined}
|
|
/>
|
|
)}
|
|
<Popover
|
|
position="bottom"
|
|
radius="lg"
|
|
shadow="xl"
|
|
transition="pop"
|
|
styles={{
|
|
body: {
|
|
boxShadow: '0 0 14px 14px rgba(0, 0, 0, 0.1), 0 14px 11px rgba(0, 0, 0, 0.1)',
|
|
},
|
|
}}
|
|
width="auto"
|
|
onClose={() => setOpened(false)}
|
|
opened={opened}
|
|
target={day}
|
|
>
|
|
<ScrollArea style={{ height: 400 }}>
|
|
{sonarrFiltered.map((media: any, index: number) => (
|
|
<React.Fragment key={index}>
|
|
<SonarrMediaDisplay media={media} />
|
|
{index < sonarrFiltered.length - 1 && <Divider variant="dashed" my="xl" />}
|
|
</React.Fragment>
|
|
))}
|
|
{radarrFiltered.length > 0 && sonarrFiltered.length > 0 && (
|
|
<Divider variant="dashed" my="xl" />
|
|
)}
|
|
{radarrFiltered.map((media: any, index: number) => (
|
|
<React.Fragment key={index}>
|
|
<RadarrMediaDisplay media={media} />
|
|
{index < radarrFiltered.length - 1 && <Divider variant="dashed" my="xl" />}
|
|
</React.Fragment>
|
|
))}
|
|
{sonarrFiltered.length > 0 && lidarrFiltered.length > 0 && (
|
|
<Divider variant="dashed" my="xl" />
|
|
)}
|
|
{lidarrFiltered.map((media: any, index: number) => (
|
|
<React.Fragment key={index}>
|
|
<LidarrMediaDisplay media={media} />
|
|
{index < lidarrFiltered.length - 1 && <Divider variant="dashed" my="xl" />}
|
|
</React.Fragment>
|
|
))}
|
|
{lidarrFiltered.length > 0 && readarrFiltered.length > 0 && (
|
|
<Divider variant="dashed" my="xl" />
|
|
)}
|
|
{readarrFiltered.map((media: any, index: number) => (
|
|
<React.Fragment key={index}>
|
|
<ReadarrMediaDisplay media={media} />
|
|
{index < readarrFiltered.length - 1 && <Divider variant="dashed" my="xl" />}
|
|
</React.Fragment>
|
|
))}
|
|
</ScrollArea>
|
|
</Popover>
|
|
</Box>
|
|
);
|
|
}
|