fix: issue activity/ comments disable and showAccessSpecifier logic.

This commit is contained in:
Prateek Shourya 2024-01-22 15:51:21 +05:30
parent 1873b445ed
commit 81b46e72c2
11 changed files with 152 additions and 135 deletions

View File

@ -9,15 +9,13 @@ import { IssueCommentCard } from "./comments/comment-card";
import { TActivityOperations } from "./root"; import { TActivityOperations } from "./root";
type TIssueActivityCommentRoot = { type TIssueActivityCommentRoot = {
workspaceSlug: string;
projectId: string;
issueId: string; issueId: string;
activityOperations: TActivityOperations; activityOperations: TActivityOperations;
disabled: boolean; showAccessSpecifier?: boolean;
}; };
export const IssueActivityCommentRoot: FC<TIssueActivityCommentRoot> = observer((props) => { export const IssueActivityCommentRoot: FC<TIssueActivityCommentRoot> = observer((props) => {
const { workspaceSlug, projectId, issueId, activityOperations, disabled } = props; const { issueId, activityOperations, showAccessSpecifier } = props;
// hooks // hooks
const { const {
activity: { getActivityCommentByIssueId }, activity: { getActivityCommentByIssueId },
@ -32,21 +30,14 @@ export const IssueActivityCommentRoot: FC<TIssueActivityCommentRoot> = observer(
{activityComments.map((activityComment, index) => {activityComments.map((activityComment, index) =>
activityComment.activity_type === "COMMENT" ? ( activityComment.activity_type === "COMMENT" ? (
<IssueCommentCard <IssueCommentCard
workspaceSlug={workspaceSlug}
projectId={projectId}
issueId={issueId}
commentId={activityComment.id} commentId={activityComment.id}
activityOperations={activityOperations} activityOperations={activityOperations}
disabled={disabled}
ends={index === 0 ? "top" : index === activityComments.length - 1 ? "bottom" : undefined} ends={index === 0 ? "top" : index === activityComments.length - 1 ? "bottom" : undefined}
showAccessSpecifier={showAccessSpecifier}
/> />
) : activityComment.activity_type === "ACTIVITY" ? ( ) : activityComment.activity_type === "ACTIVITY" ? (
<IssueActivityList <IssueActivityList
workspaceSlug={workspaceSlug}
projectId={projectId}
issueId={issueId}
activityId={activityComment.id} activityId={activityComment.id}
disabled={disabled}
ends={index === 0 ? "top" : index === activityComments.length - 1 ? "bottom" : undefined} ends={index === 0 ? "top" : index === activityComments.length - 1 ? "bottom" : undefined}
/> />
) : ( ) : (

View File

@ -24,11 +24,7 @@ import {
} from "./actions"; } from "./actions";
type TIssueActivityList = { type TIssueActivityList = {
workspaceSlug: string;
projectId: string;
issueId: string;
activityId: string; activityId: string;
disabled: boolean;
ends: "top" | "bottom" | undefined; ends: "top" | "bottom" | undefined;
}; };

View File

@ -6,14 +6,11 @@ import { useIssueDetail } from "hooks/store";
import { IssueActivityList } from "./activity-list"; import { IssueActivityList } from "./activity-list";
type TIssueActivityRoot = { type TIssueActivityRoot = {
workspaceSlug: string;
projectId: string;
issueId: string; issueId: string;
disabled: boolean;
}; };
export const IssueActivityRoot: FC<TIssueActivityRoot> = observer((props) => { export const IssueActivityRoot: FC<TIssueActivityRoot> = observer((props) => {
const { workspaceSlug, projectId, issueId, disabled } = props; const { issueId } = props;
// hooks // hooks
const { const {
activity: { getActivitiesByIssueId }, activity: { getActivitiesByIssueId },
@ -26,11 +23,7 @@ export const IssueActivityRoot: FC<TIssueActivityRoot> = observer((props) => {
<div> <div>
{activityIds.map((activityId, index) => ( {activityIds.map((activityId, index) => (
<IssueActivityList <IssueActivityList
workspaceSlug={workspaceSlug}
projectId={projectId}
issueId={issueId}
activityId={activityId} activityId={activityId}
disabled={disabled}
ends={index === 0 ? "top" : index === activityIds.length - 1 ? "bottom" : undefined} ends={index === 0 ? "top" : index === activityIds.length - 1 ? "bottom" : undefined}
/> />
))} ))}

View File

@ -44,7 +44,7 @@ export const IssueCommentBlock: FC<TIssueCommentBlock> = (props) => {
</> </>
)} )}
<div className="absolute top-2 left-4 w-5 h-5 rounded-full overflow-hidden flex justify-center items-center bg-custom-background-80"> <div className="absolute top-2 left-4 w-5 h-5 rounded-full overflow-hidden flex justify-center items-center bg-custom-background-80">
<MessageSquare className="w-3 h-3" /> <MessageSquare className="w-3 h-3" color="#6b7280" />
</div> </div>
</div> </div>
<div className="w-full relative flex "> <div className="w-full relative flex ">

View File

@ -17,18 +17,14 @@ import { TActivityOperations } from "../root";
const fileService = new FileService(); const fileService = new FileService();
type TIssueCommentCard = { type TIssueCommentCard = {
workspaceSlug: string;
projectId: string;
issueId: string;
commentId: string; commentId: string;
activityOperations: TActivityOperations; activityOperations: TActivityOperations;
disabled: boolean;
ends: "top" | "bottom" | undefined; ends: "top" | "bottom" | undefined;
showAccessSpecifier?: boolean; showAccessSpecifier?: boolean;
}; };
export const IssueCommentCard: FC<TIssueCommentCard> = (props) => { export const IssueCommentCard: FC<TIssueCommentCard> = (props) => {
const { commentId, activityOperations, ends, showAccessSpecifier = true } = props; const { commentId, activityOperations, ends, showAccessSpecifier = false } = props;
// hooks // hooks
const { const {
comment: { getCommentById }, comment: { getCommentById },

View File

@ -1,79 +0,0 @@
import { FC, useRef } from "react";
import { useForm, Controller } from "react-hook-form";
// components
import { LiteTextEditorWithRef } from "@plane/lite-text-editor";
import { Button } from "@plane/ui";
// services
import { FileService } from "services/file.service";
// types
import { TActivityOperations } from "../root";
import { TIssueComment } from "@plane/types";
const fileService = new FileService();
type TIssueCommentCreateUpdate = {
workspaceSlug: string;
activityOperations: TActivityOperations;
disabled: boolean;
};
export const IssueCommentCreateUpdate: FC<TIssueCommentCreateUpdate> = (props) => {
const { workspaceSlug, activityOperations, disabled } = props;
// refs
const editorRef = useRef<any>(null);
// react hook form
const {
handleSubmit,
control,
formState: { isSubmitting },
reset,
} = useForm<Partial<TIssueComment>>({ defaultValues: { comment_html: "<p></p>" } });
const onSubmit = async (formData: Partial<TIssueComment>) => {
await activityOperations.createComment(formData).finally(() => {
reset({ comment_html: "" });
editorRef.current?.clearEditor();
});
};
return (
<div>
<Controller
name="comment_html"
control={control}
render={({ field: { value, onChange } }) => (
<LiteTextEditorWithRef
onEnterKeyPress={(e) => {
handleSubmit(onSubmit)(e);
}}
cancelUploadImage={fileService.cancelUpload}
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}
deleteFile={fileService.deleteImage}
restoreFile={fileService.restoreImage}
ref={editorRef}
value={!value ? "<p></p>" : value}
customClassName="p-2"
editorContentCustomClassNames="min-h-[35px]"
debouncedUpdatesEnabled={false}
onChange={(comment_json: Object, comment_html: string) => {
onChange(comment_html);
}}
submitButton={
<Button
disabled={isSubmitting || disabled}
variant="primary"
type="submit"
className="!px-2.5 !py-1.5 !text-xs"
onClick={(e) => {
handleSubmit(onSubmit)(e);
}}
>
{isSubmitting ? "Adding..." : "Comment"}
</Button>
}
/>
)}
/>
</div>
);
};

View File

@ -0,0 +1,111 @@
import { FC, useRef } from "react";
import { useForm, Controller } from "react-hook-form";
// components
import { LiteTextEditorWithRef } from "@plane/lite-text-editor";
import { Button } from "@plane/ui";
// services
import { FileService } from "services/file.service";
// types
import { TActivityOperations } from "../root";
import { TIssueComment } from "@plane/types";
// icons
import { Globe2, Lock } from "lucide-react";
const fileService = new FileService();
type TIssueCommentCreate = {
workspaceSlug: string;
activityOperations: TActivityOperations;
disabled: boolean;
showAccessSpecifier?: boolean;
};
type commentAccessType = {
icon: any;
key: string;
label: "Private" | "Public";
};
const commentAccess: commentAccessType[] = [
{
icon: Lock,
key: "INTERNAL",
label: "Private",
},
{
icon: Globe2,
key: "EXTERNAL",
label: "Public",
},
];
export const IssueCommentCreate: FC<TIssueCommentCreate> = (props) => {
const { workspaceSlug, activityOperations, disabled, showAccessSpecifier = false } = props;
// refs
const editorRef = useRef<any>(null);
// react hook form
const {
handleSubmit,
control,
formState: { isSubmitting },
reset,
} = useForm<Partial<TIssueComment>>({ defaultValues: { comment_html: "<p></p>" } });
const onSubmit = async (formData: Partial<TIssueComment>) => {
await activityOperations.createComment(formData).finally(() => {
reset({ comment_html: "" });
editorRef.current?.clearEditor();
});
};
return (
<div>
<Controller
name="access"
control={control}
render={({ field: { onChange: onAccessChange, value: accessValue } }) => (
<Controller
name="comment_html"
control={control}
render={({ field: { value, onChange } }) => (
<LiteTextEditorWithRef
onEnterKeyPress={(e) => {
handleSubmit(onSubmit)(e);
}}
cancelUploadImage={fileService.cancelUpload}
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}
deleteFile={fileService.deleteImage}
restoreFile={fileService.restoreImage}
ref={editorRef}
value={!value ? "<p></p>" : value}
customClassName="p-2"
editorContentCustomClassNames="min-h-[35px]"
debouncedUpdatesEnabled={false}
onChange={(comment_json: Object, comment_html: string) => {
onChange(comment_html);
}}
commentAccessSpecifier={
showAccessSpecifier
? { accessValue: accessValue ?? "INTERNAL", onAccessChange, showAccessSpecifier, commentAccess }
: undefined
}
submitButton={
<Button
disabled={isSubmitting || disabled}
variant="primary"
type="submit"
className="!px-2.5 !py-1.5 !text-xs"
onClick={(e) => {
handleSubmit(onSubmit)(e);
}}
>
{isSubmitting ? "Adding..." : "Comment"}
</Button>
}
/>
)}
/>
)}
/>
</div>
);
};

View File

@ -8,34 +8,29 @@ import { IssueCommentCard } from "./comment-card";
import { TActivityOperations } from "../root"; import { TActivityOperations } from "../root";
type TIssueCommentRoot = { type TIssueCommentRoot = {
workspaceSlug: string;
projectId: string;
issueId: string; issueId: string;
activityOperations: TActivityOperations; activityOperations: TActivityOperations;
disabled: boolean; showAccessSpecifier?: boolean;
}; };
export const IssueCommentRoot: FC<TIssueCommentRoot> = observer((props) => { export const IssueCommentRoot: FC<TIssueCommentRoot> = observer((props) => {
const { workspaceSlug, projectId, issueId, disabled, activityOperations } = props; const { issueId, activityOperations, showAccessSpecifier } = props;
// hooks // hooks
const { const {
comment: { getCommentsByIssueId }, comment: { getCommentsByIssueId },
} = useIssueDetail(); } = useIssueDetail();
const commentIds = getCommentsByIssueId(issueId); const commentIds = getCommentsByIssueId(issueId);
if (!commentIds) return <></>; if (!commentIds) return <></>;
return ( return (
<div> <div>
{commentIds.map((commentId, index) => ( {commentIds.map((commentId, index) => (
<IssueCommentCard <IssueCommentCard
workspaceSlug={workspaceSlug}
projectId={projectId}
issueId={issueId}
commentId={commentId} commentId={commentId}
disabled={disabled}
ends={index === 0 ? "top" : index === commentIds.length - 1 ? "bottom" : undefined} ends={index === 0 ? "top" : index === commentIds.length - 1 ? "bottom" : undefined}
activityOperations={activityOperations} activityOperations={activityOperations}
showAccessSpecifier={showAccessSpecifier}
/> />
))} ))}
</div> </div>

View File

@ -9,4 +9,4 @@ export * from "./activity/activity-list";
// issue comment // issue comment
export * from "./comments/root"; export * from "./comments/root";
export * from "./comments/comment-card"; export * from "./comments/comment-card";
export * from "./comments/comment-create-update"; export * from "./comments/comment-create";

View File

@ -5,7 +5,7 @@ import { History, LucideIcon, MessageSquare, Network } from "lucide-react";
import { useIssueDetail } from "hooks/store"; import { useIssueDetail } from "hooks/store";
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// components // components
import { IssueActivityCommentRoot, IssueActivityRoot, IssueCommentRoot, IssueCommentCreateUpdate } from "./"; import { IssueActivityCommentRoot, IssueActivityRoot, IssueCommentRoot, IssueCommentCreate } from "./";
// types // types
import { TIssueComment } from "@plane/types"; import { TIssueComment } from "@plane/types";
@ -14,6 +14,7 @@ type TIssueActivity = {
projectId: string; projectId: string;
issueId: string; issueId: string;
disabled: boolean; disabled: boolean;
showAccessSpecifier?: boolean;
}; };
type TActivityTabs = "all" | "activity" | "comments"; type TActivityTabs = "all" | "activity" | "comments";
@ -45,7 +46,7 @@ export type TActivityOperations = {
}; };
export const IssueActivity: FC<TIssueActivity> = observer((props) => { export const IssueActivity: FC<TIssueActivity> = observer((props) => {
const { workspaceSlug, projectId, issueId, disabled } = props; const { workspaceSlug, projectId, issueId, disabled, showAccessSpecifier } = props;
// hooks // hooks
const { createComment, updateComment, removeComment, createCommentReaction, removeCommentReaction } = const { createComment, updateComment, removeComment, createCommentReaction, removeCommentReaction } =
useIssueDetail(); useIssueDetail();
@ -154,13 +155,6 @@ export const IssueActivity: FC<TIssueActivity> = observer((props) => {
] ]
); );
const componentCommonProps = {
workspaceSlug,
projectId,
issueId,
disabled,
};
return ( return (
<div className="space-y-3 pt-3"> <div className="space-y-3 pt-3">
{/* header */} {/* header */}
@ -191,22 +185,32 @@ export const IssueActivity: FC<TIssueActivity> = observer((props) => {
<div className="min-h-[200px]"> <div className="min-h-[200px]">
{activityTab === "all" ? ( {activityTab === "all" ? (
<> <>
<IssueActivityCommentRoot {...componentCommonProps} activityOperations={activityOperations} /> <IssueActivityCommentRoot
<IssueCommentCreateUpdate issueId={issueId}
activityOperations={activityOperations}
showAccessSpecifier={showAccessSpecifier}
/>
<IssueCommentCreate
workspaceSlug={workspaceSlug} workspaceSlug={workspaceSlug}
activityOperations={activityOperations} activityOperations={activityOperations}
disabled={disabled} disabled={disabled}
showAccessSpecifier={showAccessSpecifier}
/> />
</> </>
) : activityTab === "activity" ? ( ) : activityTab === "activity" ? (
<IssueActivityRoot {...componentCommonProps} /> <IssueActivityRoot issueId={issueId} />
) : ( ) : (
<> <>
<IssueCommentRoot {...componentCommonProps} activityOperations={activityOperations} /> <IssueCommentRoot
<IssueCommentCreateUpdate issueId={issueId}
activityOperations={activityOperations}
showAccessSpecifier={showAccessSpecifier}
/>
<IssueCommentCreate
workspaceSlug={workspaceSlug} workspaceSlug={workspaceSlug}
activityOperations={activityOperations} activityOperations={activityOperations}
disabled={disabled} disabled={disabled}
showAccessSpecifier={showAccessSpecifier}
/> />
</> </>
)} )}

View File

@ -1,7 +1,7 @@
import { useState } from "react"; import { useState } from "react";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
// hooks // hooks
import { useIssueDetail, useProjectState, useUser } from "hooks/store"; import { useIssueDetail, useProject, useProjectState, useUser } from "hooks/store";
// components // components
import { IssueDescriptionForm, IssueAttachmentRoot, IssueUpdateStatus } from "components/issues"; import { IssueDescriptionForm, IssueAttachmentRoot, IssueUpdateStatus } from "components/issues";
import { IssueParentDetail } from "./parent"; import { IssueParentDetail } from "./parent";
@ -32,10 +32,14 @@ export const IssueMainContent: React.FC<Props> = observer((props) => {
const { const {
issue: { getIssueById }, issue: { getIssueById },
} = useIssueDetail(); } = useIssueDetail();
const { getProjectById } = useProject();
const issue = getIssueById(issueId); const issue = getIssueById(issueId);
if (!issue) return <></>; if (!issue) return <></>;
const project = getProjectById(projectId);
if (!project) return <></>;
const currentIssueState = projectStates?.find((s) => s.id === issue.state_id); const currentIssueState = projectStates?.find((s) => s.id === issue.state_id);
return ( return (
@ -100,7 +104,13 @@ export const IssueMainContent: React.FC<Props> = observer((props) => {
disabled={!is_editable} disabled={!is_editable}
/> />
<IssueActivity workspaceSlug={workspaceSlug} projectId={projectId} issueId={issueId} disabled={false} /> <IssueActivity
workspaceSlug={workspaceSlug}
projectId={projectId}
issueId={issueId}
disabled={!is_editable}
showAccessSpecifier={project.is_deployed}
/>
</> </>
); );
}); });