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

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

* fix: use correct requiredquota strings

* refactor: clean up collection part request status logic

* revert: undo changes to effect dependencies

* fix(lang): tweak TV request modal request button strings

* fix: don't try to fetch other users' quotas without MANAGE_USERS perm
This commit is contained in:
TheCatLady
2021-12-24 17:10:33 -08:00
committed by GitHub
parent 488874fc17
commit af40212a73
8 changed files with 561 additions and 191 deletions

View File

@@ -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';
@@ -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 {
@@ -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>(
@@ -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(
@@ -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