mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
fix: better ordering of RequestButton options & properly handle failed requests (#2944)
* fix: better ordering of RequestButton options & properly handle failed requests * fix: appease prettier
This commit is contained in:
@@ -4430,7 +4430,16 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
nullable: true
|
nullable: true
|
||||||
enum: [all, approved, available, pending, processing, unavailable]
|
enum:
|
||||||
|
[
|
||||||
|
all,
|
||||||
|
approved,
|
||||||
|
available,
|
||||||
|
pending,
|
||||||
|
processing,
|
||||||
|
unavailable,
|
||||||
|
failed,
|
||||||
|
]
|
||||||
- in: query
|
- in: query
|
||||||
name: sort
|
name: sort
|
||||||
schema:
|
schema:
|
||||||
|
@@ -2,6 +2,7 @@ export enum MediaRequestStatus {
|
|||||||
PENDING = 1,
|
PENDING = 1,
|
||||||
APPROVED,
|
APPROVED,
|
||||||
DECLINED,
|
DECLINED,
|
||||||
|
FAILED,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum MediaType {
|
export enum MediaType {
|
||||||
|
@@ -451,10 +451,13 @@ export class MediaRequest {
|
|||||||
await mediaRepository.save(media);
|
await mediaRepository.save(media);
|
||||||
})
|
})
|
||||||
.catch(async () => {
|
.catch(async () => {
|
||||||
media[this.is4k ? 'status4k' : 'status'] = MediaStatus.UNKNOWN;
|
const requestRepository = getRepository(MediaRequest);
|
||||||
await mediaRepository.save(media);
|
|
||||||
|
this.status = MediaRequestStatus.FAILED;
|
||||||
|
requestRepository.save(this);
|
||||||
|
|
||||||
logger.warn(
|
logger.warn(
|
||||||
'Something went wrong sending movie request to Radarr, marking status as UNKNOWN',
|
'Something went wrong sending movie request to Radarr, marking status as FAILED',
|
||||||
{
|
{
|
||||||
label: 'Media Request',
|
label: 'Media Request',
|
||||||
requestId: this.id,
|
requestId: this.id,
|
||||||
@@ -684,10 +687,13 @@ export class MediaRequest {
|
|||||||
await mediaRepository.save(media);
|
await mediaRepository.save(media);
|
||||||
})
|
})
|
||||||
.catch(async () => {
|
.catch(async () => {
|
||||||
media[this.is4k ? 'status4k' : 'status'] = MediaStatus.UNKNOWN;
|
const requestRepository = getRepository(MediaRequest);
|
||||||
await mediaRepository.save(media);
|
|
||||||
|
this.status = MediaRequestStatus.FAILED;
|
||||||
|
requestRepository.save(this);
|
||||||
|
|
||||||
logger.warn(
|
logger.warn(
|
||||||
'Something went wrong sending series request to Sonarr, marking status as UNKNOWN',
|
'Something went wrong sending series request to Sonarr, marking status as FAILED',
|
||||||
{
|
{
|
||||||
label: 'Media Request',
|
label: 'Media Request',
|
||||||
requestId: this.id,
|
requestId: this.id,
|
||||||
|
@@ -40,11 +40,15 @@ requestRoutes.get<Record<string, unknown>, RequestResultsResponse>(
|
|||||||
MediaRequestStatus.APPROVED,
|
MediaRequestStatus.APPROVED,
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
|
case 'failed':
|
||||||
|
statusFilter = [MediaRequestStatus.FAILED];
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
statusFilter = [
|
statusFilter = [
|
||||||
MediaRequestStatus.PENDING,
|
MediaRequestStatus.PENDING,
|
||||||
MediaRequestStatus.APPROVED,
|
MediaRequestStatus.APPROVED,
|
||||||
MediaRequestStatus.DECLINED,
|
MediaRequestStatus.DECLINED,
|
||||||
|
MediaRequestStatus.FAILED,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -179,6 +179,11 @@ const RequestBlock = ({ request, onUpdate }: RequestBlockProps) => {
|
|||||||
{intl.formatMessage(globalMessages.pending)}
|
{intl.formatMessage(globalMessages.pending)}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
|
{request.status === MediaRequestStatus.FAILED && (
|
||||||
|
<Badge badgeType="danger">
|
||||||
|
{intl.formatMessage(globalMessages.failed)}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 flex items-center text-sm leading-5 sm:mt-0">
|
<div className="mt-2 flex items-center text-sm leading-5 sm:mt-0">
|
||||||
|
@@ -77,13 +77,13 @@ const RequestButton = ({
|
|||||||
(request) => request.status === MediaRequestStatus.PENDING && request.is4k
|
(request) => request.status === MediaRequestStatus.PENDING && request.is4k
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Current user's pending request, or the first pending request
|
||||||
const activeRequest = useMemo(() => {
|
const activeRequest = useMemo(() => {
|
||||||
return activeRequests && activeRequests.length > 0
|
return activeRequests && activeRequests.length > 0
|
||||||
? activeRequests.find((request) => request.requestedBy.id === user?.id) ??
|
? activeRequests.find((request) => request.requestedBy.id === user?.id) ??
|
||||||
activeRequests[0]
|
activeRequests[0]
|
||||||
: undefined;
|
: undefined;
|
||||||
}, [activeRequests, user]);
|
}, [activeRequests, user]);
|
||||||
|
|
||||||
const active4kRequest = useMemo(() => {
|
const active4kRequest = useMemo(() => {
|
||||||
return active4kRequests && active4kRequests.length > 0
|
return active4kRequests && active4kRequests.length > 0
|
||||||
? active4kRequests.find(
|
? active4kRequests.find(
|
||||||
@@ -121,54 +121,9 @@ const RequestButton = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const buttons: ButtonOption[] = [];
|
const buttons: ButtonOption[] = [];
|
||||||
if (
|
|
||||||
(!media || media.status === MediaStatus.UNKNOWN) &&
|
|
||||||
hasPermission(
|
|
||||||
[
|
|
||||||
Permission.REQUEST,
|
|
||||||
mediaType === 'movie'
|
|
||||||
? Permission.REQUEST_MOVIE
|
|
||||||
: Permission.REQUEST_TV,
|
|
||||||
],
|
|
||||||
{ type: 'or' }
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
buttons.push({
|
|
||||||
id: 'request',
|
|
||||||
text: intl.formatMessage(globalMessages.request),
|
|
||||||
action: () => {
|
|
||||||
setEditRequest(false);
|
|
||||||
setShowRequestModal(true);
|
|
||||||
},
|
|
||||||
svg: <DownloadIcon />,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
(!media || media.status4k === MediaStatus.UNKNOWN) &&
|
|
||||||
hasPermission(
|
|
||||||
[
|
|
||||||
Permission.REQUEST_4K,
|
|
||||||
mediaType === 'movie'
|
|
||||||
? Permission.REQUEST_4K_MOVIE
|
|
||||||
: Permission.REQUEST_4K_TV,
|
|
||||||
],
|
|
||||||
{ type: 'or' }
|
|
||||||
) &&
|
|
||||||
((settings.currentSettings.movie4kEnabled && mediaType === 'movie') ||
|
|
||||||
(settings.currentSettings.series4kEnabled && mediaType === 'tv'))
|
|
||||||
) {
|
|
||||||
buttons.push({
|
|
||||||
id: 'request4k',
|
|
||||||
text: intl.formatMessage(globalMessages.request4k),
|
|
||||||
action: () => {
|
|
||||||
setEditRequest(false);
|
|
||||||
setShowRequest4kModal(true);
|
|
||||||
},
|
|
||||||
svg: <DownloadIcon />,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// If there are pending requests, show request management options first
|
||||||
|
if (activeRequest || active4kRequest) {
|
||||||
if (
|
if (
|
||||||
activeRequest &&
|
activeRequest &&
|
||||||
(activeRequest.requestedBy.id === user?.id ||
|
(activeRequest.requestedBy.id === user?.id ||
|
||||||
@@ -186,23 +141,6 @@ const RequestButton = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
active4kRequest &&
|
|
||||||
(active4kRequest.requestedBy.id === user?.id ||
|
|
||||||
(active4kRequests?.length === 1 &&
|
|
||||||
hasPermission(Permission.MANAGE_REQUESTS)))
|
|
||||||
) {
|
|
||||||
buttons.push({
|
|
||||||
id: 'active-4k-request',
|
|
||||||
text: intl.formatMessage(messages.viewrequest4k),
|
|
||||||
action: () => {
|
|
||||||
setEditRequest(true);
|
|
||||||
setShowRequest4kModal(true);
|
|
||||||
},
|
|
||||||
svg: <InformationCircleIcon />,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
activeRequest &&
|
activeRequest &&
|
||||||
hasPermission(Permission.MANAGE_REQUESTS) &&
|
hasPermission(Permission.MANAGE_REQUESTS) &&
|
||||||
@@ -226,9 +164,7 @@ const RequestButton = ({
|
|||||||
svg: <XIcon />,
|
svg: <XIcon />,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
} else if (
|
||||||
|
|
||||||
if (
|
|
||||||
activeRequests &&
|
activeRequests &&
|
||||||
activeRequests.length > 0 &&
|
activeRequests.length > 0 &&
|
||||||
hasPermission(Permission.MANAGE_REQUESTS) &&
|
hasPermission(Permission.MANAGE_REQUESTS) &&
|
||||||
@@ -258,6 +194,23 @@ const RequestButton = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
active4kRequest &&
|
||||||
|
(active4kRequest.requestedBy.id === user?.id ||
|
||||||
|
(active4kRequests?.length === 1 &&
|
||||||
|
hasPermission(Permission.MANAGE_REQUESTS)))
|
||||||
|
) {
|
||||||
|
buttons.push({
|
||||||
|
id: 'active-4k-request',
|
||||||
|
text: intl.formatMessage(messages.viewrequest4k),
|
||||||
|
action: () => {
|
||||||
|
setEditRequest(true);
|
||||||
|
setShowRequest4kModal(true);
|
||||||
|
},
|
||||||
|
svg: <InformationCircleIcon />,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
active4kRequest &&
|
active4kRequest &&
|
||||||
hasPermission(Permission.MANAGE_REQUESTS) &&
|
hasPermission(Permission.MANAGE_REQUESTS) &&
|
||||||
@@ -281,9 +234,7 @@ const RequestButton = ({
|
|||||||
svg: <XIcon />,
|
svg: <XIcon />,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
} else if (
|
||||||
|
|
||||||
if (
|
|
||||||
active4kRequests &&
|
active4kRequests &&
|
||||||
active4kRequests.length > 0 &&
|
active4kRequests.length > 0 &&
|
||||||
hasPermission(Permission.MANAGE_REQUESTS) &&
|
hasPermission(Permission.MANAGE_REQUESTS) &&
|
||||||
@@ -312,8 +263,31 @@ const RequestButton = ({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard request button
|
||||||
if (
|
if (
|
||||||
|
(!media || media.status === MediaStatus.UNKNOWN) &&
|
||||||
|
hasPermission(
|
||||||
|
[
|
||||||
|
Permission.REQUEST,
|
||||||
|
mediaType === 'movie'
|
||||||
|
? Permission.REQUEST_MOVIE
|
||||||
|
: Permission.REQUEST_TV,
|
||||||
|
],
|
||||||
|
{ type: 'or' }
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
buttons.push({
|
||||||
|
id: 'request',
|
||||||
|
text: intl.formatMessage(globalMessages.request),
|
||||||
|
action: () => {
|
||||||
|
setEditRequest(false);
|
||||||
|
setShowRequestModal(true);
|
||||||
|
},
|
||||||
|
svg: <DownloadIcon />,
|
||||||
|
});
|
||||||
|
} else if (
|
||||||
mediaType === 'tv' &&
|
mediaType === 'tv' &&
|
||||||
(!activeRequest || activeRequest.requestedBy.id !== user?.id) &&
|
(!activeRequest || activeRequest.requestedBy.id !== user?.id) &&
|
||||||
hasPermission([Permission.REQUEST, Permission.REQUEST_TV], {
|
hasPermission([Permission.REQUEST, Permission.REQUEST_TV], {
|
||||||
@@ -335,7 +309,31 @@ const RequestButton = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4K request button
|
||||||
if (
|
if (
|
||||||
|
(!media || media.status4k === MediaStatus.UNKNOWN) &&
|
||||||
|
hasPermission(
|
||||||
|
[
|
||||||
|
Permission.REQUEST_4K,
|
||||||
|
mediaType === 'movie'
|
||||||
|
? Permission.REQUEST_4K_MOVIE
|
||||||
|
: Permission.REQUEST_4K_TV,
|
||||||
|
],
|
||||||
|
{ type: 'or' }
|
||||||
|
) &&
|
||||||
|
((settings.currentSettings.movie4kEnabled && mediaType === 'movie') ||
|
||||||
|
(settings.currentSettings.series4kEnabled && mediaType === 'tv'))
|
||||||
|
) {
|
||||||
|
buttons.push({
|
||||||
|
id: 'request4k',
|
||||||
|
text: intl.formatMessage(globalMessages.request4k),
|
||||||
|
action: () => {
|
||||||
|
setEditRequest(false);
|
||||||
|
setShowRequest4kModal(true);
|
||||||
|
},
|
||||||
|
svg: <DownloadIcon />,
|
||||||
|
});
|
||||||
|
} else if (
|
||||||
mediaType === 'tv' &&
|
mediaType === 'tv' &&
|
||||||
(!active4kRequest || active4kRequest.requestedBy.id !== user?.id) &&
|
(!active4kRequest || active4kRequest.requestedBy.id !== user?.id) &&
|
||||||
hasPermission([Permission.REQUEST_4K, Permission.REQUEST_4K_TV], {
|
hasPermission([Permission.REQUEST_4K, Permission.REQUEST_4K_TV], {
|
||||||
|
@@ -12,10 +12,7 @@ import { useInView } from 'react-intersection-observer';
|
|||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
import useSWR, { mutate } from 'swr';
|
import useSWR, { mutate } from 'swr';
|
||||||
import {
|
import { MediaRequestStatus } from '../../../server/constants/media';
|
||||||
MediaRequestStatus,
|
|
||||||
MediaStatus,
|
|
||||||
} from '../../../server/constants/media';
|
|
||||||
import type { MediaRequest } from '../../../server/entity/MediaRequest';
|
import type { MediaRequest } from '../../../server/entity/MediaRequest';
|
||||||
import type { MovieDetails } from '../../../server/models/Movie';
|
import type { MovieDetails } from '../../../server/models/Movie';
|
||||||
import type { TvDetails } from '../../../server/models/Tv';
|
import type { TvDetails } from '../../../server/models/Tv';
|
||||||
@@ -275,8 +272,7 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => {
|
|||||||
<Badge badgeType="danger">
|
<Badge badgeType="danger">
|
||||||
{intl.formatMessage(globalMessages.declined)}
|
{intl.formatMessage(globalMessages.declined)}
|
||||||
</Badge>
|
</Badge>
|
||||||
) : requestData.media[requestData.is4k ? 'status4k' : 'status'] ===
|
) : requestData.status === MediaRequestStatus.FAILED ? (
|
||||||
MediaStatus.UNKNOWN ? (
|
|
||||||
<Badge
|
<Badge
|
||||||
badgeType="danger"
|
badgeType="danger"
|
||||||
href={`/${requestData.type}/${requestData.media.tmdbId}?manage=1`}
|
href={`/${requestData.type}/${requestData.media.tmdbId}?manage=1`}
|
||||||
@@ -305,9 +301,7 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-1 items-end space-x-2">
|
<div className="flex flex-1 items-end space-x-2">
|
||||||
{requestData.media[requestData.is4k ? 'status4k' : 'status'] ===
|
{requestData.status === MediaRequestStatus.FAILED &&
|
||||||
MediaStatus.UNKNOWN &&
|
|
||||||
requestData.status !== MediaRequestStatus.DECLINED &&
|
|
||||||
hasPermission(Permission.MANAGE_REQUESTS) && (
|
hasPermission(Permission.MANAGE_REQUESTS) && (
|
||||||
<Button
|
<Button
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
|
@@ -12,10 +12,7 @@ import { useInView } from 'react-intersection-observer';
|
|||||||
import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';
|
import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import {
|
import { MediaRequestStatus } from '../../../../server/constants/media';
|
||||||
MediaRequestStatus,
|
|
||||||
MediaStatus,
|
|
||||||
} from '../../../../server/constants/media';
|
|
||||||
import type { MediaRequest } from '../../../../server/entity/MediaRequest';
|
import type { MediaRequest } from '../../../../server/entity/MediaRequest';
|
||||||
import type { MovieDetails } from '../../../../server/models/Movie';
|
import type { MovieDetails } from '../../../../server/models/Movie';
|
||||||
import type { TvDetails } from '../../../../server/models/Tv';
|
import type { TvDetails } from '../../../../server/models/Tv';
|
||||||
@@ -273,9 +270,7 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => {
|
|||||||
<Badge badgeType="danger">
|
<Badge badgeType="danger">
|
||||||
{intl.formatMessage(globalMessages.declined)}
|
{intl.formatMessage(globalMessages.declined)}
|
||||||
</Badge>
|
</Badge>
|
||||||
) : requestData.media[
|
) : requestData.status === MediaRequestStatus.FAILED ? (
|
||||||
requestData.is4k ? 'status4k' : 'status'
|
|
||||||
] === MediaStatus.UNKNOWN ? (
|
|
||||||
<Badge
|
<Badge
|
||||||
badgeType="danger"
|
badgeType="danger"
|
||||||
href={`/${requestData.type}/${requestData.media.tmdbId}?manage=1`}
|
href={`/${requestData.type}/${requestData.media.tmdbId}?manage=1`}
|
||||||
@@ -402,9 +397,7 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="z-10 mt-4 flex w-full flex-col justify-center space-y-2 pl-4 pr-4 xl:mt-0 xl:w-96 xl:items-end xl:pl-0">
|
<div className="z-10 mt-4 flex w-full flex-col justify-center space-y-2 pl-4 pr-4 xl:mt-0 xl:w-96 xl:items-end xl:pl-0">
|
||||||
{requestData.media[requestData.is4k ? 'status4k' : 'status'] ===
|
{requestData.status === MediaRequestStatus.FAILED &&
|
||||||
MediaStatus.UNKNOWN &&
|
|
||||||
requestData.status !== MediaRequestStatus.DECLINED &&
|
|
||||||
hasPermission(Permission.MANAGE_REQUESTS) && (
|
hasPermission(Permission.MANAGE_REQUESTS) && (
|
||||||
<Button
|
<Button
|
||||||
className="w-full"
|
className="w-full"
|
||||||
|
@@ -33,6 +33,7 @@ enum Filter {
|
|||||||
PROCESSING = 'processing',
|
PROCESSING = 'processing',
|
||||||
AVAILABLE = 'available',
|
AVAILABLE = 'available',
|
||||||
UNAVAILABLE = 'unavailable',
|
UNAVAILABLE = 'unavailable',
|
||||||
|
FAILED = 'failed',
|
||||||
}
|
}
|
||||||
|
|
||||||
type Sort = 'added' | 'modified';
|
type Sort = 'added' | 'modified';
|
||||||
@@ -158,6 +159,9 @@ const RequestList = () => {
|
|||||||
<option value="processing">
|
<option value="processing">
|
||||||
{intl.formatMessage(globalMessages.processing)}
|
{intl.formatMessage(globalMessages.processing)}
|
||||||
</option>
|
</option>
|
||||||
|
<option value="failed">
|
||||||
|
{intl.formatMessage(globalMessages.failed)}
|
||||||
|
</option>
|
||||||
<option value="available">
|
<option value="available">
|
||||||
{intl.formatMessage(globalMessages.available)}
|
{intl.formatMessage(globalMessages.available)}
|
||||||
</option>
|
</option>
|
||||||
|
Reference in New Issue
Block a user