diff --git a/ui/v2.5/src/components/Tagger/scenes/Config.tsx b/ui/v2.5/src/components/Tagger/scenes/Config.tsx index 1ed137fb0..f15fbd250 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,6 +14,102 @@ import { Icon } from "src/components/Shared/Icon"; import { ParseMode, TagOperation } from "../constants"; import { TaggerStateContext } from "../context"; +const Blacklist: React.FC<{ + list: string[]; + setList: (blacklist: string[]) => void; +}> = ({ list, setList }) => { + const intl = useIntl(); + + const [currentValue, setCurrentValue] = useState(""); + const [error, setError] = useState(); + + function addBlacklistItem() { + if (!currentValue) return; + + // don't add duplicate items + 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; + } + + setList([...list, currentValue]); + + setCurrentValue(""); + } + + function removeBlacklistItem(index: number) { + const newBlacklist = [...list]; + newBlacklist.splice(index, 1); + 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; } @@ -21,33 +117,6 @@ interface IConfigProps { const Config: React.FC = ({ show }) => { const { config, setConfig } = useContext(TaggerStateContext); const intl = useIntl(); - const blacklistRef = useRef(null); - - function addBlacklistItem() { - if (!blacklistRef.current) return; - - const input = blacklistRef.current.value; - if (!input) return; - - // don't add duplicate items - if (!config.blacklist.includes(input)) { - setConfig({ - ...config, - blacklist: [...config.blacklist, input], - }); - } - - blacklistRef.current.value = ""; - } - - function removeBlacklistItem(index: number) { - const newBlacklist = [...config.blacklist]; - newBlacklist.splice(index, 1); - setConfig({ - ...config, - blacklist: newBlacklist, - }); - } 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/components/Tagger/utils.ts b/ui/v2.5/src/components/Tagger/utils.ts index 6bac6cb04..92f596040 100644 --- a/ui/v2.5/src/components/Tagger/utils.ts +++ b/ui/v2.5/src/components/Tagger/utils.ts @@ -83,6 +83,17 @@ export function prepareQueryString( mode: ParseMode, blacklist: string[] ) { + const regexs = blacklist + .map((b) => { + try { + return new RegExp(b, "gi"); + } catch { + // ignore + return null; + } + }) + .filter((r) => r !== null) as RegExp[]; + if ((mode === "auto" && scene.date && scene.studio) || mode === "metadata") { let str = [ scene.date, @@ -92,8 +103,8 @@ export function prepareQueryString( ] .filter((s) => s !== "") .join(" "); - blacklist.forEach((b) => { - str = str.replace(new RegExp(b, "gi"), " "); + regexs.forEach((re) => { + str = str.replace(re, " "); }); return str; } @@ -106,8 +117,9 @@ export function prepareQueryString( } else if (mode === "dir" && paths.length) { s = paths[paths.length - 1]; } - blacklist.forEach((b) => { - s = s.replace(new RegExp(b, "gi"), " "); + + regexs.forEach((re) => { + s = s.replace(re, " "); }); s = parseDate(s); return s.replace(/\./g, " ").replace(/ +/g, " "); diff --git a/ui/v2.5/src/locales/en-GB.json b/ui/v2.5/src/locales/en-GB.json index 784579c95..143632af0 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",