Skip to content

Commit fd7849c

Browse files
committed
feat: 添加链接预览功能
1 parent c3fea4b commit fd7849c

File tree

10 files changed

+293
-28
lines changed

10 files changed

+293
-28
lines changed

README.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,17 @@
33
一个浏览器插件,用于提供 GitLab 项目中关联的飞书项目 ID 转换为飞书链接。方便在 GitLab 项目中快速跳转查看飞书项目信息。
44
[飞书参考资料](https://bytedance.larkoffice.com/wiki/XusFwYp2ZiqltkkSTaJc7eMdnYb)
55

6+
# 功能预览
7+
8+
<img src="./docs/preview1.png" alt="preview1" />
9+
<img src="./docs/preview2.png" alt="preview2" />
10+
611
# 安装
712

13+
## 商店安装
14+
15+
[Chrome 应用商店](https://chromewebstore.google.com/detail/gitlab-link-to-lark/ocmkgfnifakgckfeofcoakiniljdjcfp)
16+
817
## 源码安装
918

1019
```bash
@@ -22,10 +31,6 @@ npm run build
2231
<img src="./docs/install-2.png" alt="install" />
2332
<img src="./docs/install-3.png" alt="install" />
2433

25-
## Chrome 商店安装
26-
27-
[应用商店](https://chromewebstore.google.com/detail/gitlab-link-to-lark/ocmkgfnifakgckfeofcoakiniljdjcfp)
28-
2934
# 使用
3035

3136
打开浏览器扩展管理页面,找到 GitLab link to Lark 插件,点击 Options,填写 GitLab 地址和飞书命名空间,点击提交

docs/preview1.png

111 KB
Loading

docs/preview2.png

95 KB
Loading

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
{
22
"name": "gitlab-link-to-lark",
33
"description": "一个浏览器插件,用于提供 GitLab 项目中关联的飞书项目 ID 转换为飞书链接。方便在 GitLab 项目中快速跳转查看飞书项目信息 --- build for cyy(づ ̄3 ̄)づ╭❤~",
4-
"version": "1.1.0",
4+
"version": "1.2.0",
55
"scripts": {
66
"build": "NODE_ENV=production gulp build",
77
"dev": "NODE_ENV=development gulp dev"
88
},
99
"license": "MIT",
1010
"devDependencies": {
11+
"cheerio": "^1.0.0-rc.12",
1112
"gulp": "4",
1213
"gulp-live-server": "^0.0.31",
1314
"prettier": "^3.2.5",

src/js/background.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,83 @@
1+
import { MSG_EVENT } from "./event";
2+
import { LARK_DOMAIN_HOST } from "./utils";
3+
4+
main();
5+
6+
async function main() {
7+
chrome.runtime.onMessage.addListener(function (e, sender, sendResponse) {
8+
const { message, data } = e;
9+
const tabId = sender.tab.id;
10+
switch (message) {
11+
case MSG_EVENT.INIT:
12+
chrome.tabs.sendMessage(tabId, {
13+
message: MSG_EVENT.INIT,
14+
data: {
15+
msg: "init",
16+
},
17+
});
18+
break;
19+
case MSG_EVENT.GET_LARK_PROJECT_INFO:
20+
getLarkProjectInfo(data).then((resData) => {
21+
if (!resData) return;
22+
chrome.tabs.sendMessage(tabId, {
23+
message: MSG_EVENT.GET_LARK_PROJECT_INFO,
24+
data: resData,
25+
});
26+
});
27+
break;
28+
}
29+
});
30+
}
31+
32+
async function getLarkProjectInfo({ tid, app }) {
33+
const [t, id] = tid.split("-");
34+
const type = t === "m" ? "story" : "issue";
35+
let url = `${LARK_DOMAIN_HOST}/${app}/${type}/detail/${id}`;
36+
const res = await fetch(url);
37+
const text = await res.text();
38+
const reg = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi;
39+
const scripts = text.match(reg);
40+
let content = "";
41+
for (let i = 0; i < scripts.length; i++) {
42+
const text = scripts[i];
43+
const isContain =
44+
text.includes("window.detail") &&
45+
text.includes(`/work_item/${type}/${id}`);
46+
47+
if (isContain) {
48+
content = text;
49+
break;
50+
}
51+
}
52+
if (!content)
53+
return {
54+
error: true,
55+
tid,
56+
data: null,
57+
};
58+
content = content.replace(/\n/g, "");
59+
content = content.replace(new RegExp("\\\\x3C", "g"), "<");
60+
content = content.replace(new RegExp("\x3C", "g"), "<");
61+
content = content.replace(new RegExp("\\x3C", "g"), "<");
62+
content = content.split("};\x3C/script>")[0];
63+
content = content.replace("<script>", "");
64+
content = content.replace("}</script>", "");
65+
content = content.replace("window.detail = {", "");
66+
content = content.replace("...window.detail,", "");
67+
content = content.replace("...{", "{");
68+
content = content.replace("};", "}");
69+
content = content.replace(/\\'/g, "'");
70+
const obj = JSON.parse(content);
71+
let data = null;
72+
const key = Object.keys(obj)[0];
73+
data = obj[key].data;
74+
return {
75+
error: false,
76+
tid,
77+
data,
78+
};
79+
}
80+
181
chrome.runtime.onInstalled.addListener((details) => {
282
if (details.reason === "install") {
383
// 打开选项页

src/js/event.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const MSG_EVENT = {
2+
INIT: "INIT",
3+
GET_LARK_PROJECT_INFO: "GET_LARK_PROJECT_INFO",
4+
};

src/js/index.js

Lines changed: 65 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,42 @@
1-
import { getLarkConfig } from "./store";
1+
import { getLarkConfig, getLarkConfigSync } from "./store";
2+
import { checkCondition, LARK_DOMAIN_HOST } from "./utils";
3+
import { MSG_EVENT } from "./event";
24

35
main();
46

57
async function main() {
68
if (!checkCondition()) return;
9+
10+
// {
11+
// [tid]: {
12+
// locker: Boolean,
13+
// error: Boolean,
14+
// data: Object,
15+
// }
16+
// }
17+
let cacheMap = new Map();
718
const POPOVER_STYLE_ID = "lark-popover-link-style";
819
let dom_lark_popover = null;
920

10-
const LARK_DOMAIN_HOST = "https://project.feishu.cn";
11-
12-
let LarkConfig = null;
21+
await getLarkConfig();
1322
const nodeMap = new Map();
1423

24+
chrome.runtime.onMessage.addListener(function (e) {
25+
const { message, data } = e;
26+
switch (message) {
27+
case MSG_EVENT.GET_LARK_PROJECT_INFO:
28+
cacheMap.set(data.tid, {
29+
locker: true,
30+
error: data.error,
31+
data: data.data,
32+
});
33+
break;
34+
}
35+
});
36+
1537
initPopover();
1638
initPageListener();
1739

18-
// 检查是否满足条件
19-
async function checkCondition() {
20-
const config = await getLarkConfig();
21-
if (!config) return;
22-
LarkConfig = config;
23-
const domains = config.domain.split(",");
24-
if (domains.length === 0) return false;
25-
return domains.some((domain) => {
26-
return window.location.host.includes(domain);
27-
})
28-
? true
29-
: false;
30-
}
31-
3240
// 初始化 popover 节点
3341
function initPopover() {
3442
if (document.getElementById(POPOVER_STYLE_ID)) return;
@@ -112,7 +120,17 @@ async function main() {
112120
dom_lark_popover.style.setProperty("top", `${rect.y}px`);
113121
dom_lark_popover.style.setProperty("left", `${rect.x - 8}px`);
114122
dom_lark_popover.style.setProperty("transform", `translate(0%, -102%)`);
115-
dom_lark_popover.innerHTML = "Lark Link";
123+
const tid = e.target.dataset.tid;
124+
const cache = cacheMap.get(tid);
125+
let innerHTML = "飞书链接";
126+
if (cache) {
127+
if (cache.data) {
128+
innerHTML = cache.data.name;
129+
} else if (cache.error) {
130+
innerHTML = "未找到相关信息";
131+
}
132+
}
133+
dom_lark_popover.innerHTML = innerHTML;
116134
}
117135

118136
// 鼠标移出事件
@@ -128,11 +146,30 @@ async function main() {
128146

129147
// 获取 Lark 项目链接
130148
function getLarkProjectLink(projectId, type = "m") {
149+
const LarkConfig = getLarkConfigSync();
131150
if (type === "f")
132151
return `${LARK_DOMAIN_HOST}/${LarkConfig.app}/issue/detail/${projectId}`;
133152
return `${LARK_DOMAIN_HOST}/${LarkConfig.app}/story/detail/${projectId}`;
134153
}
135154

155+
function fetchLarkProjectInfo(data) {
156+
const { app, tid } = data;
157+
if (cacheMap.has(tid) && cacheMap.get(tid).locker) return;
158+
159+
cacheMap.set(tid, {
160+
locker: true,
161+
error: false,
162+
data: null,
163+
});
164+
chrome.runtime.sendMessage({
165+
message: MSG_EVENT.GET_LARK_PROJECT_INFO,
166+
data: {
167+
app,
168+
tid,
169+
},
170+
});
171+
}
172+
136173
// 替换项目 ID 为 Lark 项目链接
137174
function replaceProjectIdToLarkProjectLink(dom, className) {
138175
const reg = /(m|f)-\d+/g;
@@ -141,12 +178,17 @@ async function main() {
141178
const projectId = $1.split("-")[1];
142179
const type = $1.split("-")[0];
143180
isFind = true;
181+
const LarkConfig = getLarkConfigSync();
182+
const url = getLarkProjectLink(projectId, type);
183+
if ($1) {
184+
fetchLarkProjectInfo({
185+
tid: $1,
186+
app: LarkConfig.app,
187+
});
188+
}
144189
return `<a class='lark-project-link ${
145190
className ? className : ""
146-
}' href='${getLarkProjectLink(
147-
projectId,
148-
type
149-
)}' target='_blank' data-project-id="${projectId}" >${$1}</a>`;
191+
}' href='${url}' target='_blank' data-tid="${$1}" >${$1}</a>`;
150192
});
151193
return [isFind, content];
152194
}

src/js/store.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ export const getLarkConfig = async () => {
2525
return _GITLAB_LINK_TO_LARK_OPTIONS;
2626
};
2727

28+
export const getLarkConfigSync = () => {
29+
return _GITLAB_LINK_TO_LARK_OPTIONS;
30+
};
31+
2832
// 设置配置
2933
export const setLarkConfig = async (value) => {
3034
const dataString = JSON.stringify(value);

src/js/utils.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { getLarkConfig } from "./store";
2+
3+
export const LARK_DOMAIN_HOST = "https://project.feishu.cn";
4+
5+
// 检查是否满足条件
6+
export async function checkCondition() {
7+
const config = await getLarkConfig();
8+
if (!config) return;
9+
const domains = config.domain.split(",");
10+
if (domains.length === 0) return false;
11+
return domains.some((domain) => {
12+
return window.location.host.includes(domain);
13+
})
14+
? true
15+
: false;
16+
}

0 commit comments

Comments
 (0)