popup细节
Some checks failed
test / Run tests (push) Failing after 3s
build / Build (push) Failing after 5s

This commit is contained in:
2025-04-08 18:04:48 +08:00
parent 42975d47cf
commit d97a64c644
12 changed files with 166 additions and 126 deletions

View File

@ -48,6 +48,10 @@ export class ScriptClient extends Client {
getScriptRunResource(script: Script): Promise<ScriptRunResouce> {
return this.do("getScriptRunResource", script);
}
excludeUrl(uuid: string, url: string, remove: boolean) {
return this.do("excludeUrl", { uuid, url, remove });
}
}
export class ResourceClient extends Client {

View File

@ -10,6 +10,7 @@ import {
Script,
ScriptDAO,
SCRIPT_TYPE_NORMAL,
SCRIPT_RUN_STATUS_RUNNING,
} from "@App/app/repo/scripts";
import {
ScriptMenuRegisterCallbackValue,
@ -54,9 +55,11 @@ export class PopupService {
) {}
genScriptMenuByTabMap(menu: ScriptMenu[]) {
let n = 0;
menu.forEach((script) => {
// 创建脚本菜单
if (script.menus.length) {
n += script.menus.length;
chrome.contextMenus.create({
id: `scriptMenu_` + script.uuid,
title: script.name,
@ -65,7 +68,6 @@ export class PopupService {
});
script.menus.forEach((menu) => {
// 创建菜单
console.log("create menu", menu);
chrome.contextMenus.create({
id: `scriptMenu_menu_${script.uuid}_${menu.id}`,
title: menu.name,
@ -75,6 +77,7 @@ export class PopupService {
});
}
});
return n;
}
// 生成chrome菜单
@ -86,6 +89,7 @@ export class PopupService {
if (!menu.length && !backgroundMenu.length) {
return;
}
let n = 0;
// 创建根菜单
chrome.contextMenus.create({
id: "scriptMenu",
@ -93,18 +97,21 @@ export class PopupService {
contexts: ["all"],
});
if (menu) {
this.genScriptMenuByTabMap(menu);
n += this.genScriptMenuByTabMap(menu);
}
// 后台脚本的菜单
if (backgroundMenu) {
this.genScriptMenuByTabMap(backgroundMenu);
n += this.genScriptMenuByTabMap(backgroundMenu);
}
if (n === 0) {
// 如果没有菜单,删除菜单
chrome.contextMenus.remove("scriptMenu");
}
}
async registerMenuCommand(message: ScriptMenuRegisterCallbackValue) {
// 给脚本添加菜单
return this.txUpdateScriptMenu(message.tabId, async (data) => {
console.log("register menu", message, data);
const script = data.find((item) => item.uuid === message.uuid);
if (script) {
const menu = script.menus.find((item) => item.id === message.id);
@ -165,7 +172,7 @@ export class PopupService {
hasUserConfig: !!script.config,
metadata: script.metadata,
runStatus: script.runStatus,
runNum: 0,
runNum: script.type === SCRIPT_TYPE_NORMAL ? 0 : script.runStatus === SCRIPT_RUN_STATUS_RUNNING ? 1 : 0,
runNumByIframe: 0,
menus: [],
customExclude: (script as ScriptMatchInfo).customizeExcludeMatches || [],
@ -175,21 +182,21 @@ export class PopupService {
// 获取popup页面数据
async getPopupData(req: GetPopupDataReq): Promise<GetPopupDataRes> {
// 获取当前tabId
const scriptUuid = await this.runtime.getPageScriptByUrl(req.url);
const script = await this.runtime.getPageScriptByUrl(req.url, true);
// 与运行时脚本进行合并
const runScript = await this.getScriptMenu(req.tabId);
// 筛选出未运行的脚本
const notRunScript = scriptUuid.filter((script) => {
return !runScript.find((item) => item.uuid === script.uuid);
});
// 将未运行的脚本转换为菜单
const scriptList = notRunScript.map((script): ScriptMenu => {
// 合并数据
const scriptMenu = script.map((script) => {
const run = runScript.find((item) => item.uuid === script.uuid);
if (run) {
// 如果脚本已经存在,则不添加,赋值状态
run.enable = script.status === SCRIPT_STATUS_ENABLE;
return run;
}
return this.scriptToMenu(script);
});
runScript.push(...scriptList);
// 后台脚本只显示开启或者运行中的脚本
return { scriptList: runScript, backScriptList: await this.getScriptMenu(-1) };
return { scriptList: scriptMenu, backScriptList: await this.getScriptMenu(-1) };
}
async getScriptMenu(tabId: number) {
@ -263,17 +270,17 @@ export class PopupService {
return;
}
return this.txUpdateScriptMenu(-1, async (menu) => {
const scriptMenu = menu.find((item) => item.uuid === uuid);
const index = menu.findIndex((item) => item.uuid === uuid);
if (script.status === SCRIPT_STATUS_ENABLE) {
// 加入菜单
if (!scriptMenu) {
if (index === -1) {
const item = this.scriptToMenu(script);
menu.push(item);
}
} else {
// 移出菜单
if (scriptMenu) {
menu.splice(menu.indexOf(scriptMenu), 1);
if (index !== -1) {
menu.splice(index, 1);
}
}
return menu;
@ -281,9 +288,9 @@ export class PopupService {
});
subscribeScriptDelete(this.mq, async ({ uuid }) => {
return this.txUpdateScriptMenu(-1, async (menu) => {
const scriptMenu = menu.find((item) => item.uuid === uuid);
if (scriptMenu) {
menu.splice(menu.indexOf(scriptMenu), 1);
const index = menu.findIndex((item) => item.uuid === uuid);
if (index !== -1) {
menu.splice(index, 1);
return menu;
}
return null;
@ -293,6 +300,7 @@ export class PopupService {
return this.txUpdateScriptMenu(-1, async (menu) => {
const scriptMenu = menu.find((item) => item.uuid === uuid);
if (scriptMenu) {
scriptMenu.runNum = 1;
scriptMenu.runStatus = runStatus;
return menu;
}

View File

@ -67,7 +67,7 @@ export class RuntimeService {
// 加载页面脚本
await this.loadPageScript(script);
if (!data.enable) {
await this.unregistryPageScript(script);
await this.unregistryPageScript(script.uuid);
}
}
});
@ -82,15 +82,9 @@ export class RuntimeService {
}
});
// 监听脚本删除
subscribeScriptDelete(this.mq, async (data) => {
const script = await this.scriptDAO.get(data.uuid);
if (!script) {
return;
}
if (script.type === SCRIPT_TYPE_NORMAL) {
await this.unregistryPageScript(script);
this.deleteScriptMatch(script.uuid);
}
subscribeScriptDelete(this.mq, async ({ uuid }) => {
await this.unregistryPageScript(uuid);
this.deleteScriptMatch(uuid);
});
// 将开启的脚本发送一次enable消息
@ -138,24 +132,29 @@ export class RuntimeService {
return sendMessage(new ExtensionContentMessageSend(tabId, options), "content/runtime/" + action, data);
}
async getPageScriptUuidByUrl(url: string) {
async getPageScriptUuidByUrl(url: string, includeCustomize?: boolean) {
const match = await this.loadScriptMatchInfo();
// 匹配当前页面的脚本
const matchScriptUuid = match.match(url!);
// 排除自定义匹配
const excludeScriptUuid = this.scriptCustomizeMatch.match(url!);
const excludeMatch = new Set<string>();
excludeScriptUuid.forEach((uuid) => {
excludeMatch.add(uuid);
});
return matchScriptUuid.filter((value) => {
// 过滤掉自定义排除的脚本
return !excludeMatch.has(value);
});
// 包含自定义排除的脚本
if (includeCustomize) {
const excludeScriptUuid = this.scriptCustomizeMatch.match(url!);
const match = new Set<string>();
excludeScriptUuid.forEach((uuid) => {
match.add(uuid);
});
matchScriptUuid.forEach((uuid) => {
match.add(uuid);
});
// 转化为数组
console.log("matchScriptUuid", matchScriptUuid);
return Array.from(match);
}
return matchScriptUuid;
}
async getPageScriptByUrl(url: string) {
const matchScriptUuid = await this.getPageScriptUuidByUrl(url);
async getPageScriptByUrl(url: string, includeCustomize?: boolean) {
const matchScriptUuid = await this.getPageScriptUuidByUrl(url, includeCustomize);
return matchScriptUuid.map((uuid) => {
return Object.assign({}, this.scriptMatchCache?.get(uuid));
});
@ -266,15 +265,7 @@ export class RuntimeService {
Object.keys(data).forEach((key) => {
const item = data[key];
cache.set(item.uuid, item);
item.matches.forEach((match) => {
this.scriptMatch.add(match, item.uuid);
});
item.excludeMatches.forEach((match) => {
this.scriptMatch.exclude(match, item.uuid);
});
item.customizeExcludeMatches.forEach((match) => {
this.scriptCustomizeMatch.exclude(match, item.uuid);
});
this.syncAddScriptMatch(item);
});
}
});
@ -305,6 +296,11 @@ export class RuntimeService {
await this.loadScriptMatchInfo();
}
this.scriptMatchCache!.set(item.uuid, item);
this.syncAddScriptMatch(item);
this.saveScriptMatchInfo();
}
syncAddScriptMatch(item: ScriptMatchInfo) {
// 清理一下老数据
this.scriptMatch.del(item.uuid);
this.scriptCustomizeMatch.del(item.uuid);
@ -316,9 +312,8 @@ export class RuntimeService {
this.scriptMatch.exclude(match, item.uuid);
});
item.customizeExcludeMatches.forEach((match) => {
this.scriptCustomizeMatch.exclude(match, item.uuid);
this.scriptCustomizeMatch.add(match, item.uuid);
});
this.saveScriptMatchInfo();
}
async updateScriptStatus(uuid: string, status: SCRIPT_STATUS) {
@ -329,11 +324,11 @@ export class RuntimeService {
this.saveScriptMatchInfo();
}
deleteScriptMatch(uuid: string) {
async deleteScriptMatch(uuid: string) {
if (!this.scriptMatchCache) {
return;
await this.loadScriptMatchInfo();
}
this.scriptMatchCache.delete(uuid);
this.scriptMatchCache!.delete(uuid);
this.scriptMatch.del(uuid);
this.scriptCustomizeMatch.del(uuid);
this.saveScriptMatchInfo();
@ -394,24 +389,28 @@ export class RuntimeService {
if (script.metadata["run-at"]) {
registerScript.runAt = getRunAt(script.metadata["run-at"]);
}
await chrome.userScripts.register([registerScript]);
if (await Cache.getInstance().get("registryScript:" + script.uuid)) {
await chrome.userScripts.update([registerScript]);
} else {
await chrome.userScripts.register([registerScript]);
}
await Cache.getInstance().set("registryScript:" + script.uuid, true);
}
}
async unregistryPageScript(script: Script) {
if (!(await Cache.getInstance().get("registryScript:" + script.uuid))) {
async unregistryPageScript(uuid: string) {
if (!(await Cache.getInstance().get("registryScript:" + uuid))) {
return;
}
chrome.userScripts.unregister(
{
ids: [script.uuid],
ids: [uuid],
},
() => {
// 删除缓存
Cache.getInstance().del("registryScript:" + script.uuid);
Cache.getInstance().del("registryScript:" + uuid);
// 修改脚本状态为disable
this.updateScriptStatus(script.uuid, SCRIPT_STATUS_DISABLE);
this.updateScriptStatus(uuid, SCRIPT_STATUS_DISABLE);
}
);
}

View File

@ -198,7 +198,7 @@ export class ScriptService {
.then(() => {
logger.info("delete success");
this.mq.publish("deleteScript", { uuid });
return {};
return true;
})
.catch((e) => {
logger.error("delete error", Logger.E(e));
@ -279,6 +279,32 @@ export class ScriptService {
return Promise.resolve(ret);
}
async excludeUrl({ uuid, url, remove }: { uuid: string; url: string; remove: boolean }) {
const script = await this.scriptDAO.get(uuid);
if (!script) {
throw new Error("script not found");
}
script.selfMetadata = script.selfMetadata || {};
let excludes = script.selfMetadata.exclude || script.metadata.exclude || [];
if (remove) {
excludes = excludes.filter((item) => item !== url);
} else {
excludes.push(url);
}
script.selfMetadata.exclude = excludes;
return this.scriptDAO
.update(uuid, script)
.then(() => {
// 广播一下
this.mq.publish("installScript", { script, update: true });
return true;
})
.catch((e) => {
this.logger.error("exclude url error", Logger.E(e));
throw e;
});
}
init() {
this.listenerScriptInstall();
@ -290,5 +316,6 @@ export class ScriptService {
this.group.on("updateRunStatus", this.updateRunStatus.bind(this));
this.group.on("getCode", this.getCode.bind(this));
this.group.on("getScriptRunResource", this.buildScriptRunResource.bind(this));
this.group.on("excludeUrl", this.excludeUrl.bind(this));
}
}