Skip to content

Commit 7a0f77e

Browse files
authored
✨ 批量脚本链接导入 (#395)
修复 链接导入失败时模态框卡死的问题 实现 批量脚本链接导入
1 parent a34b0ae commit 7a0f77e

6 files changed

Lines changed: 132 additions & 19 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "scriptcat",
3-
"version": "0.17.0-beta",
3+
"version": "0.17.0-beta.1",
44
"description": "脚本猫,一个可以执行用户脚本的浏览器扩展,万物皆可脚本化,让你的浏览器可以做更多的事情!",
55
"author": "CodFrm",
66
"license": "GPLv3",

packages/message/server.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,13 @@ export class Server {
9494
try {
9595
const ret = func(params, new GetSender(sender!));
9696
if (ret instanceof Promise) {
97-
ret.then((data) => {
98-
sendResponse({ code: 0, data });
99-
});
97+
ret
98+
.then((data) => {
99+
sendResponse({ code: 0, data });
100+
})
101+
.catch((err) => {
102+
sendResponse({ code: -1, message: err });
103+
});
100104
return true;
101105
} else {
102106
sendResponse({ code: 0, data: ret });

src/app/service/service_worker/client.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,72 @@ export class ScriptClient extends Client {
8787
importByUrl(url: string) {
8888
return this.do("importByUrl", url);
8989
}
90+
91+
async formatUrl(url: string) {
92+
try {
93+
const newUrl = new URL(url.replace(/\/$/, ""));
94+
const { hostname, pathname } = newUrl;
95+
// 判断是否为脚本猫脚本页
96+
if (hostname === "scriptcat.org" && /script-show-page\/\d+$/.test(pathname)) {
97+
const scriptId = pathname.match(/\d+$/)![0];
98+
// 请求脚本信息
99+
const scriptInfo = await fetch(`https://scriptcat.org/api/v2/scripts/${scriptId}`)
100+
.then((res) => {
101+
return res.json();
102+
})
103+
.then((json) => {
104+
return json;
105+
});
106+
const { code, data, msg } = scriptInfo;
107+
if (code != 0) {
108+
// 无脚本访问权限
109+
return { success: false, msg };
110+
} else {
111+
// 返回脚本实际安装地址
112+
const scriptName = data.name;
113+
return `https://scriptcat.org/scripts/code/${scriptId}/${scriptName}.user.js`;
114+
}
115+
} else {
116+
return url;
117+
}
118+
} catch {
119+
return url;
120+
}
121+
}
122+
123+
importByUrls(urls: string[]) {
124+
if (urls.length == 0) {
125+
return;
126+
}
127+
return (
128+
Promise.allSettled(
129+
urls.map(async (url) => {
130+
const formattedResult = await this.formatUrl(url);
131+
if (formattedResult instanceof Object) {
132+
return Promise.resolve(formattedResult);
133+
} else {
134+
return this.do("importByUrl", formattedResult);
135+
}
136+
})
137+
// this.do 只会resolve 不会reject
138+
) as Promise<PromiseFulfilledResult<{ success: boolean; msg: string }>[]>
139+
).then((results) => {
140+
console.log(results);
141+
const stat = results.reduce(
142+
(obj, result, index) => {
143+
if (result.value.success) {
144+
obj.success++;
145+
} else {
146+
obj.fail++;
147+
obj.msg.push(`#${index + 1}: ${result.value.msg}`);
148+
}
149+
return obj;
150+
},
151+
{ success: 0, fail: 0, msg: [] as string[] }
152+
);
153+
return stat;
154+
});
155+
}
90156
}
91157

92158
export class ResourceClient extends Client {

src/app/service/service_worker/script.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -143,16 +143,22 @@ export class ScriptService {
143143
);
144144
}
145145

146-
public openInstallPageByUrl(url: string, source: InstallSource) {
146+
public openInstallPageByUrl(url: string, source: InstallSource): Promise<{ success: boolean; msg: string }> {
147147
const uuid = uuidv4();
148-
return fetchScriptInfo(url, source, false, uuidv4()).then((info) => {
149-
Cache.getInstance().set(CacheKey.scriptInstallInfo(uuid), info);
150-
setTimeout(() => {
151-
// 清理缓存
152-
Cache.getInstance().del(CacheKey.scriptInstallInfo(uuid));
153-
}, 30 * 1000);
154-
openInCurrentTab(`/src/install.html?uuid=${uuid}`);
155-
});
148+
return fetchScriptInfo(url, source, false, uuidv4())
149+
.then((info) => {
150+
Cache.getInstance().set(CacheKey.scriptInstallInfo(uuid), info);
151+
setTimeout(() => {
152+
// 清理缓存
153+
Cache.getInstance().del(CacheKey.scriptInstallInfo(uuid));
154+
}, 30 * 1000);
155+
openInCurrentTab(`/src/install.html?uuid=${uuid}`);
156+
return { success: true, msg: "" };
157+
})
158+
.catch((err) => {
159+
console.error(err);
160+
return { success: false, msg: err.message };
161+
});
156162
}
157163

158164
// 直接通过url静默安装脚本

src/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": 3,
33
"name": "__MSG_scriptcat__",
4-
"version": "0.17.0.1100",
4+
"version": "0.17.0.1200",
55
"author": "CodFrm",
66
"description": "__MSG_scriptcat_description__",
77
"options_ui": {

src/pages/components/layout/MainLayout.tsx

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,16 @@ import {
1111
Space,
1212
Typography,
1313
} from "@arco-design/web-react";
14-
import { RefInputType } from "@arco-design/web-react/es/Input/interface";
15-
import { IconDesktop, IconDown, IconLink, IconMoonFill, IconSunFill } from "@arco-design/web-react/icon";
14+
import { RefTextAreaType } from "@arco-design/web-react/es/Input";
15+
import {
16+
IconCheckCircle,
17+
IconCloseCircle,
18+
IconDesktop,
19+
IconDown,
20+
IconLink,
21+
IconMoonFill,
22+
IconSunFill,
23+
} from "@arco-design/web-react/icon";
1624
import React, { ReactNode, useRef, useState } from "react";
1725
import { useTranslation } from "react-i18next";
1826
import "./index.css";
@@ -28,7 +36,7 @@ const MainLayout: React.FC<{
2836
}> = ({ children, className, pageName }) => {
2937
const lightMode = useAppSelector(selectThemeMode);
3038
const dispatch = useAppDispatch();
31-
const importRef = useRef<RefInputType>(null);
39+
const importRef = useRef<RefTextAreaType>(null);
3240
const [importVisible, setImportVisible] = useState(false);
3341
const { t } = useTranslation();
3442

@@ -50,8 +58,32 @@ const MainLayout: React.FC<{
5058
title={t("import_link")}
5159
visible={importVisible}
5260
onOk={async () => {
61+
const urls = importRef.current!.dom.value.split("\n").filter((v) => v);
5362
try {
54-
await scriptClient.importByUrl(importRef.current!.dom.value);
63+
const stat = await scriptClient.importByUrls(urls);
64+
stat &&
65+
Modal.info({
66+
title: "链接导入结果",
67+
content: (
68+
<Space direction="vertical" style={{ width: "100%" }}>
69+
<div style={{ textAlign: "center" }}>
70+
<Space size="small" style={{ fontSize: 18 }}>
71+
<IconCheckCircle style={{ color: "green" }} />
72+
{stat.success}
73+
{""}
74+
<IconCloseCircle style={{ color: "red" }} />
75+
{stat.fail}
76+
</Space>
77+
</div>
78+
{stat.msg.length > 0 && (
79+
<>
80+
<b>失败信息:</b>
81+
{stat.msg}
82+
</>
83+
)}
84+
</Space>
85+
),
86+
});
5587
setImportVisible(false);
5688
} catch (e) {
5789
Message.error(`${t("import_link_failure")}: ${e}`);
@@ -61,7 +93,12 @@ const MainLayout: React.FC<{
6193
setImportVisible(false);
6294
}}
6395
>
64-
<Input ref={importRef} defaultValue="" />
96+
<Input.TextArea
97+
ref={importRef}
98+
rows={8}
99+
placeholder={`支持输入.user.js结尾的脚本绝对链接 或 脚本猫安装页链接\n可多行填写,每行一条\n示例:\nhttps://example.com/test.user.js \nhttps://scriptcat.org/zh-CN/script-show-page/1234`}
100+
defaultValue=""
101+
/>
65102
</Modal>
66103
<div className="flex row items-center">
67104
<img style={{ height: "40px" }} src="/assets/logo.png" alt="ScriptCat" />

0 commit comments

Comments
 (0)