Skip to content

Commit 889c7ba

Browse files
committed
feat: add depotMR tree view
1 parent e9c04c7 commit 889c7ba

File tree

7 files changed

+203
-37
lines changed

7 files changed

+203
-37
lines changed

package.json

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"main": "./out/extension",
1414
"activationEvents": [
1515
"onCustomEditor:customEditor.mrDetail",
16+
"onView:codingPlugin.treeDepotMR",
1617
"onView:codingPlugin.treeMR",
1718
"onView:codingPlugin.treeDepot",
1819
"onView:codingPlugin.webview"
@@ -55,12 +56,8 @@
5556
"viewsContainers": {
5657
"activitybar": [
5758
{
58-
"id": "CODING-MR",
59-
"title": "CODING 合并请求"
60-
},
61-
{
62-
"id": "CODING-DEPOT",
63-
"title": "CODING 仓库"
59+
"id": "CODING-DEPOT-MR",
60+
"title": "CODING 代码仓库"
6461
}
6562
],
6663
"rightside": [
@@ -71,16 +68,10 @@
7168
]
7269
},
7370
"views": {
74-
"CODING-MR": [
75-
{
76-
"id": "codingPlugin.treeMR",
77-
"name": "CODING 合并请求"
78-
}
79-
],
80-
"CODING-DEPOT": [
71+
"CODING-DEPOT-MR": [
8172
{
82-
"id": "codingPlugin.treeDepot",
83-
"name": "CODING 仓库"
73+
"id": "codingPlugin.treeDepotMR",
74+
"name": "CODING 代码仓库"
8475
}
8576
],
8677
"WebviewContainerId": [

src/init.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import hx from 'hbuilderx';
22
import DepotTreeDataProvider from './trees/depot';
33
import MRTreeDataProvider from './trees/mr';
4+
import DepotMRTreeDataProvider from './trees/depotMR';
45
import MRCustomEditorProvider from './customEditors/mergeRequest';
56

67
import toast from './utils/toast';
@@ -9,6 +10,7 @@ import { IDepot, IMRItem } from './typings/common';
910
import * as DCloudService from './services/dcloud';
1011

1112
const { registerCommand } = hx.commands;
13+
const { createTreeView } = hx.window;
1214

1315
export function registerCommands(context: IContext) {
1416
const { codingServer } = context;
@@ -53,26 +55,35 @@ export function registerCommands(context: IContext) {
5355
}
5456

5557
const team = codingServer.session?.user?.team;
56-
await codingServer.createDepot(team, depot, depot);
57-
toast.info('仓库创建成功');
58+
const result = await codingServer.createDepot(team, depot, depot);
59+
if (result) {
60+
toast.info('仓库创建成功');
61+
}
5862
}),
5963
);
6064
}
6165

6266
export function createTreeViews(context: IContext) {
6367
context.subscriptions.push(
64-
hx.window.createTreeView('codingPlugin.treeMR', {
68+
createTreeView('codingPlugin.treeMR', {
6569
showCollapseAll: false,
6670
treeDataProvider: new MRTreeDataProvider(context),
6771
}),
6872
);
6973

7074
context.subscriptions.push(
71-
hx.window.createTreeView('codingPlugin.treeDepot', {
72-
showCollapseAll: true,
75+
createTreeView('codingPlugin.treeDepot', {
76+
showCollapseAll: false,
7377
treeDataProvider: new DepotTreeDataProvider(context),
7478
}),
7579
);
80+
81+
context.subscriptions.push(
82+
createTreeView('codingPlugin.treeDepotMR', {
83+
showCollapseAll: false,
84+
treeDataProvider: new DepotMRTreeDataProvider(context),
85+
}),
86+
);
7687
}
7788

7889
export function workspaceInit() {

src/services/codingServer.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,9 @@ export default class CodingServer {
134134

135135
async createDepot(team: string, project: string, depot: string) {
136136
try {
137-
await this.createProject(team, project);
137+
const projectRes = await this.createProject(team, project);
138+
139+
if (!projectRes) return;
138140

139141
const result = await axios({
140142
method: 'post',
@@ -150,6 +152,8 @@ export default class CodingServer {
150152
shared: false,
151153
}),
152154
});
155+
156+
return result.data;
153157
} catch (err) {
154158
console.error(err);
155159
}

src/trees/depotMR.ts

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import hx from 'hbuilderx';
2+
import ACTIONS, { dispatch } from '../utils/actions';
3+
import toast from '../utils/toast';
4+
import { getMrListParams } from '../utils/mr';
5+
import { IDepot, IMRItem, IReviewer } from '../typings/common';
6+
7+
interface IItem extends ITreeItem {
8+
_create: boolean;
9+
_auth: boolean;
10+
_disabled: boolean;
11+
_isDepot: boolean;
12+
}
13+
14+
type IElement = IDepot & IMRItem & IItem;
15+
16+
const getCommand = (element: IElement) => {
17+
if (element.children || element._disabled) return '';
18+
if (element._auth) return 'codingPlugin.auth';
19+
if (element._create) return 'codingPlugin.createDepot';
20+
if (element.vcsType) return 'codingPlugin.depotTreeItemClick';
21+
return 'codingPlugin.mrTreeItemClick';
22+
};
23+
24+
class DepotMRTreeDataProvider extends hx.TreeDataProvider {
25+
constructor(context: IContext) {
26+
super();
27+
this.context = context;
28+
}
29+
30+
getUser() {
31+
return this.context.codingServer.session?.user;
32+
}
33+
34+
getRepoInfo() {
35+
const { selectedDepot, depots, codingServer } = this.context;
36+
const user = codingServer.session?.user;
37+
return getMrListParams(selectedDepot, depots, user);
38+
}
39+
40+
async getChildren(element: IElement) {
41+
const user = this.getUser();
42+
if (!user) {
43+
toast.warn('请先绑定 CODING 账户');
44+
return Promise.resolve([
45+
{
46+
name: '绑定 CODING 账户',
47+
_auth: true,
48+
},
49+
]);
50+
}
51+
52+
if (element) {
53+
return Promise.resolve(element.children);
54+
}
55+
56+
const repoInfo = this.getRepoInfo();
57+
const promises = [this.context.codingServer.getDepotList()];
58+
if (repoInfo) {
59+
promises.push(this.context.codingServer.getMrList(repoInfo));
60+
}
61+
62+
try {
63+
const [depots, list] = await Promise.all(promises);
64+
let createdList: IMRItem[] = [];
65+
let reviewerList: IMRItem[] = [];
66+
let others = [];
67+
if (list) {
68+
createdList = list.filter((item: IMRItem) => item.author.id === user.id);
69+
reviewerList = list.filter((item: IMRItem) => item.reviewers.find((r: IReviewer) => r.reviewer.id === user.id));
70+
others = list.filter((item: IMRItem) => {
71+
const isNotInCreatedList = createdList.findIndex((i) => i.id === item.id) === -1;
72+
const isNotInReviewerList = reviewerList.findIndex((i) => i.id === item.id) === -1;
73+
return isNotInCreatedList && isNotInReviewerList;
74+
});
75+
}
76+
77+
dispatch(ACTIONS.SET_DEPOTS, {
78+
context: this.context,
79+
value: depots,
80+
});
81+
82+
return Promise.resolve([
83+
{
84+
name: '+ 创建仓库',
85+
_create: true,
86+
_isDepot: true,
87+
},
88+
{
89+
name: '仓库列表',
90+
children: depots,
91+
_isDepot: true,
92+
},
93+
{
94+
title: `合并请求列表(当前选中仓库 ${this.context.selectedDepot?.name || '-'})`,
95+
children: [
96+
{
97+
title: `我创建的 (${createdList?.length})`,
98+
children: createdList,
99+
},
100+
{
101+
title: `需要我 Review 的 (${reviewerList?.length})`,
102+
children: reviewerList,
103+
},
104+
{
105+
title: `其他(${others?.length})`,
106+
children: others,
107+
},
108+
],
109+
},
110+
]);
111+
} catch {
112+
toast.error('获取数据失败');
113+
}
114+
}
115+
116+
getTreeItem(element: IElement) {
117+
if (element.vcsType || element._isDepot) {
118+
return {
119+
label: element.name,
120+
collapsibleState: element.children ? 1 : 0,
121+
command: {
122+
command: getCommand(element),
123+
arguments: element,
124+
},
125+
contextValue: 'createDepot',
126+
};
127+
}
128+
129+
const user = this.getUser();
130+
return {
131+
label: element.title,
132+
collapsibleState: element.children ? 1 : 0,
133+
command: {
134+
command: getCommand(element),
135+
arguments: [user?.team, element],
136+
},
137+
};
138+
}
139+
}
140+
141+
export default DepotMRTreeDataProvider;

src/trees/mr.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@ import { IMRItem, IReviewer } from '../typings/common';
55

66
interface IItem extends ITreeItem {
77
_disabled: boolean;
8-
_create: boolean;
98
}
109

1110
const getCommand = (element: IMRItem & IItem) => {
1211
if (element.children || element._disabled) return '';
13-
if (element._create) return 'codingPlugin.createDepot';
1412
return 'codingPlugin.mrTreeItemClick';
1513
};
1614

webviews/App.tsx

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useState } from 'react';
1+
import React, { useEffect, useState, useRef } from 'react';
22
import cn from 'classnames';
33

44
import {
@@ -12,6 +12,11 @@ import {
1212
import { MERGE_STATUS_TEXT, MERGE_STATUS } from './constants';
1313
import style from './style.css';
1414

15+
interface IReviewers {
16+
reviewers: any[];
17+
volunteer_reviewers: any[];
18+
}
19+
1520
const toast = (msg: string) => {
1621
window.hbuilderx.postMessage({
1722
command: 'webview.toast',
@@ -26,23 +31,35 @@ const App = () => {
2631
const user = session?.user;
2732
const team = user?.team;
2833

34+
// Fix initialize isAgreed doesn't work
35+
const agreedRef = useRef<boolean>(true);
36+
2937
const [isClosing, setIsClosing] = useState(false);
3038
const [isMerging, setIsMerging] = useState(false);
3139
const [isAllowing, setIsAllowing] = useState(false);
3240
const [isDisAllowing, setIsDisAllowing] = useState(false);
3341
const [mrStatus, setMrStatus] = useState<MERGE_STATUS>(MERGE_STATUS.CANMERGE);
3442
const [mrDetail, setMrDetail] = useState();
35-
const [reviewers, setReviewers] = useState<{ reviewers: any[], volunteer_reviewers: any[] }>({ reviewers: [], volunteer_reviewers: [] });
43+
const [reviewers, setReviewers] = useState<IReviewers>({ reviewers: [], volunteer_reviewers: [] });
44+
45+
const getInitialAgreed = () => {
46+
let agreed = true;
47+
const index = reviewers.reviewers.findIndex((r) => r.reviewer.id === user.id);
48+
49+
if (index >= 0) {
50+
agreed = reviewers.reviewers[index].value === 100;
51+
} else {
52+
agreed = reviewers.volunteer_reviewers.findIndex((r) => r.reviewer.id === user.id) >= 0;
53+
}
54+
return agreed;
55+
};
3656

37-
const index = reviewers.reviewers.findIndex((r) => r.reviewer.id === user.id);
38-
let agreed = true;
39-
if (index >= 0) {
40-
agreed = reviewers.reviewers[index].value === 100;
41-
} else {
42-
agreed = reviewers.volunteer_reviewers.findIndex((r) => r.reviewer.id === user.id) >= 0;
43-
}
57+
const [isAgreed, setIsAgreed] = useState(getInitialAgreed());
4458

45-
const [isAgreed, setIsAgreed] = useState(agreed);
59+
const getIsAgreed = () => {
60+
if (agreedRef.current) return getInitialAgreed();
61+
return isAgreed;
62+
};
4663

4764
const getParams = () => ({
4865
...repoInfo,
@@ -128,6 +145,7 @@ const App = () => {
128145

129146
if (!result.code) {
130147
setIsAgreed(true);
148+
agreedRef.current = false;
131149
} else {
132150
toast('操作失败');
133151
}
@@ -143,6 +161,7 @@ const App = () => {
143161

144162
if (!result.code) {
145163
setIsAgreed(false);
164+
agreedRef.current = false;
146165
} else {
147166
toast('操作失败');
148167
}
@@ -180,7 +199,7 @@ const App = () => {
180199
<div dangerouslySetInnerHTML={{ __html: body }} />
181200

182201
<div className={style.btnGroup}>
183-
{showAllowMergeBtn && !isAgreed && (
202+
{showAllowMergeBtn && !getIsAgreed() && (
184203
<div
185204
className={cn(style.btn, style.btnPrimary, isAllowing && style.disabled)}
186205
onClick={handleAllowMerge}
@@ -189,7 +208,7 @@ const App = () => {
189208
</div>
190209
)}
191210

192-
{showAllowMergeBtn && isAgreed && (
211+
{showAllowMergeBtn && getIsAgreed() && (
193212
<div
194213
className={cn(style.btn, style.btnPrimary, isDisAllowing && style.disabled)}
195214
onClick={handleDisAllowMerge}

webviews/style.css

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ a:hover {
4747

4848
.btn {
4949
margin-right: 10px;
50-
padding: 3px 5px;
50+
padding: 3px 10px;
5151
border-radius: 3px;
5252
border: 1px solid #ccc;
5353
cursor: pointer;
@@ -82,9 +82,11 @@ a:hover {
8282

8383
.status.success {
8484
background-color: #bbfada;
85+
color: #08934d;
8586
}
8687
.status.error {
87-
background-color: red;
88+
background-color: #fed2d2;
89+
color: #ca1628;
8890
}
8991
.status.merged {
9092
background-color: #cceaff;

0 commit comments

Comments
 (0)