Skip to content

Commit 7f559bd

Browse files
committed
feat(mr): update desc.
1 parent 1dcda7d commit 7f559bd

File tree

12 files changed

+190
-47
lines changed

12 files changed

+190
-47
lines changed

src/codingServer.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
ICreateMRResp,
1616
IBranchListResp,
1717
IMemberListResp,
18+
IMRContentResp,
1819
} from 'src/typings/respResult';
1920
import { PromiseAdapter, promiseFromEvent, parseQuery, parseCloneUrl } from 'src/common/utils';
2021
import { GitService } from 'src/common/gitService';
@@ -660,6 +661,30 @@ export class CodingServer {
660661
return fulfilled;
661662
}
662663

664+
public async updateMRDesc(iid: string, content: string) {
665+
try {
666+
const { repoApiPrefix } = await this.getApiPrefix();
667+
const resp: IMRContentResp = await got
668+
.put(`${repoApiPrefix}/merge/${iid}/update-content`, {
669+
form: {
670+
content,
671+
},
672+
searchParams: {
673+
access_token: this._session?.accessToken,
674+
},
675+
})
676+
.json();
677+
678+
if (resp.code) {
679+
return Promise.reject(resp);
680+
}
681+
682+
return resp;
683+
} catch (e) {
684+
return Promise.reject(e);
685+
}
686+
}
687+
663688
get loggedIn() {
664689
return this._loggedIn;
665690
}

src/panel.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,14 @@ export class Panel {
162162
} catch (err) {}
163163
break;
164164
}
165+
case `mr.update.desc`: {
166+
try {
167+
const [iid, content] = args;
168+
const resp = await codingSrv.updateMRDesc(iid, content);
169+
this.broadcast(command, [iid, resp.data]);
170+
} catch (e) {}
171+
break;
172+
}
165173
default:
166174
break;
167175
}

src/typings/commonTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export interface IMRWebViewDetail {
3636
repoInfo: IRepoInfo;
3737
data: IMRDetail & {
3838
loading: boolean;
39+
editingDesc: boolean;
3940
};
4041
user: IUserItem;
4142
}

src/typings/respResult.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,3 +229,12 @@ export interface IMemberItem {
229229
}
230230

231231
export interface IMemberListResp extends IListResponse<IMemberItem> {}
232+
233+
export interface IMRContent {
234+
body: string;
235+
body_plan: string;
236+
}
237+
238+
export interface IMRContentResp extends CodingResponse {
239+
data: IMRContent;
240+
}

webviews/App.tsx

Lines changed: 62 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
import React, { useRef, useState } from 'react';
2-
1+
import React, { FormEvent, useRef, useState } from 'react';
32
import { view } from '@risingstack/react-easy-state';
3+
44
import appStore from 'webviews/store/appStore';
55
import persistDataHook from 'webviews/hooks/persistDataHook';
66
import Activities from 'webviews/components/Activities';
77
import Reviewers from 'webviews/components/Reviewers';
88
import messageTransferHook from 'webviews/hooks/messageTransferHook';
9+
import EditButton from 'webviews/components/EditButton';
10+
import { requestUpdateMRContent } from 'webviews/service/mrService';
11+
912
import {
1013
EmptyWrapper,
1114
TitleWrapper,
@@ -17,13 +20,17 @@ import {
1720
Empty,
1821
BranchName,
1922
EditBtn,
20-
} from 'webviews/styles/MROverview';
23+
OperationBtn,
24+
SectionTitle,
25+
} from 'webviews/app.styles';
2126

2227
function App() {
23-
const { currentMR, updateMRTitle } = appStore;
24-
const [isEditing, setEditing] = useState(false);
28+
const { currentMR, updateMRTitle, toggleUpdatingDesc } = appStore;
29+
const [isEditingTitle, setEditingTitle] = useState(false);
2530
const [title, setTitle] = useState(currentMR?.data?.merge_request?.title);
2631
const inputRef = useRef<HTMLInputElement | null>(null);
32+
const [desc, setDesc] = useState(``);
33+
2734
const { repoInfo, data } = currentMR;
2835
const { merge_request: mergeRequest } = data || {};
2936

@@ -33,7 +40,7 @@ function App() {
3340
const handleKeyDown = async (event: any) => {
3441
if (event.key === 'Enter') {
3542
await updateMRTitle(title);
36-
setEditing(false);
43+
setEditingTitle(false);
3744
}
3845
};
3946

@@ -47,10 +54,23 @@ function App() {
4754
};
4855

4956
const handleEdit = () => {
50-
setEditing(true);
57+
setEditingTitle(true);
5158
inputRef.current?.focus();
5259
};
5360

61+
const onEditDesc = () => {
62+
toggleUpdatingDesc();
63+
setDesc(currentMR.data.merge_request.body_plan);
64+
};
65+
66+
const onChangeDesc = (ev: FormEvent<HTMLTextAreaElement>) => {
67+
setDesc(ev.currentTarget.value);
68+
};
69+
70+
const onSaveDesc = async () => {
71+
await requestUpdateMRContent(currentMR.iid, desc);
72+
};
73+
5474
if (!currentMR.iid) {
5575
return <EmptyWrapper>Please select an merge request first.</EmptyWrapper>;
5676
}
@@ -62,13 +82,13 @@ function App() {
6282
return (
6383
<div>
6484
<TitleWrapper>
65-
{isEditing ? (
85+
{isEditingTitle ? (
6686
<input
6787
type='text'
6888
value={title}
6989
ref={(ref) => (inputRef.current = ref)}
70-
onBlur={() => setEditing(false)}
71-
onFocus={() => setEditing(true)}
90+
onBlur={() => setEditingTitle(false)}
91+
onFocus={() => setEditingTitle(true)}
7292
onKeyDown={handleKeyDown}
7393
onChange={handleTitleChange}
7494
/>
@@ -91,14 +111,39 @@ function App() {
91111
</Row>
92112
<BodyWrap>
93113
<Body>
94-
<h3>Description</h3>
95-
<Desc>
96-
{mergeRequest?.body ? (
97-
<div dangerouslySetInnerHTML={{ __html: mergeRequest?.body }} />
98-
) : (
99-
<Empty>Empty</Empty>
114+
<SectionTitle>
115+
Description
116+
{!currentMR.data.editingDesc && <EditButton onClick={onEditDesc} />}
117+
{currentMR.data.editingDesc && (
118+
<>
119+
<OperationBtn className={`colored`} onClick={onSaveDesc}>
120+
Save
121+
</OperationBtn>
122+
<OperationBtn className={`colored secondary`} onClick={() => toggleUpdatingDesc()}>
123+
Cancel
124+
</OperationBtn>
125+
</>
100126
)}
101-
</Desc>
127+
</SectionTitle>
128+
{!currentMR.data.editingDesc && (
129+
<Desc>
130+
{mergeRequest?.body ? (
131+
<div dangerouslySetInnerHTML={{ __html: mergeRequest?.body }} />
132+
) : (
133+
<Empty>This MR has no description.</Empty>
134+
)}
135+
</Desc>
136+
)}
137+
{currentMR.data.editingDesc && (
138+
<textarea
139+
name='desc'
140+
id='mr-desc'
141+
cols={30}
142+
rows={20}
143+
value={desc}
144+
onChange={onChangeDesc}
145+
/>
146+
)}
102147
<h3>Activities</h3>
103148
<Activities />
104149
</Body>

webviews/styles/MROverview.tsx renamed to webviews/app.styles.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,11 @@ export const Empty = styled.div`
5353
export const BranchName = styled.code`
5454
margin: 0 1ex;
5555
`;
56+
57+
export const OperationBtn = styled.button`
58+
margin-left: 1em;
59+
`;
60+
61+
export const SectionTitle = styled.h3`
62+
line-height: 28px;
63+
`;

webviews/components/EditButton.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React from 'react';
2+
import styled from 'styled-components';
3+
4+
import EditIcon from 'webviews/assets/edit.svg';
5+
6+
const IconButton = styled.button`
7+
border: unset;
8+
background: unset;
9+
width: 20px;
10+
height: 20px;
11+
margin-left: 1ex;
12+
padding: 2px 0;
13+
vertical-align: middle;
14+
15+
:hover {
16+
cursor: pointer;
17+
}
18+
19+
:focus {
20+
outline: 1px solid var(--vscode-focusBorder);
21+
outline-offset: 2px;
22+
}
23+
24+
svg path {
25+
fill: var(--vscode-foreground);
26+
}
27+
`;
28+
29+
interface Props {
30+
onClick?: () => void;
31+
}
32+
33+
const EditButton = ({ onClick = () => null }: Props) => {
34+
return (
35+
<IconButton onClick={onClick}>
36+
<EditIcon />
37+
</IconButton>
38+
);
39+
};
40+
41+
export default EditButton;

webviews/components/Reviewers.tsx

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { view } from '@risingstack/react-easy-state';
44

55
import appStore from 'webviews/store/appStore';
66
import { Avatar, AuthorLink } from 'webviews/components/User';
7-
import EditIcon from 'webviews/assets/edit.svg';
7+
import EditButton from 'webviews/components/EditButton';
88

99
const Title = styled.div`
1010
margin-top: 15px;
@@ -15,36 +15,14 @@ const FlexCenter = styled.div`
1515
display: flex;
1616
align-items: center;
1717
`;
18-
const Item = styled(FlexCenter)`
18+
const Reviewer = styled(FlexCenter)`
1919
padding: 5px 0;
2020
justify-content: space-between;
2121
2222
a:first-child {
2323
margin-right: 5px;
2424
}
2525
`;
26-
const IconButton = styled.button`
27-
border: unset;
28-
background: unset;
29-
width: 20px;
30-
height: 20px;
31-
margin-left: 1ex;
32-
padding: 2px 0;
33-
vertical-align: middle;
34-
35-
:hover {
36-
cursor: pointer;
37-
}
38-
39-
:focus {
40-
outline: 1px solid var(--vscode-focusBorder);
41-
outline-offset: 2px;
42-
}
43-
44-
svg path {
45-
fill: var(--vscode-foreground);
46-
}
47-
`;
4826

4927
function Reviewers() {
5028
const { reviewers, currentMR } = appStore;
@@ -61,19 +39,17 @@ function Reviewers() {
6139
<div>
6240
<Title>
6341
Reviewers
64-
<IconButton onClick={onUpdateReviewer}>
65-
<EditIcon />
66-
</IconButton>
42+
<EditButton onClick={onUpdateReviewer} />
6743
</Title>
6844
{allReviewers.map((r) => {
6945
return (
70-
<Item key={r.reviewer.global_key}>
46+
<Reviewer key={r.reviewer.global_key}>
7147
<FlexCenter>
7248
<Avatar for={r.reviewer} />
7349
<AuthorLink for={r.reviewer} />
7450
</FlexCenter>
7551
{r.value === 100 && `👍`}
76-
</Item>
52+
</Reviewer>
7753
);
7854
})}
7955
</div>

webviews/hooks/messageTransferHook.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useEffect } from 'react';
22
import appStore from 'webviews/store/appStore';
33
import { actions } from 'webviews/store/constants';
4+
import { IMRContent } from 'src/typings/respResult';
45

56
export default function messageTransferHook() {
67
useEffect(() => {
@@ -11,6 +12,8 @@ export default function messageTransferHook() {
1112
updateMRReviewers,
1213
updateMRComments,
1314
toggleMRLoading,
15+
updateMRDesc,
16+
toggleUpdatingDesc,
1417
} = appStore;
1518
const { command, res } = ev?.data;
1619

@@ -35,6 +38,12 @@ export default function messageTransferHook() {
3538
res && updateMRReviewers(res);
3639
break;
3740
}
41+
case actions.MR_UPDATE_DESC: {
42+
const [iid, resp] = res as [string, IMRContent];
43+
updateMRDesc(iid, resp);
44+
toggleUpdatingDesc();
45+
break;
46+
}
3847
default:
3948
break;
4049
}

webviews/service/mrService.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { vscode } from 'webviews/constants/vscode';
2+
import { actions } from 'webviews/store/constants';
3+
4+
export const requestUpdateMRContent = async (iid: string, content: string) => {
5+
await vscode.postMessage({
6+
command: actions.MR_UPDATE_DESC,
7+
args: [iid, content],
8+
});
9+
};

0 commit comments

Comments
 (0)