Skip to content

Commit c884abf

Browse files
authored
🐛 ScriptEditor 列表动态更新 及 显示修正 (#1414)
* fix(UI): ScriptEditor 列表动态更新 及 显示修正 * fix delete * fix
1 parent aca786b commit c884abf

1 file changed

Lines changed: 133 additions & 7 deletions

File tree

src/pages/options/routes/script/ScriptEditor.tsx

Lines changed: 133 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Script } from "@App/app/repo/scripts";
2+
import { SCRIPT_STATUS_DISABLE, SCRIPT_STATUS_ENABLE } from "@App/app/repo/scripts";
23
import { SCRIPT_TYPE_NORMAL, ScriptCodeDAO, ScriptDAO } from "@App/app/repo/scripts";
34
import CodeEditor from "@App/pages/components/CodeEditor";
45
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
@@ -25,6 +26,9 @@ import { IconDelete, IconSearch } from "@arco-design/web-react/icon";
2526
import { lazyScriptName } from "@App/pkg/config/config";
2627
import { makeBlobURL } from "@App/pkg/utils/utils";
2728
import { VscLayoutSidebarLeft, VscLayoutSidebarLeftOff } from "react-icons/vsc";
29+
import type { TInstallScript, TDeleteScript, TEnableScript, TSortedScript } from "@App/app/service/queue";
30+
import { subscribeMessage } from "@App/pages/store/global";
31+
import { HookManager } from "@App/pkg/utils/hookManager";
2832

2933
const { Row, Col } = Grid;
3034

@@ -220,12 +224,131 @@ type EditorState = {
220224
const scriptDAO = new ScriptDAO();
221225
const scriptCodeDAO = new ScriptCodeDAO();
222226

227+
function useScriptList() {
228+
const [selectedScript, setSelectSciptButtonAndTab] = useState<string>("");
229+
const [editors, setEditors] = useState<EditorState[]>([]);
230+
const [canLoadScript, setCanLoadScript] = useState<boolean>(false);
231+
const [scriptList, setScriptList] = useState<Script[]>([]);
232+
// 监听后台消息更新状态
233+
useEffect(() => {
234+
const pageApi = {
235+
async installScript(data: TInstallScript) {
236+
const latest = await scriptDAO.all();
237+
const latestMap = new Map(latest.map((script) => [script.uuid, script]));
238+
setScriptList((list) => {
239+
const newList: Script[] = [];
240+
for (const entry of list) {
241+
if (entry.uuid !== data.script.uuid) {
242+
const latestScript = latestMap.get(entry.uuid);
243+
if (latestScript) {
244+
newList.push({
245+
...entry,
246+
sort: latestScript.sort,
247+
name: latestScript.name,
248+
updatetime: latestScript.updatetime,
249+
status: latestScript.status,
250+
});
251+
}
252+
}
253+
}
254+
const installedScript = latestMap.get(data.script.uuid);
255+
if (installedScript) {
256+
newList.push(installedScript);
257+
}
258+
newList.sort((a, b) => a.sort - b.sort);
259+
return newList;
260+
});
261+
},
262+
deleteScripts(data: TDeleteScript[]) {
263+
const dels = new Set(data.map((script) => script.uuid));
264+
setEditors((prev) => {
265+
const newList: EditorState[] = [];
266+
for (const editor of prev) {
267+
if (!dels.has(editor.script.uuid)) {
268+
newList.push(editor);
269+
}
270+
}
271+
// 关键修复:确保关闭后仍有一个 Tab 是激活的
272+
if (newList.length > 0 && !newList.some((e) => e.active)) {
273+
newList[0] = { ...newList[0], active: true };
274+
setSelectSciptButtonAndTab(newList[0].script.uuid);
275+
}
276+
return newList;
277+
});
278+
setScriptList((list) => {
279+
return list.filter((script) => !dels.has(script.uuid));
280+
});
281+
},
282+
enableScripts(data: TEnableScript[]) {
283+
const enableMap = new Map(data.map((e) => [e.uuid, e.enable]));
284+
setScriptList((list) => {
285+
const newList: Script[] = [];
286+
for (const script of list) {
287+
const oldEnable = script.status !== SCRIPT_STATUS_DISABLE;
288+
const newEnable = enableMap.get(script.uuid);
289+
if (typeof newEnable === "boolean" && oldEnable !== newEnable) {
290+
newList.push({ ...script, status: newEnable ? SCRIPT_STATUS_ENABLE : SCRIPT_STATUS_DISABLE });
291+
} else {
292+
newList.push(script);
293+
}
294+
}
295+
return newList;
296+
});
297+
},
298+
sortedScripts(sorting: TSortedScript[]) {
299+
const sortMap = new Map(sorting.map((s) => [s.uuid, s.sort]));
300+
setScriptList((list) => {
301+
const newList: Script[] = [];
302+
for (const entry of list) {
303+
const sort = sortMap.get(entry.uuid);
304+
if (sort! >= 0) {
305+
newList.push({ ...entry, sort: sort! });
306+
} else {
307+
newList.push(entry);
308+
}
309+
}
310+
newList.sort((a, b) => a.sort - b.sort);
311+
return newList;
312+
});
313+
},
314+
} as const;
315+
316+
const hookMgr = new HookManager();
317+
hookMgr.append(
318+
subscribeMessage<TInstallScript>("installScript", pageApi.installScript),
319+
subscribeMessage<TDeleteScript[]>("deleteScripts", pageApi.deleteScripts),
320+
subscribeMessage<TEnableScript[]>("enableScripts", pageApi.enableScripts),
321+
subscribeMessage<TSortedScript[]>("sortedScripts", pageApi.sortedScripts)
322+
);
323+
return hookMgr.unhook;
324+
}, []);
325+
return {
326+
scriptList,
327+
setScriptList,
328+
canLoadScript,
329+
setCanLoadScript,
330+
editors,
331+
setEditors,
332+
selectedScript,
333+
setSelectSciptButtonAndTab,
334+
};
335+
}
336+
223337
function ScriptEditor() {
224338
const [visible, setVisible] = useState<{ [key: string]: boolean }>({});
225339
const [searchKeyword, setSearchKeyword] = useState<string>("");
226340
const [showSearchInput, setShowSearchInput] = useState<boolean>(false);
227341
const [modal, contextHolder] = Modal.useModal();
228-
const [editors, setEditors] = useState<EditorState[]>([]);
342+
const {
343+
scriptList,
344+
setScriptList,
345+
canLoadScript,
346+
setCanLoadScript,
347+
editors,
348+
setEditors,
349+
selectedScript,
350+
setSelectSciptButtonAndTab,
351+
} = useScriptList();
229352
const editorsRef = useRef<EditorState[]>(editors); // 取出资料用
230353
// Sync during render (no useEffect needed)
231354
editorsRef.current = editors;
@@ -245,16 +368,13 @@ function ScriptEditor() {
245368
setTimeout(editor.focus.bind(editor), delayMs);
246369
}
247370
};
248-
const [scriptList, setScriptList] = useState<Script[]>([]);
249371
const [currentScript, setCurrentScript] = useState<Script>();
250-
const [selectedScript, setSelectSciptButtonAndTab] = useState<string>("");
251372
const [rightOperationTab, setRightOperationTab] = useState<{
252373
key: string;
253374
uuid: string;
254375
selectSciptButtonAndTab: string;
255376
}>();
256377
const cidRef = useRef<ReturnType<typeof setTimeout>>();
257-
const [canLoadScript, setCanLoadScript] = useState<boolean>(false);
258378
const [hiddenScriptList, setHiddenScriptList] = useState<boolean>(() => {
259379
return localStorage.getItem("hiddenEditorScriptList") === "true";
260380
});
@@ -1087,7 +1207,7 @@ function ScriptEditor() {
10871207
)}
10881208
{filteredScriptList.map((script) => {
10891209
const editor = editorFindItem(script.uuid);
1090-
const alpha = script.status === 2 ? 0.8 : 1.0;
1210+
const alpha = script.status === SCRIPT_STATUS_DISABLE ? 0.66 : 1.0;
10911211
return (
10921212
<div key={`s_${script.uuid}`} className="tw-relative tw-group">
10931213
<Button
@@ -1097,7 +1217,6 @@ function ScriptEditor() {
10971217
overflow: "hidden",
10981218
textOverflow: "ellipsis",
10991219
whiteSpace: "nowrap",
1100-
opacity: alpha,
11011220
color: !editor
11021221
? "var(--color-text-3)"
11031222
: editor.isChanged
@@ -1115,7 +1234,14 @@ function ScriptEditor() {
11151234
openScript(script.uuid);
11161235
}}
11171236
>
1118-
<span className="tw-overflow-hidden tw-text-ellipsis">{i18nName(script)}</span>
1237+
<span
1238+
className="tw-overflow-hidden tw-text-ellipsis"
1239+
style={{
1240+
opacity: alpha,
1241+
}}
1242+
>
1243+
{i18nName(script)}
1244+
</span>
11191245
</Button>
11201246
{/* 删除按钮,只在鼠标悬停时显示 */}
11211247
<Button

0 commit comments

Comments
 (0)