Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add quotas, advanced options, and toggles to collection request modal #1742

Merged
merged 7 commits into from Dec 25, 2021
113 changes: 12 additions & 101 deletions src/components/CollectionDetails/index.tsx
@@ -1,14 +1,11 @@
import { DownloadIcon, DuplicateIcon } from '@heroicons/react/outline';
import axios from 'axios';
import { DownloadIcon } from '@heroicons/react/outline';
import { uniq } from 'lodash';
import Link from 'next/link';
import { useRouter } from 'next/router';
import React, { useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications';
import useSWR from 'swr';
import { MediaStatus } from '../../../server/constants/media';
import type { MediaRequest } from '../../../server/entity/MediaRequest';
import type { Collection } from '../../../server/models/Collection';
import useSettings from '../../hooks/useSettings';
import { Permission, useUser } from '../../hooks/useUser';
Expand All @@ -17,23 +14,17 @@ import Error from '../../pages/_error';
import ButtonWithDropdown from '../Common/ButtonWithDropdown';
import CachedImage from '../Common/CachedImage';
import LoadingSpinner from '../Common/LoadingSpinner';
import Modal from '../Common/Modal';
import PageTitle from '../Common/PageTitle';
import RequestModal from '../RequestModal';
import Slider from '../Slider';
import StatusBadge from '../StatusBadge';
import TitleCard from '../TitleCard';
import Transition from '../Transition';

const messages = defineMessages({
overview: 'Overview',
numberofmovies: '{count} Movies',
requestcollection: 'Request Collection',
requestswillbecreated:
'The following titles will have requests created for them:',
requestcollection4k: 'Request Collection in 4K',
requestswillbecreated4k:
'The following titles will have 4K requests created for them:',
requestSuccess: '<strong>{title}</strong> requested successfully!',
});

interface CollectionDetailsProps {
Expand All @@ -46,10 +37,8 @@ const CollectionDetails: React.FC<CollectionDetailsProps> = ({
const intl = useIntl();
const router = useRouter();
const settings = useSettings();
const { addToast } = useToasts();
const { hasPermission } = useUser();
const [requestModal, setRequestModal] = useState(false);
const [isRequesting, setRequesting] = useState(false);
const [is4k, setIs4k] = useState(false);

const { data, error, revalidate } = useSWR<Collection>(
Expand Down Expand Up @@ -124,48 +113,6 @@ const CollectionDetails: React.FC<CollectionDetailsProps> = ({
!part.mediaInfo || part.mediaInfo.status4k === MediaStatus.UNKNOWN
).length > 0;

const requestableParts = data.parts.filter(
(part) =>
!part.mediaInfo ||
part.mediaInfo[is4k ? 'status4k' : 'status'] === MediaStatus.UNKNOWN
);

const requestBundle = async () => {
try {
setRequesting(true);
await Promise.all(
requestableParts.map(async (part) => {
await axios.post<MediaRequest>('/api/v1/request', {
mediaId: part.id,
mediaType: 'movie',
is4k,
});
})
);

addToast(
<span>
{intl.formatMessage(messages.requestSuccess, {
title: data?.name,
strong: function strong(msg) {
return <strong>{msg}</strong>;
},
})}
</span>,
{ appearance: 'success', autoDismiss: true }
);
} catch (e) {
addToast('Something went wrong requesting the collection.', {
appearance: 'error',
autoDismiss: true,
});
} finally {
setRequesting(false);
setRequestModal(false);
revalidate();
}
};

const collectionAttributes: React.ReactNode[] = [];

collectionAttributes.push(
Expand Down Expand Up @@ -229,53 +176,17 @@ const CollectionDetails: React.FC<CollectionDetailsProps> = ({
</div>
)}
<PageTitle title={data.name} />
<Transition
enter="opacity-0 transition duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="opacity-100 transition duration-300"
leaveFrom="opacity-100"
leaveTo="opacity-0"
<RequestModal
tmdbId={data.id}
show={requestModal}
>
<Modal
onOk={() => requestBundle()}
okText={
isRequesting
? intl.formatMessage(globalMessages.requesting)
: intl.formatMessage(
is4k ? globalMessages.request4k : globalMessages.request
)
}
okDisabled={isRequesting}
okButtonType="primary"
onCancel={() => setRequestModal(false)}
title={intl.formatMessage(
is4k ? messages.requestcollection4k : messages.requestcollection
)}
iconSvg={<DuplicateIcon />}
>
<p>
{intl.formatMessage(
is4k
? messages.requestswillbecreated4k
: messages.requestswillbecreated
)}
</p>
<ul className="py-4 pl-8 list-disc">
{data.parts
.filter(
(part) =>
!part.mediaInfo ||
part.mediaInfo[is4k ? 'status4k' : 'status'] ===
MediaStatus.UNKNOWN
)
.map((part) => (
<li key={`request-part-${part.id}`}>{part.title}</li>
))}
</ul>
</Modal>
</Transition>
type="collection"
is4k={is4k}
onComplete={() => {
revalidate();
setRequestModal(false);
}}
onCancel={() => setRequestModal(false)}
/>
<div className="media-header">
<div className="media-poster">
<CachedImage
Expand Down
2 changes: 1 addition & 1 deletion src/components/RequestModal/AdvancedRequester/index.tsx
Expand Up @@ -283,7 +283,7 @@ const AdvancedRequester: React.FC<AdvancedRequesterProps> = ({

return (
<>
<div className="flex items-center mb-2 font-bold tracking-wider">
<div className="flex items-center mt-4 mb-2 font-bold tracking-wider">
<AdjustmentsIcon className="w-5 h-5 mr-1.5" />
{intl.formatMessage(messages.advancedoptions)}
</div>
Expand Down