Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c57a0a11fa | ||
|
|
fa244b2097 | ||
|
|
79612f8a1b | ||
|
|
2bf79dbc51 |
2
.env
2
.env
@@ -2,7 +2,7 @@ GENERATE_SOURCEMAP=false
|
|||||||
|
|
||||||
REACT_APP_NAME=KISS Translator
|
REACT_APP_NAME=KISS Translator
|
||||||
REACT_APP_NAME_CN=简约翻译
|
REACT_APP_NAME_CN=简约翻译
|
||||||
REACT_APP_VERSION=1.6.12
|
REACT_APP_VERSION=1.7.0
|
||||||
|
|
||||||
REACT_APP_HOMEPAGE=https://github.com/fishjar/kiss-translator
|
REACT_APP_HOMEPAGE=https://github.com/fishjar/kiss-translator
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "kiss-translator",
|
"name": "kiss-translator",
|
||||||
"description": "A minimalist bilingual translation Extension & Greasemonkey Script",
|
"description": "A minimalist bilingual translation Extension & Greasemonkey Script",
|
||||||
"version": "1.6.12",
|
"version": "1.7.0",
|
||||||
"author": "Gabe<yugang2002@gmail.com>",
|
"author": "Gabe<yugang2002@gmail.com>",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"name": "__MSG_app_name__",
|
"name": "__MSG_app_name__",
|
||||||
"description": "__MSG_app_description__",
|
"description": "__MSG_app_description__",
|
||||||
"version": "1.6.12",
|
"version": "1.7.0",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"author": "Gabe<yugang2002@gmail.com>",
|
"author": "Gabe<yugang2002@gmail.com>",
|
||||||
"homepage_url": "https://github.com/fishjar/kiss-translator",
|
"homepage_url": "https://github.com/fishjar/kiss-translator",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "__MSG_app_name__",
|
"name": "__MSG_app_name__",
|
||||||
"description": "__MSG_app_description__",
|
"description": "__MSG_app_description__",
|
||||||
"version": "1.6.12",
|
"version": "1.7.0",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"author": "Gabe<yugang2002@gmail.com>",
|
"author": "Gabe<yugang2002@gmail.com>",
|
||||||
"homepage_url": "https://github.com/fishjar/kiss-translator",
|
"homepage_url": "https://github.com/fishjar/kiss-translator",
|
||||||
|
|||||||
@@ -285,4 +285,5 @@ export const DEFAULT_SYNC = {
|
|||||||
rulesUpdateAt: 0,
|
rulesUpdateAt: 0,
|
||||||
rulesSyncAt: 0,
|
rulesSyncAt: 0,
|
||||||
subRulesSyncAt: 0, // 订阅规则同步时间
|
subRulesSyncAt: 0, // 订阅规则同步时间
|
||||||
|
dataCaches: {}, // 缓存同步时间
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export function useStorage(key, defaultVal = null) {
|
|||||||
if (val) {
|
if (val) {
|
||||||
setData(val);
|
setData(val);
|
||||||
} else if (defaultVal) {
|
} else if (defaultVal) {
|
||||||
|
setData(defaultVal);
|
||||||
await storage.setObj(key, defaultVal);
|
await storage.setObj(key, defaultVal);
|
||||||
}
|
}
|
||||||
}, [key, defaultVal]);
|
}, [key, defaultVal]);
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export function useSubRules() {
|
|||||||
const addSub = useCallback(
|
const addSub = useCallback(
|
||||||
async (url) => {
|
async (url) => {
|
||||||
const subrulesList = [...list];
|
const subrulesList = [...list];
|
||||||
subrulesList.push({ url, selected: false, syncAt: Date.now() });
|
subrulesList.push({ url, selected: false });
|
||||||
await updateSetting({ subrulesList });
|
await updateSetting({ subrulesList });
|
||||||
},
|
},
|
||||||
[list, updateSetting]
|
[list, updateSetting]
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useCallback } from "react";
|
||||||
import { STOKEY_SYNC, DEFAULT_SYNC } from "../config";
|
import { STOKEY_SYNC, DEFAULT_SYNC } from "../config";
|
||||||
import { useStorage } from "./Storage";
|
import { useStorage } from "./Storage";
|
||||||
|
|
||||||
@@ -6,6 +7,40 @@ import { useStorage } from "./Storage";
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function useSync() {
|
export function useSync() {
|
||||||
const { data, update } = useStorage(STOKEY_SYNC, DEFAULT_SYNC);
|
const { data, update, reload } = useStorage(STOKEY_SYNC, DEFAULT_SYNC);
|
||||||
return { sync: data, updateSync: update };
|
return { sync: data, updateSync: update, reloadSync: reload };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* caches sync hook
|
||||||
|
* @param {*} url
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function useSyncCaches() {
|
||||||
|
const { sync, updateSync, reloadSync } = useSync();
|
||||||
|
|
||||||
|
const updateDataCache = useCallback(
|
||||||
|
async (url) => {
|
||||||
|
const dataCaches = sync.dataCaches || {};
|
||||||
|
dataCaches[url] = Date.now();
|
||||||
|
await updateSync({ dataCaches });
|
||||||
|
},
|
||||||
|
[sync, updateSync]
|
||||||
|
);
|
||||||
|
|
||||||
|
const deleteDataCache = useCallback(
|
||||||
|
async (url) => {
|
||||||
|
const dataCaches = sync.dataCaches || {};
|
||||||
|
delete dataCaches[url];
|
||||||
|
await updateSync({ dataCaches });
|
||||||
|
},
|
||||||
|
[sync, updateSync]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
dataCaches: sync.dataCaches || {},
|
||||||
|
updateDataCache,
|
||||||
|
deleteDataCache,
|
||||||
|
reloadSync,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,9 +49,8 @@ export const matchRule = async (
|
|||||||
mixRule[key] = val;
|
mixRule[key] = val;
|
||||||
});
|
});
|
||||||
|
|
||||||
const subRules = (await loadOrFetchSubRules(selectedSub.url)).map(
|
let subRules = await loadOrFetchSubRules(selectedSub.url);
|
||||||
(item) => ({ ...item, ...mixRule })
|
subRules = subRules.map((item) => ({ ...item, ...mixRule }));
|
||||||
);
|
|
||||||
rules.splice(-1, 0, ...subRules);
|
rules.splice(-1, 0, ...subRules);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -10,6 +10,16 @@ import { apiFetch } from "../apis";
|
|||||||
import { checkRules } from "./rules";
|
import { checkRules } from "./rules";
|
||||||
import { isAllchar } from "./utils";
|
import { isAllchar } from "./utils";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新缓存同步时间
|
||||||
|
* @param {*} url
|
||||||
|
*/
|
||||||
|
const updateSyncDataCache = async (url) => {
|
||||||
|
const { dataCaches = {} } = await getSyncWithDefault();
|
||||||
|
dataCaches[url] = Date.now();
|
||||||
|
await updateSync({ dataCaches });
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 同步订阅规则
|
* 同步订阅规则
|
||||||
* @param {*} url
|
* @param {*} url
|
||||||
@@ -35,6 +45,7 @@ export const syncAllSubRules = async (subrulesList, isBg = false) => {
|
|||||||
for (let subrules of subrulesList) {
|
for (let subrules of subrulesList) {
|
||||||
try {
|
try {
|
||||||
await syncSubRules(subrules.url, isBg);
|
await syncSubRules(subrules.url, isBg);
|
||||||
|
await updateSyncDataCache(subrules.url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(`[sync subrule error]: ${subrules.url}`, err);
|
console.log(`[sync subrule error]: ${subrules.url}`, err);
|
||||||
}
|
}
|
||||||
@@ -70,9 +81,10 @@ export const trySyncAllSubRules = async ({ subrulesList }, isBg = false) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const loadOrFetchSubRules = async (url) => {
|
export const loadOrFetchSubRules = async (url) => {
|
||||||
const rules = await getSubRules(url);
|
let rules = await getSubRules(url);
|
||||||
if (rules?.length) {
|
if (!rules || rules.length === 0) {
|
||||||
return rules;
|
rules = await syncSubRules(url);
|
||||||
|
await updateSyncDataCache(url);
|
||||||
}
|
}
|
||||||
return syncSubRules(url);
|
return rules || [];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,26 +12,26 @@ const FIXER_FONTSIZE = "fontSize";
|
|||||||
* 需要修复的站点列表
|
* 需要修复的站点列表
|
||||||
* - pattern 匹配网址
|
* - pattern 匹配网址
|
||||||
* - selector 需要修复的选择器
|
* - selector 需要修复的选择器
|
||||||
* - rootSlector 需要监听的选择器,可留空
|
* - rootSelector 需要监听的选择器,可留空
|
||||||
* - fixer 修复函数,可针对不同网址,选用不同修复函数
|
* - fixer 修复函数,可针对不同网址,选用不同修复函数
|
||||||
*/
|
*/
|
||||||
const DEFAULT_SITES = [
|
const DEFAULT_SITES = [
|
||||||
{
|
{
|
||||||
pattern: "www.phoronix.com",
|
pattern: "www.phoronix.com",
|
||||||
selector: ".content",
|
selector: ".content",
|
||||||
rootSlector: "",
|
rootSelector: "",
|
||||||
fixer: FIXER_BR,
|
fixer: FIXER_BR,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: "t.me/s/",
|
pattern: "t.me/s/",
|
||||||
selector: ".tgme_widget_message_text",
|
selector: ".tgme_widget_message_text",
|
||||||
rootSlector: ".tgme_channel_history",
|
rootSelector: ".tgme_channel_history",
|
||||||
fixer: FIXER_BR,
|
fixer: FIXER_BR,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: "baidu.com",
|
pattern: "baidu.com",
|
||||||
selector: "html",
|
selector: "html",
|
||||||
rootSlector: "",
|
rootSelector: "",
|
||||||
fixer: FIXER_FONTSIZE,
|
fixer: FIXER_FONTSIZE,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -114,9 +114,9 @@ const fixerMap = {
|
|||||||
* 查找、监听节点,并执行修复函数
|
* 查找、监听节点,并执行修复函数
|
||||||
* @param {*} selector
|
* @param {*} selector
|
||||||
* @param {*} fixer
|
* @param {*} fixer
|
||||||
* @param {*} rootSlector
|
* @param {*} rootSelector
|
||||||
*/
|
*/
|
||||||
function run(selector, fixer, rootSlector) {
|
function run(selector, fixer, rootSelector) {
|
||||||
var mutaObserver = new MutationObserver(function (mutations) {
|
var mutaObserver = new MutationObserver(function (mutations) {
|
||||||
mutations.forEach(function (mutation) {
|
mutations.forEach(function (mutation) {
|
||||||
mutation.addedNodes.forEach(function (addNode) {
|
mutation.addedNodes.forEach(function (addNode) {
|
||||||
@@ -126,8 +126,8 @@ function run(selector, fixer, rootSlector) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
var rootNodes = [document];
|
var rootNodes = [document];
|
||||||
if (rootSlector) {
|
if (rootSelector) {
|
||||||
rootNodes = document.querySelectorAll(rootSlector);
|
rootNodes = document.querySelectorAll(rootSelector);
|
||||||
}
|
}
|
||||||
|
|
||||||
rootNodes.forEach(function (rootNode) {
|
rootNodes.forEach(function (rootNode) {
|
||||||
@@ -181,7 +181,7 @@ export async function webfix(href, { injectWebfix }) {
|
|||||||
var site = sites[i];
|
var site = sites[i];
|
||||||
if (isMatch(href, site.pattern)) {
|
if (isMatch(href, site.pattern)) {
|
||||||
if (fixerMap[site.fixer]) {
|
if (fixerMap[site.fixer]) {
|
||||||
run(site.selector, fixerMap[site.fixer], site.rootSlector);
|
run(site.selector, fixerMap[site.fixer], site.rootSelector);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import { delSubRules, getSyncWithDefault } from "../../libs/storage";
|
|||||||
import OwSubRule from "./OwSubRule";
|
import OwSubRule from "./OwSubRule";
|
||||||
import ClearAllIcon from "@mui/icons-material/ClearAll";
|
import ClearAllIcon from "@mui/icons-material/ClearAll";
|
||||||
import HelpButton from "./HelpButton";
|
import HelpButton from "./HelpButton";
|
||||||
|
import { useSyncCaches } from "../../hooks/Sync";
|
||||||
|
|
||||||
function RuleFields({ rule, rules, setShow, setKeyword }) {
|
function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||||
const initFormValues = rule || {
|
const initFormValues = rule || {
|
||||||
@@ -624,8 +625,9 @@ function SubRulesItem({
|
|||||||
syncAt,
|
syncAt,
|
||||||
selectedUrl,
|
selectedUrl,
|
||||||
delSub,
|
delSub,
|
||||||
updateSub,
|
|
||||||
setSelectedRules,
|
setSelectedRules,
|
||||||
|
updateDataCache,
|
||||||
|
deleteDataCache,
|
||||||
}) {
|
}) {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
@@ -633,6 +635,7 @@ function SubRulesItem({
|
|||||||
try {
|
try {
|
||||||
await delSub(url);
|
await delSub(url);
|
||||||
await delSubRules(url);
|
await delSubRules(url);
|
||||||
|
await deleteDataCache(url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("[del subrules]", err);
|
console.log("[del subrules]", err);
|
||||||
}
|
}
|
||||||
@@ -645,7 +648,7 @@ function SubRulesItem({
|
|||||||
if (rules.length > 0 && url === selectedUrl) {
|
if (rules.length > 0 && url === selectedUrl) {
|
||||||
setSelectedRules(rules);
|
setSelectedRules(rules);
|
||||||
}
|
}
|
||||||
await updateSub(url, { syncAt: Date.now() });
|
await updateDataCache(url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("[sync sub rules]", err);
|
console.log("[sync sub rules]", err);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -680,7 +683,7 @@ function SubRulesItem({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SubRulesEdit({ subList, addSub }) {
|
function SubRulesEdit({ subList, addSub, updateDataCache }) {
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
const [inputText, setInputText] = useState("");
|
const [inputText, setInputText] = useState("");
|
||||||
const [inputError, setInputError] = useState("");
|
const [inputError, setInputError] = useState("");
|
||||||
@@ -715,6 +718,7 @@ function SubRulesEdit({ subList, addSub }) {
|
|||||||
throw new Error("empty rules");
|
throw new Error("empty rules");
|
||||||
}
|
}
|
||||||
await addSub(url);
|
await addSub(url);
|
||||||
|
await updateDataCache(url);
|
||||||
setShowInput(false);
|
setShowInput(false);
|
||||||
setInputText("");
|
setInputText("");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -787,7 +791,6 @@ function SubRules({ subRules }) {
|
|||||||
const {
|
const {
|
||||||
subList,
|
subList,
|
||||||
selectSub,
|
selectSub,
|
||||||
updateSub,
|
|
||||||
addSub,
|
addSub,
|
||||||
delSub,
|
delSub,
|
||||||
selectedUrl,
|
selectedUrl,
|
||||||
@@ -795,27 +798,38 @@ function SubRules({ subRules }) {
|
|||||||
setSelectedRules,
|
setSelectedRules,
|
||||||
loading,
|
loading,
|
||||||
} = subRules;
|
} = subRules;
|
||||||
|
const { dataCaches, updateDataCache, deleteDataCache, reloadSync } =
|
||||||
|
useSyncCaches();
|
||||||
|
|
||||||
const handleSelect = (e) => {
|
const handleSelect = (e) => {
|
||||||
const url = e.target.value;
|
const url = e.target.value;
|
||||||
selectSub(url);
|
selectSub(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
reloadSync();
|
||||||
|
}, [selectedRules, reloadSync]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack spacing={3}>
|
<Stack spacing={3}>
|
||||||
<SubRulesEdit subList={subList} addSub={addSub} />
|
<SubRulesEdit
|
||||||
|
subList={subList}
|
||||||
|
addSub={addSub}
|
||||||
|
updateDataCache={updateDataCache}
|
||||||
|
/>
|
||||||
|
|
||||||
<RadioGroup value={selectedUrl} onChange={handleSelect}>
|
<RadioGroup value={selectedUrl} onChange={handleSelect}>
|
||||||
{subList.map((item, index) => (
|
{subList.map((item, index) => (
|
||||||
<SubRulesItem
|
<SubRulesItem
|
||||||
key={item.url}
|
key={item.url}
|
||||||
url={item.url}
|
url={item.url}
|
||||||
syncAt={item.syncAt}
|
syncAt={dataCaches[item.url]}
|
||||||
index={index}
|
index={index}
|
||||||
selectedUrl={selectedUrl}
|
selectedUrl={selectedUrl}
|
||||||
delSub={delSub}
|
delSub={delSub}
|
||||||
updateSub={updateSub}
|
|
||||||
setSelectedRules={setSelectedRules}
|
setSelectedRules={setSelectedRules}
|
||||||
|
updateDataCache={updateDataCache}
|
||||||
|
deleteDataCache={deleteDataCache}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ import HelpButton from "./HelpButton";
|
|||||||
import { URL_KISS_RULES_NEW_ISSUE } from "../../config";
|
import { URL_KISS_RULES_NEW_ISSUE } from "../../config";
|
||||||
|
|
||||||
function ApiFields({ site }) {
|
function ApiFields({ site }) {
|
||||||
const { selector, rootSlector, fixer } = site;
|
const { selector, rootSelector, fixer } = site;
|
||||||
return (
|
return (
|
||||||
<Stack spacing={3}>
|
<Stack spacing={3}>
|
||||||
<TextField
|
<TextField
|
||||||
size="small"
|
size="small"
|
||||||
label={"rootSlector"}
|
label={"rootSelector"}
|
||||||
name="rootSlector"
|
name="rootSelector"
|
||||||
value={rootSlector || "document"}
|
value={rootSelector || "document"}
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
|
|||||||
Reference in New Issue
Block a user