2022-08-08 13:52:07 +02:00
|
|
|
import { Alert, Button, Checkbox, createStyles, Group, Modal, Stack, Table } from '@mantine/core';
|
2022-08-07 12:16:29 +02:00
|
|
|
import { showNotification, updateNotification } from '@mantine/notifications';
|
|
|
|
|
import { IconAlertCircle, IconCheck, IconDownload } from '@tabler/icons';
|
|
|
|
|
import axios from 'axios';
|
2022-08-08 13:45:36 +02:00
|
|
|
import Consola from 'consola';
|
2022-08-22 09:50:54 +02:00
|
|
|
import { useTranslation } from 'next-i18next';
|
2022-08-18 21:46:46 +02:00
|
|
|
|
2022-08-08 13:52:07 +02:00
|
|
|
import { useState } from 'react';
|
2022-08-07 12:16:29 +02:00
|
|
|
import { useColorTheme } from '../../tools/color';
|
|
|
|
|
import { MovieResult } from './Movie.d';
|
|
|
|
|
import { MediaType, Result } from './SearchResult.d';
|
|
|
|
|
import { TvShowResult, TvShowResultSeason } from './TvShow.d';
|
|
|
|
|
interface RequestModalProps {
|
|
|
|
|
base: Result;
|
2022-08-08 13:45:36 +02:00
|
|
|
opened: boolean;
|
|
|
|
|
setOpened: (opened: boolean) => void;
|
2022-08-07 12:16:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const useStyles = createStyles((theme) => ({
|
|
|
|
|
rowSelected: {
|
|
|
|
|
backgroundColor:
|
|
|
|
|
theme.colorScheme === 'dark'
|
|
|
|
|
? theme.fn.rgba(theme.colors[theme.primaryColor][7], 0.2)
|
|
|
|
|
: theme.colors[theme.primaryColor][0],
|
|
|
|
|
},
|
|
|
|
|
}));
|
|
|
|
|
|
2022-08-08 13:45:36 +02:00
|
|
|
export function RequestModal({ base, opened, setOpened }: RequestModalProps) {
|
2022-08-07 12:16:29 +02:00
|
|
|
const [result, setResult] = useState<MovieResult | TvShowResult>();
|
2022-08-08 13:45:36 +02:00
|
|
|
const { secondaryColor } = useColorTheme();
|
2022-08-22 09:50:54 +02:00
|
|
|
|
2022-08-08 13:45:36 +02:00
|
|
|
function getResults(base: Result) {
|
2022-08-07 12:16:29 +02:00
|
|
|
axios.get(`/api/modules/overseerr/${base.id}?type=${base.mediaType}`).then((res) => {
|
|
|
|
|
setResult(res.data);
|
|
|
|
|
});
|
2022-08-08 13:45:36 +02:00
|
|
|
}
|
|
|
|
|
if (opened && !result) {
|
|
|
|
|
getResults(base);
|
|
|
|
|
}
|
|
|
|
|
if (!result || !opened) {
|
|
|
|
|
return null;
|
2022-08-07 12:16:29 +02:00
|
|
|
}
|
|
|
|
|
return base.mediaType === 'movie' ? (
|
2022-08-08 13:45:36 +02:00
|
|
|
<MovieRequestModal result={result as MovieResult} opened={opened} setOpened={setOpened} />
|
2022-08-07 12:16:29 +02:00
|
|
|
) : (
|
2022-08-08 13:45:36 +02:00
|
|
|
<TvRequestModal result={result as TvShowResult} opened={opened} setOpened={setOpened} />
|
2022-08-07 12:16:29 +02:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-08 13:45:36 +02:00
|
|
|
export function MovieRequestModal({
|
|
|
|
|
result,
|
|
|
|
|
opened,
|
|
|
|
|
setOpened,
|
|
|
|
|
}: {
|
|
|
|
|
result: MovieResult;
|
|
|
|
|
opened: boolean;
|
|
|
|
|
setOpened: (opened: boolean) => void;
|
|
|
|
|
}) {
|
2022-08-07 12:16:29 +02:00
|
|
|
const { secondaryColor } = useColorTheme();
|
2022-08-22 09:50:54 +02:00
|
|
|
const { t } = useTranslation('modules/overseerr-module');
|
|
|
|
|
|
2022-08-08 13:45:36 +02:00
|
|
|
return (
|
|
|
|
|
<Modal
|
|
|
|
|
onClose={() => setOpened(false)}
|
|
|
|
|
radius="lg"
|
|
|
|
|
size="lg"
|
|
|
|
|
trapFocus
|
|
|
|
|
zIndex={150}
|
|
|
|
|
withinPortal
|
|
|
|
|
opened={opened}
|
|
|
|
|
title={
|
|
|
|
|
<Group>
|
|
|
|
|
<IconDownload />
|
2022-08-22 09:50:54 +02:00
|
|
|
{t('popup.item.buttons.askFor', { title: result.title })}
|
2022-08-08 13:45:36 +02:00
|
|
|
</Group>
|
|
|
|
|
}
|
|
|
|
|
>
|
2022-08-07 12:16:29 +02:00
|
|
|
<Stack>
|
|
|
|
|
<Alert
|
|
|
|
|
icon={<IconAlertCircle size={16} />}
|
2022-08-22 09:50:54 +02:00
|
|
|
title={t('popup.item.alerts.automaticApproval.title')}
|
2022-08-07 12:16:29 +02:00
|
|
|
color={secondaryColor}
|
|
|
|
|
radius="md"
|
|
|
|
|
variant="filled"
|
|
|
|
|
>
|
2022-08-22 09:50:54 +02:00
|
|
|
{t('popup.item.alerts.automaticApproval.text')}
|
2022-08-07 12:16:29 +02:00
|
|
|
</Alert>
|
2022-08-08 13:45:36 +02:00
|
|
|
<Group>
|
2022-08-08 15:43:04 +02:00
|
|
|
<Button variant="outline" color="gray" onClick={() => setOpened(false)}>
|
2022-08-22 09:50:54 +02:00
|
|
|
{t('popup.item.buttons.cancel')}
|
2022-08-08 15:43:04 +02:00
|
|
|
</Button>
|
2022-08-08 13:45:36 +02:00
|
|
|
<Button
|
2022-08-08 15:43:04 +02:00
|
|
|
variant="outline"
|
2022-08-08 13:45:36 +02:00
|
|
|
onClick={() => {
|
|
|
|
|
askForMedia(MediaType.Movie, result.id, result.title, []);
|
|
|
|
|
}}
|
|
|
|
|
>
|
2022-08-22 09:50:54 +02:00
|
|
|
{t('popup.item.buttons.request')}
|
2022-08-08 13:45:36 +02:00
|
|
|
</Button>
|
|
|
|
|
</Group>
|
2022-08-07 12:16:29 +02:00
|
|
|
</Stack>
|
2022-08-08 13:45:36 +02:00
|
|
|
</Modal>
|
|
|
|
|
);
|
2022-08-07 12:16:29 +02:00
|
|
|
}
|
|
|
|
|
|
2022-08-08 13:45:36 +02:00
|
|
|
export function TvRequestModal({
|
|
|
|
|
result,
|
|
|
|
|
opened,
|
|
|
|
|
setOpened,
|
|
|
|
|
}: {
|
|
|
|
|
result: TvShowResult;
|
|
|
|
|
opened: boolean;
|
|
|
|
|
setOpened: (opened: boolean) => void;
|
|
|
|
|
}) {
|
2022-08-07 12:16:29 +02:00
|
|
|
const [selection, setSelection] = useState<TvShowResultSeason[]>(result.seasons);
|
|
|
|
|
const { classes, cx } = useStyles();
|
2022-08-22 09:50:54 +02:00
|
|
|
const { t } = useTranslation('modules/overseerr-module');
|
2022-08-07 12:16:29 +02:00
|
|
|
|
|
|
|
|
const toggleRow = (container: TvShowResultSeason) =>
|
|
|
|
|
setSelection((current: TvShowResultSeason[]) =>
|
|
|
|
|
current.includes(container) ? current.filter((c) => c !== container) : [...current, container]
|
|
|
|
|
);
|
|
|
|
|
const toggleAll = () =>
|
|
|
|
|
setSelection((current: any) =>
|
|
|
|
|
current.length === result.seasons.length ? [] : result.seasons.map((c) => c)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const rows = result.seasons.map((element) => {
|
|
|
|
|
const selected = selection.includes(element);
|
|
|
|
|
return (
|
|
|
|
|
<tr key={element.id} className={cx({ [classes.rowSelected]: selected })}>
|
|
|
|
|
<td>
|
|
|
|
|
<Checkbox
|
|
|
|
|
key={element.id}
|
|
|
|
|
checked={selection.includes(element)}
|
|
|
|
|
onChange={() => toggleRow(element)}
|
|
|
|
|
transitionDuration={0}
|
|
|
|
|
/>
|
|
|
|
|
</td>
|
|
|
|
|
<td>{element.name}</td>
|
|
|
|
|
<td>{element.episodeCount}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
const { secondaryColor } = useColorTheme();
|
|
|
|
|
|
2022-08-08 13:45:36 +02:00
|
|
|
return (
|
2022-08-08 15:43:04 +02:00
|
|
|
<Modal
|
|
|
|
|
onClose={() => setOpened(false)}
|
|
|
|
|
radius="lg"
|
|
|
|
|
size="lg"
|
|
|
|
|
opened={opened}
|
|
|
|
|
title={
|
|
|
|
|
<Group>
|
|
|
|
|
<IconDownload />
|
2022-08-22 09:50:54 +02:00
|
|
|
{t('popup.item.buttons.askFor', {
|
2022-08-18 21:46:46 +02:00
|
|
|
title: result.name ?? result.originalName ?? 'a TV show',
|
|
|
|
|
})}
|
2022-08-08 15:43:04 +02:00
|
|
|
</Group>
|
|
|
|
|
}
|
|
|
|
|
>
|
2022-08-07 12:16:29 +02:00
|
|
|
<Stack>
|
|
|
|
|
<Alert
|
|
|
|
|
icon={<IconAlertCircle size={16} />}
|
2022-08-22 09:50:54 +02:00
|
|
|
title={t('popup.item.alerts.automaticApproval.title')}
|
2022-08-07 12:16:29 +02:00
|
|
|
color={secondaryColor}
|
|
|
|
|
radius="md"
|
|
|
|
|
variant="filled"
|
|
|
|
|
>
|
2022-08-22 09:50:54 +02:00
|
|
|
{t('popup.item.alerts.automaticApproval.text')}
|
2022-08-07 12:16:29 +02:00
|
|
|
</Alert>
|
|
|
|
|
<Table captionSide="bottom" highlightOnHover>
|
2022-08-22 09:50:54 +02:00
|
|
|
<caption>{t('popup.seasonSelector.caption')}</caption>
|
2022-08-07 12:16:29 +02:00
|
|
|
<thead>
|
|
|
|
|
<tr>
|
|
|
|
|
<th>
|
|
|
|
|
<Checkbox
|
|
|
|
|
onChange={toggleAll}
|
|
|
|
|
checked={selection.length === result.seasons.length}
|
|
|
|
|
indeterminate={selection.length > 0 && selection.length !== result.seasons.length}
|
|
|
|
|
transitionDuration={0}
|
|
|
|
|
/>
|
|
|
|
|
</th>
|
2022-08-22 09:50:54 +02:00
|
|
|
<th>{t('popup.seasonSelector.table.header.season')}</th>
|
|
|
|
|
<th>{t('popup.seasonSelector.table.header.numberOfEpisodes')}</th>
|
2022-08-07 12:16:29 +02:00
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody>{rows}</tbody>
|
|
|
|
|
</Table>
|
2022-08-08 13:45:36 +02:00
|
|
|
<Group>
|
2022-08-08 15:43:04 +02:00
|
|
|
<Button variant="outline" color="gray" onClick={() => setOpened(false)}>
|
2022-08-22 09:50:54 +02:00
|
|
|
{t('popup.item.buttons.cancel')}
|
2022-08-08 15:43:04 +02:00
|
|
|
</Button>
|
2022-08-08 13:45:36 +02:00
|
|
|
<Button
|
2022-08-08 15:43:04 +02:00
|
|
|
variant="outline"
|
2022-08-08 13:45:36 +02:00
|
|
|
disabled={selection.length === 0}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
askForMedia(
|
|
|
|
|
MediaType.Tv,
|
|
|
|
|
result.id,
|
|
|
|
|
result.name,
|
|
|
|
|
selection.map((s) => s.seasonNumber)
|
|
|
|
|
);
|
|
|
|
|
}}
|
|
|
|
|
>
|
2022-08-22 09:50:54 +02:00
|
|
|
{t('popup.item.buttons.request')}
|
2022-08-08 13:45:36 +02:00
|
|
|
</Button>
|
|
|
|
|
</Group>
|
2022-08-07 12:16:29 +02:00
|
|
|
</Stack>
|
2022-08-08 13:45:36 +02:00
|
|
|
</Modal>
|
|
|
|
|
);
|
2022-08-07 12:16:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function askForMedia(type: MediaType, id: number, name: string, seasons?: number[]) {
|
2022-08-08 13:45:36 +02:00
|
|
|
Consola.info(`Requesting ${type} ${id} ${name}`);
|
2022-08-07 12:16:29 +02:00
|
|
|
showNotification({
|
|
|
|
|
title: 'Request',
|
|
|
|
|
id: id.toString(),
|
|
|
|
|
message: `Requesting media ${name}`,
|
|
|
|
|
color: 'orange',
|
|
|
|
|
loading: true,
|
|
|
|
|
autoClose: false,
|
|
|
|
|
disallowClose: true,
|
|
|
|
|
icon: <IconAlertCircle />,
|
|
|
|
|
});
|
|
|
|
|
axios
|
|
|
|
|
.post(`/api/modules/overseerr/${id}`, { type, seasons })
|
|
|
|
|
.then(() => {
|
|
|
|
|
updateNotification({
|
|
|
|
|
id: id.toString(),
|
|
|
|
|
title: '',
|
|
|
|
|
color: 'green',
|
|
|
|
|
message: ` ${name} requested`,
|
|
|
|
|
icon: <IconCheck />,
|
|
|
|
|
autoClose: 2000,
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
.catch((err) => {
|
|
|
|
|
updateNotification({
|
|
|
|
|
id: id.toString(),
|
|
|
|
|
color: 'red',
|
|
|
|
|
title: 'There was an error',
|
|
|
|
|
message: err.message,
|
|
|
|
|
autoClose: 2000,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|