Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 11f6716

Browse files
feat(mr): status check.
1 parent c6483d2 commit 11f6716

File tree

19 files changed

+307
-60
lines changed

19 files changed

+307
-60
lines changed

‎src/codingServer.ts

Lines changed: 29 additions & 0 deletions
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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,14 @@ export class Panel {
152152
} catch (e) {}
153153
break;
154154
}
155+
case `mr.fetch.status`: {
156+
try {
157+
const { iid } = args;
158+
const resp = await this._codingSrv.fetchMRStatus(iid);
159+
this._replyMessage(message, resp.data);
160+
} catch (e) {}
161+
break;
162+
}
155163
default:
156164
return this.MESSAGE_UNHANDLED;
157165
}

‎src/tree/mrTree.ts

Lines changed: 8 additions & 10 deletions
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/respResult.ts

Lines changed: 22 additions & 0 deletions
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

Lines changed: 40 additions & 6 deletions
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+
}, 5 * 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

Lines changed: 1 addition & 1 deletion
Loading[フレーム]

‎webviews/assets/refresh.svg

Lines changed: 54 additions & 0 deletions
Loading[フレーム]

‎webviews/components/EditButton.tsx

Lines changed: 0 additions & 41 deletions
This file was deleted.

‎webviews/components/IconButton.tsx

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import React from 'react';
2+
import styled, { css, keyframes } from 'styled-components';
3+
4+
interface Props {
5+
onClick?: () => void;
6+
children: React.ReactElement;
7+
title?: string;
8+
width?: number;
9+
height?: number;
10+
rotate?: boolean;
11+
}
12+
13+
const rotate = keyframes`
14+
0% {
15+
transform: rotate(0deg);
16+
}
17+
18+
100% {
19+
transform: rotate(360deg);
20+
}
21+
`;
22+
23+
const Button = styled.button`
24+
border: unset;
25+
background: unset;
26+
width: 20px;
27+
height: 20px;
28+
margin-left: 1ex;
29+
padding: 2px 0;
30+
vertical-align: middle;
31+
32+
:hover {
33+
cursor: pointer;
34+
}
35+
36+
:focus {
37+
outline: 1px solid var(--vscode-focusBorder);
38+
outline-offset: 2px;
39+
}
40+
41+
svg {
42+
width: ${(props: Props) => props.height || 16}px;
43+
height: ${(props: Props) => props.width || 16}px;
44+
overflow: hidden;
45+
}
46+
47+
svg path {
48+
fill: var(--vscode-foreground);
49+
}
50+
`;
51+
52+
const IconButton = ({
53+
onClick = () => null,
54+
children,
55+
title = ``,
56+
width,
57+
height,
58+
rotate = false,
59+
}: Props) => {
60+
return (
61+
<Button title={title} width={width} height={height} rotate={rotate} onClick={onClick}>
62+
{children}
63+
</Button>
64+
);
65+
};
66+
67+
export default IconButton;

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /