import Box from "@mui/material/Box"; import Stack from "@mui/material/Stack"; import TextField from "@mui/material/TextField"; import Button from "@mui/material/Button"; import CircularProgress from "@mui/material/CircularProgress"; import Alert from "@mui/material/Alert"; import { GLOBAL_KEY, DEFAULT_RULE, GLOBLA_RULE, OPT_LANGS_FROM, OPT_LANGS_TO, OPT_STYLE_ALL, OPT_STYLE_DIY, // OPT_STYLE_USE_COLOR, URL_KISS_RULES_NEW_ISSUE, OPT_SYNCTYPE_WORKER, DEFAULT_TRANS_TAG, } from "../../config"; import { useState, useEffect, useMemo } from "react"; import { useI18n } from "../../hooks/I18n"; import Typography from "@mui/material/Typography"; import Accordion from "@mui/material/Accordion"; import AccordionSummary from "@mui/material/AccordionSummary"; import AccordionDetails from "@mui/material/AccordionDetails"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import { useRules } from "../../hooks/Rules"; import MenuItem from "@mui/material/MenuItem"; import Grid from "@mui/material/Grid"; import { useSetting } from "../../hooks/Setting"; import FormControlLabel from "@mui/material/FormControlLabel"; import Switch from "@mui/material/Switch"; import Tabs from "@mui/material/Tabs"; import Tab from "@mui/material/Tab"; import Radio from "@mui/material/Radio"; import RadioGroup from "@mui/material/RadioGroup"; import DeleteIcon from "@mui/icons-material/Delete"; import IconButton from "@mui/material/IconButton"; import ShareIcon from "@mui/icons-material/Share"; import SyncIcon from "@mui/icons-material/Sync"; import { useSubRules } from "../../hooks/SubRules"; import { syncSubRules } from "../../libs/subRules"; import { loadOrFetchSubRules } from "../../libs/subRules"; import { useAlert } from "../../hooks/Alert"; import { syncShareRules } from "../../libs/sync"; import { debounce } from "../../libs/utils"; import { delSubRules, getSyncWithDefault, getRulesOld, } from "../../libs/storage"; // import OwSubRule from "./OwSubRule"; import ClearAllIcon from "@mui/icons-material/ClearAll"; import HelpButton from "./HelpButton"; import { useSyncCaches } from "../../hooks/Sync"; import DownloadButton from "./DownloadButton"; import UploadButton from "./UploadButton"; import AddIcon from "@mui/icons-material/Add"; import EditIcon from "@mui/icons-material/Edit"; import CancelIcon from "@mui/icons-material/Cancel"; import SaveIcon from "@mui/icons-material/Save"; import { kissLog } from "../../libs/log"; import { useApiList } from "../../hooks/Api"; import ShowMoreButton from "./ShowMoreButton"; const calculateInitialValues = (rule) => { const base = rule?.pattern === "*" ? GLOBLA_RULE : DEFAULT_RULE; return { ...base, ...(rule || {}) }; }; function RuleFields({ rule, rules, setShow, setKeyword }) { const editMode = useMemo(() => !!rule, [rule]); const i18n = useI18n(); const [disabled, setDisabled] = useState(editMode); const [errors, setErrors] = useState({}); const [initialFormValues, setInitialFormValues] = useState(() => calculateInitialValues(rule) ); const [formValues, setFormValues] = useState(initialFormValues); const [showMore, setShowMore] = useState(!rules); const { enabledApis } = useApiList(); useEffect(() => { const newInitialValues = calculateInitialValues(rule); setInitialFormValues(newInitialValues); setFormValues(newInitialValues); }, [rule]); const { pattern, selector, keepSelector = "", rootsSelector = "", ignoreSelector = "", terms = "", selectStyle = "", parentStyle = "", injectJs = "", injectCss = "", apiSlug, fromLang, toLang, textStyle, transOpen, bgColor, textDiyStyle, transOnly = "false", autoScan = "true", hasRichText = "true", hasShadowroot = "false", // transTiming = OPT_TIMING_PAGESCROLL, transTag = DEFAULT_TRANS_TAG, transTitle = "false", // detectRemote = "true", // skipLangs = [], // fixerSelector = "", // fixerFunc = "-", transStartHook = "", transEndHook = "", // transRemoveHook = "", } = formValues; const isModified = useMemo(() => { return JSON.stringify(initialFormValues) !== JSON.stringify(formValues); }, [initialFormValues, formValues]); const hasSamePattern = (str) => { for (const item of rules.list) { if (item.pattern === str && rule?.pattern !== str) { return true; } } return false; }; const handleFocus = (e) => { e.preventDefault(); const { name } = e.target; setErrors((pre) => ({ ...pre, [name]: "" })); }; const handlePatternChange = useMemo( () => debounce(async (patterns) => { setKeyword(patterns.trim()); }, 500), [setKeyword] ); const handleChange = (e) => { e.preventDefault(); const { name, value } = e.target; setFormValues((pre) => ({ ...pre, [name]: value })); if (name === "pattern" && !editMode) { handlePatternChange(value); } }; const handleCancel = (e) => { e.preventDefault(); if (editMode) { setDisabled(true); } else { setShow(false); } setErrors({}); setFormValues(initialFormValues); }; const handleRestore = (e) => { e.preventDefault(); setFormValues(({ pattern }) => ({ ...(pattern === "*" ? GLOBLA_RULE : DEFAULT_RULE), pattern, })); }; const handleSubmit = (e) => { e.preventDefault(); const errors = {}; if (!pattern.trim()) { errors.pattern = i18n("error_cant_be_blank"); } if (hasSamePattern(pattern)) { errors.pattern = i18n("error_duplicate_values"); } if (pattern === "*" && !errors.pattern && !selector.trim()) { errors.selector = i18n("error_cant_be_blank"); } if (Object.keys(errors).length > 0) { setErrors(errors); return; } if (editMode) { // 编辑 setDisabled(true); rules.put(rule.pattern, formValues); } else { // 添加 rules.add(formValues); setShow(false); setFormValues(initialFormValues); } }; const GlobalItem = rule?.pattern !== "*" && ( ); return (
); } function RuleAccordion({ rule, rules, isExpanded = false }) { const i18n = useI18n(); const [expanded, setExpanded] = useState(isExpanded); const handleChange = (e) => { setExpanded((pre) => !pre); }; return (Sync Error:
{err.message}
>
);
} finally {
setLoading(false);
}
};
return (