-
Notifications
You must be signed in to change notification settings - Fork 299
[v1.3] 把 metadata 从 chrome.storage.session 抽走 #1027
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: release/v1.3
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -21,7 +21,8 @@ import { | |||||||||||||
| IconPlus, | ||||||||||||||
| IconSettings, | ||||||||||||||
| } from "@arco-design/web-react/icon"; | ||||||||||||||
| import { SCRIPT_RUN_STATUS_RUNNING } from "@App/app/repo/scripts"; | ||||||||||||||
| import type { SCMetadata } from "@App/app/repo/scripts"; | ||||||||||||||
| import { SCRIPT_RUN_STATUS_RUNNING, ScriptDAO } from "@App/app/repo/scripts"; | ||||||||||||||
| import { RiPlayFill, RiStopFill } from "react-icons/ri"; | ||||||||||||||
| import { useTranslation } from "react-i18next"; | ||||||||||||||
| import { ScriptIcons } from "@App/pages/options/routes/utils"; | ||||||||||||||
|
|
@@ -32,7 +33,10 @@ import type { | |||||||||||||
| ScriptMenuItemOption, | ||||||||||||||
| } from "@App/app/service/service_worker/types"; | ||||||||||||||
| import { popupClient, runtimeClient, scriptClient } from "@App/pages/store/features/script"; | ||||||||||||||
| import { i18nName } from "@App/locales/locales"; | ||||||||||||||
| import { i18nLang, i18nName } from "@App/locales/locales"; | ||||||||||||||
|
|
||||||||||||||
| // 用于读取 metadata | ||||||||||||||
| const scriptDAO = new ScriptDAO(); | ||||||||||||||
|
|
||||||||||||||
| const CollapseItem = Collapse.Item; | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -114,8 +118,8 @@ const MenuItem = React.memo(({ menuItems, uuid }: MenuItemProps) => { | |||||||||||||
| MenuItem.displayName = "MenuItem"; | ||||||||||||||
|
|
||||||||||||||
| interface CollapseHeaderProps { | ||||||||||||||
| item: ScriptMenu; | ||||||||||||||
| onEnableChange: (item: ScriptMenu, checked: boolean) => void; | ||||||||||||||
| item: ScriptMenuEntry; | ||||||||||||||
| onEnableChange: (item: ScriptMenuEntry, checked: boolean) => void; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| const CollapseHeader = React.memo( | ||||||||||||||
|
|
@@ -164,12 +168,12 @@ const CollapseHeader = React.memo( | |||||||||||||
| CollapseHeader.displayName = "CollapseHeader"; | ||||||||||||||
|
|
||||||||||||||
| interface ListMenuItemProps { | ||||||||||||||
| item: ScriptMenu; | ||||||||||||||
| item: ScriptMenuEntry; | ||||||||||||||
| scriptMenus: GroupScriptMenuItemsProp; | ||||||||||||||
| menuExpandNum: number; | ||||||||||||||
| isBackscript: boolean; | ||||||||||||||
| url: URL | null; | ||||||||||||||
| onEnableChange: (item: ScriptMenu, checked: boolean) => void; | ||||||||||||||
| onEnableChange: (item: ScriptMenuEntry, checked: boolean) => void; | ||||||||||||||
| handleDeleteScript: (uuid: string) => void; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -320,6 +324,13 @@ ListMenuItem.displayName = "ListMenuItem"; | |||||||||||||
|
|
||||||||||||||
| type TGrouppedMenus = Record<string, GroupScriptMenuItemsProp> & { __length__?: number }; | ||||||||||||||
|
|
||||||||||||||
| type ScriptMenuEntry = ScriptMenu & { | ||||||||||||||
| menuUpdated?: number; | ||||||||||||||
| metadata: SCMetadata; | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| let scriptDataAsyncCounter = 0; | ||||||||||||||
|
|
||||||||||||||
| // Popup 页面使用的脚本/选单清单元件:只负责渲染与互动,状态与持久化交由外部 client 处理。 | ||||||||||||||
| const ScriptMenuList = React.memo( | ||||||||||||||
| ({ | ||||||||||||||
|
|
@@ -335,23 +346,30 @@ const ScriptMenuList = React.memo( | |||||||||||||
| currentUrl: string; | ||||||||||||||
| menuExpandNum: number; | ||||||||||||||
| }) => { | ||||||||||||||
| const [list, setList] = useState( | ||||||||||||||
| [] as (ScriptMenu & { | ||||||||||||||
| menuUpdated?: number; | ||||||||||||||
| })[] | ||||||||||||||
| ); | ||||||||||||||
| // extraData 为 undefined 时先等待异步加载完成,避免重复渲染 | ||||||||||||||
| const [extraData, setExtraData] = useState< | ||||||||||||||
| | { | ||||||||||||||
| uuids: string; | ||||||||||||||
| lang: string; | ||||||||||||||
| metadata: Record<string, SCMetadata>; | ||||||||||||||
| } | ||||||||||||||
| | undefined | ||||||||||||||
| >(undefined); | ||||||||||||||
| const [scriptMenuList, setScriptMenuList] = useState<ScriptMenuEntry[]>([]); | ||||||||||||||
|
||||||||||||||
| const { t } = useTranslation(); | ||||||||||||||
|
|
||||||||||||||
| const [grouppedMenus, setGrouppedMenus] = useState<TGrouppedMenus>({}); | ||||||||||||||
|
|
||||||||||||||
| // 依 groupKey 进行聚合:将同语义(mainframe/subframe)命令合并为单一分组以供 UI 呈现。 | ||||||||||||||
| useEffect(() => { | ||||||||||||||
| const list_ = list; | ||||||||||||||
| const updateScriptMenuList = (scriptMenuList: ScriptMenuEntry[]) => { | ||||||||||||||
| setScriptMenuList(scriptMenuList); | ||||||||||||||
| // 因为 scriptMenuList 的修改只在这处。 | ||||||||||||||
| // 直接在这里呼叫 setGrouppedMenus, 不需要 useEffect | ||||||||||||||
| setGrouppedMenus((prev) => { | ||||||||||||||
| // 依 groupKey 进行聚合:将同语义(mainframe/subframe)命令合并为单一分组以供 UI 呈现。 | ||||||||||||||
| const ret = {} as TGrouppedMenus; | ||||||||||||||
| let changed = false; | ||||||||||||||
| let retLen = 0; | ||||||||||||||
| for (const { uuid, menus, menuUpdated: m } of list_) { | ||||||||||||||
| for (const { uuid, menus, menuUpdated: m } of scriptMenuList) { | ||||||||||||||
| retLen++; | ||||||||||||||
| const menuUpdated = m || 0; | ||||||||||||||
| if (prev[uuid]?.menuUpdated === menuUpdated) { | ||||||||||||||
|
|
@@ -383,7 +401,7 @@ const ScriptMenuList = React.memo( | |||||||||||||
| // 若无引用变更则维持原物件以降低重渲染 | ||||||||||||||
| return changed ? ret : prev; | ||||||||||||||
| }); | ||||||||||||||
| }, [list]); | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| const url = useMemo(() => { | ||||||||||||||
| let url: URL; | ||||||||||||||
|
|
@@ -399,8 +417,50 @@ const ScriptMenuList = React.memo( | |||||||||||||
| return url; | ||||||||||||||
| }, [currentUrl]); | ||||||||||||||
|
|
||||||||||||||
| // string memo 避免 uuids 以外的改变影响 | ||||||||||||||
| const uuids = useMemo(() => script.map((item) => item.uuid).join("\n"), [script]); | ||||||||||||||
| // eslint-disable-next-line react-hooks/exhaustive-deps | ||||||||||||||
| const lang = useMemo(() => i18nLang(), [t]); // 当 t 改变时,重新检查当前页面语言 | ||||||||||||||
|
|
||||||||||||||
| // 以 异步方式 取得 metadata 放入 extraData | ||||||||||||||
| // script 或 extraData 的更新时都会再次执行 | ||||||||||||||
| useEffect(() => { | ||||||||||||||
| if (extraData && extraData.uuids === uuids && extraData.lang === lang) { | ||||||||||||||
| // extraData 已取得 | ||||||||||||||
| // 把 getPopupData() 的 scriptMenuList 和 异步结果 的 metadata 合并至 scriptMenuList | ||||||||||||||
| const metadata = extraData.metadata; | ||||||||||||||
| const newScriptMenuList = script.map((item) => ({ ...item, metadata: metadata[item.uuid] || {} })); | ||||||||||||||
| updateScriptMenuList(newScriptMenuList); | ||||||||||||||
| } else { | ||||||||||||||
| // 取得 extraData | ||||||||||||||
| scriptDataAsyncCounter = (scriptDataAsyncCounter % 255) + 1; // 轮出 1 ~ 255 | ||||||||||||||
| const lastCounter = scriptDataAsyncCounter; | ||||||||||||||
| scriptDAO.gets(uuids.split("\n")).then((res) => { | ||||||||||||||
| if (lastCounter !== scriptDataAsyncCounter) { | ||||||||||||||
| // 由于 state 改变,在结果取得前 useEffect 再次执行,因此需要忽略上次结果 | ||||||||||||||
| return; | ||||||||||||||
| } | ||||||||||||||
| const metadataRecord = {} as Record<string, SCMetadata>; | ||||||||||||||
| const nameKey = `name:${lang}`; | ||||||||||||||
| for (const entry of res) { | ||||||||||||||
| if (entry) { | ||||||||||||||
| const m = entry.metadata; | ||||||||||||||
| const [icon] = m.icon || m.iconurl || m.icon64 || m.icon64url || []; | ||||||||||||||
| // metadataRecord 的储存量不影响 storage.session 但影响页面的记忆体 | ||||||||||||||
| // 按需要可以增加其他 metadata, 例如 @match @include @exclude | ||||||||||||||
| metadataRecord[entry.uuid] = { | ||||||||||||||
| icon: [icon], // 只储存单个 icon | ||||||||||||||
| [nameKey]: [i18nName(entry)], // 只储存 i18n 的 name | ||||||||||||||
| } satisfies SCMetadata; | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| setExtraData({ uuids, lang, metadata: metadataRecord }); | ||||||||||||||
| // 再次触发 useEffect | ||||||||||||||
|
||||||||||||||
| // 再次触发 useEffect | |
| // 再次触发 useEffect | |
| }).catch((error) => { | |
| console.error("Failed to load script metadata:", error); | |
| // 降级处理:使用空的 metadata | |
| setExtraData({ uuids, lang, metadata: {} }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
雖然不預期出錯
Uh oh!
There was an error while loading. Please reload this page.