回归代码评审issue

This commit is contained in:
何童崇 2022-08-11 16:19:19 +08:00
parent 732afc85f5
commit 08d59f4612
6 changed files with 217 additions and 124 deletions

View File

@ -31,7 +31,7 @@
<script src="%PUBLIC_URL%js/editormd/editormd.min.js"></script>
<script src="%PUBLIC_URL%js/codemirror/merge/merge.js"></script>
<script src="https://gw.alipayobjects.com/os/lib/moment/2.29.4/moment.js"></script>
<script src="https://gw.alipayobjects.com/os/lib/alipay/alex/2.0.11/bundle/alex.all.global.js"></script>
<script src="https://gw.alipayobjects.com/os/lib/alipay/alex/2.0.12-0/bundle/alex.all.global.js"></script>
<%= htmlWebpackPlugin.tags.bodyTags %>
</body>

View File

@ -118,7 +118,7 @@ export function timeAgo(backDate) {
var days = Math.floor(time / (1000 * 60 * 60 * 24));
var hours = Math.floor((time % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
var minutes = Math.floor((time % (1000 * 60 * 60)) / (1000 * 60));
var seconds = Math.floor((time % (1000 * 60 * 60)) / 1000);
// var seconds = Math.floor((time % (1000 * 60 * 60)) / 1000);
if (time <= 0) {
return "刚刚";
}
@ -129,10 +129,10 @@ export function timeAgo(backDate) {
return hours + "小时前";
}
if (minutes) {
return minutes + "分前";
}
if (seconds) {
return seconds + "秒前";
return minutes + "分钟前";
}
// if (seconds) {
// return seconds + "秒前";
// }
return "刚刚";
}

View File

@ -22,9 +22,9 @@ import {
Popconfirm,
Checkbox,
} from "antd";
// @ts-ignore
import { Link } from "react-router-dom";
import { usePersistFn } from "ahooks";
import { getImageUrl } from "educoder";
import { getImageUrl, timeAgo } from "educoder";
import { camelcaseToUnderscore } from '../utils/camelcase-convert';
import "./style.module.less";
import { useAcr, useNote, useGlobal } from "../../model";
@ -42,21 +42,21 @@ const noteType = {
export const Commenting = (props) => {
const { user } = useGlobal();
const { addComment } = useNote();
const { toVersion,stDiff } = useAcr();
const { toVersion, stDiff } = useAcr();
console.log('Commenting--props');
console.log(props);
// console.log(stDiff);
const submit = usePersistFn(async (note, type) => {
try {
console.log(note,type);
console.log(note, type);
await addComment({
note,
diffId: toVersion.id,
line_code: props.lineCode,
discussionId: props.discussionId,
parent_id: props.id,
path: props.path,
type,
diff:stDiff,
type: type || 'comment',
diff:props.id?null: stDiff,
});
props.onClose && props.onClose();
} catch (e) {
@ -90,136 +90,186 @@ export const Commenting = (props) => {
}
return (
<div className="arc-container">
<div
className={`arc-commenting-container ${props.noPadding ? "no-padding" : ""
}`}
>
<div className="avatar">
<Tooltip title={user.name}>
<Avatar src={ user && user.image_url
? getImageUrl(`/${user.image_url}`)
: "images/avatars/User/b"} size={24} />
</Tooltip>
</div>
<Input.TextArea
autoFocus
placeholder="请输入描述信息"
value={note}
onChange={(e) => setNote(e.target.value)}
style={{ height: 150 }}
/>
<div className="action">
<div>
<Button
type="primary"
onClick={onSubmit}
disabled={!note.trim()}
style={{ marginRight: 8 }}
// <div className="arc-container">
<div
className={`arc-commenting-container ${props.noPadding ? "no-padding" : ""}`}>
<div className="avatar">
<Tooltip title={user.name}>
<Avatar src={user && user.image_url
? getImageUrl(`/${user.image_url}`)
: "images/avatars/User/b"} size={24} />
</Tooltip>
</div>
<Input.TextArea
autoFocus
placeholder="请输入评论信息"
value={note}
onChange={(e) => setNote(e.target.value)}
style={{ height: 150 }}
/>
<div className="action">
<div>
<Button
type="primary"
onClick={onSubmit}
disabled={!note.trim()}
style={{ marginRight: 8 }}
>
评论
</Button>
{note.trim() ? (
<Popconfirm
title="你确定要取消?"
okText="确定"
cancelText="取消"
onConfirm={props.onClose}
>
评论
</Button>
{note.trim() ? (
<Popconfirm
title="你确定要取消?"
okText="确定"
cancelText="取消"
onConfirm={props.onClose}
>
<Button>取消</Button>
</Popconfirm>
) : (
<Button onClick={props.onClose}>取消</Button>
)}
</div>
<div>
{replyNote && (
<div className="reply-note">Reply to {replyNote.author.name}</div>
)}
</div>
<Button>取消</Button>
</Popconfirm>
) : (
<Button onClick={props.onClose}>取消</Button>
)}
</div>
<div>
{replyNote && (
<div className="reply-note">Reply to {replyNote.author.name}</div>
)}
</div>
</div>
</div>
// </div>
);
};
const ReplyItem = memo(({note, isChild}) => {
const { user } = useGlobal();
const { deleteComment } = useNote();
const [visible, setVisible] = useState(false);
return <div className={`reply-item ${isChild?'reply-item-child':''}`}>
<div className="reply-item-head">
<Link
to={`/${note.user && note.user.login}`}
className="show-user-link"
>
<Avatar src={note.user && note.user.imageUrl
? getImageUrl(`/${note.user.imageUrl}`)
: "images/avatars/User/b"} size={24} />
</Link>
<Link
to={`/${note.user && note.user.login}`}
className="show-user-link color-black ml10 fwb"
>
{note.user && note.user.username}
</Link> <span className="show-user-link color-black">{timeAgo(note.createdAt)}</span> 发表评论
<span className="btn-right">
{user &&
(user.admin ||
user.login === note.user.login) ? (
<Popconfirm
placement="bottom"
title={"确定要删除当前评论吗?"}
okText="是"
cancelText="否"
onConfirm={() => deleteComment(note.id)}
>
<Button type="link">
<i className="iconfont icon-shanchu3 font-15 color-grey-6 mr5 ver-middle"></i>
<span className="font-12 color-grey-6">删除</span>
</Button>
</Popconfirm>
) : (
""
)}
{!isChild ? <Button
type="link"
className="ml-10"
onClick={() => setVisible(true)}
>
<i className="iconfont icon-huifu1 font-15 color-grey-6 mr5 ver-middle"></i>
<span className="font-12 color-grey-6">回复</span>
</Button> : ''}
</span>
</div>
<div className="reply-item-content">{note.note}</div>
{visible && <Commenting style={{paddingLeft:'20px'}} onClose={() => { setVisible(false) }} {...note} />}
</div>
})
export const DiscussionItem = memo((props) => {
const { noteId } = props;
const { commentPack } = useNote();
const { commentPack, deleteComment } = useNote();
const [commenting, setCommenting] = useState(false);
const [commentReply, setCommentReply] = useState();
// const [commenting, setCommenting] = useState(false);
// const [commentReply, setCommentReply] = useState();
const replyIdSet = commentPack.noteIdToReplyIdSet.get(noteId);
const replyIds = replyIdSet
? Array.from(replyIdSet).sort((a, b) => a - b)
: null;
const pending = (function () {
if (!commentPack.hasPendingReview) {
return false;
}
const reviewId = commentPack.noteIdToReviewId.get(noteId);
if (!reviewId) return false;
const review = reviewId ? commentPack.reviewIdToReview.get(reviewId) : null;
if (!review) return false;
return review.pending;
})();
const hasPendingNote = (function () {
if (pending) return true;
if (replyIds) {
for (const replyId of replyIds) {
const reviewId = commentPack.noteIdToReviewId.get(replyId);
if (reviewId && commentPack.reviewIdToReview.get(reviewId) && commentPack.reviewIdToReview.get(reviewId).pending) {
return true;
}
}
}
return false;
})();
// const pending = (function () {
// if (!commentPack.hasPendingReview) {
// return false;
// }
// const reviewId = commentPack.noteIdToReviewId.get(noteId);
// if (!reviewId) return false;
// const review = reviewId ? commentPack.reviewIdToReview.get(reviewId) : null;
// if (!review) return false;
// return review.pending;
// })();
// const hasPendingNote = (function () {
// if (pending) return true;
// if (replyIds) {
// for (const replyId of replyIds) {
// const reviewId = commentPack.noteIdToReviewId.get(replyId);
// if (reviewId && commentPack.reviewIdToReview.get(reviewId) && commentPack.reviewIdToReview.get(reviewId).pending) {
// return true;
// }
// }
// }
// return false;
// })();
const handleReply = (replyNote) => {
if (replyNote) {
setCommentReply(replyNote);
}
setCommenting(true);
};
// const handleReply = (replyNote) => {
// if (replyNote) {
// setCommentReply(replyNote);
// }
// setCommenting(true);
// };
// useEffect(() => {
// if (!commenting) {
// setCommentReply(undefined);
// }
// }, [commenting]);
useEffect(() => {
if (!commenting) {
setCommentReply(undefined);
}
}, [commenting]);
const note = commentPack.noteIdToNote.get(noteId);
if (!note) return null;
console.log('note');
console.log(note);
return (
<div className="arc-container">
<div>
{/* {note.type === noteType.problem && (
{/* <div>
{note.type === noteType.problem && (
<Fragment>
{note.state === "resolved" && !pending && "已解决"}
{note.state === "opened" && !pending && "待解决"}
{pending && "需要回应"}
</Fragment>
)} */}
</div>
<div style={{ paddingLeft: 16 }}>
<div>{note.author.name} 发表评论</div>
<div>{note.note}</div>
</div>
)}
</div> */}
<ReplyItem note={note}/>
{replyIds && (
<div style={{ paddingLeft: 32 }}>
{replyIds.map((replyId) => {
<div style={{ paddingLeft: 50 }}>
{replyIds.reverse().map((replyId) => {
const note = commentPack.noteIdToNote.get(replyId);
if (!note) return null;
return (
<div key={replyId}>
<div>{note.author.name} 发表评论</div>
<div>{note.note}</div>
</div>
);
return <ReplyItem note={note} key={replyId} isChild={true} />
})}
</div>
)}
@ -248,7 +298,6 @@ export const Menubar = memo((props) => {
};
}, [isFullscreen]);
const { project, pr } = useGlobal();
const { versions, fromVersion, toVersion, updateQuery } = useAcr();
const idToVerbose = new Map();

View File

@ -1,8 +1,32 @@
.arc-container {
border: solid 1px #eee;
border-left: none;
border-right: none;
// border: solid 1px #eee;
// border-left: none;
// border-right: none;
.reply-item{
padding:16px 16px 20px;
&.reply-item-child{
margin-top:-10px;
padding:5px 0;
}
}
.reply-item-head{
line-height: 30px;
color: #666;
}
.btn-right{
float: right;
}
.reply-item-content{
padding-left:35px;
line-height: 1.4;
}
.show-user-link{
font-weight: 600;
}
}
.arc-menubar {

View File

@ -96,6 +96,7 @@ export const prService = {
item.author.username=item.user.name;
item.type='Common';
item.stDiff=item.diff;
item.discussionId=item.parentId
// item.lineType='old';
}
return journals;
@ -172,6 +173,11 @@ export const prService = {
return commentData;
},
async deleteComment(projectsId, owner, mergeId,id){
const res = await axios.delete(`/v1/${owner}/${projectsId}/pulls/${mergeId}/journals/${id}.json`);
return res.data;
},
async createReview(projectsId, owner, mergeId, data) {
const res = await axios.post(`/v1/${owner}/${projectsId}/pulls/${mergeId}/reviews.json`, data);
console.log('createReview--data');

View File

@ -176,8 +176,7 @@ const useCommentPack = (projectId, prId) => {
}
fetchingRef.current = true;
//
// const pack = await prService.getCommentPack(projectsId, owner, mergeId);
const commentPack = await prService.getCommentPack(projectsId, owner, mergeId,{path:filePath});
const commentPack = await prService.getCommentPack(projectsId, owner, mergeId,{path:filePath,is_full:true});
const reviews = await prService.getReviews(projectsId, owner, mergeId);
// debugger
for (const review of reviews) {
@ -250,6 +249,11 @@ const useCommentPack = (projectId, prId) => {
setUpdateFlag({});
}
function manualRemoveNote(id) {
noteIdToNote.delete(id);
setUpdateFlag({});
}
function manualUpdateNote(note) {
const reviewId = noteIdToReviewId.get(note.id);
const review = reviewId ? reviewIdToReview.get(reviewId) : null;
@ -310,6 +314,7 @@ const useCommentPack = (projectId, prId) => {
doRefresh,
manualUpdateReview,
manualAddNote,
manualRemoveNote,
manualUpdateNote,
pendingReviewRef,
getRelatedFilePathByNoteId,
@ -346,6 +351,14 @@ const [NoteProvider, useNote] = createContainer(() => {
commentPack.manualAddNote(note, commentPack.pendingReviewRef.current);
}
async function deleteComment(id){
const res = await prService.deleteComment(projectsId, owner, mergeId,id);
if(res.status==0){
commentPack.manualRemoveNote(id);
}
console.log(res);
}
async function commitReview(body) {
if (!commentPack.hasPendingReview) {
let reviewData = {
@ -364,6 +377,7 @@ const [NoteProvider, useNote] = createContainer(() => {
commentPack,
...commentPack,
addComment,
deleteComment,
commitReview,
prId,
activateRef,
@ -570,9 +584,9 @@ const [AcrProvider, useAcr] = createContainer(() => {
}
);
// useEffect(() => {
// commentPack.doRefresh();
// }, []);
useEffect(() => {
commentPack.doRefresh();
}, []);
async function getDiffById(diffId) {
let thisStDiff= await prService.getDiffById(projectsId, owner, mergeId, toVersion.id,
@ -641,7 +655,7 @@ const [AcrProvider, useAcr] = createContainer(() => {
export const Provider = (props) => {
const match = props.match;
let project = {};
let user = mockService['/api/v3/user'];
// let user = mockService['/api/v3/user'];
if (props.projectDetail) {
let details = props.projectDetail;
project = {
@ -650,12 +664,12 @@ export const Provider = (props) => {
pathWithNamespace: details.full_name,
}
}
let user={};
if (props.current_user) {
let current_user = props.current_user;
user = { ...user, ...current_user, avatar_url: current_user.image_url, name: current_user.username, };
user = { ...current_user, avatar_url: current_user.image_url, name: current_user.username, };
}
console.log(user);
console.log('user');
return (
<ContextProps.Provider value={{
project, user, params: match.params