mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-09 23:15:46 +01:00
🏗️ Migrate overseerr media request decisions to tRPC
This commit is contained in:
@@ -166,4 +166,49 @@ export const overseerrRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
decide: publicProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
configName: z.string(),
|
||||||
|
id: z.number(),
|
||||||
|
isApproved: z.boolean(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
const config = getConfig(input.configName);
|
||||||
|
Consola.log(
|
||||||
|
`Got a request to ${input.isApproved ? 'approve' : 'decline'} a request`,
|
||||||
|
input.id
|
||||||
|
);
|
||||||
|
const app = config.apps.find(
|
||||||
|
(app) => app.integration?.type === 'overseerr' || app.integration?.type === 'jellyseerr'
|
||||||
|
);
|
||||||
|
|
||||||
|
const apiKey = app?.integration?.properties.find((x) => x.field === 'apiKey')?.value;
|
||||||
|
if (!apiKey) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: 'BAD_REQUEST',
|
||||||
|
message: 'No app found',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const appUrl = new URL(app.url);
|
||||||
|
const action = input.isApproved ? 'approve' : 'decline';
|
||||||
|
return axios
|
||||||
|
.post(
|
||||||
|
`${appUrl.origin}/api/v1/request/${input.id}/${action}`,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'X-Api-Key': apiKey,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then((res) => res.data)
|
||||||
|
.catch((err) => {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: 'INTERNAL_SERVER_ERROR',
|
||||||
|
message: err.message,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,16 +10,16 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { useTranslation } from 'next-i18next';
|
|
||||||
import { IconCheck, IconGitPullRequest, IconThumbDown, IconThumbUp } from '@tabler/icons-react';
|
|
||||||
import { useMutation } from '@tanstack/react-query';
|
|
||||||
import axios from 'axios';
|
|
||||||
import { notifications } from '@mantine/notifications';
|
import { notifications } from '@mantine/notifications';
|
||||||
|
import { IconCheck, IconGitPullRequest, IconThumbDown, IconThumbUp } from '@tabler/icons-react';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
|
import { api } from '~/utils/api';
|
||||||
import { defineWidget } from '../helper';
|
import { defineWidget } from '../helper';
|
||||||
import { WidgetLoading } from '../loading';
|
import { WidgetLoading } from '../loading';
|
||||||
import { IWidget } from '../widgets';
|
import { IWidget } from '../widgets';
|
||||||
import { useMediaRequestQuery } from './media-request-query';
|
import { useMediaRequestQuery } from './media-request-query';
|
||||||
import { MediaRequest, MediaRequestStatus } from './media-request-types';
|
import { MediaRequest, MediaRequestStatus } from './media-request-types';
|
||||||
|
import { useConfigContext } from '~/config/provider';
|
||||||
|
|
||||||
const definition = defineWidget({
|
const definition = defineWidget({
|
||||||
id: 'media-requests-list',
|
id: 'media-requests-list',
|
||||||
@@ -45,21 +45,55 @@ interface MediaRequestListWidgetProps {
|
|||||||
widget: MediaRequestListWidget;
|
widget: MediaRequestListWidget;
|
||||||
}
|
}
|
||||||
|
|
||||||
function MediaRequestListTile({ widget }: MediaRequestListWidgetProps) {
|
type MediaRequestDecisionVariables = {
|
||||||
const { t } = useTranslation('modules/media-requests-list');
|
request: MediaRequest;
|
||||||
const { data, refetch, isLoading } = useMediaRequestQuery();
|
isApproved: boolean;
|
||||||
// Use mutation to approve or deny a pending request
|
};
|
||||||
const mutate = useMutation({
|
const useMediaRequestDecisionMutation = () => {
|
||||||
mutationFn: async (e: { request: MediaRequest; action: string }) => {
|
const { name: configName } = useConfigContext();
|
||||||
const data = await axios.put(`/api/modules/overseerr/${e.request.id}?action=${e.action}`);
|
const utils = api.useContext();
|
||||||
notifications.show({
|
const { mutateAsync } = api.overseerr.decide.useMutation({
|
||||||
title: t('requestUpdated'),
|
onSuccess() {
|
||||||
message: t('requestUpdatedMessage', { title: e.request.name }),
|
utils.mediaRequest.all.invalidate();
|
||||||
color: 'blue',
|
|
||||||
});
|
|
||||||
refetch();
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
return async (variables: MediaRequestDecisionVariables) => {
|
||||||
|
const action = variables.isApproved ? 'Approving' : 'Declining';
|
||||||
|
notifications.show({
|
||||||
|
id: `decide-${variables.request.id}`,
|
||||||
|
color: 'yellow',
|
||||||
|
title: `${action} request...`,
|
||||||
|
message: undefined,
|
||||||
|
loading: true,
|
||||||
|
});
|
||||||
|
await mutateAsync(
|
||||||
|
{
|
||||||
|
configName: configName!,
|
||||||
|
id: variables.request.id,
|
||||||
|
isApproved: variables.isApproved,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSuccess(_data, variables) {
|
||||||
|
const title = variables.isApproved ? 'Request was approved!' : 'Request was declined!';
|
||||||
|
notifications.update({
|
||||||
|
id: `decide-${variables.id}`,
|
||||||
|
color: 'teal',
|
||||||
|
title,
|
||||||
|
message: undefined,
|
||||||
|
icon: <IconCheck size="1rem" />,
|
||||||
|
autoClose: 2000,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
function MediaRequestListTile({ widget }: MediaRequestListWidgetProps) {
|
||||||
|
const { t } = useTranslation('modules/media-requests-list');
|
||||||
|
const { data, isLoading } = useMediaRequestQuery();
|
||||||
|
// Use mutation to approve or deny a pending request
|
||||||
|
const decideAsync = useMediaRequestDecisionMutation();
|
||||||
|
|
||||||
if (!data || isLoading) {
|
if (!data || isLoading) {
|
||||||
return <WidgetLoading />;
|
return <WidgetLoading />;
|
||||||
@@ -157,18 +191,10 @@ function MediaRequestListTile({ widget }: MediaRequestListWidgetProps) {
|
|||||||
loading: true,
|
loading: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
await mutate.mutateAsync({ request: item, action: 'approve' }).then(() =>
|
await decideAsync({
|
||||||
notifications.update({
|
request: item,
|
||||||
id: `approve ${item.id}`,
|
isApproved: true,
|
||||||
color: 'teal',
|
});
|
||||||
title: 'Request was approved!',
|
|
||||||
message: undefined,
|
|
||||||
icon: <IconCheck size="1rem" />,
|
|
||||||
autoClose: 2000,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
await refetch();
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<IconThumbUp />
|
<IconThumbUp />
|
||||||
@@ -179,8 +205,10 @@ function MediaRequestListTile({ widget }: MediaRequestListWidgetProps) {
|
|||||||
variant="light"
|
variant="light"
|
||||||
color="red"
|
color="red"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await mutate.mutateAsync({ request: item, action: 'decline' });
|
await decideAsync({
|
||||||
await refetch();
|
request: item,
|
||||||
|
isApproved: false,
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<IconThumbDown />
|
<IconThumbDown />
|
||||||
|
|||||||
Reference in New Issue
Block a user