setting
This commit is contained in:
@@ -3,12 +3,8 @@ import { version } from "../../package.json";
|
||||
export const ExtVersion = version;
|
||||
|
||||
export const ExtServer = "https://ext.scriptcat.org/";
|
||||
export const ExtServerApi = ExtServer + "api/v1/";
|
||||
|
||||
export const ExternalWhitelist = [
|
||||
"greasyfork.org",
|
||||
"scriptcat.org",
|
||||
"tampermonkey.net.cn",
|
||||
"openuserjs.org",
|
||||
];
|
||||
export const ExternalWhitelist = ["greasyfork.org", "scriptcat.org", "tampermonkey.net.cn", "openuserjs.org"];
|
||||
|
||||
export const ExternalMessage = "externalMessage";
|
||||
|
@@ -358,5 +358,6 @@
|
||||
"collapse": "Collapse",
|
||||
"expand": "Expand",
|
||||
"menu_expand_num_before": "Menu item more than",
|
||||
"menu_expand_num_after": "Auto-hide."
|
||||
"menu_expand_num_after": "Auto-hide.",
|
||||
"eslint_config_format_error": "ESLint configuration format error"
|
||||
}
|
@@ -363,5 +363,6 @@
|
||||
"expand": "展开",
|
||||
"menu_expand_num_before": "菜单项超过",
|
||||
"menu_expand_num_after": "个时,自动隐藏",
|
||||
"script_name_cannot_be_set_to_empty": "脚本name不可以设置为空"
|
||||
"script_name_cannot_be_set_to_empty": "脚本name不可以设置为空",
|
||||
"eslint_config_format_error": "eslint配置格式错误"
|
||||
}
|
@@ -1,6 +1,8 @@
|
||||
import React from "react";
|
||||
import { Input, Select, Space } from "@arco-design/web-react";
|
||||
import FileSystemFactory, { FileSystemType } from "@Packages/filesystem/factory";
|
||||
|
||||
const fsParams = FileSystemFactory.params();
|
||||
|
||||
const fileSystemList: {
|
||||
key: FileSystemType;
|
||||
@@ -65,10 +67,7 @@ const FileSystemParams: React.FC<{
|
||||
<>
|
||||
<span>{fsParams[fileSystemType][key].title}</span>
|
||||
<Select
|
||||
value={
|
||||
fileSystemParams[key] ||
|
||||
fsParams[fileSystemType][key].options![0]
|
||||
}
|
||||
value={fileSystemParams[key] || fsParams[fileSystemType][key].options![0]}
|
||||
onChange={(value) => {
|
||||
onChangeFileSystemParams({
|
||||
...fileSystemParams,
|
||||
|
@@ -1,29 +1,28 @@
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Collapse,
|
||||
Link,
|
||||
Message,
|
||||
Space,
|
||||
Typography,
|
||||
} from "@arco-design/web-react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Button, Card, Collapse, Link, Message, Space, Typography } from "@arco-design/web-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import FileSystemParams from "../FileSystemParams";
|
||||
import { systemConfig } from "@App/pages/store/global";
|
||||
import { FileSystemType } from "@Packages/filesystem/factory";
|
||||
|
||||
const CollapseItem = Collapse.Item;
|
||||
|
||||
const GMApiSetting: React.FC = () => {
|
||||
const systemConfig = IoC.instance(SystemConfig) as SystemConfig;
|
||||
const [status, setStatus] = useState(systemConfig.catFileStorage.status);
|
||||
const [fileSystemType, setFilesystemType] = useState<FileSystemType>(
|
||||
systemConfig.catFileStorage.filesystem
|
||||
);
|
||||
const [status, setStatus] = useState("unset");
|
||||
const [fileSystemType, setFilesystemType] = useState<FileSystemType>("webdav");
|
||||
const [fileSystemParams, setFilesystemParam] = useState<{
|
||||
[key: string]: any;
|
||||
}>(systemConfig.catFileStorage.params[fileSystemType] || {});
|
||||
}>({});
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
systemConfig.getCatFileStorage().then((res) => {
|
||||
setStatus(res.status);
|
||||
setFilesystemType(res.filesystem);
|
||||
setFilesystemParam(res.params[res.filesystem] || {});
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Card title={t("gm_api")} bordered={false}>
|
||||
<Collapse bordered={false} defaultActiveKey={["storage"]}>
|
||||
@@ -48,10 +47,7 @@ const GMApiSetting: React.FC = () => {
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
try {
|
||||
await FileSystemFactory.create(
|
||||
fileSystemType,
|
||||
fileSystemParams
|
||||
);
|
||||
await FileSystemFactory.create(fileSystemType, fileSystemParams);
|
||||
} catch (e) {
|
||||
Message.error(`${t("account_validation_failed")}: ${e}`);
|
||||
return;
|
||||
@@ -87,10 +83,7 @@ const GMApiSetting: React.FC = () => {
|
||||
type="secondary"
|
||||
onClick={async () => {
|
||||
try {
|
||||
let fs = await FileSystemFactory.create(
|
||||
fileSystemType,
|
||||
fileSystemParams
|
||||
);
|
||||
let fs = await FileSystemFactory.create(fileSystemType, fileSystemParams);
|
||||
fs = await fs.openDir("ScriptCat/app");
|
||||
window.open(await fs.getDirUrl(), "_black");
|
||||
} catch (e) {
|
||||
@@ -110,17 +103,9 @@ const GMApiSetting: React.FC = () => {
|
||||
setFilesystemParam(params);
|
||||
}}
|
||||
/>
|
||||
{status === "unset" && (
|
||||
<Typography.Text type="secondary">{t("not_set")}</Typography.Text>
|
||||
)}
|
||||
{status === "success" && (
|
||||
<Typography.Text type="success">{t("in_use")}</Typography.Text>
|
||||
)}
|
||||
{status === "error" && (
|
||||
<Typography.Text type="error">
|
||||
{t("storage_error")}
|
||||
</Typography.Text>
|
||||
)}
|
||||
{status === "unset" && <Typography.Text type="secondary">{t("not_set")}</Typography.Text>}
|
||||
{status === "success" && <Typography.Text type="success">{t("in_use")}</Typography.Text>}
|
||||
{status === "error" && <Typography.Text type="error">{t("storage_error")}</Typography.Text>}
|
||||
</Space>
|
||||
</CollapseItem>
|
||||
</Collapse>
|
||||
|
@@ -14,12 +14,11 @@ import { RiPlayFill, RiStopFill } from "react-icons/ri";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ScriptIcons } from "@App/pages/options/routes/utils";
|
||||
import { ScriptMenu, ScriptMenuItem } from "@App/app/service/service_worker/popup";
|
||||
import { selectMenuExpandNum } from "@App/pages/store/features/setting";
|
||||
import { useAppSelector } from "@App/pages/store/hooks";
|
||||
import { popupClient, runtimeClient, scriptClient } from "@App/pages/store/features/script";
|
||||
import { i18nName } from "@App/locales/locales";
|
||||
import { subscribeScriptRunStatus } from "@App/app/service/queue";
|
||||
import { messageQueue } from "@App/pages/store/global";
|
||||
import { messageQueue, systemConfig } from "@App/pages/store/global";
|
||||
|
||||
const CollapseItem = Collapse.Item;
|
||||
|
||||
@@ -46,7 +45,7 @@ const ScriptMenuList: React.FC<{
|
||||
[key: string]: boolean;
|
||||
}>({});
|
||||
const { t } = useTranslation();
|
||||
const menuExpandNum = useAppSelector(selectMenuExpandNum);
|
||||
const [menuExpandNum, setMenuExpandNum] = useState(5);
|
||||
|
||||
let url: URL;
|
||||
try {
|
||||
@@ -70,6 +69,10 @@ const ScriptMenuList: React.FC<{
|
||||
return newList;
|
||||
});
|
||||
});
|
||||
// 获取配置
|
||||
systemConfig.getMenuExpandNum().then((num) => {
|
||||
setMenuExpandNum(num);
|
||||
});
|
||||
return () => {
|
||||
unsub();
|
||||
};
|
||||
|
@@ -16,7 +16,7 @@ import React, { ReactNode, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import "./index.css";
|
||||
import { useAppDispatch, useAppSelector } from "@App/pages/store/hooks";
|
||||
import { selectThemeMode, setDarkMode } from "@App/pages/store/features/setting";
|
||||
import { selectThemeMode, setDarkMode } from "@App/pages/store/features/config";
|
||||
import { RiFileCodeLine, RiImportLine, RiPlayListAddLine, RiTerminalBoxLine, RiTimerLine } from "react-icons/ri";
|
||||
|
||||
const MainLayout: React.FC<{
|
||||
|
@@ -80,7 +80,7 @@ import {
|
||||
requestStopScript,
|
||||
requestRunScript,
|
||||
} from "@App/pages/store/features/script";
|
||||
import { selectScriptListColumnWidth } from "@App/pages/store/features/setting";
|
||||
import { systemConfig } from "@App/pages/store/global";
|
||||
|
||||
type ListType = Script & { loading?: boolean };
|
||||
|
||||
@@ -93,7 +93,6 @@ function ScriptList() {
|
||||
const [cloudScript, setCloudScript] = useState<Script>();
|
||||
const dispatch = useAppDispatch();
|
||||
const scriptList = useAppSelector(selectScripts);
|
||||
const scriptListColumnWidth = useAppSelector(selectScriptListColumnWidth);
|
||||
const inputRef = useRef<RefInputType>(null);
|
||||
const navigate = useNavigate();
|
||||
const openUserConfig = useSearchParams()[0].get("userConfig") || "";
|
||||
@@ -557,12 +556,14 @@ function ScriptList() {
|
||||
});
|
||||
}
|
||||
}
|
||||
setNewColumns(
|
||||
columns.map((item) => {
|
||||
item.width = scriptListColumnWidth[item.key!] ?? item.width;
|
||||
return item;
|
||||
})
|
||||
);
|
||||
systemConfig.getScriptListColumnWidth().then((columnWidth) => {
|
||||
setNewColumns(
|
||||
columns.map((item) => {
|
||||
item.width = columnWidth[item.key!] ?? item.width;
|
||||
return item;
|
||||
})
|
||||
);
|
||||
});
|
||||
}, []);
|
||||
|
||||
// 处理拖拽排序
|
||||
|
@@ -1,13 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Checkbox,
|
||||
Input,
|
||||
Message,
|
||||
Select,
|
||||
Space,
|
||||
} from "@arco-design/web-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Button, Card, Checkbox, Input, Message, Select, Space } from "@arco-design/web-react";
|
||||
import Title from "@arco-design/web-react/es/Typography/title";
|
||||
import { IconQuestionCircleFill } from "@arco-design/web-react/icon";
|
||||
import { format } from "prettier";
|
||||
@@ -17,22 +9,24 @@ import i18n from "@App/locales/locales";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import dayjs from "dayjs";
|
||||
import Logger from "@App/app/logger/logger";
|
||||
import { systemConfig } from "@App/pages/store/global";
|
||||
import { FileSystemType } from "@Packages/filesystem/factory";
|
||||
import FileSystemParams from "@App/pages/components/FileSystemParams";
|
||||
|
||||
function Setting() {
|
||||
const systemConfig = IoC.instance(SystemConfig) as SystemConfig;
|
||||
const [syncDelete, setSyncDelete] = useState<boolean>(
|
||||
systemConfig.cloudSync.syncDelete
|
||||
);
|
||||
const [enableCloudSync, setEnableCloudSync] = useState(
|
||||
systemConfig.cloudSync.enable
|
||||
);
|
||||
const [fileSystemType, setFilesystemType] = useState<FileSystemType>(
|
||||
systemConfig.cloudSync.filesystem
|
||||
);
|
||||
const [syncDelete, setSyncDelete] = useState<boolean>();
|
||||
const [enableCloudSync, setEnableCloudSync] = useState<boolean>();
|
||||
const [fileSystemType, setFilesystemType] = useState<FileSystemType>("webdav");
|
||||
const [fileSystemParams, setFilesystemParam] = useState<{
|
||||
[key: string]: any;
|
||||
}>(systemConfig.cloudSync.params[fileSystemType] || {});
|
||||
}>({});
|
||||
const [language, setLanguage] = useState(i18n.language);
|
||||
const [menuExpandNum, setMenuExpandNum] = useState(5);
|
||||
const [checkScriptUpdateCycle, setCheckScriptUpdateCycle] = useState(0);
|
||||
const [updateDisableScript, setUpdateDisableScript] = useState(false);
|
||||
const [silenceUpdateScript, setSilenceUpdateScript] = useState(false);
|
||||
const [enableEslint, setEnableEslint] = useState(false);
|
||||
const [eslintConfig, setEslintConfig] = useState("");
|
||||
const languageList: { key: string; title: string }[] = [];
|
||||
const { t } = useTranslation();
|
||||
Object.keys(i18n.store.data).forEach((key) => {
|
||||
@@ -49,6 +43,34 @@ function Setting() {
|
||||
title: t("help_translate"),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const loadConfigs = async () => {
|
||||
const [cloudSync, menuExpandNum, checkCycle, updateDisabled, silenceUpdate, eslintConfig, enableEslint] =
|
||||
await Promise.all([
|
||||
systemConfig.getCloudSync(),
|
||||
systemConfig.getMenuExpandNum(),
|
||||
systemConfig.getCheckScriptUpdateCycle(),
|
||||
systemConfig.getUpdateDisableScript(),
|
||||
systemConfig.getSilenceUpdateScript(),
|
||||
systemConfig.getEslintConfig(),
|
||||
systemConfig.getEnableEslint(),
|
||||
]);
|
||||
|
||||
setSyncDelete(cloudSync.syncDelete);
|
||||
setEnableCloudSync(cloudSync.enable);
|
||||
setFilesystemType(cloudSync.filesystem);
|
||||
setFilesystemParam(cloudSync.params[cloudSync.filesystem] || {});
|
||||
setMenuExpandNum(menuExpandNum);
|
||||
setCheckScriptUpdateCycle(checkCycle);
|
||||
setUpdateDisableScript(updateDisabled);
|
||||
setSilenceUpdateScript(silenceUpdate);
|
||||
setEslintConfig(eslintConfig);
|
||||
setEnableEslint(enableEslint);
|
||||
};
|
||||
|
||||
loadConfigs();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Space
|
||||
className="setting"
|
||||
@@ -69,10 +91,7 @@ function Setting() {
|
||||
className="w-24"
|
||||
onChange={(value) => {
|
||||
if (value === "help") {
|
||||
window.open(
|
||||
"https://crowdin.com/project/scriptcat",
|
||||
"_blank"
|
||||
);
|
||||
window.open("https://crowdin.com/project/scriptcat", "_blank");
|
||||
return;
|
||||
}
|
||||
setLanguage(value);
|
||||
@@ -94,9 +113,11 @@ function Setting() {
|
||||
<Input
|
||||
style={{ width: "64px" }}
|
||||
type="number"
|
||||
defaultValue={systemConfig.menuExpandNum.toString()}
|
||||
value={menuExpandNum.toString()}
|
||||
onChange={(val) => {
|
||||
systemConfig.menuExpandNum = parseInt(val, 10);
|
||||
const num = parseInt(val, 10);
|
||||
setMenuExpandNum(num);
|
||||
systemConfig.setMenuExpandNum(num);
|
||||
}}
|
||||
/>
|
||||
{t("menu_expand_num_after")}
|
||||
@@ -134,16 +155,9 @@ function Setting() {
|
||||
if (enableCloudSync) {
|
||||
Message.info(t("cloud_sync_account_verification")!);
|
||||
try {
|
||||
await FileSystemFactory.create(
|
||||
fileSystemType,
|
||||
fileSystemParams
|
||||
);
|
||||
await FileSystemFactory.create(fileSystemType, fileSystemParams);
|
||||
} catch (e) {
|
||||
Message.error(
|
||||
`${t(
|
||||
"cloud_sync_verification_failed"
|
||||
)}: ${JSON.stringify(Logger.E(e))}`
|
||||
);
|
||||
Message.error(`${t("cloud_sync_verification_failed")}: ${JSON.stringify(Logger.E(e))}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -177,12 +191,14 @@ function Setting() {
|
||||
<Space>
|
||||
<span>{t("script_subscription_check_interval")}:</span>
|
||||
<Select
|
||||
defaultValue={systemConfig.checkScriptUpdateCycle.toString()}
|
||||
value={checkScriptUpdateCycle.toString()}
|
||||
style={{
|
||||
width: 120,
|
||||
}}
|
||||
onChange={(value) => {
|
||||
systemConfig.checkScriptUpdateCycle = parseInt(value, 10);
|
||||
const num = parseInt(value, 10);
|
||||
setCheckScriptUpdateCycle(num);
|
||||
systemConfig.setCheckScriptUpdateCycle(num);
|
||||
}}
|
||||
>
|
||||
<Select.Option value="0">{t("never")}</Select.Option>
|
||||
@@ -194,17 +210,19 @@ function Setting() {
|
||||
</Space>
|
||||
<Checkbox
|
||||
onChange={(checked) => {
|
||||
systemConfig.updateDisableScript = checked;
|
||||
setEnableCloudSync(checked);
|
||||
systemConfig.setUpdateDisableScript(checked);
|
||||
}}
|
||||
defaultChecked={systemConfig.updateDisableScript}
|
||||
checked={updateDisableScript}
|
||||
>
|
||||
{t("update_disabled_scripts")}
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
onChange={(checked) => {
|
||||
systemConfig.silenceUpdateScript = checked;
|
||||
setSilenceUpdateScript(checked);
|
||||
systemConfig.setSilenceUpdateScript(checked);
|
||||
}}
|
||||
defaultChecked={systemConfig.silenceUpdateScript}
|
||||
checked={silenceUpdateScript}
|
||||
>
|
||||
{t("silent_update_non_critical_changes")}
|
||||
</Checkbox>
|
||||
@@ -215,9 +233,10 @@ function Setting() {
|
||||
<Space direction="vertical" className="w-full">
|
||||
<Checkbox
|
||||
onChange={(checked) => {
|
||||
systemConfig.enableEslint = checked;
|
||||
setEnableEslint(checked);
|
||||
systemConfig.setEnableEslint(checked);
|
||||
}}
|
||||
defaultChecked={systemConfig.enableEslint}
|
||||
checked={enableEslint}
|
||||
>
|
||||
{t("enable_eslint")}
|
||||
</Checkbox>
|
||||
@@ -246,12 +265,21 @@ function Setting() {
|
||||
minRows: 4,
|
||||
maxRows: 8,
|
||||
}}
|
||||
defaultValue={format(systemConfig.eslintConfig, {
|
||||
parser: "json",
|
||||
plugins: [babel],
|
||||
})}
|
||||
value={eslintConfig}
|
||||
onChange={(v) => {
|
||||
setEslintConfig(v);
|
||||
}}
|
||||
onBlur={(v) => {
|
||||
systemConfig.eslintConfig = v.target.value;
|
||||
format(eslintConfig, {
|
||||
parser: "json",
|
||||
plugins: [babel],
|
||||
})
|
||||
.then((res) => {
|
||||
systemConfig.setEslintConfig(v.target.value);
|
||||
})
|
||||
.catch((e) => {
|
||||
Message.error(`${t("eslint_config_format_error")}: ${JSON.stringify(Logger.E(e))}`);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Space>
|
||||
|
@@ -1,16 +1,5 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Checkbox,
|
||||
Drawer,
|
||||
Empty,
|
||||
Input,
|
||||
List,
|
||||
Message,
|
||||
Modal,
|
||||
Space,
|
||||
} from "@arco-design/web-react";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { Button, Card, Checkbox, Drawer, Empty, Input, List, Message, Modal, Space } from "@arco-design/web-react";
|
||||
import Title from "@arco-design/web-react/es/Typography/title";
|
||||
import { formatUnixTime } from "@App/pkg/utils/utils";
|
||||
import FileSystemParams from "@App/pages/components/FileSystemParams";
|
||||
@@ -18,20 +7,27 @@ import { IconQuestionCircleFill } from "@arco-design/web-react/icon";
|
||||
import { RefInputType } from "@arco-design/web-react/es/Input/interface";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FileSystemType } from "@Packages/filesystem/factory";
|
||||
import { systemConfig } from "@App/pages/store/global";
|
||||
|
||||
function Tools() {
|
||||
const [loading, setLoading] = useState<{ [key: string]: boolean }>({});
|
||||
const fileRef = useRef<HTMLInputElement>(null);
|
||||
const [fileSystemType, setFilesystemType] = useState<FileSystemType>(
|
||||
systemConfig.backup.filesystem
|
||||
);
|
||||
const [fileSystemType, setFilesystemType] = useState<FileSystemType>("webdav");
|
||||
const [fileSystemParams, setFilesystemParam] = useState<{
|
||||
[key: string]: any;
|
||||
}>(systemConfig.backup.params[fileSystemType] || {});
|
||||
}>({});
|
||||
const [backupFileList, setBackupFileList] = useState<File[]>([]);
|
||||
const vscodeRef = useRef<RefInputType>(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
// 获取配置
|
||||
systemConfig.getBackup().then((backup) => {
|
||||
setFilesystemType(backup.filesystem);
|
||||
setFilesystemParam(backup.params[backup.filesystem] || {});
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Space
|
||||
className="tools"
|
||||
@@ -47,12 +43,7 @@ function Tools() {
|
||||
<Space direction="vertical">
|
||||
<Title heading={6}>{t("local")}</Title>
|
||||
<Space>
|
||||
<input
|
||||
type="file"
|
||||
ref={fileRef}
|
||||
style={{ display: "none" }}
|
||||
accept=".zip"
|
||||
/>
|
||||
<input type="file" ref={fileRef} style={{ display: "none" }} accept=".zip" />
|
||||
<Button
|
||||
type="primary"
|
||||
loading={loading.local}
|
||||
@@ -96,12 +87,12 @@ function Tools() {
|
||||
loading={loading.cloud}
|
||||
onClick={() => {
|
||||
// Store parameters
|
||||
const params = { ...systemConfig.backup.params };
|
||||
const params = { ...fileSystemParams };
|
||||
params[fileSystemType] = fileSystemParams;
|
||||
systemConfig.backup = {
|
||||
systemConfig.setBackup({
|
||||
filesystem: fileSystemType,
|
||||
params,
|
||||
};
|
||||
});
|
||||
setLoading((prev) => ({ ...prev, cloud: true }));
|
||||
Message.info(t("preparing_backup")!);
|
||||
syncCtrl
|
||||
@@ -123,10 +114,7 @@ function Tools() {
|
||||
key="list"
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
let fs = await FileSystemFactory.create(
|
||||
fileSystemType,
|
||||
fileSystemParams
|
||||
);
|
||||
let fs = await FileSystemFactory.create(fileSystemType, fileSystemParams);
|
||||
try {
|
||||
fs = await fs.openDir("ScriptCat");
|
||||
let list = await fs.list();
|
||||
@@ -158,10 +146,7 @@ function Tools() {
|
||||
type="secondary"
|
||||
size="mini"
|
||||
onClick={async () => {
|
||||
let fs = await FileSystemFactory.create(
|
||||
fileSystemType,
|
||||
fileSystemParams
|
||||
);
|
||||
let fs = await FileSystemFactory.create(fileSystemType, fileSystemParams);
|
||||
try {
|
||||
fs = await fs.openDir("ScriptCat");
|
||||
const url = await fs.getDirUrl();
|
||||
@@ -190,20 +175,14 @@ function Tools() {
|
||||
dataSource={backupFileList}
|
||||
render={(item: File) => (
|
||||
<List.Item key={item.name}>
|
||||
<List.Item.Meta
|
||||
title={item.name}
|
||||
description={formatUnixTime(item.updatetime / 1000)}
|
||||
/>
|
||||
<List.Item.Meta title={item.name} description={formatUnixTime(item.updatetime / 1000)} />
|
||||
<Space className="w-full justify-end">
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
onClick={async () => {
|
||||
Message.info(t("pulling_data_from_cloud")!);
|
||||
let fs = await FileSystemFactory.create(
|
||||
fileSystemType,
|
||||
fileSystemParams
|
||||
);
|
||||
let fs = await FileSystemFactory.create(fileSystemType, fileSystemParams);
|
||||
let file: FileReader;
|
||||
let data: Blob;
|
||||
try {
|
||||
@@ -237,22 +216,13 @@ function Tools() {
|
||||
onClick={() => {
|
||||
Modal.confirm({
|
||||
title: t("confirm_delete"),
|
||||
content: `${t("confirm_delete_backup_file")}${
|
||||
item.name
|
||||
}?`,
|
||||
content: `${t("confirm_delete_backup_file")}${item.name}?`,
|
||||
onOk: async () => {
|
||||
let fs = await FileSystemFactory.create(
|
||||
fileSystemType,
|
||||
fileSystemParams
|
||||
);
|
||||
let fs = await FileSystemFactory.create(fileSystemType, fileSystemParams);
|
||||
try {
|
||||
fs = await fs.openDir("ScriptCat");
|
||||
await fs.delete(item.name);
|
||||
setBackupFileList(
|
||||
backupFileList.filter(
|
||||
(i) => i.name !== item.name
|
||||
)
|
||||
);
|
||||
setBackupFileList(backupFileList.filter((i) => i.name !== item.name));
|
||||
Message.success(t("delete_success")!);
|
||||
} catch (e) {
|
||||
Message.error(`${t("delete_failed")}${e}`);
|
||||
|
51
src/pages/store/features/config.ts
Normal file
51
src/pages/store/features/config.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { SystemConfig } from "@App/pkg/config/config";
|
||||
import { createAppSlice } from "../hooks";
|
||||
import { PayloadAction } from "@reduxjs/toolkit";
|
||||
import { editor } from "monaco-editor";
|
||||
|
||||
function setAutoMode() {
|
||||
const darkTheme = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
const isMatch = (match: boolean) => {
|
||||
if (match) {
|
||||
document.body.setAttribute("arco-theme", "dark");
|
||||
editor.setTheme("vs-dark");
|
||||
} else {
|
||||
document.body.removeAttribute("arco-theme");
|
||||
editor.setTheme("vs");
|
||||
}
|
||||
};
|
||||
darkTheme.addEventListener("change", (e) => {
|
||||
isMatch(e.matches);
|
||||
});
|
||||
isMatch(darkTheme.matches);
|
||||
}
|
||||
|
||||
export const configSlice = createAppSlice({
|
||||
name: "setting",
|
||||
initialState: {
|
||||
lightMode: localStorage.lightMode || "auto",
|
||||
},
|
||||
reducers: (create) => {
|
||||
// 初始化黑夜模式
|
||||
setAutoMode();
|
||||
return {
|
||||
setDarkMode: create.reducer((state, action: PayloadAction<"light" | "dark" | "auto">) => {
|
||||
localStorage.loghtMode = action.payload;
|
||||
state.lightMode = action.payload;
|
||||
if (action.payload === "auto") {
|
||||
setAutoMode();
|
||||
} else {
|
||||
document.body.setAttribute("arco-theme", action.payload);
|
||||
editor.setTheme(action.payload === "dark" ? "vs-dark" : "vs");
|
||||
}
|
||||
}),
|
||||
};
|
||||
},
|
||||
selectors: {
|
||||
selectThemeMode: (state) => state.lightMode,
|
||||
},
|
||||
});
|
||||
|
||||
export const { setDarkMode } = configSlice.actions;
|
||||
|
||||
export const { selectThemeMode } = configSlice.selectors;
|
@@ -1,82 +0,0 @@
|
||||
import { createAppSlice } from "../hooks";
|
||||
import { PayloadAction } from "@reduxjs/toolkit";
|
||||
import { editor } from "monaco-editor";
|
||||
|
||||
function setAutoMode() {
|
||||
const darkTheme = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
const isMatch = (match: boolean) => {
|
||||
if (match) {
|
||||
document.body.setAttribute("arco-theme", "dark");
|
||||
editor.setTheme("vs-dark");
|
||||
} else {
|
||||
document.body.removeAttribute("arco-theme");
|
||||
editor.setTheme("vs");
|
||||
}
|
||||
};
|
||||
darkTheme.addEventListener("change", (e) => {
|
||||
isMatch(e.matches);
|
||||
});
|
||||
isMatch(darkTheme.matches);
|
||||
}
|
||||
|
||||
export type SystemConfig = {
|
||||
lightMode: "light" | "dark" | "auto";
|
||||
eslint: {
|
||||
enable: boolean;
|
||||
config: string;
|
||||
};
|
||||
scriptListColumnWidth: { [key: string]: number };
|
||||
menuExpandNum: number;
|
||||
};
|
||||
|
||||
export const settingSlice = createAppSlice({
|
||||
name: "setting",
|
||||
initialState: {
|
||||
lightMode: localStorage.lightMode || "auto",
|
||||
eslint: {
|
||||
enable: true,
|
||||
config: "",
|
||||
},
|
||||
scriptListColumnWidth: {} as { [key: string]: number },
|
||||
menuExpandNum: 5,
|
||||
} as SystemConfig,
|
||||
reducers: (create) => {
|
||||
// 初始化黑夜模式
|
||||
setAutoMode();
|
||||
// 加载配置
|
||||
chrome.storage.sync.get("systemSetting", (result) => {
|
||||
const systemSetting = result.systemSetting as SystemConfig;
|
||||
settingSlice.actions.initSetting(systemSetting);
|
||||
if (systemSetting) {
|
||||
localStorage.lightMode = systemSetting.lightMode;
|
||||
}
|
||||
});
|
||||
return {
|
||||
initSetting: create.reducer((state, action: PayloadAction<SystemConfig>) => {
|
||||
state.menuExpandNum = action.payload.menuExpandNum;
|
||||
}),
|
||||
setDarkMode: create.reducer((state, action: PayloadAction<"light" | "dark" | "auto">) => {
|
||||
localStorage.loghtMode = action.payload;
|
||||
state.lightMode = action.payload;
|
||||
if (action.payload === "auto") {
|
||||
setAutoMode();
|
||||
} else {
|
||||
document.body.setAttribute("arco-theme", action.payload);
|
||||
editor.setTheme(action.payload === "dark" ? "vs-dark" : "vs");
|
||||
}
|
||||
}),
|
||||
menuExpandNum: create.reducer((state, action: PayloadAction<number>) => {
|
||||
state.menuExpandNum = action.payload;
|
||||
}),
|
||||
};
|
||||
},
|
||||
selectors: {
|
||||
selectThemeMode: (state) => state.lightMode,
|
||||
selectScriptListColumnWidth: (state) => state.scriptListColumnWidth,
|
||||
selectMenuExpandNum: (state) => state.menuExpandNum,
|
||||
},
|
||||
});
|
||||
|
||||
export const { setDarkMode } = settingSlice.actions;
|
||||
|
||||
export const { selectThemeMode, selectScriptListColumnWidth, selectMenuExpandNum } = settingSlice.selectors;
|
@@ -1,5 +1,7 @@
|
||||
import { SystemConfig } from "@App/pkg/config/config";
|
||||
import { ExtensionMessage } from "@Packages/message/extension_message";
|
||||
import { MessageQueue } from "@Packages/message/message_queue";
|
||||
|
||||
export const message = new ExtensionMessage();
|
||||
export const messageQueue = new MessageQueue();
|
||||
export const systemConfig = new SystemConfig(messageQueue);
|
||||
|
@@ -1,12 +1,12 @@
|
||||
import type { Action, ThunkAction } from "@reduxjs/toolkit";
|
||||
import { combineSlices, configureStore } from "@reduxjs/toolkit";
|
||||
import { setupListeners } from "@reduxjs/toolkit/query";
|
||||
import { settingSlice } from "./features/setting";
|
||||
import { scriptSlice } from "./features/script";
|
||||
import { configSlice } from "./features/config";
|
||||
|
||||
// `combineSlices` automatically combines the reducers using
|
||||
// their `reducerPath`s, therefore we no longer need to call `combineReducers`.
|
||||
const rootReducer = combineSlices(settingSlice, scriptSlice);
|
||||
const rootReducer = combineSlices(configSlice, scriptSlice);
|
||||
// Infer the `RootState` type from the root reducer
|
||||
export type RootState = ReturnType<typeof rootReducer>;
|
||||
|
||||
|
58
src/pkg/config/chrome_storage.ts
Normal file
58
src/pkg/config/chrome_storage.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
export default class ChromeStorage {
|
||||
private prefix: string;
|
||||
|
||||
private storage: chrome.storage.StorageArea;
|
||||
|
||||
constructor(prefix: string, sync: boolean) {
|
||||
this.prefix = `${prefix}_`;
|
||||
this.storage = sync ? chrome.storage.sync : chrome.storage.local;
|
||||
}
|
||||
|
||||
public buildKey(key: string): string {
|
||||
return this.prefix + key;
|
||||
}
|
||||
|
||||
public get(key: string): Promise<any> {
|
||||
return new Promise((resolve) => {
|
||||
key = this.buildKey(key);
|
||||
this.storage.get(key, (items) => {
|
||||
resolve(items[key]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public set(key: string, value: any): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
const kvp: { [key: string]: any } = {};
|
||||
kvp[this.buildKey(key)] = value;
|
||||
this.storage.set(kvp, () => resolve());
|
||||
});
|
||||
}
|
||||
|
||||
public remove(key: string): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
this.storage.remove(this.buildKey(key), () => resolve());
|
||||
});
|
||||
}
|
||||
|
||||
public removeAll(): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
this.storage.clear(() => resolve());
|
||||
});
|
||||
}
|
||||
|
||||
public keys(): Promise<{ [key: string]: any }> {
|
||||
return new Promise((resolve) => {
|
||||
const ret: { [key: string]: any } = {};
|
||||
const prefix = this.buildKey("");
|
||||
this.storage.get((items) => {
|
||||
Object.keys(items).forEach((key) => {
|
||||
if (key.startsWith(prefix)) {
|
||||
ret[key.substring(prefix.length)] = items[key];
|
||||
}
|
||||
});
|
||||
resolve(ret);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
217
src/pkg/config/config.ts
Normal file
217
src/pkg/config/config.ts
Normal file
@@ -0,0 +1,217 @@
|
||||
import { Message } from "@arco-design/web-react";
|
||||
import ChromeStorage from "./chrome_storage";
|
||||
import { defaultConfig } from "../../../eslint/linter-config";
|
||||
import { FileSystemType } from "@Packages/filesystem/factory";
|
||||
import { MessageQueue } from "@Packages/message/message_queue";
|
||||
|
||||
export const SystamConfigChange = "systemConfigChange";
|
||||
|
||||
export type CloudSyncConfig = {
|
||||
enable: boolean;
|
||||
syncDelete: boolean;
|
||||
filesystem: FileSystemType;
|
||||
params: { [key: string]: any };
|
||||
};
|
||||
|
||||
export type CATFileStorage = {
|
||||
filesystem: FileSystemType;
|
||||
params: { [key: string]: any };
|
||||
status: "unset" | "success" | "error";
|
||||
};
|
||||
|
||||
export class SystemConfig {
|
||||
public cache = new Map<string, any>();
|
||||
|
||||
public storage = new ChromeStorage("system", true);
|
||||
|
||||
constructor(private mq: MessageQueue) {
|
||||
this.mq.subscribe("systemConfigChange", (msg) => {
|
||||
const { key, value } = msg;
|
||||
this.cache.set(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
async getAll(): Promise<{ [key: string]: any }> {
|
||||
const ret: { [key: string]: any } = {};
|
||||
const list = await this.storage.keys();
|
||||
Object.keys(list).forEach((key) => {
|
||||
this.cache.set(key, list[key]);
|
||||
ret[key] = list[key];
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
get<T>(key: string, defaultValue: T): Promise<T> {
|
||||
if (this.cache.has(key)) {
|
||||
return Promise.resolve(this.cache.get(key));
|
||||
}
|
||||
return this.storage.get(key).then((val) => {
|
||||
if (val === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
this.cache.set(key, val);
|
||||
return val;
|
||||
});
|
||||
}
|
||||
|
||||
public set(key: string, val: any) {
|
||||
this.cache.set(key, val);
|
||||
this.storage.set(key, val);
|
||||
// 发送消息通知更新
|
||||
this.mq.publish(SystamConfigChange, {
|
||||
key,
|
||||
value: val,
|
||||
});
|
||||
}
|
||||
|
||||
public getChangetime() {
|
||||
return this.get("changetime", 0);
|
||||
}
|
||||
|
||||
public setChangetime(n: number) {
|
||||
this.set("changetime", 0);
|
||||
}
|
||||
|
||||
// 检查更新周期,单位为秒
|
||||
public getCheckScriptUpdateCycle() {
|
||||
return this.get("check_script_update_cycle", 86400);
|
||||
}
|
||||
|
||||
public setCheckScriptUpdateCycle(n: number) {
|
||||
this.set("check_script_update_cycle", n);
|
||||
}
|
||||
|
||||
public getSilenceUpdateScript() {
|
||||
return this.get("silence_update_script", false);
|
||||
}
|
||||
|
||||
public setSilenceUpdateScript(val: boolean) {
|
||||
this.set("silence_update_script", val);
|
||||
}
|
||||
|
||||
public getEnableAutoSync() {
|
||||
return this.get("enable_auto_sync", true);
|
||||
}
|
||||
|
||||
public setEnableAutoSync(enable: boolean) {
|
||||
this.set("enable_auto_sync", enable);
|
||||
}
|
||||
|
||||
// 更新已经禁用的脚本
|
||||
public getUpdateDisableScript() {
|
||||
return this.get("update_disable_script", true);
|
||||
}
|
||||
|
||||
public setUpdateDisableScript(enable: boolean) {
|
||||
this.set("update_disable_script", enable);
|
||||
}
|
||||
|
||||
public getVscodeUrl() {
|
||||
return this.get("vscode_url", "ws://localhost:8642");
|
||||
}
|
||||
|
||||
public setVscodeUrl(val: string) {
|
||||
this.set("vscode_url", val);
|
||||
}
|
||||
|
||||
public getVscodeReconnect() {
|
||||
return this.get("vscode_reconnect", false);
|
||||
}
|
||||
|
||||
public setVscodeReconnect(val: boolean) {
|
||||
this.set("vscode_reconnect", val);
|
||||
}
|
||||
|
||||
public getBackup(): Promise<{
|
||||
filesystem: FileSystemType;
|
||||
params: { [key: string]: any };
|
||||
}> {
|
||||
return this.get("backup", {
|
||||
filesystem: "webdav",
|
||||
params: {},
|
||||
});
|
||||
}
|
||||
|
||||
public setBackup(data: { filesystem: FileSystemType; params: { [key: string]: any } }) {
|
||||
this.set("backup", data);
|
||||
}
|
||||
|
||||
getCloudSync(): Promise<CloudSyncConfig> {
|
||||
return this.get("cloud_sync", {
|
||||
enable: false,
|
||||
syncDelete: true,
|
||||
filesystem: "webdav",
|
||||
params: {},
|
||||
});
|
||||
}
|
||||
|
||||
setCloudSync(data: CloudSyncConfig) {
|
||||
this.set("cloud_sync", data);
|
||||
}
|
||||
|
||||
getCatFileStorage(): Promise<CATFileStorage> {
|
||||
return this.get("cat_file_storage", {
|
||||
status: "unset",
|
||||
filesystem: "webdav",
|
||||
params: {},
|
||||
});
|
||||
}
|
||||
|
||||
setCatFileStorage(data: CATFileStorage | undefined) {
|
||||
this.set("cat_file_storage", data);
|
||||
}
|
||||
|
||||
getEnableEslint() {
|
||||
return this.get("enable_eslint", true);
|
||||
}
|
||||
|
||||
setEnableEslint(val: boolean) {
|
||||
this.set("enable_eslint", val);
|
||||
}
|
||||
|
||||
getEslintConfig() {
|
||||
return this.get("eslint_config", defaultConfig);
|
||||
}
|
||||
|
||||
setEslintConfig(v: string) {
|
||||
if (v === "") {
|
||||
this.set("eslint_config", v);
|
||||
Message.success("ESLint规则已重置");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
JSON.parse(v);
|
||||
this.set("eslint_config", v);
|
||||
Message.success("ESLint规则已保存");
|
||||
} catch (err: any) {
|
||||
Message.error(err.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// 日志清理周期
|
||||
getLogCleanCycle() {
|
||||
return this.get("log_clean_cycle", 7);
|
||||
}
|
||||
|
||||
setLogCleanCycle(val: number) {
|
||||
this.set("log_clean_cycle", val);
|
||||
}
|
||||
|
||||
// 设置脚本列表列宽度
|
||||
getScriptListColumnWidth() {
|
||||
return this.get<{ [key: string]: number }>("script_list_column_width", {});
|
||||
}
|
||||
|
||||
setScriptListColumnWidth(val: { [key: string]: number }) {
|
||||
this.set("script_list_column_width", val);
|
||||
}
|
||||
|
||||
// 展开菜单数
|
||||
getMenuExpandNum() {
|
||||
return this.get("menu_expand_num", 5);
|
||||
}
|
||||
|
||||
setMenuExpandNum(val: number) {
|
||||
this.set("menu_expand_num", val);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user