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