diff --git a/ui/v2.5/src/components/Tagger/scenes/Config.tsx b/ui/v2.5/src/components/Tagger/scenes/Config.tsx index 1ed137fb04a..f15fbd250f5 100644 --- a/ui/v2.5/src/components/Tagger/scenes/Config.tsx +++ b/ui/v2.5/src/components/Tagger/scenes/Config.tsx @@ -1,5 +1,5 @@ import { faTimes } from "@fortawesome/free-solid-svg-icons"; -import React, { useRef, useContext } from "react"; +import React, { useContext, useState } from "react"; import { Badge, Button, @@ -14,41 +14,110 @@ import { Icon } from "src/components/Shared/Icon"; import { ParseMode, TagOperation } from "../constants"; import { TaggerStateContext } from "../context"; -interface IConfigProps { - show: boolean; -} - -const Config: React.FC = ({ show }) => { - const { config, setConfig } = useContext(TaggerStateContext); +const Blacklist: React.FC<{ + list: string[]; + setList: (blacklist: string[]) => void; +}> = ({ list, setList }) => { const intl = useIntl(); - const blacklistRef = useRef(null); - function addBlacklistItem() { - if (!blacklistRef.current) return; + const [currentValue, setCurrentValue] = useState(""); + const [error, setError] = useState(); - const input = blacklistRef.current.value; - if (!input) return; + function addBlacklistItem() { + if (!currentValue) return; // don't add duplicate items - if (!config.blacklist.includes(input)) { - setConfig({ - ...config, - blacklist: [...config.blacklist, input], - }); + if (list.includes(currentValue)) { + setError( + intl.formatMessage({ + id: "component_tagger.config.errors.blacklist_duplicate", + }) + ); + return; + } + + // validate regex + try { + new RegExp(currentValue); + } catch (e) { + setError((e as SyntaxError).message); + return; } - blacklistRef.current.value = ""; + setList([...list, currentValue]); + + setCurrentValue(""); } function removeBlacklistItem(index: number) { - const newBlacklist = [...config.blacklist]; + const newBlacklist = [...list]; newBlacklist.splice(index, 1); - setConfig({ - ...config, - blacklist: newBlacklist, - }); + setList(newBlacklist); } + return ( +
+
+ +
+ + + { + setCurrentValue(e.currentTarget.value); + setError(undefined); + }} + onKeyDown={(e: React.KeyboardEvent) => { + if (e.key === "Enter") { + addBlacklistItem(); + e.preventDefault(); + } + }} + isInvalid={!!error} + /> + + + + {error} + + +
+ {intl.formatMessage( + { id: "component_tagger.config.blacklist_desc" }, + { chars_require_escape: [\^$.|?*+() } + )} +
+ {list.map((item, index) => ( + + {item.toString()} + + + ))} +
+ ); +}; + +interface IConfigProps { + show: boolean; +} + +const Config: React.FC = ({ show }) => { + const { config, setConfig } = useContext(TaggerStateContext); + const intl = useIntl(); + return ( @@ -198,47 +267,10 @@ const Config: React.FC = ({ show }) => {
-
- -
- - ) => { - if (e.key === "Enter") { - addBlacklistItem(); - e.preventDefault(); - } - }} - /> - - - - -
- {intl.formatMessage( - { id: "component_tagger.config.blacklist_desc" }, - { chars_require_escape: [\^$.|?*+() } - )} -
- {config.blacklist.map((item, index) => ( - - {item.toString()} - - - ))} + setConfig({ ...config, blacklist })} + />
diff --git a/ui/v2.5/src/locales/en-GB.json b/ui/v2.5/src/locales/en-GB.json index 784579c95b8..143632af005 100644 --- a/ui/v2.5/src/locales/en-GB.json +++ b/ui/v2.5/src/locales/en-GB.json @@ -173,6 +173,9 @@ "active_instance": "Active stash-box instance:", "blacklist_desc": "Blacklist items are excluded from queries. Note that they are regular expressions and also case-insensitive. Certain characters must be escaped with a backslash: {chars_require_escape}", "blacklist_label": "Blacklist", + "errors": { + "blacklist_duplicate": "Duplicate blacklist item" + }, "mark_organized_desc": "Immediately mark the scene as Organized after the Save button is clicked.", "mark_organized_label": "Mark as Organized on save", "query_mode_auto": "Auto",