feat: add request media (#1811)

This commit is contained in:
Manuel
2024-12-31 11:05:30 +01:00
committed by GitHub
parent e53b06e8c2
commit 91e1cef611
14 changed files with 436 additions and 16 deletions

View File

@@ -1,8 +1,12 @@
import { Group, Image, Kbd, Stack, Text } from "@mantine/core";
import { IconSearch } from "@tabler/icons-react";
import { IconDownload, IconSearch } from "@tabler/icons-react";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import type { IntegrationKind } from "@homarr/definitions";
import { getIntegrationKindsByCategory, getIntegrationName } from "@homarr/definitions";
import { useModalAction } from "@homarr/modals";
import { RequestMediaModal } from "@homarr/modals-collection";
import { useScopedI18n } from "@homarr/translation/client";
import { createChildrenOptions } from "../../lib/children";
@@ -11,6 +15,100 @@ import { interaction } from "../../lib/interaction";
type SearchEngine = RouterOutputs["searchEngine"]["search"][number];
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
type MediaRequestChildrenProps = {
result: {
id: number;
image?: string;
name: string;
link: string;
text?: string;
type: "tv" | "movie";
inLibrary: boolean;
};
integration: {
kind: IntegrationKind;
url: string;
id: string;
};
};
const mediaRequestsChildrenOptions = createChildrenOptions<MediaRequestChildrenProps>({
useActions() {
const { openModal } = useModalAction(RequestMediaModal);
return [
{
key: "request",
hide: (option) => option.result.inLibrary,
Component(option) {
const t = useScopedI18n("search.mode.media");
return (
<Group mx="md" my="sm" wrap="nowrap">
<IconDownload stroke={1.5} />
{option.result.type === "tv" && <Text>{t("requestSeries")}</Text>}
{option.result.type === "movie" && <Text>{t("requestMovie")}</Text>}
</Group>
);
},
useInteraction: interaction.javaScript((option) => ({
onSelect() {
openModal(
{
integrationId: option.integration.id,
mediaId: option.result.id,
mediaType: option.result.type,
},
{
title(t) {
return t("search.engine.media.request.modal.title", { name: option.result.name });
},
},
);
},
})),
},
{
key: "open",
Component({ integration }) {
const tChildren = useScopedI18n("search.mode.media");
return (
<Group mx="md" my="sm" wrap="nowrap">
<IconSearch stroke={1.5} />
<Text>{tChildren("openIn", { kind: getIntegrationName(integration.kind) })}</Text>
</Group>
);
},
useInteraction({ result }) {
return {
type: "link",
href: result.link,
newTab: true,
};
},
},
];
},
DetailComponent({ options }) {
return (
<Group mx="md" my="sm" wrap="nowrap">
{options.result.image ? (
<Image src={options.result.image} w={35} h={50} fit="cover" radius={"md"} />
) : (
<IconSearch stroke={1.5} size={35} />
)}
<Stack gap={2}>
<Text>{options.result.name}</Text>
{options.result.text && (
<Text c="dimmed" size="sm" lineClamp={2}>
{options.result.text}
</Text>
)}
</Stack>
</Group>
);
},
});
export const searchEnginesChildrenOptions = createChildrenOptions<SearchEngine>({
useActions: (searchEngine, query) => {
const { data } = clientApi.integration.searchInIntegration.useQuery(
@@ -64,10 +162,48 @@ export const searchEnginesChildrenOptions = createChildrenOptions<SearchEngine>(
</Group>
);
},
useInteraction: interaction.link(() => ({
href: searchResult.link,
newTab: true,
})),
useInteraction(searchEngine) {
if (searchEngine.type !== "fromIntegration") {
throw new Error("Invalid search engine type");
}
if (!searchEngine.integration) {
throw new Error("Invalid search engine integration");
}
if (
getIntegrationKindsByCategory("mediaRequest").some(
(categoryKind) => categoryKind === searchEngine.integration?.kind,
) &&
"type" in searchResult
) {
const type = searchResult.type;
if (type === "person") {
return {
type: "link",
href: searchResult.link,
newTab: true,
};
}
return {
type: "children",
...mediaRequestsChildrenOptions({
result: {
...searchResult,
type,
},
integration: searchEngine.integration,
}),
};
}
return {
type: "link",
href: searchResult.link,
newTab: true,
};
},
}));
},
DetailComponent({ options }) {