Skip to content

Commit 661b7b0

Browse files
committed
✨ 站点匹配在列表里可以展示并支持点击 #419
1 parent b8941af commit 661b7b0

8 files changed

Lines changed: 318 additions & 27 deletions

File tree

src/app/service/offscreen/client.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ export function stopScript(msg: MessageSend, uuid: string) {
3333
return sendMessage(msg, "offscreen/script/stopScript", uuid);
3434
}
3535

36-
export function createObjectURL(msg: MessageSend, data: Blob) {
37-
return sendMessage(msg, "offscreen/createObjectURL", data);
36+
export function createObjectURL(msg: MessageSend, data: Blob, persistence: boolean = false) {
37+
return sendMessage(msg, "offscreen/createObjectURL", { data, persistence });
3838
}
3939

4040
export class VscodeConnectClient extends Client {

src/app/service/offscreen/index.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,14 @@ export class OffscreenManager {
5858
const vscodeConnect = new VSCodeConnect(this.windowServer.group("vscodeConnect"), this.extensionMessage);
5959
vscodeConnect.init();
6060

61-
this.windowServer.on("createObjectURL", (data: Blob) => {
62-
const url = URL.createObjectURL(data);
63-
setTimeout(() => {
64-
URL.revokeObjectURL(url);
65-
}, 1000 * 60);
61+
this.windowServer.on("createObjectURL", (params: { data: Blob; persistence: boolean }) => {
62+
const url = URL.createObjectURL(params.data);
63+
if (!params.persistence) {
64+
// 如果不是持久化的,则在1分钟后释放
65+
setTimeout(() => {
66+
URL.revokeObjectURL(url);
67+
}, 1000 * 60);
68+
}
6669
return Promise.resolve(url);
6770
});
6871
}

src/app/service/service_worker/client.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,4 +349,8 @@ export class SystemClient extends Client {
349349
connectVSCode(params: Parameters<VSCodeConnect["connect"]>[0]): ReturnType<VSCodeConnect["connect"]> {
350350
return this.do("connectVSCode", params);
351351
}
352+
353+
loadFavicon(icon: string): Promise<string> {
354+
return this.do("loadFavicon", icon);
355+
}
352356
}

src/app/service/service_worker/system.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { SystemConfig } from "@App/pkg/config/config";
22
import { Group, MessageSend } from "@Packages/message/server";
3-
import { VscodeConnectClient } from "../offscreen/client";
3+
import { createObjectURL, VscodeConnectClient } from "../offscreen/client";
44
import Cache from "@App/app/cache";
55

66
// 一些系统服务
@@ -30,5 +30,14 @@ export class SystemService {
3030
this.group.on("connectVSCode", (params) => {
3131
return vscodeConnect.connect(params);
3232
});
33+
this.group.on("loadFavicon", async (url) => {
34+
// 加载favicon图标
35+
return fetch(url)
36+
.then((response) => response.blob())
37+
.then((blob) => createObjectURL(this.sender, blob, true))
38+
.catch(() => {
39+
return "";
40+
});
41+
});
3342
}
3443
}

src/pages/options/routes/ScriptList.tsx

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { useEffect, useRef, useState } from "react";
22
import {
3+
Avatar,
34
Button,
45
Card,
56
Divider,
@@ -16,6 +17,7 @@ import {
1617
Tooltip,
1718
Typography,
1819
} from "@arco-design/web-react";
20+
import { TbWorldWww } from "react-icons/tb";
1921
import { ColumnProps } from "@arco-design/web-react/es/Table";
2022
import { ComponentsProps } from "@arco-design/web-react/es/Table/interface";
2123
import {
@@ -30,7 +32,7 @@ import {
3032
} from "@App/app/repo/scripts";
3133
import {
3234
IconClockCircle,
33-
IconCommon,
35+
IconDesktop,
3436
IconEdit,
3537
IconLink,
3638
IconMenu,
@@ -88,9 +90,9 @@ import {
8890
} from "@App/pages/store/features/script";
8991
import { message, systemConfig } from "@App/pages/store/global";
9092
import { ValueClient } from "@App/app/service/service_worker/client";
91-
import { JSX } from "react/jsx-runtime";
93+
import { loadScriptFavicons } from "@App/pages/store/utils";
9294

93-
type ListType = Script & { loading?: boolean };
95+
type ListType = ScriptLoading;
9496

9597
function ScriptList() {
9698
const [userConfig, setUserConfig] = useState<{
@@ -113,7 +115,12 @@ function ScriptList() {
113115
const [dealColumns, setDealColumns] = useState<ColumnProps[]>([]);
114116

115117
useEffect(() => {
116-
dispatch(fetchScriptList());
118+
dispatch(fetchScriptList()).then((action) => {
119+
if (fetchScriptList.fulfilled.match(action)) {
120+
// 在脚本列表加载完成后,加载favicon
121+
loadScriptFavicons(action.payload);
122+
}
123+
});
117124
}, [dispatch]);
118125

119126
const columns: ColumnProps[] = [
@@ -250,7 +257,7 @@ function ScriptList() {
250257
title: t("apply_to_run_status"),
251258
width: t("script_list_apply_to_run_status_width"),
252259
className: "apply_to_run_status",
253-
render(col, item: Script) {
260+
render(col, item: ListType) {
254261
const toLogger = () => {
255262
navigate({
256263
pathname: "logger",
@@ -266,20 +273,44 @@ function ScriptList() {
266273
});
267274
};
268275
if (item.type === SCRIPT_TYPE_NORMAL) {
276+
// 处理站点icon
269277
return (
270-
<Tooltip content={t("foreground_page_script_tooltip")}>
271-
<Tag
272-
style={{
273-
cursor: "pointer",
274-
}}
275-
icon={<IconCommon color="" />}
276-
color="cyan"
277-
bordered
278-
onClick={toLogger}
279-
>
280-
{t("page_script")}
281-
</Tag>
282-
</Tooltip>
278+
<>
279+
<Avatar.Group size={20} style={{ margin: 10 }}>
280+
{item.favorite &&
281+
// 排序并且只显示前5个
282+
// 排序将有icon的放在前面
283+
[...item.favorite]
284+
.sort((a, b) => {
285+
if (a.icon && !b.icon) return -1;
286+
if (!a.icon && b.icon) return 1;
287+
return a.match.localeCompare(b.match);
288+
})
289+
.slice(0, 5)
290+
.map((fav) => (
291+
<Avatar
292+
key={fav.match}
293+
shape="square"
294+
style={{
295+
backgroundColor: "unset",
296+
borderWidth: 1,
297+
}}
298+
className={fav.website ? "cursor-pointer" : "cursor-default"}
299+
onClick={() => {
300+
if (fav.website) {
301+
window.open(fav.website, "_blank");
302+
}
303+
}}
304+
>
305+
{fav.icon ? (
306+
<img title={fav.match} src={fav.icon} />
307+
) : (
308+
<TbWorldWww title={fav.match} color="#aaa" size={24} />
309+
)}
310+
</Avatar>
311+
))}
312+
</Avatar.Group>
313+
</>
283314
);
284315
}
285316
let tooltip = "";

src/pages/store/features/script.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import {
1919
ValueClient,
2020
} from "@App/app/service/service_worker/client";
2121
import { message } from "../global";
22+
import { extractFavicons } from "@App/pkg/utils/favicon";
23+
import { store } from "../store";
2224

2325
export const scriptClient = new ScriptClient(message);
2426
export const subscribeClient = new SubscribeClient(message);
@@ -52,7 +54,15 @@ export const requestDeleteScript = createAsyncThunk("script/deleteScript", async
5254
return scriptClient.delete(uuid);
5355
});
5456

55-
export type ScriptLoading = Script & { enableLoading?: boolean; actionLoading?: boolean };
57+
export type ScriptLoading = Script & {
58+
enableLoading?: boolean;
59+
actionLoading?: boolean;
60+
favorite?: {
61+
match: string;
62+
website?: string;
63+
icon?: string;
64+
}[];
65+
};
5666

5767
const updateScript = (scripts: ScriptLoading[], uuid: string, update: (s: ScriptLoading) => void) => {
5868
const script = scripts.find((s) => s.uuid === uuid);
@@ -120,6 +130,12 @@ export const scriptSlice = createAppSlice({
120130
return s;
121131
});
122132
},
133+
setScriptFavicon: (state, action: PayloadAction<{ uuid: string; fav: { match: string; icon?: string }[] }>) => {
134+
const script = state.scripts.find((s) => s.uuid === action.payload.uuid);
135+
if (script) {
136+
script.favorite = action.payload.fav;
137+
}
138+
},
123139
},
124140
extraReducers: (builder) => {
125141
builder

src/pages/store/utils.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { Script } from "@App/app/repo/scripts";
2+
import { extractFavicons } from "@App/pkg/utils/favicon";
3+
import { store } from "./store";
4+
import { scriptSlice } from "./features/script";
5+
import Cache from "@App/app/cache";
6+
import { SystemClient } from "@App/app/service/service_worker/client";
7+
import { message } from "./global";
8+
9+
// 在scriptSlice创建后处理favicon加载
10+
export const loadScriptFavicons = (scripts: Script[]) => {
11+
scripts.forEach(async (item) => {
12+
const icons = await Cache.getInstance().getOrSet(`favicon:${item.uuid}`, () => {
13+
return extractFavicons(item.metadata!.match || [], item.metadata!.include || []).then(async (icons) => {
14+
if (icons.length > 0) {
15+
// 从缓存中获取favicon图标
16+
const systemClient = new SystemClient(message);
17+
const newIcons = await Promise.all(
18+
icons.map((icon) => {
19+
// 没有的话缓存到本地使用URL.createObjectURL
20+
if (!icon.icon) {
21+
return Promise.resolve({
22+
match: icon.match,
23+
website: icon.website,
24+
icon: "",
25+
});
26+
}
27+
// 因为需要持久化URL.createObjectURL,所以需要通过调用到offscreen来创建
28+
return systemClient
29+
.loadFavicon(icon.icon)
30+
.then((url) => {
31+
return {
32+
match: icon.match,
33+
website: icon.website,
34+
icon: url,
35+
};
36+
})
37+
.catch(() => {
38+
return {
39+
match: icon.match,
40+
website: icon.website,
41+
icon: "",
42+
};
43+
});
44+
})
45+
);
46+
return newIcons;
47+
}
48+
return [];
49+
});
50+
});
51+
store.dispatch(scriptSlice.actions.setScriptFavicon({ uuid: item.uuid, fav: icons }));
52+
});
53+
};

0 commit comments

Comments
 (0)