权限确认
This commit is contained in:
parent
2a0286e47d
commit
44b6f11b19
@ -35,6 +35,7 @@ export default defineConfig({
|
|||||||
inject: `${src}/inject.ts`,
|
inject: `${src}/inject.ts`,
|
||||||
popup: `${src}/pages/popup/main.tsx`,
|
popup: `${src}/pages/popup/main.tsx`,
|
||||||
install: `${src}/pages/install/main.tsx`,
|
install: `${src}/pages/install/main.tsx`,
|
||||||
|
confirm: `${src}/pages/confirm/main.tsx`,
|
||||||
options: `${src}/pages/options/main.tsx`,
|
options: `${src}/pages/options/main.tsx`,
|
||||||
"editor.worker": "monaco-editor/esm/vs/editor/editor.worker.js",
|
"editor.worker": "monaco-editor/esm/vs/editor/editor.worker.js",
|
||||||
"ts.worker": "monaco-editor/esm/vs/language/typescript/ts.worker.js",
|
"ts.worker": "monaco-editor/esm/vs/language/typescript/ts.worker.js",
|
||||||
@ -151,6 +152,15 @@ export default defineConfig({
|
|||||||
minify: true,
|
minify: true,
|
||||||
chunks: ["install"],
|
chunks: ["install"],
|
||||||
}),
|
}),
|
||||||
|
,
|
||||||
|
new rspack.HtmlRspackPlugin({
|
||||||
|
filename: `${dist}/ext/src/confirm.html`,
|
||||||
|
template: `${src}/pages/template.html`,
|
||||||
|
inject: "head",
|
||||||
|
title: "Confirm - ScriptCat",
|
||||||
|
minify: true,
|
||||||
|
chunks: ["confirm"],
|
||||||
|
}),
|
||||||
new rspack.HtmlRspackPlugin({
|
new rspack.HtmlRspackPlugin({
|
||||||
filename: `${dist}/ext/src/options.html`,
|
filename: `${dist}/ext/src/options.html`,
|
||||||
template: `${src}/pages/options.html`,
|
template: `${src}/pages/options.html`,
|
||||||
|
@ -3,7 +3,7 @@ import Logger from "@App/app/logger/logger";
|
|||||||
import { Script, ScriptDAO } from "@App/app/repo/scripts";
|
import { Script, ScriptDAO } from "@App/app/repo/scripts";
|
||||||
import { ExtMessageSender, GetSender, Group, MessageSend } from "@Packages/message/server";
|
import { ExtMessageSender, GetSender, Group, MessageSend } from "@Packages/message/server";
|
||||||
import { ValueService } from "@App/app/service/service_worker/value";
|
import { ValueService } from "@App/app/service/service_worker/value";
|
||||||
import PermissionVerify from "./permission_verify";
|
import PermissionVerify, { ConfirmParam } from "./permission_verify";
|
||||||
import { connect, sendMessage } from "@Packages/message/client";
|
import { connect, sendMessage } from "@Packages/message/client";
|
||||||
import Cache, { incr } from "@App/app/cache";
|
import Cache, { incr } from "@App/app/cache";
|
||||||
import EventEmitter from "eventemitter3";
|
import EventEmitter from "eventemitter3";
|
||||||
@ -13,6 +13,7 @@ import { getIcon, isFirefox } from "@App/pkg/utils/utils";
|
|||||||
import { PopupService } from "./popup";
|
import { PopupService } from "./popup";
|
||||||
import { act } from "react";
|
import { act } from "react";
|
||||||
import { MockMessageConnect } from "@Packages/message/mock_message";
|
import { MockMessageConnect } from "@Packages/message/mock_message";
|
||||||
|
import i18next, { i18nName } from "@App/locales/locales";
|
||||||
|
|
||||||
// GMApi,处理脚本的GM API调用请求
|
// GMApi,处理脚本的GM API调用请求
|
||||||
|
|
||||||
@ -357,8 +358,35 @@ export default class GMApi {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: maxRedirects实现
|
@PermissionVerify.API({
|
||||||
@PermissionVerify.API()
|
confirm: async (request: Request) => {
|
||||||
|
console.log("confirm", request);
|
||||||
|
const config = <GMSend.XHRDetails>request.params[0];
|
||||||
|
const url = new URL(config.url);
|
||||||
|
if (request.script.metadata.connect) {
|
||||||
|
const { connect } = request.script.metadata;
|
||||||
|
for (let i = 0; i < connect.length; i += 1) {
|
||||||
|
if (url.hostname.endsWith(connect[i])) {
|
||||||
|
return Promise.resolve(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const metadata: { [key: string]: string } = {};
|
||||||
|
metadata[i18next.t("script_name")] = i18nName(request.script);
|
||||||
|
metadata[i18next.t("request_domain")] = url.hostname;
|
||||||
|
metadata[i18next.t("request_url")] = config.url;
|
||||||
|
|
||||||
|
return Promise.resolve({
|
||||||
|
permission: "cors",
|
||||||
|
permissionValue: url.hostname,
|
||||||
|
title: i18next.t("script_accessing_cross_origin_resource"),
|
||||||
|
metadata,
|
||||||
|
describe: i18next.t("confirm_operation_description"),
|
||||||
|
wildcard: true,
|
||||||
|
permissionContent: i18next.t("domain"),
|
||||||
|
} as ConfirmParam);
|
||||||
|
},
|
||||||
|
})
|
||||||
async GM_xmlhttpRequest(request: Request, sender: GetSender) {
|
async GM_xmlhttpRequest(request: Request, sender: GetSender) {
|
||||||
if (request.params.length === 0) {
|
if (request.params.length === 0) {
|
||||||
throw new Error("param is failed");
|
throw new Error("param is failed");
|
||||||
|
@ -183,6 +183,7 @@ export default class PermissionVerify {
|
|||||||
}
|
}
|
||||||
return Promise.resolve(model);
|
return Promise.resolve(model);
|
||||||
});
|
});
|
||||||
|
console.log("confirm", request, confirm);
|
||||||
// 有查询到结果,进入判断,不再需要用户确认
|
// 有查询到结果,进入判断,不再需要用户确认
|
||||||
if (ret) {
|
if (ret) {
|
||||||
if (ret.allow) {
|
if (ret.allow) {
|
||||||
@ -249,9 +250,27 @@ export default class PermissionVerify {
|
|||||||
|
|
||||||
// 弹出窗口让用户进行确认
|
// 弹出窗口让用户进行确认
|
||||||
async confirmWindow(script: Script, confirm: ConfirmParam): Promise<UserConfirm> {
|
async confirmWindow(script: Script, confirm: ConfirmParam): Promise<UserConfirm> {
|
||||||
return Promise.resolve({
|
return new Promise((resolve, reject) => {
|
||||||
allow: true,
|
const uuid = uuidv4();
|
||||||
type: 1,
|
// 超时处理
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
this.confirmMap.delete(uuid);
|
||||||
|
reject(new Error("permission confirm timeout"));
|
||||||
|
}, 40 * 1000);
|
||||||
|
// 保存到map中
|
||||||
|
this.confirmMap.set(uuid, {
|
||||||
|
confirm,
|
||||||
|
script,
|
||||||
|
resolve: (value: UserConfirm) => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
resolve(value);
|
||||||
|
},
|
||||||
|
reject,
|
||||||
|
});
|
||||||
|
// 打开窗口
|
||||||
|
chrome.tabs.create({
|
||||||
|
url: chrome.runtime.getURL(`src/confirm.html?uuid=${uuid}`),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,11 @@ import zhTW from "./zh-TW/translation.json";
|
|||||||
import achUG from "./ach-UG/translation.json";
|
import achUG from "./ach-UG/translation.json";
|
||||||
import "dayjs/locale/zh-cn";
|
import "dayjs/locale/zh-cn";
|
||||||
import "dayjs/locale/zh-tw";
|
import "dayjs/locale/zh-tw";
|
||||||
|
import { systemConfig } from "@App/pages/store/global";
|
||||||
|
|
||||||
i18n.use(initReactI18next).init({
|
i18n.use(initReactI18next).init({
|
||||||
fallbackLng: "zh-CN",
|
fallbackLng: "zh-CN",
|
||||||
lng: localStorage.language || chrome.i18n.getUILanguage(),
|
lng: chrome.i18n.getUILanguage(),
|
||||||
interpolation: {
|
interpolation: {
|
||||||
escapeValue: false, // react already safes from xss => https://www.i18next.com/translation-function/interpolation#unescape
|
escapeValue: false, // react already safes from xss => https://www.i18next.com/translation-function/interpolation#unescape
|
||||||
},
|
},
|
||||||
@ -26,22 +27,13 @@ i18n.use(initReactI18next).init({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!localStorage.language) {
|
chrome.i18n.getAcceptLanguages((lngs) => {
|
||||||
chrome.i18n.getAcceptLanguages((lngs) => {
|
systemConfig.getLanguage().then((lng) => {
|
||||||
// 遍历数组寻找匹配语言
|
i18n.changeLanguage(lng);
|
||||||
for (let i = 0; i < lngs.length; i += 1) {
|
dayjs.locale(lng.toLocaleLowerCase());
|
||||||
const lng = lngs[i];
|
|
||||||
if (i18n.hasResourceBundle(lng, "translation")) {
|
|
||||||
localStorage.language = lng;
|
|
||||||
i18n.changeLanguage(lng);
|
|
||||||
dayjs.locale(lng.toLocaleLowerCase());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else {
|
});
|
||||||
dayjs.locale((localStorage.language as string).toLocaleLowerCase());
|
|
||||||
}
|
|
||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime);
|
||||||
|
|
||||||
export function i18nName(script: { name: string; metadata: Metadata }) {
|
export function i18nName(script: { name: string; metadata: Metadata }) {
|
||||||
|
@ -3,7 +3,8 @@ import { Button, Card, Collapse, Link, Message, Space, Typography } from "@arco-
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import FileSystemParams from "../FileSystemParams";
|
import FileSystemParams from "../FileSystemParams";
|
||||||
import { systemConfig } from "@App/pages/store/global";
|
import { systemConfig } from "@App/pages/store/global";
|
||||||
import { FileSystemType } from "@Packages/filesystem/factory";
|
import FileSystemFactory, { FileSystemType } from "@Packages/filesystem/factory";
|
||||||
|
import { set } from "node_modules/yaml/dist/schema/yaml-1.1/set";
|
||||||
|
|
||||||
const CollapseItem = Collapse.Item;
|
const CollapseItem = Collapse.Item;
|
||||||
|
|
||||||
@ -52,13 +53,13 @@ const GMApiSetting: React.FC = () => {
|
|||||||
Message.error(`${t("account_validation_failed")}: ${e}`);
|
Message.error(`${t("account_validation_failed")}: ${e}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const params = { ...systemConfig.catFileStorage.params };
|
const params = { ...fileSystemParams };
|
||||||
params[fileSystemType] = fileSystemParams;
|
params[fileSystemType] = fileSystemParams;
|
||||||
systemConfig.catFileStorage = {
|
systemConfig.setCatFileStorage({
|
||||||
status: "success",
|
status: "success",
|
||||||
filesystem: fileSystemType,
|
filesystem: fileSystemType,
|
||||||
params,
|
params,
|
||||||
};
|
});
|
||||||
setStatus("success");
|
setStatus("success");
|
||||||
Message.success(t("save_success")!);
|
Message.success(t("save_success")!);
|
||||||
}}
|
}}
|
||||||
@ -68,10 +69,14 @@ const GMApiSetting: React.FC = () => {
|
|||||||
<Button
|
<Button
|
||||||
key="reset"
|
key="reset"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const config = systemConfig.catFileStorage;
|
systemConfig.setCatFileStorage({
|
||||||
config.status = "unset";
|
status: "unset",
|
||||||
systemConfig.catFileStorage = config;
|
filesystem: "webdav",
|
||||||
|
params: {},
|
||||||
|
});
|
||||||
setStatus("unset");
|
setStatus("unset");
|
||||||
|
setFilesystemParam({});
|
||||||
|
setFilesystemType("webdav");
|
||||||
}}
|
}}
|
||||||
type="primary"
|
type="primary"
|
||||||
status="danger"
|
status="danger"
|
||||||
|
142
src/pages/confirm/App.tsx
Normal file
142
src/pages/confirm/App.tsx
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import { ConfirmParam } from "@App/app/service/service_worker/permission_verify";
|
||||||
|
import { Button, Message, Space } from "@arco-design/web-react";
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const uuid = window.location.search.split("=")[1];
|
||||||
|
const [confirm, setConfirm] = React.useState<ConfirmParam>();
|
||||||
|
const [likeNum, setLikeNum] = React.useState(0);
|
||||||
|
const [second, setSecond] = React.useState(30);
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
if (second === 0) {
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setSecond(second - 1);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener("beforeunload", () => {
|
||||||
|
permissionCtrl.sendConfirm(uuid, {
|
||||||
|
allow: false,
|
||||||
|
type: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
permissionCtrl
|
||||||
|
.getConfirm(uuid)
|
||||||
|
.then((data) => {
|
||||||
|
setConfirm(data.confirm);
|
||||||
|
setLikeNum(data.likeNum);
|
||||||
|
})
|
||||||
|
.catch((e: any) => {
|
||||||
|
Message.error(e.message || t("get_confirm_error"));
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleConfirm = (allow: boolean, type: number) => {
|
||||||
|
return async () => {
|
||||||
|
try {
|
||||||
|
await permissionCtrl.sendConfirm(uuid, {
|
||||||
|
allow,
|
||||||
|
type,
|
||||||
|
});
|
||||||
|
window.close();
|
||||||
|
} catch (e: any) {
|
||||||
|
Message.error(e.message || t("confirm_error"));
|
||||||
|
setTimeout(() => {
|
||||||
|
window.close();
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full">
|
||||||
|
<Space direction="vertical">
|
||||||
|
<span className="text-2xl font-500">{confirm?.title}</span>
|
||||||
|
{confirm &&
|
||||||
|
confirm.metadata &&
|
||||||
|
Object.keys(confirm.metadata).map((key) => (
|
||||||
|
<span className="text-base" key={key}>
|
||||||
|
{key}: {confirm!.metadata![key]}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
<span className="text-xl font-500">{confirm?.describe}</span>
|
||||||
|
<div>
|
||||||
|
<Button type="primary" onClick={handleConfirm(false, 1)}>
|
||||||
|
{t("ignore")} ({second})
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Space>
|
||||||
|
<Button onClick={handleConfirm(true, 1)} status="success">
|
||||||
|
{t("allow_once")}
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleConfirm(true, 3)} status="success">
|
||||||
|
{t("temporary_allow", {
|
||||||
|
permissionContent: confirm?.permissionContent,
|
||||||
|
})}
|
||||||
|
</Button>
|
||||||
|
{likeNum > 2 && (
|
||||||
|
<Button onClick={handleConfirm(true, 2)} status="success">
|
||||||
|
{t("temporary_allow_all", {
|
||||||
|
permissionContent: confirm?.permissionContent,
|
||||||
|
})}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button onClick={handleConfirm(true, 5)} status="success">
|
||||||
|
{t("permanent_allow", {
|
||||||
|
permissionContent: confirm?.permissionContent,
|
||||||
|
})}
|
||||||
|
</Button>
|
||||||
|
{likeNum > 2 && (
|
||||||
|
<Button onClick={handleConfirm(true, 4)} status="success">
|
||||||
|
{t("permanent_allow_all", {
|
||||||
|
permissionContent: confirm?.permissionContent,
|
||||||
|
})}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Space>
|
||||||
|
<Button onClick={handleConfirm(false, 1)} status="danger">
|
||||||
|
{t("deny_once")}
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleConfirm(false, 3)} status="danger">
|
||||||
|
{t("temporary_deny", {
|
||||||
|
permissionContent: confirm?.permissionContent,
|
||||||
|
})}
|
||||||
|
</Button>
|
||||||
|
{likeNum > 2 && (
|
||||||
|
<Button onClick={handleConfirm(false, 2)} status="danger">
|
||||||
|
{t("temporary_deny_all", {
|
||||||
|
permissionContent: confirm?.permissionContent,
|
||||||
|
})}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button onClick={handleConfirm(false, 5)} status="danger">
|
||||||
|
{t("permanent_deny", {
|
||||||
|
permissionContent: confirm?.permissionContent,
|
||||||
|
})}
|
||||||
|
</Button>
|
||||||
|
{likeNum > 2 && (
|
||||||
|
<Button onClick={handleConfirm(false, 4)} status="danger">
|
||||||
|
{t("permanent_deny_all", {
|
||||||
|
permissionContent: confirm?.permissionContent,
|
||||||
|
})}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
33
src/pages/confirm/main.tsx
Normal file
33
src/pages/confirm/main.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import React from "react";
|
||||||
|
import ReactDOM from "react-dom/client";
|
||||||
|
import App from "./App.tsx";
|
||||||
|
import MainLayout from "../components/layout/MainLayout.tsx";
|
||||||
|
import "@arco-design/web-react/dist/css/arco.css";
|
||||||
|
import "@App/locales/locales";
|
||||||
|
import "@App/index.css";
|
||||||
|
import { Provider } from "react-redux";
|
||||||
|
import { store } from "@App/pages/store/store.ts";
|
||||||
|
import LoggerCore from "@App/app/logger/core.ts";
|
||||||
|
import migrate from "@App/app/migrate.ts";
|
||||||
|
import { LoggerDAO } from "@App/app/repo/logger.ts";
|
||||||
|
import DBWriter from "@App/app/logger/db_writer.ts";
|
||||||
|
|
||||||
|
// 初始化数据库
|
||||||
|
migrate();
|
||||||
|
// 初始化日志组件
|
||||||
|
const loggerCore = new LoggerCore({
|
||||||
|
writer: new DBWriter(new LoggerDAO()),
|
||||||
|
labels: { env: "install" },
|
||||||
|
});
|
||||||
|
|
||||||
|
loggerCore.logger().debug("page start");
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<Provider store={store}>
|
||||||
|
<MainLayout className="!flex-col !px-4 box-border">
|
||||||
|
<App />
|
||||||
|
</MainLayout>
|
||||||
|
</Provider>
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
@ -10,9 +10,9 @@ import i18n from "@App/locales/locales";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import Logger from "@App/app/logger/logger";
|
import Logger from "@App/app/logger/logger";
|
||||||
import { systemConfig } from "@App/pages/store/global";
|
import FileSystemFactory, { FileSystemType } from "@Packages/filesystem/factory";
|
||||||
import { FileSystemType } from "@Packages/filesystem/factory";
|
|
||||||
import FileSystemParams from "@App/pages/components/FileSystemParams";
|
import FileSystemParams from "@App/pages/components/FileSystemParams";
|
||||||
|
import { systemConfig } from "@App/pages/store/global";
|
||||||
|
|
||||||
function Setting() {
|
function Setting() {
|
||||||
const [syncDelete, setSyncDelete] = useState<boolean>();
|
const [syncDelete, setSyncDelete] = useState<boolean>();
|
||||||
@ -46,16 +46,25 @@ function Setting() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadConfigs = async () => {
|
const loadConfigs = async () => {
|
||||||
const [cloudSync, menuExpandNum, checkCycle, updateDisabled, silenceUpdate, eslintConfig, enableEslint] =
|
const [
|
||||||
await Promise.all([
|
cloudSync,
|
||||||
systemConfig.getCloudSync(),
|
menuExpandNum,
|
||||||
systemConfig.getMenuExpandNum(),
|
checkCycle,
|
||||||
systemConfig.getCheckScriptUpdateCycle(),
|
updateDisabled,
|
||||||
systemConfig.getUpdateDisableScript(),
|
silenceUpdate,
|
||||||
systemConfig.getSilenceUpdateScript(),
|
eslintConfig,
|
||||||
systemConfig.getEslintConfig(),
|
enableEslint,
|
||||||
systemConfig.getEnableEslint(),
|
language,
|
||||||
]);
|
] = await Promise.all([
|
||||||
|
systemConfig.getCloudSync(),
|
||||||
|
systemConfig.getMenuExpandNum(),
|
||||||
|
systemConfig.getCheckScriptUpdateCycle(),
|
||||||
|
systemConfig.getUpdateDisableScript(),
|
||||||
|
systemConfig.getSilenceUpdateScript(),
|
||||||
|
systemConfig.getEslintConfig(),
|
||||||
|
systemConfig.getEnableEslint(),
|
||||||
|
systemConfig.getLanguage(),
|
||||||
|
]);
|
||||||
|
|
||||||
setSyncDelete(cloudSync.syncDelete);
|
setSyncDelete(cloudSync.syncDelete);
|
||||||
setEnableCloudSync(cloudSync.enable);
|
setEnableCloudSync(cloudSync.enable);
|
||||||
@ -67,6 +76,7 @@ function Setting() {
|
|||||||
setSilenceUpdateScript(silenceUpdate);
|
setSilenceUpdateScript(silenceUpdate);
|
||||||
setEslintConfig(eslintConfig);
|
setEslintConfig(eslintConfig);
|
||||||
setEnableEslint(enableEslint);
|
setEnableEslint(enableEslint);
|
||||||
|
setLanguage(language);
|
||||||
};
|
};
|
||||||
|
|
||||||
loadConfigs();
|
loadConfigs();
|
||||||
@ -96,9 +106,7 @@ function Setting() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setLanguage(value);
|
setLanguage(value);
|
||||||
i18n.changeLanguage(value);
|
systemConfig.setLanguage(value);
|
||||||
dayjs.locale(value.toLocaleLowerCase());
|
|
||||||
localStorage.language = value;
|
|
||||||
Message.success(t("language_change_tip")!);
|
Message.success(t("language_change_tip")!);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -3,6 +3,8 @@ import ChromeStorage from "./chrome_storage";
|
|||||||
import { defaultConfig } from "../../../packages/eslint/linter-config";
|
import { defaultConfig } from "../../../packages/eslint/linter-config";
|
||||||
import { FileSystemType } from "@Packages/filesystem/factory";
|
import { FileSystemType } from "@Packages/filesystem/factory";
|
||||||
import { MessageQueue } from "@Packages/message/message_queue";
|
import { MessageQueue } from "@Packages/message/message_queue";
|
||||||
|
import i18n from "@App/locales/locales";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
export const SystamConfigChange = "systemConfigChange";
|
export const SystamConfigChange = "systemConfigChange";
|
||||||
|
|
||||||
@ -214,4 +216,26 @@ export class SystemConfig {
|
|||||||
setMenuExpandNum(val: number) {
|
setMenuExpandNum(val: number) {
|
||||||
this.set("menu_expand_num", val);
|
this.set("menu_expand_num", val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getLanguage() {
|
||||||
|
const defaultLanguage = await new Promise<string>((resolve) => {
|
||||||
|
chrome.i18n.getAcceptLanguages((lngs) => {
|
||||||
|
// 遍历数组寻找匹配语言
|
||||||
|
for (let i = 0; i < lngs.length; i += 1) {
|
||||||
|
const lng = lngs[i];
|
||||||
|
if (i18n.hasResourceBundle(lng, "translation")) {
|
||||||
|
resolve(lng);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return this.get("language", defaultLanguage || chrome.i18n.getUILanguage());
|
||||||
|
}
|
||||||
|
|
||||||
|
setLanguage(value: any) {
|
||||||
|
this.set("language", value);
|
||||||
|
i18n.changeLanguage(value);
|
||||||
|
dayjs.locale(value.toLocaleLowerCase());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,8 @@ async function main() {
|
|||||||
// 初始化管理器
|
// 初始化管理器
|
||||||
const message = new ExtensionMessage(true);
|
const message = new ExtensionMessage(true);
|
||||||
const server = new Server("serviceWorker", message);
|
const server = new Server("serviceWorker", message);
|
||||||
const manager = new ServiceWorkerManager(server, new MessageQueue(), new ServiceWorkerMessageSend());
|
const messageQueue = new MessageQueue();
|
||||||
|
const manager = new ServiceWorkerManager(server, messageQueue, new ServiceWorkerMessageSend());
|
||||||
manager.initManager();
|
manager.initManager();
|
||||||
// 初始化沙盒环境
|
// 初始化沙盒环境
|
||||||
await setupOffscreenDocument();
|
await setupOffscreenDocument();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user