mirror of https://github.com/morpheus65535/bazarr
293 lines
7.4 KiB
TypeScript
293 lines
7.4 KiB
TypeScript
import { faTrash } from "@fortawesome/free-solid-svg-icons";
|
|
import React, {
|
|
FunctionComponent,
|
|
useCallback,
|
|
useEffect,
|
|
useMemo,
|
|
useState,
|
|
} from "react";
|
|
import { Button, Form } from "react-bootstrap";
|
|
import { Column, TableUpdater } from "react-table";
|
|
import { useEnabledLanguagesContext } from ".";
|
|
import {
|
|
ActionButton,
|
|
BaseModal,
|
|
BaseModalProps,
|
|
Chips,
|
|
LanguageSelector,
|
|
Selector,
|
|
SimpleTable,
|
|
useModalInformation,
|
|
} from "../../components";
|
|
import { BuildKey } from "../../utilities";
|
|
import { Input, Message } from "../components";
|
|
import { cutoffOptions } from "./options";
|
|
interface Props {
|
|
update: (profile: Language.Profile) => void;
|
|
}
|
|
|
|
function createDefaultProfile(): Language.Profile {
|
|
return {
|
|
profileId: -1,
|
|
name: "",
|
|
items: [],
|
|
cutoff: null,
|
|
mustContain: [],
|
|
mustNotContain: [],
|
|
};
|
|
}
|
|
|
|
const LanguagesProfileModal: FunctionComponent<Props & BaseModalProps> = (
|
|
props
|
|
) => {
|
|
const { update, ...modal } = props;
|
|
|
|
const { payload: profile, closeModal } =
|
|
useModalInformation<Language.Profile>(modal.modalKey);
|
|
|
|
const languages = useEnabledLanguagesContext();
|
|
|
|
const [current, setProfile] = useState(createDefaultProfile);
|
|
|
|
useEffect(() => {
|
|
if (profile) {
|
|
setProfile(profile);
|
|
} else {
|
|
setProfile(createDefaultProfile);
|
|
}
|
|
}, [profile]);
|
|
|
|
const cutoff: SelectorOption<number>[] = useMemo(() => {
|
|
const options = [...cutoffOptions];
|
|
|
|
const newOptions = current.items.map<SelectorOption<number>>((v) => ({
|
|
label: `ID ${v.id} (${v.language})`,
|
|
value: v.id,
|
|
}));
|
|
|
|
options.push(...newOptions);
|
|
|
|
return options;
|
|
}, [current.items]);
|
|
|
|
const updateProfile = useCallback(
|
|
<K extends keyof Language.Profile>(key: K, value: Language.Profile[K]) => {
|
|
const newProfile = { ...current };
|
|
newProfile[key] = value;
|
|
setProfile(newProfile);
|
|
},
|
|
[current]
|
|
);
|
|
|
|
const updateRow = useCallback<TableUpdater<Language.ProfileItem>>(
|
|
(row, item: Language.ProfileItem) => {
|
|
const list = [...current.items];
|
|
if (item) {
|
|
list[row.index] = item;
|
|
} else {
|
|
list.splice(row.index, 1);
|
|
}
|
|
updateProfile("items", list);
|
|
},
|
|
[current.items, updateProfile]
|
|
);
|
|
|
|
const addItem = useCallback(() => {
|
|
const id =
|
|
1 +
|
|
current.items.reduce<number>((val, item) => Math.max(item.id, val), 0);
|
|
|
|
if (languages.length > 0) {
|
|
const language = languages[0].code2;
|
|
|
|
const item: Language.ProfileItem = {
|
|
id,
|
|
language,
|
|
audio_exclude: "False",
|
|
hi: "False",
|
|
forced: "False",
|
|
};
|
|
|
|
const list = [...current.items];
|
|
|
|
list.push(item);
|
|
updateProfile("items", list);
|
|
}
|
|
}, [current.items, updateProfile, languages]);
|
|
|
|
const canSave = current.name.length > 0 && current.items.length > 0;
|
|
|
|
const footer = (
|
|
<Button
|
|
disabled={!canSave}
|
|
onClick={() => {
|
|
closeModal();
|
|
update(current);
|
|
}}
|
|
>
|
|
Save
|
|
</Button>
|
|
);
|
|
|
|
const columns = useMemo<Column<Language.ProfileItem>[]>(
|
|
() => [
|
|
{
|
|
Header: "ID",
|
|
accessor: "id",
|
|
},
|
|
{
|
|
Header: "Language",
|
|
accessor: "language",
|
|
Cell: ({ value, row, update }) => {
|
|
const code = value;
|
|
const item = row.original;
|
|
const lang = useMemo(
|
|
() => languages.find((l) => l.code2 === code) ?? null,
|
|
[code]
|
|
);
|
|
return (
|
|
<div style={{ width: "8rem" }}>
|
|
<LanguageSelector
|
|
options={languages}
|
|
value={lang}
|
|
onChange={(l) => {
|
|
if (l) {
|
|
item.language = l.code2;
|
|
update && update(row, item);
|
|
}
|
|
}}
|
|
></LanguageSelector>
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
Header: "Forced",
|
|
accessor: "forced",
|
|
Cell: ({ row, value, update }) => {
|
|
const item = row.original;
|
|
return (
|
|
<Form.Check
|
|
custom
|
|
id={BuildKey(item.id, item.language, "forced")}
|
|
checked={value === "True"}
|
|
onChange={(v) => {
|
|
item.forced = v.target.checked ? "True" : "False";
|
|
update && update(row, item);
|
|
}}
|
|
></Form.Check>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
Header: "HI",
|
|
accessor: "hi",
|
|
Cell: ({ row, value, update }) => {
|
|
const item = row.original;
|
|
return (
|
|
<Form.Check
|
|
custom
|
|
id={BuildKey(item.id, item.language, "hi")}
|
|
checked={value === "True"}
|
|
onChange={(v) => {
|
|
item.hi = v.target.checked ? "True" : "False";
|
|
update && update(row, item);
|
|
}}
|
|
></Form.Check>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
Header: "Exclude Audio",
|
|
accessor: "audio_exclude",
|
|
Cell: ({ row, value, update }) => {
|
|
const item = row.original;
|
|
return (
|
|
<Form.Check
|
|
custom
|
|
id={BuildKey(item.id, item.language, "audio")}
|
|
checked={value === "True"}
|
|
onChange={(v) => {
|
|
item.audio_exclude = v.target.checked ? "True" : "False";
|
|
update && update(row, item);
|
|
}}
|
|
></Form.Check>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
id: "action",
|
|
accessor: "id",
|
|
Cell: ({ row, update }) => {
|
|
return (
|
|
<ActionButton
|
|
icon={faTrash}
|
|
onClick={() => update && update(row)}
|
|
></ActionButton>
|
|
);
|
|
},
|
|
},
|
|
],
|
|
[languages]
|
|
);
|
|
|
|
return (
|
|
<BaseModal size="lg" title="Languages Profile" footer={footer} {...modal}>
|
|
<Input>
|
|
<Form.Control
|
|
type="text"
|
|
placeholder="Name"
|
|
value={current.name}
|
|
onChange={(v) => {
|
|
updateProfile("name", v.target.value);
|
|
}}
|
|
></Form.Control>
|
|
</Input>
|
|
<Input>
|
|
<SimpleTable
|
|
responsive={false}
|
|
columns={columns}
|
|
data={current.items}
|
|
update={updateRow}
|
|
></SimpleTable>
|
|
<Button block variant="light" onClick={addItem}>
|
|
Add
|
|
</Button>
|
|
</Input>
|
|
<Input name="Cutoff">
|
|
<Selector
|
|
clearable
|
|
options={cutoff}
|
|
value={current.cutoff}
|
|
onChange={(num) => updateProfile("cutoff", num)}
|
|
></Selector>
|
|
<Message>Ignore others if existing</Message>
|
|
</Input>
|
|
<Input name="Release info must contain">
|
|
<Chips
|
|
value={current.mustContain}
|
|
onChange={(mc) => updateProfile("mustContain", mc)}
|
|
></Chips>
|
|
<Message>
|
|
Subtitles release info must include one of those words or they will be
|
|
excluded from search results (regex supported).
|
|
</Message>
|
|
</Input>
|
|
<Input name="Release info must not contain">
|
|
<Chips
|
|
value={current.mustNotContain}
|
|
onChange={(mnc: string[]) => {
|
|
updateProfile("mustNotContain", mnc);
|
|
}}
|
|
></Chips>
|
|
<Message>
|
|
Subtitles release info including one of those words (case insensitive)
|
|
will be excluded from search results (regex supported).
|
|
</Message>
|
|
</Input>
|
|
</BaseModal>
|
|
);
|
|
};
|
|
|
|
export default LanguagesProfileModal;
|