dev...
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import { fetchPolyfill } from "../libs/fetch";
|
import { fetchPolyfill } from "../libs/fetch";
|
||||||
import {
|
import {
|
||||||
|
GLOBAL_KEY,
|
||||||
OPT_TRANS_GOOGLE,
|
OPT_TRANS_GOOGLE,
|
||||||
OPT_TRANS_MICROSOFT,
|
OPT_TRANS_MICROSOFT,
|
||||||
OPT_TRANS_OPENAI,
|
OPT_TRANS_OPENAI,
|
||||||
@@ -10,8 +11,9 @@ import {
|
|||||||
PROMPT_PLACE_TO,
|
PROMPT_PLACE_TO,
|
||||||
KV_SALT_SYNC,
|
KV_SALT_SYNC,
|
||||||
} from "../config";
|
} from "../config";
|
||||||
import { getSetting, detectLang } from "../libs";
|
import { detectLang } from "../libs/browser";
|
||||||
import { sha256 } from "../libs/utils";
|
import { sha256 } from "../libs/utils";
|
||||||
|
import { checkRules } from "../libs/rules";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 同步数据
|
* 同步数据
|
||||||
@@ -31,6 +33,20 @@ export const apiSyncData = async (url, key, data, isBg = false) =>
|
|||||||
isBg,
|
isBg,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载订阅规则
|
||||||
|
* @param {*} url
|
||||||
|
* @param {*} isBg
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const apiFetchRules = async (url, isBg = false) => {
|
||||||
|
const res = await fetchPolyfill(url, { isBg });
|
||||||
|
const rules = checkRules(res).filter(
|
||||||
|
(rule) => rule.pattern.replaceAll(GLOBAL_KEY, "") !== ""
|
||||||
|
);
|
||||||
|
return rules;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 谷歌翻译
|
* 谷歌翻译
|
||||||
* @param {*} text
|
* @param {*} text
|
||||||
@@ -38,7 +54,8 @@ export const apiSyncData = async (url, key, data, isBg = false) =>
|
|||||||
* @param {*} from
|
* @param {*} from
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const apiGoogleTranslate = async (translator, text, to, from) => {
|
const apiGoogleTranslate = async (translator, text, to, from, setting) => {
|
||||||
|
const { googleUrl } = setting;
|
||||||
const params = {
|
const params = {
|
||||||
client: "gtx",
|
client: "gtx",
|
||||||
dt: "t",
|
dt: "t",
|
||||||
@@ -48,7 +65,6 @@ const apiGoogleTranslate = async (translator, text, to, from) => {
|
|||||||
tl: to,
|
tl: to,
|
||||||
q: text,
|
q: text,
|
||||||
};
|
};
|
||||||
const { googleUrl } = await getSetting();
|
|
||||||
const input = `${googleUrl}?${queryString.stringify(params)}`;
|
const input = `${googleUrl}?${queryString.stringify(params)}`;
|
||||||
return fetchPolyfill(input, {
|
return fetchPolyfill(input, {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -93,9 +109,8 @@ const apiMicrosoftTranslate = (translator, text, to, from) => {
|
|||||||
* @param {*} from
|
* @param {*} from
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const apiOpenaiTranslate = async (translator, text, to, from) => {
|
const apiOpenaiTranslate = async (translator, text, to, from, setting) => {
|
||||||
const { openaiUrl, openaiKey, openaiModel, openaiPrompt } =
|
const { openaiUrl, openaiKey, openaiModel, openaiPrompt } = setting;
|
||||||
await getSetting();
|
|
||||||
let prompt = openaiPrompt
|
let prompt = openaiPrompt
|
||||||
.replaceAll(PROMPT_PLACE_FROM, from)
|
.replaceAll(PROMPT_PLACE_FROM, from)
|
||||||
.replaceAll(PROMPT_PLACE_TO, to);
|
.replaceAll(PROMPT_PLACE_TO, to);
|
||||||
@@ -131,7 +146,13 @@ const apiOpenaiTranslate = async (translator, text, to, from) => {
|
|||||||
* @param {*} param0
|
* @param {*} param0
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const apiTranslate = async ({ translator, q, fromLang, toLang }) => {
|
export const apiTranslate = async ({
|
||||||
|
translator,
|
||||||
|
q,
|
||||||
|
fromLang,
|
||||||
|
toLang,
|
||||||
|
setting,
|
||||||
|
}) => {
|
||||||
let trText = "";
|
let trText = "";
|
||||||
let isSame = false;
|
let isSame = false;
|
||||||
|
|
||||||
@@ -139,7 +160,7 @@ export const apiTranslate = async ({ translator, q, fromLang, toLang }) => {
|
|||||||
let to = OPT_LANGS_SPECIAL?.[translator]?.get(toLang) ?? toLang;
|
let to = OPT_LANGS_SPECIAL?.[translator]?.get(toLang) ?? toLang;
|
||||||
|
|
||||||
if (translator === OPT_TRANS_GOOGLE) {
|
if (translator === OPT_TRANS_GOOGLE) {
|
||||||
const res = await apiGoogleTranslate(translator, q, to, from);
|
const res = await apiGoogleTranslate(translator, q, to, from, setting);
|
||||||
trText = res.sentences.map((item) => item.trans).join(" ");
|
trText = res.sentences.map((item) => item.trans).join(" ");
|
||||||
isSame = to === res.src;
|
isSame = to === res.src;
|
||||||
} else if (translator === OPT_TRANS_MICROSOFT) {
|
} else if (translator === OPT_TRANS_MICROSOFT) {
|
||||||
@@ -147,9 +168,11 @@ export const apiTranslate = async ({ translator, q, fromLang, toLang }) => {
|
|||||||
trText = res[0].translations[0].text;
|
trText = res[0].translations[0].text;
|
||||||
isSame = to === res[0].detectedLanguage.language;
|
isSame = to === res[0].detectedLanguage.language;
|
||||||
} else if (translator === OPT_TRANS_OPENAI) {
|
} else if (translator === OPT_TRANS_OPENAI) {
|
||||||
const res = await apiOpenaiTranslate(translator, q, to, from);
|
const res = await apiOpenaiTranslate(translator, q, to, from, setting);
|
||||||
trText = res?.choices?.[0].message.content;
|
trText = res?.choices?.[0].message.content;
|
||||||
isSame = (await detectLang(q)) === (await detectLang(trText));
|
const sLang = await detectLang(q);
|
||||||
|
const tLang = await detectLang(trText);
|
||||||
|
isSame = q === trText || (sLang && tLang && sLang === tLang);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [trText, isSame];
|
return [trText, isSame];
|
||||||
|
|||||||
@@ -7,35 +7,19 @@ import {
|
|||||||
MSG_TRANS_TOGGLE_STYLE,
|
MSG_TRANS_TOGGLE_STYLE,
|
||||||
CMD_TOGGLE_TRANSLATE,
|
CMD_TOGGLE_TRANSLATE,
|
||||||
CMD_TOGGLE_STYLE,
|
CMD_TOGGLE_STYLE,
|
||||||
DEFAULT_SETTING,
|
|
||||||
DEFAULT_RULES,
|
|
||||||
DEFAULT_SYNC,
|
|
||||||
STOKEY_SETTING,
|
|
||||||
STOKEY_RULES,
|
|
||||||
STOKEY_SYNC,
|
|
||||||
CACHE_NAME,
|
|
||||||
STOKEY_RULESCACHE_PREFIX,
|
|
||||||
BUILTIN_RULES,
|
|
||||||
} from "./config";
|
} from "./config";
|
||||||
import storage from "./libs/storage";
|
import { getSettingWithDefault, tryInitDefaultData } from "./libs/storage";
|
||||||
import { getSetting } from "./libs";
|
|
||||||
import { trySyncAll } from "./libs/sync";
|
import { trySyncAll } from "./libs/sync";
|
||||||
import { fetchData, fetchPool } from "./libs/fetch";
|
import { fetchData, fetchPool } from "./libs/fetch";
|
||||||
import { sendTabMsg } from "./libs/msg";
|
import { sendTabMsg } from "./libs/msg";
|
||||||
import { trySyncAllSubRules } from "./libs/rules";
|
import { trySyncAllSubRules } from "./libs/subRules";
|
||||||
|
import { tryClearCaches } from "./libs";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 插件安装
|
* 插件安装
|
||||||
*/
|
*/
|
||||||
browser.runtime.onInstalled.addListener(() => {
|
browser.runtime.onInstalled.addListener(() => {
|
||||||
console.log("KISS Translator onInstalled");
|
tryInitDefaultData();
|
||||||
storage.trySetObj(STOKEY_SETTING, DEFAULT_SETTING);
|
|
||||||
storage.trySetObj(STOKEY_RULES, DEFAULT_RULES);
|
|
||||||
storage.trySetObj(STOKEY_SYNC, DEFAULT_SYNC);
|
|
||||||
storage.trySetObj(
|
|
||||||
`${STOKEY_RULESCACHE_PREFIX}${process.env.REACT_APP_RULESURL}`,
|
|
||||||
BUILTIN_RULES
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,13 +32,9 @@ browser.runtime.onStartup.addListener(async () => {
|
|||||||
await trySyncAll(true);
|
await trySyncAll(true);
|
||||||
|
|
||||||
// 清除缓存
|
// 清除缓存
|
||||||
const setting = await getSetting();
|
const setting = await getSettingWithDefault();
|
||||||
if (setting.clearCache) {
|
if (setting.clearCache) {
|
||||||
try {
|
tryClearCaches();
|
||||||
caches.delete(CACHE_NAME);
|
|
||||||
} catch (err) {
|
|
||||||
console.log("[clean caches]", err.message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 同步订阅规则
|
// 同步订阅规则
|
||||||
|
|||||||
4
src/config/app.js
Normal file
4
src/config/app.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export const APP_NAME = process.env.REACT_APP_NAME.trim()
|
||||||
|
.split(/\s+/)
|
||||||
|
.join("-");
|
||||||
|
export const APP_LCNAME = APP_NAME.toLowerCase();
|
||||||
@@ -5,12 +5,9 @@ import {
|
|||||||
DEFAULT_RULE,
|
DEFAULT_RULE,
|
||||||
BUILTIN_RULES,
|
BUILTIN_RULES,
|
||||||
} from "./rules";
|
} from "./rules";
|
||||||
|
import { APP_NAME, APP_LCNAME } from "./app";
|
||||||
export { I18N, UI_LANGS } from "./i18n";
|
export { I18N, UI_LANGS } from "./i18n";
|
||||||
export { GLOBAL_KEY, SHADOW_KEY, DEFAULT_RULE, BUILTIN_RULES };
|
export { GLOBAL_KEY, SHADOW_KEY, DEFAULT_RULE, BUILTIN_RULES, APP_LCNAME };
|
||||||
|
|
||||||
const APP_NAME = process.env.REACT_APP_NAME.trim().split(/\s+/).join("-");
|
|
||||||
|
|
||||||
export const APP_LCNAME = APP_NAME.toLowerCase();
|
|
||||||
|
|
||||||
export const STOKEY_MSAUTH = `${APP_NAME}_msauth`;
|
export const STOKEY_MSAUTH = `${APP_NAME}_msauth`;
|
||||||
export const STOKEY_SETTING = `${APP_NAME}_setting`;
|
export const STOKEY_SETTING = `${APP_NAME}_setting`;
|
||||||
@@ -163,6 +160,7 @@ export const DEFAULT_SUBRULES_LIST = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
url: "https://fishjar.github.io/kiss-translator/kiss-translator-rules.json",
|
url: "https://fishjar.github.io/kiss-translator/kiss-translator-rules.json",
|
||||||
|
selected: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -5,17 +5,18 @@ import {
|
|||||||
MSG_TRANS_GETRULE,
|
MSG_TRANS_GETRULE,
|
||||||
MSG_TRANS_PUTRULE,
|
MSG_TRANS_PUTRULE,
|
||||||
} from "./config";
|
} from "./config";
|
||||||
import { getSetting, getRules, matchRule } from "./libs";
|
import { getSettingWithDefault, getRulesWithDefault } from "./libs/storage";
|
||||||
import { Translator } from "./libs/translator";
|
import { Translator } from "./libs/translator";
|
||||||
import { isIframe } from "./libs/iframe";
|
import { isIframe } from "./libs/iframe";
|
||||||
|
import { matchRule } from "./libs/rules";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 入口函数
|
* 入口函数
|
||||||
*/
|
*/
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
const href = isIframe ? document.referrer : document.location.href;
|
const href = isIframe ? document.referrer : document.location.href;
|
||||||
const setting = await getSetting();
|
const setting = await getSettingWithDefault();
|
||||||
const rules = await getRules();
|
const rules = await getRulesWithDefault();
|
||||||
const rule = await matchRule(rules, href, setting);
|
const rule = await matchRule(rules, href, setting);
|
||||||
const translator = new Translator(rule, setting);
|
const translator = new Translator(rule, setting);
|
||||||
|
|
||||||
|
|||||||
@@ -20,11 +20,6 @@ export function AlertProvider({ children }) {
|
|||||||
const [severity, setSeverity] = useState("info");
|
const [severity, setSeverity] = useState("info");
|
||||||
const [message, setMessage] = useState("");
|
const [message, setMessage] = useState("");
|
||||||
|
|
||||||
const error = (msg) => showAlert(msg, "error");
|
|
||||||
const warning = (msg) => showAlert(msg, "warning");
|
|
||||||
const info = (msg) => showAlert(msg, "info");
|
|
||||||
const success = (msg) => showAlert(msg, "success");
|
|
||||||
|
|
||||||
const showAlert = (msg, type) => {
|
const showAlert = (msg, type) => {
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
setMessage(msg);
|
setMessage(msg);
|
||||||
@@ -38,6 +33,11 @@ export function AlertProvider({ children }) {
|
|||||||
setOpen(false);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const error = (msg) => showAlert(msg, "error");
|
||||||
|
const warning = (msg) => showAlert(msg, "warning");
|
||||||
|
const info = (msg) => showAlert(msg, "info");
|
||||||
|
const success = (msg) => showAlert(msg, "success");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AlertContext.Provider value={{ error, warning, info, success }}>
|
<AlertContext.Provider value={{ error, warning, info, success }}>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -1,22 +1,19 @@
|
|||||||
import { useSetting, useSettingUpdate } from "./Setting";
|
import { useCallback } from "react";
|
||||||
|
import { useSetting } from "./Setting";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 深色模式hook
|
* 深色模式hook
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function useDarkMode() {
|
export function useDarkMode() {
|
||||||
const setting = useSetting();
|
const {
|
||||||
return !!setting?.darkMode;
|
setting: { darkMode },
|
||||||
}
|
updateSetting,
|
||||||
|
} = useSetting();
|
||||||
|
|
||||||
/**
|
const toggleDarkMode = useCallback(async () => {
|
||||||
* 切换深色模式
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function useDarkModeSwitch() {
|
|
||||||
const darkMode = useDarkMode();
|
|
||||||
const updateSetting = useSettingUpdate();
|
|
||||||
return async () => {
|
|
||||||
await updateSetting({ darkMode: !darkMode });
|
await updateSetting({ darkMode: !darkMode });
|
||||||
};
|
}, [darkMode]);
|
||||||
|
|
||||||
|
return { darkMode, toggleDarkMode };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import { useFetch } from "./Fetch";
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const useI18n = () => {
|
export const useI18n = () => {
|
||||||
const { uiLang } = useSetting() ?? {};
|
const {
|
||||||
|
setting: { uiLang },
|
||||||
|
} = useSetting();
|
||||||
return (key, defaultText = "") => I18N?.[key]?.[uiLang] ?? defaultText;
|
return (key, defaultText = "") => I18N?.[key]?.[uiLang] ?? defaultText;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +1,33 @@
|
|||||||
import { STOKEY_RULES, DEFAULT_SUBRULES_LIST } from "../config";
|
import { STOKEY_RULES, DEFAULT_RULES } from "../config";
|
||||||
import storage from "../libs/storage";
|
import { useStorage } from "./Storage";
|
||||||
import { useStorages } from "./Storage";
|
|
||||||
import { trySyncRules } from "../libs/sync";
|
import { trySyncRules } from "../libs/sync";
|
||||||
import { useSync } from "./Sync";
|
import { useSync } from "./Sync";
|
||||||
import { useSetting, useSettingUpdate } from "./Setting";
|
|
||||||
import { checkRules } from "../libs/rules";
|
import { checkRules } from "../libs/rules";
|
||||||
|
import { useCallback } from "react";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 匹配规则增删改查 hook
|
* 规则 hook
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function useRules() {
|
export function useRules() {
|
||||||
const storages = useStorages();
|
const { data: list, save } = useStorage(STOKEY_RULES, DEFAULT_RULES);
|
||||||
const list = storages?.[STOKEY_RULES] || [];
|
const {
|
||||||
const sync = useSync();
|
sync: { rulesUpdateAt },
|
||||||
|
updateSync,
|
||||||
|
} = useSync();
|
||||||
|
|
||||||
const update = async (rules) => {
|
const updateRules = useCallback(
|
||||||
const updateAt = sync.opt?.rulesUpdateAt ? Date.now() : 0;
|
async (rules) => {
|
||||||
await storage.setObj(STOKEY_RULES, rules);
|
const updateAt = rulesUpdateAt ? Date.now() : 0;
|
||||||
await sync.update({ rulesUpdateAt: updateAt });
|
await save(rules);
|
||||||
|
await updateSync({ rulesUpdateAt: updateAt });
|
||||||
trySyncRules();
|
trySyncRules();
|
||||||
};
|
},
|
||||||
|
[rulesUpdateAt]
|
||||||
|
);
|
||||||
|
|
||||||
const add = async (rule) => {
|
const add = useCallback(
|
||||||
|
async (rule) => {
|
||||||
const rules = [...list];
|
const rules = [...list];
|
||||||
if (rule.pattern === "*") {
|
if (rule.pattern === "*") {
|
||||||
return;
|
return;
|
||||||
@@ -31,77 +36,90 @@ export function useRules() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
rules.unshift(rule);
|
rules.unshift(rule);
|
||||||
await update(rules);
|
await updateRules(rules);
|
||||||
};
|
},
|
||||||
|
[list, updateRules]
|
||||||
|
);
|
||||||
|
|
||||||
const del = async (pattern) => {
|
const del = useCallback(
|
||||||
|
async (pattern) => {
|
||||||
let rules = [...list];
|
let rules = [...list];
|
||||||
if (pattern === "*") {
|
if (pattern === "*") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
rules = rules.filter((item) => item.pattern !== pattern);
|
rules = rules.filter((item) => item.pattern !== pattern);
|
||||||
await update(rules);
|
await updateRules(rules);
|
||||||
};
|
},
|
||||||
|
[list, updateRules]
|
||||||
|
);
|
||||||
|
|
||||||
const put = async (pattern, obj) => {
|
const put = useCallback(
|
||||||
|
async (pattern, obj) => {
|
||||||
const rules = [...list];
|
const rules = [...list];
|
||||||
if (pattern === "*") {
|
if (pattern === "*") {
|
||||||
obj.pattern = "*";
|
obj.pattern = "*";
|
||||||
}
|
}
|
||||||
const rule = rules.find((r) => r.pattern === pattern);
|
const rule = rules.find((r) => r.pattern === pattern);
|
||||||
rule && Object.assign(rule, obj);
|
rule && Object.assign(rule, obj);
|
||||||
await update(rules);
|
await updateRules(rules);
|
||||||
};
|
},
|
||||||
|
[list, updateRules]
|
||||||
|
);
|
||||||
|
|
||||||
const merge = async (newRules) => {
|
const merge = useCallback(
|
||||||
|
async (newRules) => {
|
||||||
const rules = [...list];
|
const rules = [...list];
|
||||||
newRules = checkRules(newRules);
|
newRules = checkRules(newRules);
|
||||||
newRules.forEach((newRule) => {
|
newRules.forEach((newRule) => {
|
||||||
const rule = rules.find((oldRule) => oldRule.pattern === newRule.pattern);
|
const rule = rules.find(
|
||||||
|
(oldRule) => oldRule.pattern === newRule.pattern
|
||||||
|
);
|
||||||
if (rule) {
|
if (rule) {
|
||||||
Object.assign(rule, newRule);
|
Object.assign(rule, newRule);
|
||||||
} else {
|
} else {
|
||||||
rules.unshift(newRule);
|
rules.unshift(newRule);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await update(rules);
|
await updateRules(rules);
|
||||||
};
|
},
|
||||||
|
[list, updateRules]
|
||||||
|
);
|
||||||
|
|
||||||
return { list, add, del, put, merge };
|
return { list, add, del, put, merge };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 订阅规则
|
// * 订阅规则
|
||||||
* @returns
|
// * @returns
|
||||||
*/
|
// */
|
||||||
export function useSubrules() {
|
// export function useSubrules() {
|
||||||
const setting = useSetting();
|
// const setting = useSetting();
|
||||||
const updateSetting = useSettingUpdate();
|
// const updateSetting = useSettingUpdate();
|
||||||
const list = setting?.subrulesList || DEFAULT_SUBRULES_LIST;
|
// const list = setting?.subrulesList || DEFAULT_SUBRULES_LIST;
|
||||||
|
|
||||||
const select = async (url) => {
|
// const select = async (url) => {
|
||||||
const subrulesList = [...list];
|
// const subrulesList = [...list];
|
||||||
subrulesList.forEach((item) => {
|
// subrulesList.forEach((item) => {
|
||||||
if (item.url === url) {
|
// if (item.url === url) {
|
||||||
item.selected = true;
|
// item.selected = true;
|
||||||
} else {
|
// } else {
|
||||||
item.selected = false;
|
// item.selected = false;
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
await updateSetting({ subrulesList });
|
// await updateSetting({ subrulesList });
|
||||||
};
|
// };
|
||||||
|
|
||||||
const add = async (url) => {
|
// const add = async (url) => {
|
||||||
const subrulesList = [...list];
|
// const subrulesList = [...list];
|
||||||
subrulesList.push({ url });
|
// subrulesList.push({ url });
|
||||||
await updateSetting({ subrulesList });
|
// await updateSetting({ subrulesList });
|
||||||
};
|
// };
|
||||||
|
|
||||||
const del = async (url) => {
|
// const del = async (url) => {
|
||||||
let subrulesList = [...list];
|
// let subrulesList = [...list];
|
||||||
subrulesList = subrulesList.filter((item) => item.url !== url);
|
// subrulesList = subrulesList.filter((item) => item.url !== url);
|
||||||
await updateSetting({ subrulesList });
|
// await updateSetting({ subrulesList });
|
||||||
};
|
// };
|
||||||
|
|
||||||
return { list, select, add, del };
|
// return { list, select, add, del };
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -1,28 +1,79 @@
|
|||||||
import { STOKEY_SETTING } from "../config";
|
import { STOKEY_SETTING, DEFAULT_SETTING } from "../config";
|
||||||
import storage from "../libs/storage";
|
import { useStorage } from "./Storage";
|
||||||
import { useStorages } from "./Storage";
|
|
||||||
import { useSync } from "./Sync";
|
import { useSync } from "./Sync";
|
||||||
import { trySyncSetting } from "../libs/sync";
|
import { trySyncSetting } from "../libs/sync";
|
||||||
|
import { createContext, useCallback, useContext } from "react";
|
||||||
|
|
||||||
|
const SettingContext = createContext({
|
||||||
|
setting: null,
|
||||||
|
updateSetting: async () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
export function SettingProvider({ children }) {
|
||||||
|
const { data, update } = useStorage(STOKEY_SETTING, DEFAULT_SETTING);
|
||||||
|
const {
|
||||||
|
sync: { settingUpdateAt },
|
||||||
|
updateSync,
|
||||||
|
} = useSync();
|
||||||
|
|
||||||
|
const updateSetting = useCallback(
|
||||||
|
async (obj) => {
|
||||||
|
const updateAt = settingUpdateAt ? Date.now() : 0;
|
||||||
|
await update(obj);
|
||||||
|
await updateSync({ settingUpdateAt: updateAt });
|
||||||
|
trySyncSetting();
|
||||||
|
},
|
||||||
|
[settingUpdateAt]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingContext.Provider
|
||||||
|
value={{
|
||||||
|
setting: data,
|
||||||
|
updateSetting,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</SettingContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置hook
|
* 设置 hook
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function useSetting() {
|
export function useSetting() {
|
||||||
const storages = useStorages();
|
return useContext(SettingContext);
|
||||||
return storages?.[STOKEY_SETTING];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// export function useSetting() {
|
||||||
* 更新设置
|
// const [setting,setSeting]= useState(null);
|
||||||
* @returns
|
// useEffect(()=>{
|
||||||
*/
|
// (async ()=>{
|
||||||
export function useSettingUpdate() {
|
// const
|
||||||
const sync = useSync();
|
// })()
|
||||||
return async (obj) => {
|
// },[])
|
||||||
const updateAt = sync.opt?.settingUpdateAt ? Date.now() : 0;
|
// }
|
||||||
await storage.putObj(STOKEY_SETTING, obj);
|
|
||||||
await sync.update({ settingUpdateAt: updateAt });
|
// /**
|
||||||
trySyncSetting();
|
// * 设置hook
|
||||||
};
|
// * @returns
|
||||||
}
|
// */
|
||||||
|
// export function useSetting() {
|
||||||
|
// const storages = useStorages();
|
||||||
|
// return storages?.[STOKEY_SETTING];
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 更新设置
|
||||||
|
// * @returns
|
||||||
|
// */
|
||||||
|
// export function useSettingUpdate() {
|
||||||
|
// const sync = useSync();
|
||||||
|
// return async (obj) => {
|
||||||
|
// const updateAt = sync.opt?.settingUpdateAt ? Date.now() : 0;
|
||||||
|
// await storage.putObj(STOKEY_SETTING, obj);
|
||||||
|
// await sync.update({ settingUpdateAt: updateAt });
|
||||||
|
// trySyncSetting();
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|||||||
@@ -1,91 +1,115 @@
|
|||||||
import { createContext, useContext, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { browser, isExt, isGm, isWeb } from "../libs/browser";
|
import { storage } from "../libs/storage";
|
||||||
import {
|
|
||||||
STOKEY_SETTING,
|
|
||||||
STOKEY_RULES,
|
|
||||||
STOKEY_SYNC,
|
|
||||||
DEFAULT_SETTING,
|
|
||||||
DEFAULT_RULES,
|
|
||||||
DEFAULT_SYNC,
|
|
||||||
} from "../config";
|
|
||||||
import storage from "../libs/storage";
|
|
||||||
|
|
||||||
/**
|
export function useStorage(key, defaultVal = null) {
|
||||||
* 默认配置
|
const [data, setData] = useState(defaultVal);
|
||||||
*/
|
|
||||||
export const defaultStorage = {
|
|
||||||
[STOKEY_SETTING]: DEFAULT_SETTING,
|
|
||||||
[STOKEY_RULES]: DEFAULT_RULES,
|
|
||||||
[STOKEY_SYNC]: DEFAULT_SYNC,
|
|
||||||
};
|
|
||||||
|
|
||||||
const activeKeys = Object.keys(defaultStorage);
|
const save = useCallback(
|
||||||
|
async (val) => {
|
||||||
const StoragesContext = createContext(null);
|
setData(val);
|
||||||
|
await storage.setObj(key, val);
|
||||||
export function StoragesProvider({ children }) {
|
|
||||||
const [storages, setStorages] = useState(null);
|
|
||||||
|
|
||||||
const handleChanged = (changes) => {
|
|
||||||
if (isWeb || isGm) {
|
|
||||||
const { key, oldValue, newValue } = changes;
|
|
||||||
changes = {
|
|
||||||
[key]: {
|
|
||||||
oldValue,
|
|
||||||
newValue,
|
|
||||||
},
|
},
|
||||||
};
|
[key]
|
||||||
}
|
);
|
||||||
const newStorages = {};
|
|
||||||
Object.entries(changes)
|
const update = useCallback(
|
||||||
.filter(
|
async (obj) => {
|
||||||
([key, { oldValue, newValue }]) =>
|
setData((pre) => ({ ...pre, ...obj }));
|
||||||
activeKeys.includes(key) && oldValue !== newValue
|
await storage.putObj(key, obj);
|
||||||
)
|
},
|
||||||
.forEach(([key, { newValue }]) => {
|
[key]
|
||||||
newStorages[key] = JSON.parse(newValue);
|
);
|
||||||
});
|
|
||||||
if (Object.keys(newStorages).length !== 0) {
|
const remove = useCallback(async () => {
|
||||||
setStorages((pre) => ({ ...pre, ...newStorages }));
|
setData(null);
|
||||||
}
|
await storage.del(key);
|
||||||
};
|
}, [key]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 首次从storage同步配置到内存
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const curStorages = {};
|
setData(await storage.getObj(key));
|
||||||
for (const key of activeKeys) {
|
|
||||||
const val = await storage.get(key);
|
|
||||||
if (val) {
|
|
||||||
curStorages[key] = JSON.parse(val);
|
|
||||||
} else {
|
|
||||||
await storage.setObj(key, defaultStorage[key]);
|
|
||||||
curStorages[key] = defaultStorage[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setStorages(curStorages);
|
|
||||||
})();
|
})();
|
||||||
|
}, [key]);
|
||||||
|
|
||||||
// 监听storage,并同步到内存中
|
return { data, save, update, remove };
|
||||||
storage.onChanged(handleChanged);
|
|
||||||
|
|
||||||
// 解除监听
|
|
||||||
return () => {
|
|
||||||
if (isExt) {
|
|
||||||
browser.storage.onChanged.removeListener(handleChanged);
|
|
||||||
} else {
|
|
||||||
window.removeEventListener("storage", handleChanged);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StoragesContext.Provider value={storages}>
|
|
||||||
{children}
|
|
||||||
</StoragesContext.Provider>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useStorages() {
|
// /**
|
||||||
return useContext(StoragesContext);
|
// * 默认配置
|
||||||
}
|
// */
|
||||||
|
// export const defaultStorage = {
|
||||||
|
// [STOKEY_SETTING]: DEFAULT_SETTING,
|
||||||
|
// [STOKEY_RULES]: DEFAULT_RULES,
|
||||||
|
// [STOKEY_SYNC]: DEFAULT_SYNC,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const activeKeys = Object.keys(defaultStorage);
|
||||||
|
|
||||||
|
// const StoragesContext = createContext(null);
|
||||||
|
|
||||||
|
// export function StoragesProvider({ children }) {
|
||||||
|
// const [storages, setStorages] = useState(null);
|
||||||
|
|
||||||
|
// const handleChanged = (changes) => {
|
||||||
|
// if (isWeb || isGm) {
|
||||||
|
// const { key, oldValue, newValue } = changes;
|
||||||
|
// changes = {
|
||||||
|
// [key]: {
|
||||||
|
// oldValue,
|
||||||
|
// newValue,
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// const newStorages = {};
|
||||||
|
// Object.entries(changes)
|
||||||
|
// .filter(
|
||||||
|
// ([key, { oldValue, newValue }]) =>
|
||||||
|
// activeKeys.includes(key) && oldValue !== newValue
|
||||||
|
// )
|
||||||
|
// .forEach(([key, { newValue }]) => {
|
||||||
|
// newStorages[key] = JSON.parse(newValue);
|
||||||
|
// });
|
||||||
|
// if (Object.keys(newStorages).length !== 0) {
|
||||||
|
// setStorages((pre) => ({ ...pre, ...newStorages }));
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// // 首次从storage同步配置到内存
|
||||||
|
// (async () => {
|
||||||
|
// const curStorages = {};
|
||||||
|
// for (const key of activeKeys) {
|
||||||
|
// const val = await storage.get(key);
|
||||||
|
// if (val) {
|
||||||
|
// curStorages[key] = JSON.parse(val);
|
||||||
|
// } else {
|
||||||
|
// await storage.setObj(key, defaultStorage[key]);
|
||||||
|
// curStorages[key] = defaultStorage[key];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// setStorages(curStorages);
|
||||||
|
// })();
|
||||||
|
|
||||||
|
// // 监听storage,并同步到内存中
|
||||||
|
// storage.onChanged(handleChanged);
|
||||||
|
|
||||||
|
// // 解除监听
|
||||||
|
// return () => {
|
||||||
|
// if (isExt) {
|
||||||
|
// browser.storage.onChanged.removeListener(handleChanged);
|
||||||
|
// } else {
|
||||||
|
// window.removeEventListener("storage", handleChanged);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
// }, []);
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <StoragesContext.Provider value={storages}>
|
||||||
|
// {children}
|
||||||
|
// </StoragesContext.Provider>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export function useStorages() {
|
||||||
|
// return useContext(StoragesContext);
|
||||||
|
// }
|
||||||
|
|||||||
47
src/hooks/SubRules.js
Normal file
47
src/hooks/SubRules.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { DEFAULT_SUBRULES_LIST } from "../config";
|
||||||
|
import { useSetting } from "./Setting";
|
||||||
|
import { useCallback } from "react";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订阅规则
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function useSubRules() {
|
||||||
|
const { data: setting, update: updateSetting } = useSetting();
|
||||||
|
const list = setting?.subRulesList || DEFAULT_SUBRULES_LIST;
|
||||||
|
|
||||||
|
const select = useCallback(
|
||||||
|
async (url) => {
|
||||||
|
const subRulesList = [...list];
|
||||||
|
subRulesList.forEach((item) => {
|
||||||
|
if (item.url === url) {
|
||||||
|
item.selected = true;
|
||||||
|
} else {
|
||||||
|
item.selected = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await updateSetting({ subRulesList });
|
||||||
|
},
|
||||||
|
[list]
|
||||||
|
);
|
||||||
|
|
||||||
|
const add = useCallback(
|
||||||
|
async (url) => {
|
||||||
|
const subRulesList = [...list];
|
||||||
|
subRulesList.push({ url, selected: false });
|
||||||
|
await updateSetting({ subRulesList });
|
||||||
|
},
|
||||||
|
[list]
|
||||||
|
);
|
||||||
|
|
||||||
|
const del = useCallback(
|
||||||
|
async (url) => {
|
||||||
|
let subRulesList = [...list];
|
||||||
|
subRulesList = subRulesList.filter((item) => item.url !== url);
|
||||||
|
await updateSetting({ subRulesList });
|
||||||
|
},
|
||||||
|
[list]
|
||||||
|
);
|
||||||
|
|
||||||
|
return { list, select, add, del };
|
||||||
|
}
|
||||||
@@ -1,20 +1,22 @@
|
|||||||
import { useCallback } from "react";
|
import { STOKEY_SYNC, DEFAULT_SYNC } from "../config";
|
||||||
import { STOKEY_SYNC } from "../config";
|
import { useStorage } from "./Storage";
|
||||||
import storage from "../libs/storage";
|
|
||||||
import { useStorages } from "./Storage";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sync hook
|
* sync hook
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function useSync() {
|
export function useSync() {
|
||||||
const storages = useStorages();
|
const { data, update } = useStorage(STOKEY_SYNC, DEFAULT_SYNC);
|
||||||
const opt = storages?.[STOKEY_SYNC];
|
return { sync: data, updateSync: update };
|
||||||
const update = useCallback(async (obj) => {
|
|
||||||
await storage.putObj(STOKEY_SYNC, obj);
|
|
||||||
}, []);
|
|
||||||
return {
|
|
||||||
opt,
|
|
||||||
update,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
// export function useSync() {
|
||||||
|
// const storages = useStorages();
|
||||||
|
// const opt = storages?.[STOKEY_SYNC];
|
||||||
|
// const update = useCallback(async (obj) => {
|
||||||
|
// await storage.putObj(STOKEY_SYNC, obj);
|
||||||
|
// }, []);
|
||||||
|
// return {
|
||||||
|
// opt,
|
||||||
|
// update,
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import { THEME_DARK, THEME_LIGHT } from "../config";
|
|||||||
* @param {*} param0
|
* @param {*} param0
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export default function MuiThemeProvider({ children, options }) {
|
export default function Theme({ children, options }) {
|
||||||
const darkMode = useDarkMode();
|
const { darkMode } = useDarkMode();
|
||||||
const theme = useMemo(() => {
|
const theme = useMemo(() => {
|
||||||
return createTheme({
|
return createTheme({
|
||||||
palette: {
|
palette: {
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { detectLang } from "../libs";
|
import { detectLang } from "../libs/browser";
|
||||||
import { apiTranslate } from "../apis";
|
import { apiTranslate } from "../apis";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 翻译hook
|
* 翻译hook
|
||||||
* @param {*} q
|
* @param {*} q
|
||||||
* @param {*} rule
|
* @param {*} rule
|
||||||
|
* @param {*} setting
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function useTranslate(q, rule) {
|
export function useTranslate(q, rule, setting) {
|
||||||
const [text, setText] = useState("");
|
const [text, setText] = useState("");
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [sameLang, setSamelang] = useState(false);
|
const [sameLang, setSamelang] = useState(false);
|
||||||
@@ -30,6 +31,7 @@ export function useTranslate(q, rule) {
|
|||||||
q,
|
q,
|
||||||
fromLang,
|
fromLang,
|
||||||
toLang,
|
toLang,
|
||||||
|
setting,
|
||||||
});
|
});
|
||||||
setText(trText);
|
setText(trText);
|
||||||
setSamelang(isSame);
|
setSamelang(isSame);
|
||||||
@@ -40,7 +42,7 @@ export function useTranslate(q, rule) {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}, [q, translator, fromLang, toLang]);
|
}, [q, translator, fromLang, toLang, setting]);
|
||||||
|
|
||||||
return { text, sameLang, loading };
|
return { text, sameLang, loading };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { CLIENT_EXTS, CLIENT_USERSCRIPT, CLIENT_WEB } from "../config";
|
// import { CLIENT_EXTS, CLIENT_USERSCRIPT, CLIENT_WEB } from "../config";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 浏览器兼容插件,另可用于判断是插件模式还是网页模式,方便开发
|
* 浏览器兼容插件,另可用于判断是插件模式还是网页模式,方便开发
|
||||||
@@ -13,7 +13,21 @@ function _browser() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const browser = _browser();
|
export const browser = _browser();
|
||||||
export const client = process.env.REACT_APP_CLIENT;
|
// export const client = process.env.REACT_APP_CLIENT;
|
||||||
export const isExt = CLIENT_EXTS.includes(client);
|
// export const isExt = CLIENT_EXTS.includes(client);
|
||||||
export const isGm = client === CLIENT_USERSCRIPT;
|
// export const isGm = client === CLIENT_USERSCRIPT;
|
||||||
export const isWeb = client === CLIENT_WEB;
|
// export const isWeb = client === CLIENT_WEB;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本地语言识别
|
||||||
|
* @param {*} q
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const detectLang = async (q) => {
|
||||||
|
try {
|
||||||
|
const res = await browser?.i18n?.detectLanguage(q);
|
||||||
|
return res?.languages?.[0]?.language;
|
||||||
|
} catch (err) {
|
||||||
|
console.log("[detect lang]", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
6
src/libs/client.js
Normal file
6
src/libs/client.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { CLIENT_EXTS, CLIENT_USERSCRIPT, CLIENT_WEB } from "../config";
|
||||||
|
|
||||||
|
export const client = process.env.REACT_APP_CLIENT;
|
||||||
|
export const isExt = CLIENT_EXTS.includes(client);
|
||||||
|
export const isGm = client === CLIENT_USERSCRIPT;
|
||||||
|
export const isWeb = client === CLIENT_WEB;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { isExt, isGm } from "./browser";
|
import { isExt, isGm } from "./client";
|
||||||
import { sendMsg } from "./msg";
|
import { sendMsg } from "./msg";
|
||||||
import { taskPool } from "./pool";
|
import { taskPool } from "./pool";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,108 +1,12 @@
|
|||||||
import storage from "./storage";
|
import { CACHE_NAME } from "../config";
|
||||||
import {
|
|
||||||
DEFAULT_SETTING,
|
|
||||||
STOKEY_SETTING,
|
|
||||||
STOKEY_RULES,
|
|
||||||
STOKEY_FAB,
|
|
||||||
GLOBLA_RULE,
|
|
||||||
GLOBAL_KEY,
|
|
||||||
DEFAULT_SUBRULES_LIST,
|
|
||||||
} from "../config";
|
|
||||||
import { browser } from "./browser";
|
|
||||||
import { isMatch } from "./utils";
|
|
||||||
import { loadSubRules } from "./rules";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询storage中的设置
|
* 清除缓存数据
|
||||||
* @returns
|
|
||||||
*/
|
*/
|
||||||
export const getSetting = async () => ({
|
export const tryClearCaches = async () => {
|
||||||
...DEFAULT_SETTING,
|
|
||||||
...((await storage.getObj(STOKEY_SETTING)) || {}),
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询规则列表
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const getRules = async () => (await storage.getObj(STOKEY_RULES)) || [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询fab位置信息
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const getFab = async () => (await storage.getObj(STOKEY_FAB)) || {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置fab位置信息
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const setFab = async (obj) => await storage.setObj(STOKEY_FAB, obj);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据href匹配规则
|
|
||||||
* @param {*} rules
|
|
||||||
* @param {string} href
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const matchRule = async (
|
|
||||||
rules,
|
|
||||||
href,
|
|
||||||
{ injectRules = true, subrulesList = DEFAULT_SUBRULES_LIST }
|
|
||||||
) => {
|
|
||||||
rules = [...rules];
|
|
||||||
if (injectRules) {
|
|
||||||
try {
|
try {
|
||||||
const selectedSub = subrulesList.find((item) => item.selected);
|
caches.delete(CACHE_NAME);
|
||||||
if (selectedSub?.url) {
|
|
||||||
const subRules = await loadSubRules(selectedSub.url);
|
|
||||||
rules.splice(-1, 0, ...subRules);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("[load injectRules]", err);
|
console.log("[clean caches]", err.message);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const rule = rules.find((r) =>
|
|
||||||
r.pattern.split(",").some((p) => isMatch(href, p.trim()))
|
|
||||||
);
|
|
||||||
|
|
||||||
const globalRule =
|
|
||||||
rules.find((r) => r.pattern.split(",").some((p) => p.trim() === "*")) ||
|
|
||||||
GLOBLA_RULE;
|
|
||||||
|
|
||||||
if (!rule) {
|
|
||||||
return globalRule;
|
|
||||||
}
|
|
||||||
|
|
||||||
rule.selector =
|
|
||||||
rule?.selector?.trim() ||
|
|
||||||
globalRule?.selector?.trim() ||
|
|
||||||
GLOBLA_RULE.selector;
|
|
||||||
|
|
||||||
rule.bgColor = rule?.bgColor?.trim() || globalRule?.bgColor?.trim();
|
|
||||||
|
|
||||||
["translator", "fromLang", "toLang", "textStyle", "transOpen"].forEach(
|
|
||||||
(key) => {
|
|
||||||
if (rule[key] === GLOBAL_KEY) {
|
|
||||||
rule[key] = globalRule[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return rule;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 本地语言识别
|
|
||||||
* @param {*} q
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const detectLang = async (q) => {
|
|
||||||
try {
|
|
||||||
const res = await browser?.i18n?.detectLanguage(q);
|
|
||||||
return res?.languages?.[0]?.language;
|
|
||||||
} catch (err) {
|
|
||||||
console.log("[detect lang]", err);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,18 +1,78 @@
|
|||||||
import storage from "./storage";
|
|
||||||
import { fetchPolyfill } from "./fetch";
|
|
||||||
import { matchValue, type } from "./utils";
|
|
||||||
import {
|
import {
|
||||||
STOKEY_RULESCACHE_PREFIX,
|
getSyncWithDefault,
|
||||||
|
updateSync,
|
||||||
|
getSubRulesWithDefault,
|
||||||
|
getSubRules,
|
||||||
|
delSubRules,
|
||||||
|
setSubRules,
|
||||||
|
} from "./storage";
|
||||||
|
import { fetchPolyfill } from "./fetch";
|
||||||
|
import { matchValue, type, isMatch } from "./utils";
|
||||||
|
import {
|
||||||
GLOBAL_KEY,
|
GLOBAL_KEY,
|
||||||
OPT_TRANS_ALL,
|
OPT_TRANS_ALL,
|
||||||
OPT_STYLE_ALL,
|
OPT_STYLE_ALL,
|
||||||
OPT_LANGS_FROM,
|
OPT_LANGS_FROM,
|
||||||
OPT_LANGS_TO,
|
OPT_LANGS_TO,
|
||||||
|
GLOBLA_RULE,
|
||||||
|
DEFAULT_SUBRULES_LIST,
|
||||||
} from "../config";
|
} from "../config";
|
||||||
import { syncOpt } from "./sync";
|
|
||||||
|
|
||||||
const fromLangs = OPT_LANGS_FROM.map((item) => item[0]);
|
// import { syncOpt } from "./sync";
|
||||||
const toLangs = OPT_LANGS_TO.map((item) => item[0]);
|
|
||||||
|
/**
|
||||||
|
* 根据href匹配规则
|
||||||
|
* @param {*} rules
|
||||||
|
* @param {string} href
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const matchRule = async (
|
||||||
|
rules,
|
||||||
|
href,
|
||||||
|
{ injectRules = true, subrulesList = DEFAULT_SUBRULES_LIST }
|
||||||
|
) => {
|
||||||
|
rules = [...rules];
|
||||||
|
if (injectRules) {
|
||||||
|
try {
|
||||||
|
const selectedSub = subrulesList.find((item) => item.selected);
|
||||||
|
if (selectedSub?.url) {
|
||||||
|
const subRules = await loadSubRules(selectedSub.url);
|
||||||
|
rules.splice(-1, 0, ...subRules);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("[load injectRules]", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rule = rules.find((r) =>
|
||||||
|
r.pattern.split(",").some((p) => isMatch(href, p.trim()))
|
||||||
|
);
|
||||||
|
|
||||||
|
const globalRule =
|
||||||
|
rules.find((r) => r.pattern.split(",").some((p) => p.trim() === "*")) ||
|
||||||
|
GLOBLA_RULE;
|
||||||
|
|
||||||
|
if (!rule) {
|
||||||
|
return globalRule;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule.selector =
|
||||||
|
rule?.selector?.trim() ||
|
||||||
|
globalRule?.selector?.trim() ||
|
||||||
|
GLOBLA_RULE.selector;
|
||||||
|
|
||||||
|
rule.bgColor = rule?.bgColor?.trim() || globalRule?.bgColor?.trim();
|
||||||
|
|
||||||
|
["translator", "fromLang", "toLang", "textStyle", "transOpen"].forEach(
|
||||||
|
(key) => {
|
||||||
|
if (rule[key] === GLOBAL_KEY) {
|
||||||
|
rule[key] = globalRule[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return rule;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查过滤rules
|
* 检查过滤rules
|
||||||
@@ -27,6 +87,8 @@ export const checkRules = (rules) => {
|
|||||||
throw new Error("data error");
|
throw new Error("data error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fromLangs = OPT_LANGS_FROM.map((item) => item[0]);
|
||||||
|
const toLangs = OPT_LANGS_TO.map((item) => item[0]);
|
||||||
const patternSet = new Set();
|
const patternSet = new Set();
|
||||||
rules = rules
|
rules = rules
|
||||||
.filter((rule) => type(rule) === "object")
|
.filter((rule) => type(rule) === "object")
|
||||||
@@ -62,84 +124,18 @@ export const checkRules = (rules) => {
|
|||||||
return rules;
|
return rules;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 订阅规则的本地缓存
|
// * 订阅规则的本地缓存
|
||||||
*/
|
// */
|
||||||
export const rulesCache = {
|
// export const rulesCache = {
|
||||||
fetch: async (url, isBg = false) => {
|
// fetch: async (url, isBg = false) => {
|
||||||
const res = await fetchPolyfill(url, { isBg });
|
// const res = await fetchPolyfill(url, { isBg });
|
||||||
const rules = checkRules(res).filter(
|
// const rules = checkRules(res).filter(
|
||||||
(rule) => rule.pattern.replaceAll(GLOBAL_KEY, "") !== ""
|
// (rule) => rule.pattern.replaceAll(GLOBAL_KEY, "") !== ""
|
||||||
);
|
// );
|
||||||
return rules;
|
// return rules;
|
||||||
},
|
// },
|
||||||
set: async (url, rules) => {
|
// set: (url, rules) => setSubRules(url, rules),
|
||||||
await storage.setObj(`${STOKEY_RULESCACHE_PREFIX}${url}`, rules);
|
// get: (url) => getSubRulesWithDefault(url),
|
||||||
},
|
// del: (url) => delSubRules(url),
|
||||||
get: async (url) => {
|
// };
|
||||||
return await storage.getObj(`${STOKEY_RULESCACHE_PREFIX}${url}`);
|
|
||||||
},
|
|
||||||
del: async (url) => {
|
|
||||||
await storage.del(`${STOKEY_RULESCACHE_PREFIX}${url}`);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同步订阅规则
|
|
||||||
* @param {*} url
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const syncSubRules = async (url, isBg = false) => {
|
|
||||||
const rules = await rulesCache.fetch(url, isBg);
|
|
||||||
if (rules.length > 0) {
|
|
||||||
await rulesCache.set(url, rules);
|
|
||||||
}
|
|
||||||
return rules;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同步所有订阅规则
|
|
||||||
* @param {*} url
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const syncAllSubRules = async (subrulesList, isBg = false) => {
|
|
||||||
for (let subrules of subrulesList) {
|
|
||||||
try {
|
|
||||||
await syncSubRules(subrules.url, isBg);
|
|
||||||
} catch (err) {
|
|
||||||
console.log(`[sync subrule error]: ${subrules.url}`, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据时间同步所有订阅规则
|
|
||||||
* @param {*} url
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const trySyncAllSubRules = async ({ subrulesList }, isBg = false) => {
|
|
||||||
try {
|
|
||||||
const { subRulesSyncAt } = await syncOpt.load();
|
|
||||||
const now = Date.now();
|
|
||||||
const interval = 24 * 60 * 60 * 1000; // 间隔一天
|
|
||||||
if (now - subRulesSyncAt > interval) {
|
|
||||||
await syncAllSubRules(subrulesList, isBg);
|
|
||||||
await syncOpt.update({ subRulesSyncAt: now });
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.log("[try sync all subrules]", err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从缓存或远程加载订阅规则
|
|
||||||
* @param {*} url
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const loadSubRules = async (url) => {
|
|
||||||
const rules = await rulesCache.get(url);
|
|
||||||
if (rules?.length) {
|
|
||||||
return rules;
|
|
||||||
}
|
|
||||||
return await syncSubRules(url);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,28 +1,41 @@
|
|||||||
import { browser, isExt, isGm } from "./browser";
|
import {
|
||||||
|
STOKEY_SETTING,
|
||||||
|
STOKEY_RULES,
|
||||||
|
STOKEY_FAB,
|
||||||
|
STOKEY_SYNC,
|
||||||
|
STOKEY_MSAUTH,
|
||||||
|
STOKEY_RULESCACHE_PREFIX,
|
||||||
|
DEFAULT_SETTING,
|
||||||
|
DEFAULT_RULES,
|
||||||
|
DEFAULT_SYNC,
|
||||||
|
BUILTIN_RULES,
|
||||||
|
} from "../config";
|
||||||
|
import { browser, isExt, isGm } from "./client";
|
||||||
|
// import { APP_NAME } from "../config/app";
|
||||||
|
|
||||||
async function set(key, val) {
|
async function set(key, val) {
|
||||||
if (isExt) {
|
if (isExt) {
|
||||||
await browser.storage.local.set({ [key]: val });
|
await browser.storage.local.set({ [key]: val });
|
||||||
} else if (isGm) {
|
} else if (isGm) {
|
||||||
const oldValue = await (window.KISS_GM || GM).getValue(key);
|
// const oldValue = await (window.KISS_GM || GM).getValue(key);
|
||||||
await (window.KISS_GM || GM).setValue(key, val);
|
await (window.KISS_GM || GM).setValue(key, val);
|
||||||
window.dispatchEvent(
|
// window.dispatchEvent(
|
||||||
new StorageEvent("storage", {
|
// new StorageEvent("storage", {
|
||||||
key,
|
// key,
|
||||||
oldValue,
|
// oldValue,
|
||||||
newValue: val,
|
// newValue: val,
|
||||||
})
|
// })
|
||||||
);
|
// );
|
||||||
} else {
|
} else {
|
||||||
const oldValue = window.localStorage.getItem(key);
|
// const oldValue = window.localStorage.getItem(key);
|
||||||
window.localStorage.setItem(key, val);
|
window.localStorage.setItem(key, val);
|
||||||
window.dispatchEvent(
|
// window.dispatchEvent(
|
||||||
new StorageEvent("storage", {
|
// new StorageEvent("storage", {
|
||||||
key,
|
// key,
|
||||||
oldValue,
|
// oldValue,
|
||||||
newValue: val,
|
// newValue: val,
|
||||||
})
|
// })
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,25 +54,25 @@ async function del(key) {
|
|||||||
if (isExt) {
|
if (isExt) {
|
||||||
await browser.storage.local.remove([key]);
|
await browser.storage.local.remove([key]);
|
||||||
} else if (isGm) {
|
} else if (isGm) {
|
||||||
const oldValue = await (window.KISS_GM || GM).getValue(key);
|
// const oldValue = await (window.KISS_GM || GM).getValue(key);
|
||||||
await (window.KISS_GM || GM).deleteValue(key);
|
await (window.KISS_GM || GM).deleteValue(key);
|
||||||
window.dispatchEvent(
|
// window.dispatchEvent(
|
||||||
new StorageEvent("storage", {
|
// new StorageEvent("storage", {
|
||||||
key,
|
// key,
|
||||||
oldValue,
|
// oldValue,
|
||||||
newValue: null,
|
// newValue: null,
|
||||||
})
|
// })
|
||||||
);
|
// );
|
||||||
} else {
|
} else {
|
||||||
const oldValue = window.localStorage.getItem(key);
|
// const oldValue = window.localStorage.getItem(key);
|
||||||
window.localStorage.removeItem(key);
|
window.localStorage.removeItem(key);
|
||||||
window.dispatchEvent(
|
// window.dispatchEvent(
|
||||||
new StorageEvent("storage", {
|
// new StorageEvent("storage", {
|
||||||
key,
|
// key,
|
||||||
oldValue,
|
// oldValue,
|
||||||
newValue: null,
|
// newValue: null,
|
||||||
})
|
// })
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,22 +96,22 @@ async function putObj(key, obj) {
|
|||||||
await setObj(key, { ...cur, ...obj });
|
await setObj(key, { ...cur, ...obj });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 监听storage事件
|
// * 监听storage事件
|
||||||
* @param {*} handleChanged
|
// * @param {*} handleChanged
|
||||||
*/
|
// */
|
||||||
function onChanged(handleChanged) {
|
// function onChanged(handleChanged) {
|
||||||
if (isExt) {
|
// if (isExt) {
|
||||||
browser.storage.onChanged.addListener(handleChanged);
|
// browser.storage.onChanged.addListener(handleChanged);
|
||||||
} else {
|
// } else {
|
||||||
window.addEventListener("storage", handleChanged);
|
// window.addEventListener("storage", handleChanged);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对storage的封装
|
* 对storage的封装
|
||||||
*/
|
*/
|
||||||
const storage = {
|
export const storage = {
|
||||||
get,
|
get,
|
||||||
set,
|
set,
|
||||||
del,
|
del,
|
||||||
@@ -106,7 +119,70 @@ const storage = {
|
|||||||
trySetObj,
|
trySetObj,
|
||||||
getObj,
|
getObj,
|
||||||
putObj,
|
putObj,
|
||||||
onChanged,
|
// onChanged,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default storage;
|
/**
|
||||||
|
* 设置信息
|
||||||
|
*/
|
||||||
|
export const getSetting = () => getObj(STOKEY_SETTING);
|
||||||
|
export const getSettingWithDefault = async () => ({
|
||||||
|
...DEFAULT_SETTING,
|
||||||
|
...((await getSetting()) || {}),
|
||||||
|
});
|
||||||
|
export const setSetting = (val) => setObj(STOKEY_SETTING, val);
|
||||||
|
export const updateSetting = (obj) => putObj(STOKEY_SETTING, obj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 规则列表
|
||||||
|
*/
|
||||||
|
export const getRules = () => getObj(STOKEY_RULES);
|
||||||
|
export const getRulesWithDefault = async () =>
|
||||||
|
(await getRules()) || DEFAULT_RULES;
|
||||||
|
export const setRules = (val) => setObj(STOKEY_RULES, val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订阅规则
|
||||||
|
*/
|
||||||
|
export const getSubRules = (url) => getObj(STOKEY_RULESCACHE_PREFIX + url);
|
||||||
|
export const getSubRulesWithDefault = async () => (await getSubRules()) || [];
|
||||||
|
export const delSubRules = (url) => del(STOKEY_RULESCACHE_PREFIX + url);
|
||||||
|
export const setSubRules = (url, val) =>
|
||||||
|
setObj(STOKEY_RULESCACHE_PREFIX + url, val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fab位置
|
||||||
|
*/
|
||||||
|
export const getFab = () => getObj(STOKEY_FAB);
|
||||||
|
export const getFabWithDefault = async () => (await getFab()) || {};
|
||||||
|
export const setFab = (obj) => setObj(STOKEY_FAB, obj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据同步
|
||||||
|
*/
|
||||||
|
export const getSync = () => getObj(STOKEY_SYNC);
|
||||||
|
export const getSyncWithDefault = async () => (await getSync()) || DEFAULT_SYNC;
|
||||||
|
export const updateSync = (obj) => putObj(STOKEY_SYNC, obj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ms auth
|
||||||
|
*/
|
||||||
|
export const getMsauth = () => getObj(STOKEY_MSAUTH);
|
||||||
|
export const setMsauth = (val) => setObj(STOKEY_MSAUTH, val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存入默认数据
|
||||||
|
*/
|
||||||
|
export const tryInitDefaultData = async () => {
|
||||||
|
try {
|
||||||
|
await trySetObj(STOKEY_SETTING, DEFAULT_SETTING);
|
||||||
|
await trySetObj(STOKEY_RULES, DEFAULT_RULES);
|
||||||
|
await trySetObj(STOKEY_SYNC, DEFAULT_SYNC);
|
||||||
|
await trySetObj(
|
||||||
|
`${STOKEY_RULESCACHE_PREFIX}${process.env.REACT_APP_RULESURL}`,
|
||||||
|
BUILTIN_RULES
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
console.log("[init default]", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
62
src/libs/subRules.js
Normal file
62
src/libs/subRules.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { getSyncWithDefault, updateSync } from "./storage";
|
||||||
|
import { apiFetchRules } from "../apis";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步订阅规则
|
||||||
|
* @param {*} url
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const syncSubRules = async (url, isBg = false) => {
|
||||||
|
const rules = await apiFetchRules(url, isBg);
|
||||||
|
if (rules.length > 0) {
|
||||||
|
await rulesCache.set(url, rules);
|
||||||
|
}
|
||||||
|
return rules;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步所有订阅规则
|
||||||
|
* @param {*} url
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const syncAllSubRules = async (subrulesList, isBg = false) => {
|
||||||
|
for (let subrules of subrulesList) {
|
||||||
|
try {
|
||||||
|
await syncSubRules(subrules.url, isBg);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(`[sync subrule error]: ${subrules.url}`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据时间同步所有订阅规则
|
||||||
|
* @param {*} url
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const trySyncAllSubRules = async ({ subrulesList }, isBg = false) => {
|
||||||
|
try {
|
||||||
|
const { subRulesSyncAt } = await getSyncWithDefault();
|
||||||
|
const now = Date.now();
|
||||||
|
const interval = 24 * 60 * 60 * 1000; // 间隔一天
|
||||||
|
if (now - subRulesSyncAt > interval) {
|
||||||
|
await syncAllSubRules(subrulesList, isBg);
|
||||||
|
await updateSync({ subRulesSyncAt: now });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("[try sync all subrules]", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从缓存或远程加载订阅规则
|
||||||
|
* @param {*} url
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const loadOrFetchSubRules = async (url) => {
|
||||||
|
const rules = await apiFetchRules(url);
|
||||||
|
if (rules?.length) {
|
||||||
|
return rules;
|
||||||
|
}
|
||||||
|
return await syncSubRules(url);
|
||||||
|
};
|
||||||
@@ -8,27 +8,27 @@ import {
|
|||||||
STOKEY_RULES,
|
STOKEY_RULES,
|
||||||
KV_SALT_SHARE,
|
KV_SALT_SHARE,
|
||||||
} from "../config";
|
} from "../config";
|
||||||
import storage from "../libs/storage";
|
import { storage, getSyncWithDefault, updateSync } from "../libs/storage";
|
||||||
import { getSetting, getRules } from ".";
|
import { getSetting, getRules } from ".";
|
||||||
import { apiSyncData } from "../apis";
|
import { apiSyncData } from "../apis";
|
||||||
import { sha256 } from "./utils";
|
import { sha256 } from "./utils";
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 同步相关数据
|
// * 同步相关数据
|
||||||
*/
|
// */
|
||||||
export const syncOpt = {
|
// export const syncOpt = {
|
||||||
load: async () => (await storage.getObj(STOKEY_SYNC)) || DEFAULT_SYNC,
|
// load: async () => (await storage.getObj(STOKEY_SYNC)) || DEFAULT_SYNC,
|
||||||
update: async (obj) => {
|
// update: async (obj) => {
|
||||||
await storage.putObj(STOKEY_SYNC, obj);
|
// await storage.putObj(STOKEY_SYNC, obj);
|
||||||
},
|
// },
|
||||||
};
|
// };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 同步设置
|
* 同步设置
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const syncSetting = async (isBg = false) => {
|
const syncSetting = async (isBg = false) => {
|
||||||
const { syncUrl, syncKey, settingUpdateAt } = await syncOpt.load();
|
const { syncUrl, syncKey, settingUpdateAt } = await getSyncWithDefault();
|
||||||
if (!syncUrl || !syncKey) {
|
if (!syncUrl || !syncKey) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -46,13 +46,13 @@ export const syncSetting = async (isBg = false) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (res && res.updateAt > settingUpdateAt) {
|
if (res && res.updateAt > settingUpdateAt) {
|
||||||
await syncOpt.update({
|
await updateSync({
|
||||||
settingUpdateAt: res.updateAt,
|
settingUpdateAt: res.updateAt,
|
||||||
settingSyncAt: res.updateAt,
|
settingSyncAt: res.updateAt,
|
||||||
});
|
});
|
||||||
await storage.setObj(STOKEY_SETTING, res.value);
|
await storage.setObj(STOKEY_SETTING, res.value);
|
||||||
} else {
|
} else {
|
||||||
await syncOpt.update({ settingSyncAt: res.updateAt });
|
await updateSync({ settingSyncAt: res.updateAt });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,8 +68,8 @@ export const trySyncSetting = async (isBg = false) => {
|
|||||||
* 同步规则
|
* 同步规则
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const syncRules = async (isBg = false) => {
|
const syncRules = async (isBg = false) => {
|
||||||
const { syncUrl, syncKey, rulesUpdateAt } = await syncOpt.load();
|
const { syncUrl, syncKey, rulesUpdateAt } = await getSyncWithDefault();
|
||||||
if (!syncUrl || !syncKey) {
|
if (!syncUrl || !syncKey) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -87,13 +87,13 @@ export const syncRules = async (isBg = false) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (res && res.updateAt > rulesUpdateAt) {
|
if (res && res.updateAt > rulesUpdateAt) {
|
||||||
await syncOpt.update({
|
await updateSync({
|
||||||
rulesUpdateAt: res.updateAt,
|
rulesUpdateAt: res.updateAt,
|
||||||
rulesSyncAt: res.updateAt,
|
rulesSyncAt: res.updateAt,
|
||||||
});
|
});
|
||||||
await storage.setObj(STOKEY_RULES, res.value);
|
await storage.setObj(STOKEY_RULES, res.value);
|
||||||
} else {
|
} else {
|
||||||
await syncOpt.update({ rulesSyncAt: res.updateAt });
|
await updateSync({ rulesSyncAt: res.updateAt });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -12,14 +12,16 @@ import {
|
|||||||
import Content from "../views/Content";
|
import Content from "../views/Content";
|
||||||
import { fetchUpdate, fetchClear } from "./fetch";
|
import { fetchUpdate, fetchClear } from "./fetch";
|
||||||
import { debounce } from "./utils";
|
import { debounce } from "./utils";
|
||||||
|
import { isExt } from "./client";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 翻译类
|
* 翻译类
|
||||||
*/
|
*/
|
||||||
export class Translator {
|
export class Translator {
|
||||||
_rule = {};
|
_rule = {};
|
||||||
_minLength = 0;
|
_setting = {};
|
||||||
_maxLength = 0;
|
_rootNodes = new Set();
|
||||||
|
_tranNodes = new Map();
|
||||||
_skipNodeNames = [
|
_skipNodeNames = [
|
||||||
APP_LCNAME,
|
APP_LCNAME,
|
||||||
"style",
|
"style",
|
||||||
@@ -36,8 +38,6 @@ export class Translator {
|
|||||||
"script",
|
"script",
|
||||||
"iframe",
|
"iframe",
|
||||||
];
|
];
|
||||||
_rootNodes = new Set();
|
|
||||||
_tranNodes = new Map();
|
|
||||||
|
|
||||||
// 显示
|
// 显示
|
||||||
_interseObserver = new IntersectionObserver(
|
_interseObserver = new IntersectionObserver(
|
||||||
@@ -89,12 +89,14 @@ export class Translator {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(rule, { fetchInterval, fetchLimit, minLength, maxLength }) {
|
constructor(rule, setting) {
|
||||||
|
const { fetchInterval, fetchLimit } = setting;
|
||||||
fetchUpdate(fetchInterval, fetchLimit);
|
fetchUpdate(fetchInterval, fetchLimit);
|
||||||
this._overrideAttachShadow();
|
this._overrideAttachShadow();
|
||||||
this._minLength = minLength ?? TRANS_MIN_LENGTH;
|
|
||||||
this._maxLength = maxLength ?? TRANS_MAX_LENGTH;
|
this._setting = setting;
|
||||||
this.rule = rule;
|
this._rule = rule;
|
||||||
|
|
||||||
if (rule.transOpen === "true") {
|
if (rule.transOpen === "true") {
|
||||||
this._register();
|
this._register();
|
||||||
}
|
}
|
||||||
@@ -268,7 +270,11 @@ export class Translator {
|
|||||||
this._tranNodes.set(el, q);
|
this._tranNodes.set(el, q);
|
||||||
|
|
||||||
// 太长或太短
|
// 太长或太短
|
||||||
if (!q || q.length < this._minLength || q.length > this._maxLength) {
|
if (
|
||||||
|
!q ||
|
||||||
|
q.length < (this._setting.minLength ?? TRANS_MIN_LENGTH) ||
|
||||||
|
q.length > (this._setting.maxLength ?? TRANS_MAX_LENGTH)
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import { StoragesProvider } from "./hooks/Storage";
|
import { SettingProvider } from "./hooks/Setting";
|
||||||
import ThemeProvider from "./hooks/Theme";
|
import ThemeProvider from "./hooks/Theme";
|
||||||
import Popup from "./views/Popup";
|
import Popup from "./views/Popup";
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById("root"));
|
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<StoragesProvider>
|
<SettingProvider>
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<Popup />
|
<Popup />
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</StoragesProvider>
|
</SettingProvider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import createCache from "@emotion/cache";
|
|||||||
import { CacheProvider } from "@emotion/react";
|
import { CacheProvider } from "@emotion/react";
|
||||||
import { getSetting, getRules, matchRule, getFab } from "./libs";
|
import { getSetting, getRules, matchRule, getFab } from "./libs";
|
||||||
import { Translator } from "./libs/translator";
|
import { Translator } from "./libs/translator";
|
||||||
import { trySyncAllSubRules } from "./libs/rules";
|
import { trySyncAllSubRules } from "./libs/subRules";
|
||||||
import { isGm } from "./libs/browser";
|
import { isGm } from "./libs/client";
|
||||||
import { MSG_TRANS_TOGGLE, MSG_TRANS_PUTRULE } from "./config";
|
import { MSG_TRANS_TOGGLE, MSG_TRANS_PUTRULE } from "./config";
|
||||||
import { isIframe } from "./libs/iframe";
|
import { isIframe } from "./libs/iframe";
|
||||||
import { handlePing, injectScript } from "./libs/gm";
|
import { handlePing, injectScript } from "./libs/gm";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { limitNumber } from "../../libs/utils";
|
import { limitNumber } from "../../libs/utils";
|
||||||
import { isMobile } from "../../libs/mobile";
|
import { isMobile } from "../../libs/mobile";
|
||||||
import { setFab } from "../../libs";
|
import { setFab } from "../../libs/storage";
|
||||||
|
|
||||||
const getEdgePosition = (
|
const getEdgePosition = (
|
||||||
{ x: left, y: top, edge },
|
{ x: left, y: top, edge },
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import IconButton from "@mui/material/IconButton";
|
|||||||
import CloseIcon from "@mui/icons-material/Close";
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
import Stack from "@mui/material/Stack";
|
import Stack from "@mui/material/Stack";
|
||||||
import { useEffect, useState, useMemo, useCallback } from "react";
|
import { useEffect, useState, useMemo, useCallback } from "react";
|
||||||
import { StoragesProvider } from "../../hooks/Storage";
|
import { SettingProvider } from "../../hooks/Setting";
|
||||||
import Popup from "../Popup";
|
import Popup from "../Popup";
|
||||||
import { debounce } from "../../libs/utils";
|
import { debounce } from "../../libs/utils";
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ export default function Action({ translator, fab }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StoragesProvider>
|
<SettingProvider>
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<Draggable
|
<Draggable
|
||||||
key="pop"
|
key="pop"
|
||||||
@@ -139,6 +139,6 @@ export default function Action({ translator, fab }) {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</StoragesProvider>
|
</SettingProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { useTranslate } from "../../hooks/Translate";
|
|||||||
export default function Content({ q, translator }) {
|
export default function Content({ q, translator }) {
|
||||||
const [rule, setRule] = useState(translator.rule);
|
const [rule, setRule] = useState(translator.rule);
|
||||||
const [hover, setHover] = useState(false);
|
const [hover, setHover] = useState(false);
|
||||||
const { text, sameLang, loading } = useTranslate(q, rule);
|
const { text, sameLang, loading } = useTranslate(q, rule, translator.setting);
|
||||||
const { textStyle, bgColor } = rule;
|
const { textStyle, bgColor } = rule;
|
||||||
|
|
||||||
const handleMouseEnter = () => {
|
const handleMouseEnter = () => {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import IconButton from "@mui/material/IconButton";
|
|||||||
import MenuIcon from "@mui/icons-material/Menu";
|
import MenuIcon from "@mui/icons-material/Menu";
|
||||||
import Toolbar from "@mui/material/Toolbar";
|
import Toolbar from "@mui/material/Toolbar";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import { useDarkModeSwitch } from "../../hooks/ColorMode";
|
|
||||||
import { useDarkMode } from "../../hooks/ColorMode";
|
import { useDarkMode } from "../../hooks/ColorMode";
|
||||||
import LightModeIcon from "@mui/icons-material/LightMode";
|
import LightModeIcon from "@mui/icons-material/LightMode";
|
||||||
import DarkModeIcon from "@mui/icons-material/DarkMode";
|
import DarkModeIcon from "@mui/icons-material/DarkMode";
|
||||||
@@ -14,8 +13,7 @@ import { useI18n } from "../../hooks/I18n";
|
|||||||
function Header(props) {
|
function Header(props) {
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
const { onDrawerToggle } = props;
|
const { onDrawerToggle } = props;
|
||||||
const switchColorMode = useDarkModeSwitch();
|
const { darkMode, toggleDarkMode } = useDarkMode();
|
||||||
const darkMode = useDarkMode();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppBar
|
<AppBar
|
||||||
@@ -43,7 +41,7 @@ function Header(props) {
|
|||||||
href={process.env.REACT_APP_HOMEPAGE}
|
href={process.env.REACT_APP_HOMEPAGE}
|
||||||
>{`${i18n("app_name")} v${process.env.REACT_APP_VERSION}`}</Link>
|
>{`${i18n("app_name")} v${process.env.REACT_APP_VERSION}`}</Link>
|
||||||
</Box>
|
</Box>
|
||||||
<IconButton onClick={switchColorMode} color="inherit">
|
<IconButton onClick={toggleDarkMode} color="inherit">
|
||||||
{darkMode ? <LightModeIcon /> : <DarkModeIcon />}
|
{darkMode ? <LightModeIcon /> : <DarkModeIcon />}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import TextField from "@mui/material/TextField";
|
|||||||
import MenuItem from "@mui/material/MenuItem";
|
import MenuItem from "@mui/material/MenuItem";
|
||||||
import FormControl from "@mui/material/FormControl";
|
import FormControl from "@mui/material/FormControl";
|
||||||
import Select from "@mui/material/Select";
|
import Select from "@mui/material/Select";
|
||||||
import { useSetting, useSettingUpdate } from "../../hooks/Setting";
|
import { useSetting } from "../../hooks/Setting";
|
||||||
import { limitNumber, debounce } from "../../libs/utils";
|
import { limitNumber, debounce } from "../../libs/utils";
|
||||||
import { useI18n } from "../../hooks/I18n";
|
import { useI18n } from "../../hooks/I18n";
|
||||||
import { UI_LANGS } from "../../config";
|
import { UI_LANGS } from "../../config";
|
||||||
@@ -13,8 +13,7 @@ import { useMemo } from "react";
|
|||||||
|
|
||||||
export default function Settings() {
|
export default function Settings() {
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
const setting = useSetting();
|
const { setting, updateSetting } = useSetting();
|
||||||
const updateSetting = useSettingUpdate();
|
|
||||||
|
|
||||||
const handleChange = useMemo(
|
const handleChange = useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import Rules from "./Rules";
|
|||||||
import Setting from "./Setting";
|
import Setting from "./Setting";
|
||||||
import Layout from "./Layout";
|
import Layout from "./Layout";
|
||||||
import SyncSetting from "./SyncSetting";
|
import SyncSetting from "./SyncSetting";
|
||||||
import { StoragesProvider } from "../../hooks/Storage";
|
import { SettingProvider } from "../../hooks/Setting";
|
||||||
import ThemeProvider from "../../hooks/Theme";
|
import ThemeProvider from "../../hooks/Theme";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { isGm } from "../../libs/browser";
|
import { isGm } from "../../libs/client";
|
||||||
import { sleep } from "../../libs/utils";
|
import { sleep } from "../../libs/utils";
|
||||||
import CircularProgress from "@mui/material/CircularProgress";
|
import CircularProgress from "@mui/material/CircularProgress";
|
||||||
import { trySyncAll } from "../../libs/sync";
|
import { trySyncAll } from "../../libs/sync";
|
||||||
@@ -89,7 +89,7 @@ export default function Options() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StoragesProvider>
|
<SettingProvider>
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<AlertProvider>
|
<AlertProvider>
|
||||||
<HashRouter>
|
<HashRouter>
|
||||||
@@ -104,6 +104,6 @@ export default function Options() {
|
|||||||
</HashRouter>
|
</HashRouter>
|
||||||
</AlertProvider>
|
</AlertProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</StoragesProvider>
|
</SettingProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import FormControlLabel from "@mui/material/FormControlLabel";
|
|||||||
import Switch from "@mui/material/Switch";
|
import Switch from "@mui/material/Switch";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import { sendTabMsg } from "../../libs/msg";
|
import { sendTabMsg } from "../../libs/msg";
|
||||||
import { browser, isExt } from "../../libs/browser";
|
import { browser } from "../../libs/browser";
|
||||||
|
import { isExt } from "../../libs/client";
|
||||||
import { useI18n } from "../../hooks/I18n";
|
import { useI18n } from "../../hooks/I18n";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import {
|
import {
|
||||||
|
|||||||
Reference in New Issue
Block a user