+ {title.backdropPath && (
+
+
-
- {requestData.requestedBy.displayName}
-
-
-
- {requestData.media.status && (
-
)}
- {request.seasons.length > 0 && (
-
-
{intl.formatMessage(messages.seasons)}
- {!isMovie(title) &&
- title.seasons.filter((season) => season.seasonNumber !== 0)
- .length === request.seasons.length ? (
-
- {intl.formatMessage(messages.all)}
-
- ) : (
-
- {request.seasons.map((season) => (
-
- {season.seasonNumber}
-
- ))}
-
+
+
+ {(isMovie(title) ? title.releaseDate : title.firstAirDate)?.slice(
+ 0,
+ 4
)}
- )}
- {requestData.status === MediaRequestStatus.PENDING &&
- hasPermission(Permission.MANAGE_REQUESTS) && (
-
-
+
+
+ {isMovie(title) ? title.title : title.name}
+
+
+ {hasPermission(
+ [Permission.MANAGE_REQUESTS, Permission.REQUEST_VIEW],
+ { type: 'or' }
+ ) && (
+
+ )}
+ {!isMovie(title) && request.seasons.length > 0 && (
+
+
+ {intl.formatMessage(messages.seasons, {
+ seasonCount:
+ title.seasons.filter((season) => season.seasonNumber !== 0)
+ .length === request.seasons.length
+ ? 0
+ : request.seasons.length,
+ })}
+
+ {title.seasons.filter((season) => season.seasonNumber !== 0)
+ .length === request.seasons.length ? (
+
+ {intl.formatMessage(globalMessages.all)}
+
+ ) : (
+
+ {request.seasons.map((season) => (
+
+ {season.seasonNumber}
+
+ ))}
+
+ )}
+
+ )}
+
+
+ {intl.formatMessage(globalMessages.status)}
+
+ {requestData.status === MediaRequestStatus.DECLINED ? (
+
+ {intl.formatMessage(globalMessages.declined)}
+
+ ) : requestData.media[requestData.is4k ? 'status4k' : 'status'] ===
+ MediaStatus.UNKNOWN ? (
+
+ {intl.formatMessage(globalMessages.failed)}
+
+ ) : (
+ 0
+ }
+ is4k={requestData.is4k}
+ tmdbId={requestData.media.tmdbId}
+ mediaType={requestData.type}
+ plexUrl={
+ requestData.media[
+ requestData.is4k ? 'mediaUrl4k' : 'mediaUrl'
+ ]
+ }
+ />
+ )}
+
+
+ {requestData.media[requestData.is4k ? 'status4k' : 'status'] ===
+ MediaStatus.UNKNOWN &&
+ requestData.status !== MediaRequestStatus.DECLINED &&
+ hasPermission(Permission.MANAGE_REQUESTS) && (
-
-
+ )}
+ {requestData.status === MediaRequestStatus.PENDING &&
+ hasPermission(Permission.MANAGE_REQUESTS) && (
+ <>
+
+
+ >
+ )}
+ {requestData.status === MediaRequestStatus.PENDING &&
+ !hasPermission(Permission.MANAGE_REQUESTS) &&
+ requestData.requestedBy.id === user?.id &&
+ (requestData.type === 'tv' ||
+ hasPermission(Permission.REQUEST_ADVANCED)) && (
+
+ )}
+ {requestData.status === MediaRequestStatus.PENDING &&
+ !hasPermission(Permission.MANAGE_REQUESTS) &&
+ requestData.requestedBy.id === user?.id && (
-
-
- )}
-
-
+ )}
+
+
-

+
+
+
-
+ >
);
};
diff --git a/src/components/RequestList/RequestItem/index.tsx b/src/components/RequestList/RequestItem/index.tsx
index 372725588..bb0cbd8d4 100644
--- a/src/components/RequestList/RequestItem/index.tsx
+++ b/src/components/RequestList/RequestItem/index.tsx
@@ -1,13 +1,15 @@
+import {
+ CheckIcon,
+ PencilIcon,
+ RefreshIcon,
+ TrashIcon,
+ XIcon,
+} from '@heroicons/react/solid';
import axios from 'axios';
import Link from 'next/link';
-import React, { useContext, useState } from 'react';
+import React, { useState } from 'react';
import { useInView } from 'react-intersection-observer';
-import {
- defineMessages,
- FormattedDate,
- FormattedRelativeTime,
- useIntl,
-} from 'react-intl';
+import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications';
import useSWR from 'swr';
import {
@@ -17,25 +19,70 @@ import {
import type { MediaRequest } from '../../../../server/entity/MediaRequest';
import type { MovieDetails } from '../../../../server/models/Movie';
import type { TvDetails } from '../../../../server/models/Tv';
-import { LanguageContext } from '../../../context/LanguageContext';
import { Permission, useUser } from '../../../hooks/useUser';
import globalMessages from '../../../i18n/globalMessages';
import Badge from '../../Common/Badge';
import Button from '../../Common/Button';
-import Table from '../../Common/Table';
+import CachedImage from '../../Common/CachedImage';
+import ConfirmButton from '../../Common/ConfirmButton';
import RequestModal from '../../RequestModal';
import StatusBadge from '../../StatusBadge';
const messages = defineMessages({
- seasons: 'Seasons',
- notavailable: 'N/A',
+ seasons: '{seasonCount, plural, one {Season} other {Seasons}}',
failedretry: 'Something went wrong while retrying the request.',
+ requested: 'Requested',
+ requesteddate: 'Requested',
+ modified: 'Modified',
+ modifieduserdate: '{date} by {user}',
+ mediaerror: 'The associated title for this request is no longer available.',
+ editrequest: 'Edit Request',
+ deleterequest: 'Delete Request',
+ cancelRequest: 'Cancel Request',
});
const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => {
return (movie as MovieDetails).title !== undefined;
};
+interface RequestItemErroProps {
+ mediaId?: number;
+ revalidateList: () => void;
+}
+
+const RequestItemError: React.FC
= ({
+ mediaId,
+ revalidateList,
+}) => {
+ const intl = useIntl();
+ const { hasPermission } = useUser();
+
+ const deleteRequest = async () => {
+ await axios.delete(`/api/v1/media/${mediaId}`);
+ revalidateList();
+ };
+
+ return (
+
+
+ {intl.formatMessage(messages.mediaerror)}
+
+ {hasPermission(Permission.MANAGE_REQUESTS) && mediaId && (
+
+
+
+ )}
+
+ );
+};
+
interface RequestItemProps {
request: MediaRequest;
revalidateList: () => void;
@@ -50,23 +97,21 @@ const RequestItem: React.FC = ({
});
const { addToast } = useToasts();
const intl = useIntl();
- const { hasPermission } = useUser();
+ const { user, hasPermission } = useUser();
const [showEditModal, setShowEditModal] = useState(false);
- const { locale } = useContext(LanguageContext);
const url =
request.type === 'movie'
? `/api/v1/movie/${request.media.tmdbId}`
: `/api/v1/tv/${request.media.tmdbId}`;
const { data: title, error } = useSWR(
- inView ? `${url}?language=${locale}` : null
+ inView ? url : null
+ );
+ const { data: requestData, mutate: revalidate } = useSWR(
+ `/api/v1/request/${request.id}`,
+ {
+ initialData: request,
+ }
);
- const {
- data: requestData,
- revalidate,
- mutate,
- } = useSWR(`/api/v1/request/${request.id}`, {
- initialData: request,
- });
const [isRetrying, setRetrying] = useState(false);
@@ -89,7 +134,7 @@ const RequestItem: React.FC = ({
try {
const result = await axios.post(`/api/v1/request/${request.id}/retry`);
- mutate(result.data);
+ revalidate(result.data);
} catch (e) {
addToast(intl.formatMessage(messages.failedretry), {
autoDismiss: true,
@@ -102,22 +147,24 @@ const RequestItem: React.FC = ({
if (!title && !error) {
return (
-
- |
-
+
);
}
if (!title || !requestData) {
return (
-
- |
-
+
);
}
return (
-
+ <>
= ({
setShowEditModal(false);
}}
/>
-
-
-
-
-
-
-
-
+
+ {title.backdropPath && (
+
+ )}
+
+
= ({
: `/tv/${requestData.media.tmdbId}`
}
>
-
- {isMovie(title) ? title.title : title.name}
-
-
-
-
-
+
-
- {requestData.requestedBy.displayName}
-
- {requestData.seasons.length > 0 && (
-
-
- {intl.formatMessage(messages.seasons)}
-
- {requestData.seasons.map((season) => (
-
- {season.seasonNumber}
+
+
+ {(isMovie(title)
+ ? title.releaseDate
+ : title.firstAirDate
+ )?.slice(0, 4)}
+
+
+
+ {isMovie(title) ? title.title : title.name}
+
+
+ {!isMovie(title) && request.seasons.length > 0 && (
+
+
+ {intl.formatMessage(messages.seasons, {
+ seasonCount:
+ title.seasons.filter(
+ (season) => season.seasonNumber !== 0
+ ).length === request.seasons.length
+ ? 0
+ : request.seasons.length,
+ })}
- ))}
+ {title.seasons.filter((season) => season.seasonNumber !== 0)
+ .length === request.seasons.length ? (
+
+ {intl.formatMessage(globalMessages.all)}
+
+ ) : (
+
+ {request.seasons.map((season) => (
+
+ {season.seasonNumber}
+
+ ))}
+
+ )}
+
+ )}
+
+
+
+
+
+ {intl.formatMessage(globalMessages.status)}
+
+ {requestData.status === MediaRequestStatus.DECLINED ? (
+
+ {intl.formatMessage(globalMessages.declined)}
+
+ ) : requestData.media[
+ requestData.is4k ? 'status4k' : 'status'
+ ] === MediaStatus.UNKNOWN ? (
+
+ {intl.formatMessage(globalMessages.failed)}
+
+ ) : (
+ 0
+ }
+ is4k={requestData.is4k}
+ tmdbId={requestData.media.tmdbId}
+ mediaType={requestData.type}
+ plexUrl={
+ requestData.media[
+ requestData.is4k ? 'mediaUrl4k' : 'mediaUrl'
+ ]
+ }
+ />
+ )}
+
+
+ {hasPermission(
+ [Permission.MANAGE_REQUESTS, Permission.REQUEST_VIEW],
+ { type: 'or' }
+ ) ? (
+ <>
+
+ {intl.formatMessage(messages.requested)}
+
+
+ {intl.formatMessage(messages.modifieduserdate, {
+ date: (
+
+ ),
+ user: (
+
+
+
+
+ {requestData.requestedBy.displayName}
+
+
+
+ ),
+ })}
+
+ >
+ ) : (
+ <>
+
+ {intl.formatMessage(messages.requesteddate)}
+
+
+
+
+ >
+ )}
+
+ {requestData.modifiedBy && (
+
)}
-
-
- {requestData.media[requestData.is4k ? 'status4k' : 'status'] ===
- MediaStatus.UNKNOWN ||
- requestData.status === MediaRequestStatus.DECLINED ? (
-
- {requestData.status === MediaRequestStatus.DECLINED
- ? intl.formatMessage(globalMessages.declined)
- : intl.formatMessage(globalMessages.failed)}
-
- ) : (
- 0
- }
- is4k={requestData.is4k}
- />
- )}
-
-
-
-
-
-
-
-
-
-
- {requestData.modifiedBy ? (
-
-
-

+ {requestData.media[requestData.is4k ? 'status4k' : 'status'] ===
+ MediaStatus.UNKNOWN &&
+ requestData.status !== MediaRequestStatus.DECLINED &&
+ hasPermission(Permission.MANAGE_REQUESTS) && (
+
+ )}
+ {requestData.status !== MediaRequestStatus.PENDING &&
+ hasPermission(Permission.MANAGE_REQUESTS) && (
+
deleteRequest()}
+ confirmText={intl.formatMessage(globalMessages.areyousure)}
+ className="w-full"
+ >
+
+ {intl.formatMessage(messages.deleterequest)}
+
+ )}
+ {requestData.status === MediaRequestStatus.PENDING &&
+ hasPermission(Permission.MANAGE_REQUESTS) && (
+
+
+
+
+
+
-
- ) : (
-
N/A
- )}
-
-
-
- {requestData.media[requestData.is4k ? 'status4k' : 'status'] ===
- MediaStatus.UNKNOWN &&
- requestData.status !== MediaRequestStatus.DECLINED &&
- hasPermission(Permission.MANAGE_REQUESTS) && (
-
- )}
- {requestData.status !== MediaRequestStatus.PENDING &&
- hasPermission(Permission.MANAGE_REQUESTS) && (
-
- )}
- {requestData.status === MediaRequestStatus.PENDING &&
- hasPermission(Permission.MANAGE_REQUESTS) && (
- <>
-
-
-
-
-
-
-
+ )}
+ {requestData.status === MediaRequestStatus.PENDING &&
+ (hasPermission(Permission.MANAGE_REQUESTS) ||
+ (requestData.requestedBy.id === user?.id &&
+ (requestData.type === 'tv' ||
+ hasPermission(Permission.REQUEST_ADVANCED)))) && (
+
- >
- )}
-
-
+ )}
+ {requestData.status === MediaRequestStatus.PENDING &&
+ !hasPermission(Permission.MANAGE_REQUESTS) &&
+ requestData.requestedBy.id === user?.id && (
+ deleteRequest()}
+ confirmText={intl.formatMessage(globalMessages.areyousure)}
+ className="w-full"
+ >
+
+ {intl.formatMessage(messages.cancelRequest)}
+
+ )}
+
+