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 } 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";
import { SelectFieldCreatable } from "../../components/inputs/select_wide";
const Input = (props: InputProps) => (
);
const Control = (props: ControlProps) => (
);
const Menu = (props: MenuProps) => (
);
const Option = (props: OptionProps) => (
);
// const isRequired = (message: string) => (value?: string | undefined) => (!!value ? undefined : message);
function validateField(s: IndexerSetting) {
return (value?: string | undefined) => {
if (s.required) {
if (s.default !== "") {
if (value && s.default === value) {
return "Default value, please edit";
}
}
return !!value ? undefined : "Required";
}
};
}
const IrcSettingFields = (ind: IndexerDefinition, indexer: string) => {
if (indexer !== "") {
return (
{ind && ind.irc && ind.irc.settings && (
IRC
Networks and channels are configured automatically in the background.
{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;
})}
);
}
};
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") {
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_id: 0
};
mutation.mutate(formData as Indexer, {
onSuccess: (indexer) => {
// @eslint-ignore
createFeed.indexer_id = indexer.id;
feedMutation.mutate(createFeed);
}
});
return;
} else if (formData.implementation === "rss") {
const createFeed: FeedCreate = {
name: formData.name,
enabled: false,
type: "RSS",
url: formData.feed.url,
interval: 30,
timeout: 60,
indexer_id: 0
};
mutation.mutate(formData as Indexer, {
onSuccess: (indexer) => {
// @eslint-ignore
createFeed.indexer_id = indexer.id;
feedMutation.mutate(createFeed);
}
});
return;
} else 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: formData.irc.pass || "",
enabled: false,
connected: false,
server: ind.irc.server,
port: ind.irc.port,
tls: ind.irc.tls,
nick: formData.irc.nick,
auth: {
mechanism: "NONE"
// account: formData.irc.auth.account,
// password: formData.irc.auth.password
},
invite_command: formData.irc.invite_command,
channels: channels
};
if (formData.irc.auth) {
if (formData.irc.auth.account !== "" && formData.irc.auth.password !== "") {
network.auth.mechanism = "SASL_PLAIN";
network.auth.account = formData.irc.auth.account;
network.auth.password = formData.irc.auth.password;
}
}
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) => );
toggle();
}
});
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,
base_url: indexer.base_url,
settings: indexer.settings?.reduce(
(o: Record, obj: IndexerSetting) => ({
...o,
[obj.name]: obj.value
} as Record),
{} as Record
)
};
return (
{() => (
{indexer.implementation == "irc" && (
({ value: u, label: u, key: u })) }
/>
)}
{renderSettingFields(indexer.settings)}
)}
);
}