feat(ui): Add support for requesting collections in 4K (#968)

This commit is contained in:
TheCatLady
2021-02-20 08:59:44 -05:00
committed by GitHub
parent 2e35aff903
commit 139341b043
6 changed files with 251 additions and 124 deletions

View File

@@ -8,16 +8,17 @@ import { MediaStatus } from '../../../server/constants/media';
import type { MediaRequest } from '../../../server/entity/MediaRequest';
import type { Collection } from '../../../server/models/Collection';
import { LanguageContext } from '../../context/LanguageContext';
import globalMessages from '../../i18n/globalMessages';
import Error from '../../pages/_error';
import Badge from '../Common/Badge';
import Button from '../Common/Button';
import StatusBadge from '../StatusBadge';
import ButtonWithDropdown from '../Common/ButtonWithDropdown';
import LoadingSpinner from '../Common/LoadingSpinner';
import Modal from '../Common/Modal';
import Slider from '../Slider';
import TitleCard from '../TitleCard';
import Transition from '../Transition';
import PageTitle from '../Common/PageTitle';
import { useUser, Permission } from '../../hooks/useUser';
import useSettings from '../../hooks/useSettings';
const messages = defineMessages({
overviewunavailable: 'Overview unavailable.',
@@ -29,6 +30,10 @@ const messages = defineMessages({
requestcollection: 'Request Collection',
requestswillbecreated:
'The following titles will have requests created for them:',
request4k: 'Request 4K',
requestcollection4k: 'Request Collection in 4K',
requestswillbecreated4k:
'The following titles will have 4K requests created for them:',
requestSuccess: '<strong>{title}</strong> successfully requested!',
});
@@ -41,10 +46,14 @@ const CollectionDetails: React.FC<CollectionDetailsProps> = ({
}) => {
const intl = useIntl();
const router = useRouter();
const settings = useSettings();
const { addToast } = useToasts();
const { locale } = useContext(LanguageContext);
const { hasPermission } = useUser();
const [requestModal, setRequestModal] = useState(false);
const [isRequesting, setRequesting] = useState(false);
const [is4k, setIs4k] = useState(false);
const { data, error, revalidate } = useSWR<Collection>(
`/api/v1/collection/${router.query.collectionId}?language=${locale}`,
{
@@ -61,8 +70,45 @@ const CollectionDetails: React.FC<CollectionDetailsProps> = ({
return <Error statusCode={404} />;
}
let collectionStatus = MediaStatus.UNKNOWN;
let collectionStatus4k = MediaStatus.UNKNOWN;
if (
data.parts.every(
(part) =>
part.mediaInfo && part.mediaInfo.status === MediaStatus.AVAILABLE
)
) {
collectionStatus = MediaStatus.AVAILABLE;
} else if (
data.parts.some(
(part) =>
part.mediaInfo && part.mediaInfo.status === MediaStatus.AVAILABLE
)
) {
collectionStatus = MediaStatus.PARTIALLY_AVAILABLE;
}
if (
data.parts.every(
(part) =>
part.mediaInfo && part.mediaInfo.status4k === MediaStatus.AVAILABLE
)
) {
collectionStatus4k = MediaStatus.AVAILABLE;
} else if (
data.parts.some(
(part) =>
part.mediaInfo && part.mediaInfo.status4k === MediaStatus.AVAILABLE
)
) {
collectionStatus4k = MediaStatus.PARTIALLY_AVAILABLE;
}
const requestableParts = data.parts.filter(
(part) => !part.mediaInfo || part.mediaInfo.status === MediaStatus.UNKNOWN
(part) =>
!part.mediaInfo ||
part.mediaInfo[is4k ? 'status4k' : 'status'] === MediaStatus.UNKNOWN
);
const requestBundle = async () => {
@@ -73,6 +119,7 @@ const CollectionDetails: React.FC<CollectionDetailsProps> = ({
await axios.post<MediaRequest>('/api/v1/request', {
mediaId: part.id,
mediaType: 'movie',
is4k,
});
})
);
@@ -123,12 +170,14 @@ const CollectionDetails: React.FC<CollectionDetailsProps> = ({
okText={
isRequesting
? intl.formatMessage(messages.requesting)
: intl.formatMessage(messages.request)
: intl.formatMessage(is4k ? messages.request4k : messages.request)
}
okDisabled={isRequesting}
okButtonType="primary"
onCancel={() => setRequestModal(false)}
title={intl.formatMessage(messages.requestcollection)}
title={intl.formatMessage(
is4k ? messages.requestcollection4k : messages.requestcollection
)}
iconSvg={
<svg
className="w-6 h-6"
@@ -146,13 +195,20 @@ const CollectionDetails: React.FC<CollectionDetailsProps> = ({
</svg>
}
>
<p>{intl.formatMessage(messages.requestswillbecreated)}</p>
<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?.status === MediaStatus.UNKNOWN
part.mediaInfo[is4k ? 'status4k' : 'status'] ===
MediaStatus.UNKNOWN
)
.map((part) => (
<li key={`request-part-${part.id}`}>{part.title}</li>
@@ -160,64 +216,128 @@ const CollectionDetails: React.FC<CollectionDetailsProps> = ({
</ul>
</Modal>
</Transition>
<div className="flex flex-col items-center pt-4 md:flex-row md:items-end">
<div className="flex-shrink-0 md:mr-4">
<div className="flex flex-col items-center pt-4 lg:flex-row lg:items-end">
<div className="lg:mr-4">
<img
src={`//image.tmdb.org/t/p/w600_and_h900_bestv2${data.posterPath}`}
alt=""
className="w-32 rounded shadow md:rounded-lg md:shadow-2xl md:w-52"
className="w-32 rounded shadow md:rounded-lg md:shadow-2xl md:w-44 lg:w-52"
/>
</div>
<div className="flex flex-col mt-4 text-center text-white md:mr-4 md:mt-0 md:text-left">
<div className="mb-2">
{data.parts.every(
(part) => part.mediaInfo?.status === MediaStatus.AVAILABLE
) && (
<Badge badgeType="success">
{intl.formatMessage(globalMessages.available)}
</Badge>
)}
{!data.parts.every(
(part) => part.mediaInfo?.status === MediaStatus.AVAILABLE
) &&
data.parts.some(
(part) => part.mediaInfo?.status === MediaStatus.AVAILABLE
<div className="flex flex-col flex-1 mt-4 text-center text-white lg:mr-4 lg:mt-0 lg:text-left">
<div className="mb-2 space-x-2">
<span className="ml-2 lg:ml-0">
<StatusBadge
status={collectionStatus}
inProgress={data.parts.some(
(part) => (part.mediaInfo?.downloadStatus ?? []).length > 0
)}
/>
</span>
{settings.currentSettings.movie4kEnabled &&
hasPermission(
[Permission.REQUEST_4K, Permission.REQUEST_4K_MOVIE],
{
type: 'or',
}
) && (
<Badge badgeType="success">
{intl.formatMessage(globalMessages.partiallyavailable)}
</Badge>
<span>
<StatusBadge
status={collectionStatus4k}
is4k
inProgress={data.parts.some(
(part) =>
(part.mediaInfo?.downloadStatus4k ?? []).length > 0
)}
/>
</span>
)}
</div>
<h1 className="text-2xl md:text-4xl">{data.name}</h1>
<span className="mt-1 text-xs md:text-base md:mt-0">
<span className="mt-1 text-xs lg:text-base lg:mt-0">
{intl.formatMessage(messages.numberofmovies, {
count: data.parts.length,
})}
</span>
</div>
<div className="flex justify-end flex-1 mt-4 md:mt-0">
{data.parts.some(
(part) =>
!part.mediaInfo || part.mediaInfo?.status === MediaStatus.UNKNOWN
) && (
<Button buttonType="primary" onClick={() => setRequestModal(true)}>
<svg
className="w-4 mr-1"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
/>
</svg>
{intl.formatMessage(messages.requestcollection)}
</Button>
)}
<div className="relative z-10 flex flex-wrap justify-center flex-shrink-0 mt-4 sm:justify-end sm:flex-nowrap lg:mt-0">
{hasPermission(Permission.REQUEST) &&
(collectionStatus !== MediaStatus.AVAILABLE ||
(settings.currentSettings.movie4kEnabled &&
hasPermission(
[Permission.REQUEST_4K, Permission.REQUEST_4K_MOVIE],
{ type: 'or' }
) &&
collectionStatus4k !== MediaStatus.AVAILABLE)) && (
<div className="mb-3 sm:mb-0">
<ButtonWithDropdown
buttonType="primary"
onClick={() => {
setRequestModal(true);
setIs4k(collectionStatus === MediaStatus.AVAILABLE);
}}
text={
<>
<svg
className="w-4 mr-1"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
/>
</svg>
<span>
{intl.formatMessage(
collectionStatus === MediaStatus.AVAILABLE
? messages.requestcollection4k
: messages.requestcollection
)}
</span>
</>
}
>
{settings.currentSettings.movie4kEnabled &&
hasPermission(
[Permission.REQUEST_4K, Permission.REQUEST_4K_MOVIE],
{ type: 'or' }
) &&
collectionStatus !== MediaStatus.AVAILABLE &&
collectionStatus4k !== MediaStatus.AVAILABLE && (
<ButtonWithDropdown.Item
buttonType="primary"
onClick={() => {
setRequestModal(true);
setIs4k(true);
}}
>
<svg
className="w-4 mr-1"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
/>
</svg>
<span>
{intl.formatMessage(messages.requestcollection4k)}
</span>
</ButtonWithDropdown.Item>
)}
</ButtonWithDropdown>
</div>
)}
</div>
</div>
<div className="flex flex-col pt-8 pb-4 text-white md:flex-row">