forked from github/plane
chore: option to switch access of a comment (#2116)
This commit is contained in:
parent
d26aa1b2da
commit
81436902a3
@ -38,7 +38,7 @@ export const InboxIssueActivity: React.FC<Props> = ({ issueDetails }) => {
|
||||
: null
|
||||
);
|
||||
|
||||
const handleCommentUpdate = async (comment: IIssueComment) => {
|
||||
const handleCommentUpdate = async (commentId: string, data: Partial<IIssueComment>) => {
|
||||
if (!workspaceSlug || !projectId || !inboxIssueId) return;
|
||||
|
||||
await issuesService
|
||||
@ -46,8 +46,8 @@ export const InboxIssueActivity: React.FC<Props> = ({ issueDetails }) => {
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
inboxIssueId as string,
|
||||
comment.id,
|
||||
comment,
|
||||
commentId,
|
||||
data,
|
||||
user
|
||||
)
|
||||
.then(() => mutateIssueActivity());
|
||||
|
@ -15,14 +15,16 @@ import { IIssueActivity, IIssueComment } from "types";
|
||||
|
||||
type Props = {
|
||||
activity: IIssueActivity[] | undefined;
|
||||
handleCommentUpdate: (comment: IIssueComment) => Promise<void>;
|
||||
handleCommentUpdate: (commentId: string, data: Partial<IIssueComment>) => Promise<void>;
|
||||
handleCommentDelete: (commentId: string) => Promise<void>;
|
||||
showAccessSpecifier?: boolean;
|
||||
};
|
||||
|
||||
export const IssueActivitySection: React.FC<Props> = ({
|
||||
activity,
|
||||
handleCommentUpdate,
|
||||
handleCommentDelete,
|
||||
showAccessSpecifier = false,
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
@ -131,10 +133,11 @@ export const IssueActivitySection: React.FC<Props> = ({
|
||||
return (
|
||||
<div key={activityItem.id} className="mt-4">
|
||||
<CommentCard
|
||||
workspaceSlug={workspaceSlug as string}
|
||||
comment={activityItem as IIssueComment}
|
||||
onSubmit={handleCommentUpdate}
|
||||
handleCommentDeletion={handleCommentDelete}
|
||||
onSubmit={handleCommentUpdate}
|
||||
showAccessSpecifier={showAccessSpecifier}
|
||||
workspaceSlug={workspaceSlug as string}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -7,7 +7,7 @@ import { ChatBubbleLeftEllipsisIcon, CheckIcon, XMarkIcon } from "@heroicons/rea
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
// ui
|
||||
import { CustomMenu } from "components/ui";
|
||||
import { CustomMenu, Icon } from "components/ui";
|
||||
import { CommentReaction } from "components/issues";
|
||||
import { TipTapEditor } from "components/tiptap";
|
||||
// helpers
|
||||
@ -16,17 +16,19 @@ import { timeAgo } from "helpers/date-time.helper";
|
||||
import type { IIssueComment } from "types";
|
||||
|
||||
type Props = {
|
||||
workspaceSlug: string;
|
||||
comment: IIssueComment;
|
||||
onSubmit: (comment: IIssueComment) => void;
|
||||
handleCommentDeletion: (comment: string) => void;
|
||||
onSubmit: (commentId: string, data: Partial<IIssueComment>) => void;
|
||||
showAccessSpecifier?: boolean;
|
||||
workspaceSlug: string;
|
||||
};
|
||||
|
||||
export const CommentCard: React.FC<Props> = ({
|
||||
comment,
|
||||
workspaceSlug,
|
||||
onSubmit,
|
||||
handleCommentDeletion,
|
||||
onSubmit,
|
||||
showAccessSpecifier = false,
|
||||
workspaceSlug,
|
||||
}) => {
|
||||
const { user } = useUser();
|
||||
|
||||
@ -45,11 +47,11 @@ export const CommentCard: React.FC<Props> = ({
|
||||
defaultValues: comment,
|
||||
});
|
||||
|
||||
const onEnter = (formData: IIssueComment) => {
|
||||
const onEnter = (formData: Partial<IIssueComment>) => {
|
||||
if (isSubmitting) return;
|
||||
setIsEditing(false);
|
||||
|
||||
onSubmit(formData);
|
||||
onSubmit(comment.id, formData);
|
||||
|
||||
editorRef.current?.setEditorValue(formData.comment_html);
|
||||
showEditorRef.current?.setEditorValue(formData.comment_html);
|
||||
@ -99,7 +101,7 @@ export const CommentCard: React.FC<Props> = ({
|
||||
: comment.actor_detail.display_name}
|
||||
</div>
|
||||
<p className="mt-0.5 text-xs text-custom-text-200">
|
||||
Commented {timeAgo(comment.created_at)}
|
||||
commented {timeAgo(comment.created_at)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="issue-comments-section p-0">
|
||||
@ -137,7 +139,15 @@ export const CommentCard: React.FC<Props> = ({
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div className={`${isEditing ? "hidden" : ""}`}>
|
||||
<div className={`relative ${isEditing ? "hidden" : ""}`}>
|
||||
{showAccessSpecifier && (
|
||||
<div className="absolute top-1 right-1.5 z-[1] text-custom-text-300">
|
||||
<Icon
|
||||
iconName={comment.access === "INTERNAL" ? "lock" : "public"}
|
||||
className="!text-xs"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<TipTapEditor
|
||||
workspaceSlug={workspaceSlug as string}
|
||||
ref={showEditorRef}
|
||||
@ -151,13 +161,44 @@ export const CommentCard: React.FC<Props> = ({
|
||||
</div>
|
||||
{user?.id === comment.actor && (
|
||||
<CustomMenu ellipsis>
|
||||
<CustomMenu.MenuItem onClick={() => setIsEditing(true)}>Edit</CustomMenu.MenuItem>
|
||||
<CustomMenu.MenuItem
|
||||
onClick={() => setIsEditing(true)}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<Icon iconName="edit" />
|
||||
Edit comment
|
||||
</CustomMenu.MenuItem>
|
||||
{showAccessSpecifier && (
|
||||
<>
|
||||
{comment.access === "INTERNAL" ? (
|
||||
<CustomMenu.MenuItem
|
||||
renderAs="button"
|
||||
onClick={() => onSubmit(comment.id, { access: "EXTERNAL" })}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<Icon iconName="public" />
|
||||
Switch to public comment
|
||||
</CustomMenu.MenuItem>
|
||||
) : (
|
||||
<CustomMenu.MenuItem
|
||||
renderAs="button"
|
||||
onClick={() => onSubmit(comment.id, { access: "INTERNAL" })}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<Icon iconName="lock" />
|
||||
Switch to private comment
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<CustomMenu.MenuItem
|
||||
onClick={() => {
|
||||
handleCommentDeletion(comment.id);
|
||||
}}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
Delete
|
||||
<Icon iconName="delete" />
|
||||
Delete comment
|
||||
</CustomMenu.MenuItem>
|
||||
</CustomMenu>
|
||||
)}
|
||||
|
@ -77,7 +77,7 @@ export const IssueMainContent: React.FC<Props> = ({
|
||||
: null
|
||||
);
|
||||
|
||||
const handleCommentUpdate = async (comment: IIssueComment) => {
|
||||
const handleCommentUpdate = async (commentId: string, data: Partial<IIssueComment>) => {
|
||||
if (!workspaceSlug || !projectId || !issueId) return;
|
||||
|
||||
await issuesService
|
||||
@ -85,8 +85,8 @@ export const IssueMainContent: React.FC<Props> = ({
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
issueId as string,
|
||||
comment.id,
|
||||
comment,
|
||||
commentId,
|
||||
data,
|
||||
user
|
||||
)
|
||||
.then(() => mutateIssueActivity());
|
||||
@ -222,6 +222,7 @@ export const IssueMainContent: React.FC<Props> = ({
|
||||
activity={issueActivity}
|
||||
handleCommentUpdate={handleCommentUpdate}
|
||||
handleCommentDelete={handleCommentDelete}
|
||||
showAccessSpecifier={projectDetails && projectDetails.is_deployed}
|
||||
/>
|
||||
<AddComment
|
||||
onSubmit={handleAddComment}
|
||||
|
@ -5,6 +5,7 @@ import issuesService from "services/issues.service";
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
import useToast from "hooks/use-toast";
|
||||
import useProjectDetails from "hooks/use-project-details";
|
||||
// components
|
||||
import { AddComment, IssueActivitySection } from "components/issues";
|
||||
// types
|
||||
@ -22,6 +23,7 @@ export const PeekOverviewIssueActivity: React.FC<Props> = ({ workspaceSlug, issu
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { user } = useUser();
|
||||
const { projectDetails } = useProjectDetails();
|
||||
|
||||
const { data: issueActivity, mutate: mutateIssueActivity } = useSWR(
|
||||
workspaceSlug && issue ? PROJECT_ISSUES_ACTIVITY(issue.id) : null,
|
||||
@ -30,18 +32,11 @@ export const PeekOverviewIssueActivity: React.FC<Props> = ({ workspaceSlug, issu
|
||||
: null
|
||||
);
|
||||
|
||||
const handleCommentUpdate = async (comment: IIssueComment) => {
|
||||
const handleCommentUpdate = async (commentId: string, data: Partial<IIssueComment>) => {
|
||||
if (!workspaceSlug || !issue) return;
|
||||
|
||||
await issuesService
|
||||
.patchIssueComment(
|
||||
workspaceSlug as string,
|
||||
issue.project,
|
||||
issue.id,
|
||||
comment.id,
|
||||
comment,
|
||||
user
|
||||
)
|
||||
.patchIssueComment(workspaceSlug as string, issue.project, issue.id, commentId, data, user)
|
||||
.then(() => mutateIssueActivity());
|
||||
};
|
||||
|
||||
@ -80,9 +75,13 @@ export const PeekOverviewIssueActivity: React.FC<Props> = ({ workspaceSlug, issu
|
||||
activity={issueActivity}
|
||||
handleCommentUpdate={handleCommentUpdate}
|
||||
handleCommentDelete={handleCommentDelete}
|
||||
showAccessSpecifier={projectDetails && projectDetails.is_deployed}
|
||||
/>
|
||||
<div className="mt-4">
|
||||
<AddComment onSubmit={handleAddComment} />
|
||||
<AddComment
|
||||
onSubmit={handleAddComment}
|
||||
showAccessSpecifier={projectDetails && projectDetails.is_deployed}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -204,7 +204,7 @@ class ProjectIssuesServices extends APIService {
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
commentId: string,
|
||||
data: IIssueComment,
|
||||
data: Partial<IIssueComment>,
|
||||
user: ICurrentUserResponse | undefined
|
||||
): Promise<any> {
|
||||
return this.patch(
|
||||
|
Loading…
Reference in New Issue
Block a user