diff --git a/src/components/profile/rule-item.tsx b/src/components/profile/rule-item.tsx new file mode 100644 index 00000000..e5c55396 --- /dev/null +++ b/src/components/profile/rule-item.tsx @@ -0,0 +1,63 @@ +import { + Box, + Divider, + IconButton, + ListItem, + ListItemText, + Typography, + alpha, +} from "@mui/material"; +import { DeleteForeverRounded, UndoRounded } from "@mui/icons-material"; +interface Props { + type: "prepend" | "original" | "delete" | "append"; + ruleRaw: string; + onDelete: () => void; +} + +export const RuleItem = (props: Props) => { + let { type, ruleRaw, onDelete } = props; + const rule = ruleRaw.replace(",no-resolve", "").split(","); + + return ( + ({ + p: 0, + borderRadius: "10px", + border: "solid 2px", + borderColor: + type === "original" + ? "var(--divider-color)" + : type === "delete" + ? alpha(palette.error.main, 0.5) + : alpha(palette.success.main, 0.5), + mb: 1, + })} + > + + + {rule.length === 3 ? rule[1] : "-"} + + + } + secondary={ + + {rule[0]} + {rule.length === 3 ? rule[2] : rule[1]} + + } + /> + + + {type === "delete" ? : } + + + ); +}; diff --git a/src/components/profile/rules-editor-viewer.tsx b/src/components/profile/rules-editor-viewer.tsx index f05bc1b3..fe5cac2e 100644 --- a/src/components/profile/rules-editor-viewer.tsx +++ b/src/components/profile/rules-editor-viewer.tsx @@ -16,11 +16,11 @@ import { TextField, styled, } from "@mui/material"; -import { useThemeMode } from "@/services/states"; + import { readProfileFile, saveProfileFile } from "@/services/cmds"; import { Notice, Switch } from "@/components/base"; import getSystem from "@/utils/get-system"; -import MonacoEditor from "react-monaco-editor"; +import { RuleItem } from "@/components/profile/rule-item"; interface Props { profileUid: string; @@ -119,10 +119,7 @@ export const RulesEditorViewer = (props: Props) => { const { title, profileUid, property, open, onClose, onChange } = props; const { t } = useTranslation(); - const themeMode = useThemeMode(); const [prevData, setPrevData] = useState(""); - const [currData, setCurrData] = useState(""); - const [rule, setRule] = useState(""); const [ruleType, setRuleType] = useState<(typeof RuleTypeList)[number]>("DOMAIN"); const [ruleContent, setRuleContent] = useState(""); @@ -131,28 +128,17 @@ export const RulesEditorViewer = (props: Props) => { const [proxyPolicyList, setProxyPolicyList] = useState([]); const [ruleList, setRuleList] = useState([]); - const editorOptions = { - tabSize: 2, - minimap: { enabled: false }, - mouseWheelZoom: true, - quickSuggestions: { - strings: true, - comments: true, - other: true, - }, - padding: { - top: 33, - }, - fontFamily: `Fira Code, JetBrains Mono, Roboto Mono, "Source Code Pro", Consolas, Menlo, Monaco, monospace, "Courier New", "Apple Color Emoji"${ - getSystem() === "windows" ? ", twemoji mozilla" : "" - }`, - fontLigatures: true, - smoothScrolling: true, - }; + const [prependSeq, setPrependSeq] = useState([]); + const [appendSeq, setAppendSeq] = useState([]); + const [deleteSeq, setDeleteSeq] = useState([]); const fetchContent = async () => { let data = await readProfileFile(property); - setCurrData(data); + let obj = yaml.load(data) as { prepend: []; append: []; delete: [] }; + + setPrependSeq(obj.prepend || []); + setAppendSeq(obj.append || []); + setDeleteSeq(obj.delete || []); setPrevData(data); }; @@ -170,42 +156,6 @@ export const RulesEditorViewer = (props: Props) => { setRuleList(obj.rules); }; - const addSeq = async (method: "prepend" | "append" | "delete") => { - let obj = yaml.load(currData) as ISeqProfileConfig; - if (!obj.prepend) { - obj = { prepend: [], append: [], delete: [] }; - } - switch (method) { - case "append": { - obj.append.push( - `${ruleType}${ - ruleType === "MATCH" ? "" : "," + ruleContent - },${proxyPolicy}${ - NoResolveList.includes(ruleType) && noResolve ? ",no-resolve" : "" - }` - ); - break; - } - case "prepend": { - obj.prepend.push( - `${ruleType}${ - ruleType === "MATCH" ? "" : "," + ruleContent - },${proxyPolicy}${ - NoResolveList.includes(ruleType) && noResolve ? ",no-resolve" : "" - }` - ); - break; - } - case "delete": { - obj.delete.push(rule); - break; - } - } - let raw = yaml.dump(obj); - - setCurrData(raw); - }; - useEffect(() => { fetchContent(); fetchProfile(); @@ -213,6 +163,11 @@ export const RulesEditorViewer = (props: Props) => { const onSave = useLockFn(async () => { try { + let currData = yaml.dump({ + prepend: prependSeq, + append: appendSeq, + delete: deleteSeq, + }); await saveProfileFile(property, currData); onChange?.(prevData, currData); onClose(); @@ -288,7 +243,15 @@ export const RulesEditorViewer = (props: Props) => { fullWidth variant="contained" onClick={() => { - addSeq("prepend"); + let raw = `${ruleType}${ + ruleType === "MATCH" ? "" : "," + ruleContent + },${proxyPolicy}${ + NoResolveList.includes(ruleType) && noResolve + ? ",no-resolve" + : "" + }`; + if (prependSeq.includes(raw)) return; + setPrependSeq([...prependSeq, raw]); }} > {t("Add Prepend Rule")} @@ -299,55 +262,82 @@ export const RulesEditorViewer = (props: Props) => { fullWidth variant="contained" onClick={() => { - addSeq("append"); + let raw = `${ruleType}${ + ruleType === "MATCH" ? "" : "," + ruleContent + },${proxyPolicy}${ + NoResolveList.includes(ruleType) && noResolve + ? ",no-resolve" + : "" + }`; + if (appendSeq.includes(raw)) return; + setAppendSeq([...appendSeq, raw]); }} > {t("Add Append Rule")} - - { - if (v) setRule(v); - }} - renderInput={(params) => } - /> - - - -
- { - if (value) setCurrData(value); - }} - options={editorOptions} - /> + {prependSeq.length > 0 && ( + + {prependSeq.map((item, index) => { + return ( + { + setPrependSeq(prependSeq.filter((v) => v !== item)); + }} + /> + ); + })} + + )} + + + {ruleList.map((item, index) => { + return ( + { + if (deleteSeq.includes(item)) { + setDeleteSeq(deleteSeq.filter((v) => v !== item)); + } else { + setDeleteSeq([...deleteSeq, item]); + } + }} + /> + ); + })} + + + {appendSeq.length > 0 && ( + + {appendSeq.map((item, index) => { + return ( + { + setAppendSeq(appendSeq.filter((v) => v !== item)); + }} + /> + ); + })} + + )}