This commit is contained in:
Gabe Yuan
2023-08-30 18:05:37 +08:00
parent d7cee8cca6
commit c46fe7d1c6
33 changed files with 770 additions and 559 deletions

View File

@@ -20,11 +20,6 @@ export function AlertProvider({ children }) {
const [severity, setSeverity] = useState("info");
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) => {
setOpen(true);
setMessage(msg);
@@ -38,6 +33,11 @@ export function AlertProvider({ children }) {
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 (
<AlertContext.Provider value={{ error, warning, info, success }}>
{children}

View File

@@ -1,22 +1,19 @@
import { useSetting, useSettingUpdate } from "./Setting";
import { useCallback } from "react";
import { useSetting } from "./Setting";
/**
* 深色模式hook
* @returns
*/
export function useDarkMode() {
const setting = useSetting();
return !!setting?.darkMode;
}
const {
setting: { darkMode },
updateSetting,
} = useSetting();
/**
* 切换深色模式
* @returns
*/
export function useDarkModeSwitch() {
const darkMode = useDarkMode();
const updateSetting = useSettingUpdate();
return async () => {
const toggleDarkMode = useCallback(async () => {
await updateSetting({ darkMode: !darkMode });
};
}, [darkMode]);
return { darkMode, toggleDarkMode };
}

View File

@@ -7,7 +7,9 @@ import { useFetch } from "./Fetch";
* @returns
*/
export const useI18n = () => {
const { uiLang } = useSetting() ?? {};
const {
setting: { uiLang },
} = useSetting();
return (key, defaultText = "") => I18N?.[key]?.[uiLang] ?? defaultText;
};

View File

@@ -1,107 +1,125 @@
import { STOKEY_RULES, DEFAULT_SUBRULES_LIST } from "../config";
import storage from "../libs/storage";
import { useStorages } from "./Storage";
import { STOKEY_RULES, DEFAULT_RULES } from "../config";
import { useStorage } from "./Storage";
import { trySyncRules } from "../libs/sync";
import { useSync } from "./Sync";
import { useSetting, useSettingUpdate } from "./Setting";
import { checkRules } from "../libs/rules";
import { useCallback } from "react";
/**
* 匹配规则增删改查 hook
* 规则 hook
* @returns
*/
export function useRules() {
const storages = useStorages();
const list = storages?.[STOKEY_RULES] || [];
const sync = useSync();
const { data: list, save } = useStorage(STOKEY_RULES, DEFAULT_RULES);
const {
sync: { rulesUpdateAt },
updateSync,
} = useSync();
const update = async (rules) => {
const updateAt = sync.opt?.rulesUpdateAt ? Date.now() : 0;
await storage.setObj(STOKEY_RULES, rules);
await sync.update({ rulesUpdateAt: updateAt });
trySyncRules();
};
const updateRules = useCallback(
async (rules) => {
const updateAt = rulesUpdateAt ? Date.now() : 0;
await save(rules);
await updateSync({ rulesUpdateAt: updateAt });
trySyncRules();
},
[rulesUpdateAt]
);
const add = async (rule) => {
const rules = [...list];
if (rule.pattern === "*") {
return;
}
if (rules.map((item) => item.pattern).includes(rule.pattern)) {
return;
}
rules.unshift(rule);
await update(rules);
};
const del = async (pattern) => {
let rules = [...list];
if (pattern === "*") {
return;
}
rules = rules.filter((item) => item.pattern !== pattern);
await update(rules);
};
const put = async (pattern, obj) => {
const rules = [...list];
if (pattern === "*") {
obj.pattern = "*";
}
const rule = rules.find((r) => r.pattern === pattern);
rule && Object.assign(rule, obj);
await update(rules);
};
const merge = async (newRules) => {
const rules = [...list];
newRules = checkRules(newRules);
newRules.forEach((newRule) => {
const rule = rules.find((oldRule) => oldRule.pattern === newRule.pattern);
if (rule) {
Object.assign(rule, newRule);
} else {
rules.unshift(newRule);
const add = useCallback(
async (rule) => {
const rules = [...list];
if (rule.pattern === "*") {
return;
}
});
await update(rules);
};
if (rules.map((item) => item.pattern).includes(rule.pattern)) {
return;
}
rules.unshift(rule);
await updateRules(rules);
},
[list, updateRules]
);
const del = useCallback(
async (pattern) => {
let rules = [...list];
if (pattern === "*") {
return;
}
rules = rules.filter((item) => item.pattern !== pattern);
await updateRules(rules);
},
[list, updateRules]
);
const put = useCallback(
async (pattern, obj) => {
const rules = [...list];
if (pattern === "*") {
obj.pattern = "*";
}
const rule = rules.find((r) => r.pattern === pattern);
rule && Object.assign(rule, obj);
await updateRules(rules);
},
[list, updateRules]
);
const merge = useCallback(
async (newRules) => {
const rules = [...list];
newRules = checkRules(newRules);
newRules.forEach((newRule) => {
const rule = rules.find(
(oldRule) => oldRule.pattern === newRule.pattern
);
if (rule) {
Object.assign(rule, newRule);
} else {
rules.unshift(newRule);
}
});
await updateRules(rules);
},
[list, updateRules]
);
return { list, add, del, put, merge };
}
/**
* 订阅规则
* @returns
*/
export function useSubrules() {
const setting = useSetting();
const updateSetting = useSettingUpdate();
const list = setting?.subrulesList || DEFAULT_SUBRULES_LIST;
// /**
// * 订阅规则
// * @returns
// */
// export function useSubrules() {
// const setting = useSetting();
// const updateSetting = useSettingUpdate();
// const list = setting?.subrulesList || DEFAULT_SUBRULES_LIST;
const select = async (url) => {
const subrulesList = [...list];
subrulesList.forEach((item) => {
if (item.url === url) {
item.selected = true;
} else {
item.selected = false;
}
});
await updateSetting({ subrulesList });
};
// const select = async (url) => {
// const subrulesList = [...list];
// subrulesList.forEach((item) => {
// if (item.url === url) {
// item.selected = true;
// } else {
// item.selected = false;
// }
// });
// await updateSetting({ subrulesList });
// };
const add = async (url) => {
const subrulesList = [...list];
subrulesList.push({ url });
await updateSetting({ subrulesList });
};
// const add = async (url) => {
// const subrulesList = [...list];
// subrulesList.push({ url });
// await updateSetting({ subrulesList });
// };
const del = async (url) => {
let subrulesList = [...list];
subrulesList = subrulesList.filter((item) => item.url !== url);
await updateSetting({ subrulesList });
};
// const del = async (url) => {
// let subrulesList = [...list];
// subrulesList = subrulesList.filter((item) => item.url !== url);
// await updateSetting({ subrulesList });
// };
return { list, select, add, del };
}
// return { list, select, add, del };
// }

View File

@@ -1,28 +1,79 @@
import { STOKEY_SETTING } from "../config";
import storage from "../libs/storage";
import { useStorages } from "./Storage";
import { STOKEY_SETTING, DEFAULT_SETTING } from "../config";
import { useStorage } from "./Storage";
import { useSync } from "./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
*/
export function useSetting() {
const storages = useStorages();
return storages?.[STOKEY_SETTING];
return useContext(SettingContext);
}
/**
* 更新设置
* @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();
};
}
// export function useSetting() {
// const [setting,setSeting]= useState(null);
// useEffect(()=>{
// (async ()=>{
// const
// })()
// },[])
// }
// /**
// * 设置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();
// };
// }

View File

@@ -1,91 +1,115 @@
import { createContext, useContext, useEffect, useState } from "react";
import { browser, isExt, isGm, isWeb } from "../libs/browser";
import {
STOKEY_SETTING,
STOKEY_RULES,
STOKEY_SYNC,
DEFAULT_SETTING,
DEFAULT_RULES,
DEFAULT_SYNC,
} from "../config";
import storage from "../libs/storage";
import { useCallback, useEffect, useState } from "react";
import { storage } from "../libs/storage";
/**
* 默认配置
*/
export const defaultStorage = {
[STOKEY_SETTING]: DEFAULT_SETTING,
[STOKEY_RULES]: DEFAULT_RULES,
[STOKEY_SYNC]: DEFAULT_SYNC,
};
export function useStorage(key, defaultVal = null) {
const [data, setData] = useState(defaultVal);
const activeKeys = Object.keys(defaultStorage);
const save = useCallback(
async (val) => {
setData(val);
await storage.setObj(key, val);
},
[key]
);
const StoragesContext = createContext(null);
const update = useCallback(
async (obj) => {
setData((pre) => ({ ...pre, ...obj }));
await storage.putObj(key, obj);
},
[key]
);
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 }));
}
};
const remove = useCallback(async () => {
setData(null);
await storage.del(key);
}, [key]);
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);
setData(await storage.getObj(key));
})();
}, [key]);
// 监听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>
);
return { data, save, update, remove };
}
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
View 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 };
}

View File

@@ -1,20 +1,22 @@
import { useCallback } from "react";
import { STOKEY_SYNC } from "../config";
import storage from "../libs/storage";
import { useStorages } from "./Storage";
import { STOKEY_SYNC, DEFAULT_SYNC } from "../config";
import { useStorage } from "./Storage";
/**
* sync hook
* @returns
*/
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,
};
const { data, update } = useStorage(STOKEY_SYNC, DEFAULT_SYNC);
return { sync: data, updateSync: 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,
// };
// }

View File

@@ -9,8 +9,8 @@ import { THEME_DARK, THEME_LIGHT } from "../config";
* @param {*} param0
* @returns
*/
export default function MuiThemeProvider({ children, options }) {
const darkMode = useDarkMode();
export default function Theme({ children, options }) {
const { darkMode } = useDarkMode();
const theme = useMemo(() => {
return createTheme({
palette: {

View File

@@ -1,15 +1,16 @@
import { useEffect } from "react";
import { useState } from "react";
import { detectLang } from "../libs";
import { detectLang } from "../libs/browser";
import { apiTranslate } from "../apis";
/**
* 翻译hook
* @param {*} q
* @param {*} rule
* @param {*} setting
* @returns
*/
export function useTranslate(q, rule) {
export function useTranslate(q, rule, setting) {
const [text, setText] = useState("");
const [loading, setLoading] = useState(false);
const [sameLang, setSamelang] = useState(false);
@@ -30,6 +31,7 @@ export function useTranslate(q, rule) {
q,
fromLang,
toLang,
setting,
});
setText(trText);
setSamelang(isSame);
@@ -40,7 +42,7 @@ export function useTranslate(q, rule) {
setLoading(false);
}
})();
}, [q, translator, fromLang, toLang]);
}, [q, translator, fromLang, toLang, setting]);
return { text, sameLang, loading };
}