Skip to content

Commit f69d2c6

Browse files
authored
Merge pull request #18 from cangzhang/feat/mr-status
2 parents c6483d2 + 16b38d1 commit f69d2c6

24 files changed

+362
-77
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
## How to use
44

55
1. Install the plugin
6-
1. Open a repo host by `coding.net`
6+
1. Open a repo hosted by `coding.net`
77
1. Click on `Log in` button
8-
1. You will be redirected back to the editor, free to try & open an issue
8+
1. You will be redirected back to the editor, feel free to try & open an issue
99

1010
## Development
1111

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "coding-plugin",
33
"displayName": "CODING Merge Requests & Releases",
44
"description": "CODING plugin for VS Code.",
5-
"version": "0.2.1",
5+
"version": "0.3.0",
66
"publisher": "coding-net",
77
"license": "MIT",
88
"engines": {
@@ -19,7 +19,7 @@
1919
"icon": "assets/coding.png",
2020
"repository": {
2121
"type": "git",
22-
"url": "https://github.com/cangzhang/coding-vscode.git"
22+
"url": "https://github.com/Coding/coding-vscode.git"
2323
},
2424
"main": "./out/extension.js",
2525
"contributes": {

src/codingServer.ts

+29
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
IMemberListResp,
1818
IMRContentResp,
1919
ICreateCommentResp,
20+
IMRStatusResp,
2021
} from 'src/typings/respResult';
2122
import { PromiseAdapter, promiseFromEvent, parseQuery, parseCloneUrl } from 'src/common/utils';
2223
import { GitService } from 'src/common/gitService';
@@ -96,6 +97,13 @@ export class CodingServer {
9697
await vscode.commands.executeCommand('setContext', 'hasRepo', !!repoInfo?.repo);
9798
const result = await this.getUserInfo(repoInfo?.team || ``, accessToken);
9899
const { data: userInfo } = result;
100+
101+
if (userInfo.team !== repoInfo?.team) {
102+
this._loggedIn = false;
103+
await vscode.commands.executeCommand('setContext', 'loggedIn', this._loggedIn);
104+
throw new Error(`team not match`);
105+
}
106+
99107
const ret: ISessionData = {
100108
id: nanoid(),
101109
user: userInfo,
@@ -686,6 +694,27 @@ export class CodingServer {
686694
}
687695
}
688696

697+
public async fetchMRStatus(iid: string) {
698+
try {
699+
const { repoApiPrefix } = await this.getApiPrefix();
700+
const resp: IMRStatusResp = await got
701+
.get(`${repoApiPrefix}/merge/${iid}/commit-statuses`, {
702+
searchParams: {
703+
access_token: this._session?.accessToken,
704+
},
705+
})
706+
.json();
707+
708+
if (resp.code) {
709+
return Promise.reject(resp);
710+
}
711+
712+
return resp;
713+
} catch (e) {
714+
return Promise.reject(e);
715+
}
716+
}
717+
689718
get loggedIn() {
690719
return this._loggedIn;
691720
}

src/panel.ts

+15-2
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,17 @@ export class Panel {
106106
break;
107107
case 'mr.update.reviewers': {
108108
try {
109-
const { iid, list: selected }: { iid: string; list: number[] } = args;
109+
const {
110+
iid,
111+
list: selected,
112+
author,
113+
}: { iid: string; list: number[]; author: string } = args;
110114
const {
111115
data: { list: memberList },
112116
} = await this._codingSrv.getProjectMembers();
117+
113118
const list = memberList
114-
.filter((i) => i.user.global_key !== this._codingSrv.session?.user?.global_key)
119+
.filter((i) => i.user.global_key !== author)
115120
.map((i) => ({
116121
label: i.user.name,
117122
description: i.user.global_key,
@@ -152,6 +157,14 @@ export class Panel {
152157
} catch (e) {}
153158
break;
154159
}
160+
case `mr.fetch.status`: {
161+
try {
162+
const { iid } = args;
163+
const resp = await this._codingSrv.fetchMRStatus(iid);
164+
this._replyMessage(message, resp.data);
165+
} catch (e) {}
166+
break;
167+
}
155168
default:
156169
return this.MESSAGE_UNHANDLED;
157170
}

src/tree/mrTree.ts

+8-10
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ enum ItemType {
2020
Node = `node`,
2121
}
2222

23+
const capitalized = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
24+
2325
const getIcon = (name: string, theme: string) =>
2426
path.join(__filename, `../../../assets/${theme}/${name}.png`);
2527

@@ -58,7 +60,7 @@ export class MRTreeDataProvider implements vscode.TreeDataProvider<ListItem<ITre
5860
private _disposables: vscode.Disposable[];
5961

6062
private _context: vscode.ExtensionContext;
61-
private _service: CodingServer;
63+
private readonly _service: CodingServer;
6264

6365
constructor(context: vscode.ExtensionContext, service: CodingServer) {
6466
this._context = context;
@@ -83,7 +85,7 @@ export class MRTreeDataProvider implements vscode.TreeDataProvider<ListItem<ITre
8385

8486
getChildren(element?: ListItem<ITreeNode>): Thenable<ListItem<ITreeNode>[]> {
8587
if (!this._service.loggedIn) {
86-
vscode.window.showErrorMessage(`[MR Tree] Invalid credentials.`);
88+
console.error(`[MR Tree] Invalid credentials.`);
8789
return Promise.resolve([]);
8890
}
8991

@@ -94,17 +96,13 @@ export class MRTreeDataProvider implements vscode.TreeDataProvider<ListItem<ITre
9496

9597
if (!element) {
9698
return Promise.resolve([
99+
new CategoryItem(capitalized(MRType.Open), MRType.Open, TreeItemCollapsibleState.Collapsed),
97100
new CategoryItem(
98-
MRType.Open.toUpperCase(),
99-
MRType.Open,
100-
TreeItemCollapsibleState.Collapsed,
101-
),
102-
new CategoryItem(
103-
MRType.Closed.toUpperCase(),
101+
capitalized(MRType.Closed),
104102
MRType.Closed,
105103
TreeItemCollapsibleState.Collapsed,
106104
),
107-
new CategoryItem(MRType.All.toUpperCase(), MRType.All, TreeItemCollapsibleState.Collapsed),
105+
new CategoryItem(capitalized(MRType.All), MRType.All, TreeItemCollapsibleState.Collapsed),
108106
]);
109107
}
110108

@@ -115,7 +113,7 @@ export class MRTreeDataProvider implements vscode.TreeDataProvider<ListItem<ITre
115113
.then((resp) => {
116114
if (resp.code) {
117115
const msg = Object.values(resp.msg || {})[0];
118-
vscode.window.showErrorMessage(`[MR] list: ${msg}`);
116+
console.error(`[MR] list: ${msg}`);
119117
return [];
120118
}
121119

src/typings/commonTypes.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { IMRDetail, IUserItem } from './respResult';
1+
import { IMRDetail, IMRStatusItem, IUserItem } from './respResult';
22

33
export interface IRepoInfo {
44
team: string;
@@ -37,6 +37,7 @@ export interface IMRWebViewDetail {
3737
data: IMRDetail & {
3838
loading: boolean;
3939
editingDesc: boolean;
40+
commit_statuses: IMRStatusItem[];
4041
};
4142
user: IUserItem;
4243
}

src/typings/respResult.ts

+22
Original file line numberDiff line numberDiff line change
@@ -244,3 +244,25 @@ export interface IMRContentResp extends CodingResponse {
244244
export interface ICreateCommentResp extends CodingResponse {
245245
data: IComment;
246246
}
247+
248+
export interface IMRStatusItem {
249+
state: string;
250+
sha: string;
251+
context: string;
252+
target_url: string;
253+
description: string;
254+
ignore: boolean;
255+
}
256+
257+
export interface IMRStatus {
258+
state: string;
259+
pendingStateCount: number;
260+
successStateCount: number;
261+
failureStateCount: number;
262+
errorStateCount: number;
263+
statuses: IMRStatusItem[];
264+
}
265+
266+
export interface IMRStatusResp extends CodingResponse {
267+
data: IMRStatus;
268+
}

webviews/App.tsx

+40-6
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
import React, { FormEvent, useRef, useState } from 'react';
1+
import React, { FormEvent, useEffect, useRef, useState, useCallback } from 'react';
22
import { view } from '@risingstack/react-easy-state';
33

4+
import { IMRStatus } from 'src/typings/respResult';
5+
46
import appStore from 'webviews/store/appStore';
57
import persistDataHook from 'webviews/hooks/persistDataHook';
6-
import Activities from 'webviews/components/Activities';
7-
import Reviewers from 'webviews/components/Reviewers';
88
import initDataHook from 'webviews/hooks/initDataHook';
9-
import EditButton from 'webviews/components/EditButton';
10-
// import { requestUpdateMRContent } from 'webviews/service/mrService';
9+
10+
import Activities from 'webviews/components/mr/Activities';
11+
import Reviewers from 'webviews/components/mr/Reviewers';
12+
import EditButton from 'webviews/components/mr/EditButton';
13+
import StatusCheck from 'webviews/components/mr/StatusCheck';
1114

1215
import {
1316
EmptyWrapper,
@@ -25,18 +28,46 @@ import {
2528
} from 'webviews/app.styles';
2629

2730
function App() {
28-
const { currentMR, updateMRTitle, toggleUpdatingDesc, updateMRDesc } = appStore;
31+
const { currentMR, updateMRTitle, toggleUpdatingDesc, updateMRDesc, fetchMRStatus } = appStore;
2932
const [isEditingTitle, setEditingTitle] = useState(false);
3033
const [title, setTitle] = useState(currentMR?.data?.merge_request?.title);
3134
const inputRef = useRef<HTMLInputElement | null>(null);
3235
const [desc, setDesc] = useState(``);
36+
const statusChecker = useRef<undefined | number>();
37+
const [statusData, setStatusData] = useState<IMRStatus | null>(null);
3338

3439
const { repoInfo, data } = currentMR;
3540
const { merge_request: mergeRequest } = data || {};
3641

3742
persistDataHook();
3843
initDataHook();
3944

45+
useEffect(() => {
46+
statusChecker.current = window.setTimeout(async () => {
47+
try {
48+
await onRefreshStatus();
49+
} finally {
50+
window.clearTimeout(statusChecker.current);
51+
52+
statusChecker.current = window.setInterval(async () => {
53+
await onRefreshStatus();
54+
}, 30 * 1000);
55+
}
56+
}, 3 * 1000);
57+
58+
return () => {
59+
window.clearTimeout(statusChecker.current);
60+
window.clearInterval(statusChecker.current);
61+
setStatusData(null);
62+
};
63+
}, [currentMR.iid]);
64+
65+
const onRefreshStatus = useCallback(async () => {
66+
const resp = await fetchMRStatus(currentMR.iid);
67+
setStatusData(resp);
68+
return null;
69+
}, [currentMR.iid]);
70+
4071
const handleKeyDown = async (event: any) => {
4172
if (event.key === 'Enter') {
4273
await updateMRTitle(title);
@@ -146,6 +177,9 @@ function App() {
146177
onChange={onChangeDesc}
147178
/>
148179
)}
180+
181+
<StatusCheck data={statusData} onRefresh={onRefreshStatus} />
182+
149183
<SectionTitle>Activities</SectionTitle>
150184
<Activities />
151185
</Body>

webviews/assets/edit.svg

+1-1
Loading

webviews/assets/refresh.svg

+54
Loading

webviews/components/EditButton.tsx

-41
This file was deleted.

0 commit comments

Comments
 (0)