import React, { FunctionComponent, useCallback, useMemo } from "react"; import { Accordion, Button, Checkbox, Select, Stack, Switch, Text, TextInput, } from "@mantine/core"; import { useForm } from "@mantine/form"; import { faTrash } from "@fortawesome/free-solid-svg-icons"; import { ColumnDef } from "@tanstack/react-table"; import { Action, Selector, SelectorOption } from "@/components"; import ChipInput from "@/components/inputs/ChipInput"; import SimpleTable from "@/components/tables/SimpleTable"; import { useModals, withModal } from "@/modules/modals"; import { useArrayAction, useSelectorOptions } from "@/utilities"; import { LOG } from "@/utilities/console"; import FormUtils from "@/utilities/form"; import styles from "./ProfileEditForm.module.scss"; export const anyCutoff = 65535; const defaultCutoffOptions: SelectorOption[] = [ { label: "Any", value: { id: anyCutoff, // eslint-disable-next-line camelcase audio_exclude: "False", forced: "False", hi: "False", language: "any", }, }, ]; const subtitlesTypeOptions: SelectorOption[] = [ { label: "Normal or hearing-impaired", value: "normal", }, { label: "Hearing-impaired required", value: "hi", }, { label: "Forced (foreign part only)", value: "forced", }, ]; interface Props { onComplete?: (profile: Language.Profile) => void; languages: readonly Language.Info[]; profile: Language.Profile; } const ProfileEditForm: FunctionComponent = ({ onComplete, languages, profile, }) => { const modals = useModals(); const form = useForm({ initialValues: profile, validate: { name: FormUtils.validation( (value) => value.length > 0, "Must have a name", ), items: FormUtils.validation( (value) => value.length > 0, "Must contain at lease 1 language", ), }, }); const languageOptions = useSelectorOptions(languages, (l) => l.name); const itemCutoffOptions = useSelectorOptions( form.values.items, (v) => { const suffix = v.hi === "True" ? ":hi" : v.forced === "True" ? ":forced" : ""; return v.language + suffix; }, (v) => String(v.id), ); const cutoffOptions = useMemo( () => ({ ...itemCutoffOptions, options: [...itemCutoffOptions.options, ...defaultCutoffOptions], }), [itemCutoffOptions], ); const selectedCutoff = useMemo( () => cutoffOptions.options.find((v) => v.value.id === form.values.cutoff) ?.value ?? null, [cutoffOptions, form.values.cutoff], ); const mustContainOptions = useSelectorOptions( form.values.mustContain, (v) => v, ); const mustNotContainOptions = useSelectorOptions( form.values.mustNotContain, (v) => v, ); const action = useArrayAction((fn) => { form.setValues((values) => ({ ...values, items: fn(values.items ?? []) })); }); const addItem = useCallback(() => { const id = 1 + form.values.items.reduce( (val, item) => Math.max(item.id, val), 0, ); if (languages.length > 0) { const language = languages[0].code2; const item: Language.ProfileItem = { id, language, // eslint-disable-next-line camelcase audio_exclude: "False", hi: "False", forced: "False", }; const list = [...form.values.items, item]; form.setValues((values) => ({ ...values, items: list })); } }, [form, languages]); const LanguageCell = React.memo( ({ item, index }: { item: Language.ProfileItem; index: number }) => { const code = useMemo( () => languageOptions.options.find((l) => l.value.code2 === item.language) ?.value ?? null, [item.language], ); return ( { if (value) { item.language = value.code2; action.mutate(index, { ...item, language: value.code2 }); } }} > ); }, ); const SubtitleTypeCell = React.memo( ({ item, index }: { item: Language.ProfileItem; index: number }) => { const selectValue = useMemo(() => { if (item.forced === "True") { return "forced"; } else if (item.hi === "True") { return "hi"; } else { return "normal"; } }, [item.forced, item.hi]); return ( ); }, ); const columns = useMemo[]>( () => [ { header: "ID", accessorKey: "id", }, { header: "Language", accessorKey: "language", cell: ({ row: { original: item, index } }) => { return ; }, }, { header: "Subtitles Type", accessorKey: "forced", cell: ({ row: { original: item, index } }) => { return ; }, }, { header: "Exclude If Matching Audio", accessorKey: "audio_exclude", cell: ({ row: { original: item, index } }) => { return ( { action.mutate(index, { ...item, // eslint-disable-next-line camelcase audio_exclude: checked ? "True" : "False", }); }} > ); }, }, { id: "action", cell: ({ row }) => { return ( action.remove(row.index)} > ); }, }, ], [action, LanguageCell, SubtitleTypeCell], ); return (
{ LOG("info", "Submitting language profile", value); onComplete?.(value); modals.closeSelf(); })} > {form.errors.items} { form.setFieldValue("cutoff", value?.id ?? null); }} > Subtitles release info must include one of those words or they will be excluded from search results (regex supported). Subtitles release info including one of those words (case insensitive) will be excluded from search results (regex supported). Download subtitle file without format conversion
); }; export const ProfileEditModal = withModal( ProfileEditForm, "languages-profile-editor", { title: "Edit Languages Profile", size: "xl", }, );