import { Fragment, useState } from "react";
import { toast } from "react-hot-toast";
import { useMutation, useQuery } from "react-query";
import Select, { components, ControlProps, InputProps, MenuProps, OptionProps } from "react-select";
import type { FieldProps } from "formik";
import { Field, Form, Formik, FormikValues } from "formik";
import { XMarkIcon } from "@heroicons/react/24/solid";
import { Dialog, Transition } from "@headlessui/react";
import { sleep, slugify } from "../../utils";
import { queryClient } from "../../App";
import DEBUG from "../../components/debug";
import { APIClient } from "../../api/APIClient";
import { PasswordFieldWide, SwitchGroupWide, TextFieldWide } from "../../components/inputs";
import { SlideOver } from "../../components/panels";
import Toast from "../../components/notifications/Toast";
const Input = (props: InputProps) => {
return (
);
};
const Control = (props: ControlProps) => {
return (
);
};
const Menu = (props: MenuProps) => {
return (
);
};
const Option = (props: OptionProps) => {
return (
);
};
const IrcSettingFields = (ind: IndexerDefinition, indexer: string) => {
if (indexer !== "") {
return (
{ind && ind.irc && ind.irc.settings && (
IRC
Networks, channels and invite commands are configured automatically.
{ind.irc.settings.map((f: IndexerSetting, idx: number) => {
switch (f.type) {
case "text":
return
;
case "secret":
if (f.name === "invite_command") {
return
;
}
return
;
}
return null;
})}
)}
);
}
};
const FeedSettingFields = (ind: IndexerDefinition, indexer: string) => {
if (indexer !== "") {
return (
{ind && ind.torznab && ind.torznab.settings && (
{ind.torznab.settings.map((f: IndexerSetting, idx: number) => {
switch (f.type) {
case "text":
return
;
case "secret":
return
;
}
return null;
})}
)}
);
}
};
const RSSFeedSettingFields = (ind: IndexerDefinition, indexer: string) => {
if (indexer !== "") {
return (
{ind && ind.rss && ind.rss.settings && (
{ind.rss.settings.map((f: IndexerSetting, idx: number) => {
switch (f.type) {
case "text":
return
;
case "secret":
return
;
}
return null;
})}
)}
);
}
};
const SettingFields = (ind: IndexerDefinition, indexer: string) => {
if (indexer !== "") {
return (
{ind && ind.settings && ind.settings.map((f, idx: number) => {
switch (f.type) {
case "text":
return (
);
case "secret":
return (
);
}
return null;
})}
);
}
};
function slugIdentifier(name: string, prefix?: string) {
const l = name.toLowerCase();
if (prefix && prefix !== "") {
const r = l.replaceAll(prefix, "");
return slugify(`${prefix}-${r}`);
}
return slugify(l);
}
// interface initialValues {
// enabled: boolean;
// identifier: string;
// implementation: string;
// name: string;
// irc?: Record;
// feed?: Record;
// settings?: Record;
// }
type SelectValue = {
label: string;
value: string;
};
interface AddProps {
isOpen: boolean;
toggle: () => void;
}
export function IndexerAddForm({ isOpen, toggle }: AddProps) {
const [indexer, setIndexer] = useState({} as IndexerDefinition);
const { data } = useQuery(
"indexerDefinition",
() => APIClient.indexers.getSchema(),
{
enabled: isOpen,
refetchOnWindowFocus: false
}
);
const mutation = useMutation(
(indexer: Indexer) => APIClient.indexers.create(indexer), {
onSuccess: () => {
queryClient.invalidateQueries(["indexer"]);
toast.custom((t) => );
sleep(1500);
toggle();
},
onError: () => {
toast.custom((t) => );
}
});
const ircMutation = useMutation(
(network: IrcNetworkCreate) => APIClient.irc.createNetwork(network)
);
const feedMutation = useMutation(
(feed: FeedCreate) => APIClient.feeds.create(feed)
);
const onSubmit = (formData: FormikValues) => {
const ind = data && data.find(i => i.identifier === formData.identifier);
if (!ind)
return;
if (formData.implementation === "torznab") {
// create slug for indexer identifier as "torznab-indexer_name"
const name = slugIdentifier(formData.name, "torznab");
const createFeed: FeedCreate = {
name: formData.name,
enabled: false,
type: "TORZNAB",
url: formData.feed.url,
api_key: formData.feed.api_key,
interval: 30,
timeout: 60,
indexer: name,
indexer_id: 0
};
mutation.mutate(formData as Indexer, {
onSuccess: (indexer) => {
// @eslint-ignore
createFeed.indexer_id = indexer.id;
feedMutation.mutate(createFeed);
}
});
return;
}
if (formData.implementation === "rss") {
// create slug for indexer identifier as "torznab-indexer_name"
const name = slugIdentifier(formData.name, "rss");
const createFeed: FeedCreate = {
name: formData.name,
enabled: false,
type: "RSS",
url: formData.feed.url,
interval: 30,
timeout: 60,
indexer: name,
indexer_id: 0
};
mutation.mutate(formData as Indexer, {
onSuccess: (indexer) => {
// @eslint-ignore
createFeed.indexer_id = indexer.id;
feedMutation.mutate(createFeed);
}
});
return;
}
if (formData.implementation === "irc") {
const channels: IrcChannel[] = [];
if (ind.irc?.channels.length) {
ind.irc.channels.forEach(element => {
channels.push({
id: 0,
enabled: true,
name: element,
password: "",
detached: false,
monitoring: false
});
});
}
const network: IrcNetworkCreate = {
name: ind.irc.network,
pass: "",
enabled: false,
connected: false,
server: ind.irc.server,
port: ind.irc.port,
tls: ind.irc.tls,
nickserv: formData.irc.nickserv,
invite_command: formData.irc.invite_command,
channels: channels
};
mutation.mutate(formData as Indexer, {
onSuccess: () => {
ircMutation.mutate(network);
}
});
}
};
return (
);
}
interface UpdateProps {
isOpen: boolean;
toggle: () => void;
indexer: IndexerDefinition;
}
export function IndexerUpdateForm({ isOpen, toggle, indexer }: UpdateProps) {
const mutation = useMutation((indexer: Indexer) => APIClient.indexers.update(indexer), {
onSuccess: () => {
queryClient.invalidateQueries(["indexer"]);
toast.custom((t) => );
sleep(1500);
toggle();
}
});
const deleteMutation = useMutation((id: number) => APIClient.indexers.delete(id), {
onSuccess: () => {
queryClient.invalidateQueries(["indexer"]);
toast.custom((t) => );
}
});
const onSubmit = (data: unknown) => {
// TODO clear data depending on type
mutation.mutate(data as Indexer);
};
const deleteAction = () => {
deleteMutation.mutate(indexer.id ?? 0);
};
const renderSettingFields = (settings: IndexerSetting[]) => {
if (settings === undefined) {
return null;
}
return (
{settings.map((f: IndexerSetting, idx: number) => {
switch (f.type) {
case "text":
return (
);
case "secret":
return (
);
}
return null;
})}
);
};
const initialValues = {
id: indexer.id,
name: indexer.name,
enabled: indexer.enabled,
identifier: indexer.identifier,
implementation: indexer.implementation,
settings: indexer.settings?.reduce(
(o: Record, obj: IndexerSetting) => ({
...o,
[obj.name]: obj.value
} as Record),
{} as Record
)
};
return (
{() => (
{renderSettingFields(indexer.settings)}
)}
);
}