[WEB-3529] fix: comment reset + edit comment font size + comment box position (#6909)
* fix: comment reset + edit comment font size * fix: dynamically setting the position of the comment box * fix: refactor * fix: nomenclature
This commit is contained in:
parent
882520b3c7
commit
2818310619
5 changed files with 77 additions and 25 deletions
|
|
@ -40,7 +40,7 @@ export type TIssueComment = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TCommentsOperations = {
|
export type TCommentsOperations = {
|
||||||
createComment: (data: Partial<TIssueComment>) => Promise<void>;
|
createComment: (data: Partial<TIssueComment>) => Promise<Partial<TIssueComment> | undefined>;
|
||||||
updateComment: (commentId: string, data: Partial<TIssueComment>) => Promise<void>;
|
updateComment: (commentId: string, data: Partial<TIssueComment>) => Promise<void>;
|
||||||
removeComment: (commentId: string) => Promise<void>;
|
removeComment: (commentId: string) => Promise<void>;
|
||||||
uploadCommentAsset: (blockId: string, file: File, commentId?: string) => Promise<TFileSignedURLResponse>;
|
uploadCommentAsset: (blockId: string, file: File, commentId?: string) => Promise<TFileSignedURLResponse>;
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,8 @@ export const CommentCard: FC<TCommentCard> = observer((props) => {
|
||||||
return asset_id;
|
return asset_id;
|
||||||
}}
|
}}
|
||||||
projectId={projectId?.toString() ?? ""}
|
projectId={projectId?.toString() ?? ""}
|
||||||
|
editorClassName="[&>*]:!py-0 [&>*]:!text-sm"
|
||||||
|
parentClassName="p-2"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-1 self-end">
|
<div className="flex gap-1 self-end">
|
||||||
|
|
@ -181,7 +183,10 @@ export const CommentCard: FC<TCommentCard> = observer((props) => {
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="group rounded border border-red-500 bg-red-500/20 p-2 shadow-md duration-300 hover:bg-red-500"
|
className="group rounded border border-red-500 bg-red-500/20 p-2 shadow-md duration-300 hover:bg-red-500"
|
||||||
onClick={() => setIsEditing(false)}
|
onClick={() => {
|
||||||
|
setIsEditing(false);
|
||||||
|
editorRef.current?.setEditorValue(comment.comment_html ?? "<p></p>");
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<X className="size-3 text-red-500 duration-300 group-hover:text-white" />
|
<X className="size-3 text-red-500 duration-300 group-hover:text-white" />
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,17 @@ import { useForm, Controller } from "react-hook-form";
|
||||||
import { EIssueCommentAccessSpecifier } from "@plane/constants";
|
import { EIssueCommentAccessSpecifier } from "@plane/constants";
|
||||||
// plane editor
|
// plane editor
|
||||||
import { EditorRefApi } from "@plane/editor";
|
import { EditorRefApi } from "@plane/editor";
|
||||||
// components
|
// plane types
|
||||||
import { TIssueComment, TCommentsOperations } from "@plane/types";
|
import { TIssueComment, TCommentsOperations } from "@plane/types";
|
||||||
|
// components
|
||||||
import { LiteTextEditor } from "@/components/editor";
|
import { LiteTextEditor } from "@/components/editor";
|
||||||
// constants
|
// constants
|
||||||
// helpers
|
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
|
// helpers
|
||||||
import { isCommentEmpty } from "@/helpers/string.helper";
|
import { isCommentEmpty } from "@/helpers/string.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useWorkspace } from "@/hooks/store";
|
import { useWorkspace } from "@/hooks/store";
|
||||||
|
// services
|
||||||
import { FileService } from "@/services/file.service";
|
import { FileService } from "@/services/file.service";
|
||||||
|
|
||||||
type TCommentCreate = {
|
type TCommentCreate = {
|
||||||
|
|
@ -22,12 +24,21 @@ type TCommentCreate = {
|
||||||
activityOperations: TCommentsOperations;
|
activityOperations: TCommentsOperations;
|
||||||
showToolbarInitially?: boolean;
|
showToolbarInitially?: boolean;
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
|
onSubmitCallback?: (elementId: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
// services
|
// services
|
||||||
const fileService = new FileService();
|
const fileService = new FileService();
|
||||||
|
|
||||||
export const CommentCreate: FC<TCommentCreate> = observer((props) => {
|
export const CommentCreate: FC<TCommentCreate> = observer((props) => {
|
||||||
const { workspaceSlug, entityId, activityOperations, showToolbarInitially = false, projectId } = props;
|
const {
|
||||||
|
workspaceSlug,
|
||||||
|
entityId,
|
||||||
|
activityOperations,
|
||||||
|
showToolbarInitially = false,
|
||||||
|
projectId,
|
||||||
|
onSubmitCallback,
|
||||||
|
} = props;
|
||||||
// states
|
// states
|
||||||
const [uploadedAssetIds, setUploadedAssetIds] = useState<string[]>([]);
|
const [uploadedAssetIds, setUploadedAssetIds] = useState<string[]>([]);
|
||||||
// refs
|
// refs
|
||||||
|
|
@ -51,7 +62,8 @@ export const CommentCreate: FC<TCommentCreate> = observer((props) => {
|
||||||
|
|
||||||
const onSubmit = async (formData: Partial<TIssueComment>) => {
|
const onSubmit = async (formData: Partial<TIssueComment>) => {
|
||||||
try {
|
try {
|
||||||
await activityOperations.createComment(formData);
|
const comment = await activityOperations.createComment(formData);
|
||||||
|
if (comment?.id) onSubmitCallback?.(comment.id);
|
||||||
if (uploadedAssetIds.length > 0) {
|
if (uploadedAssetIds.length > 0) {
|
||||||
if (projectId) {
|
if (projectId) {
|
||||||
await fileService.updateBulkProjectAssetsUploadStatus(workspaceSlug, projectId.toString(), entityId, {
|
await fileService.updateBulkProjectAssetsUploadStatus(workspaceSlug, projectId.toString(), entityId, {
|
||||||
|
|
@ -81,7 +93,15 @@ export const CommentCreate: FC<TCommentCreate> = observer((props) => {
|
||||||
<div
|
<div
|
||||||
className={cn("sticky bottom-0 z-[4] bg-custom-background-100 sm:static")}
|
className={cn("sticky bottom-0 z-[4] bg-custom-background-100 sm:static")}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === "Enter" && !e.shiftKey && !e.ctrlKey && !e.metaKey && !isEmpty && !isSubmitting)
|
if (
|
||||||
|
e.key === "Enter" &&
|
||||||
|
!e.shiftKey &&
|
||||||
|
!e.ctrlKey &&
|
||||||
|
!e.metaKey &&
|
||||||
|
!isEmpty &&
|
||||||
|
!isSubmitting &&
|
||||||
|
editorRef.current?.isEditorReadyToDiscard()
|
||||||
|
)
|
||||||
handleSubmit(onSubmit)(e);
|
handleSubmit(onSubmit)(e);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { FC } from "react";
|
import React, { FC, useMemo } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
// plane imports
|
// plane imports
|
||||||
|
import smoothScrollIntoView from "smooth-scroll-into-view-if-needed";
|
||||||
|
import { E_SORT_ORDER } from "@plane/constants";
|
||||||
import { TCommentsOperations, TIssueComment } from "@plane/types";
|
import { TCommentsOperations, TIssueComment } from "@plane/types";
|
||||||
// local components
|
// local components
|
||||||
import { CommentCard } from "./comment-card";
|
import { CommentCard } from "./comment-card";
|
||||||
|
|
@ -15,26 +17,45 @@ type TCommentsWrapper = {
|
||||||
isEditingAllowed?: boolean;
|
isEditingAllowed?: boolean;
|
||||||
activityOperations: TCommentsOperations;
|
activityOperations: TCommentsOperations;
|
||||||
comments: TIssueComment[] | string[];
|
comments: TIssueComment[] | string[];
|
||||||
|
sortOrder?: E_SORT_ORDER;
|
||||||
getCommentById?: (activityId: string) => TIssueComment | undefined;
|
getCommentById?: (activityId: string) => TIssueComment | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CommentsWrapper: FC<TCommentsWrapper> = observer((props) => {
|
export const CommentsWrapper: FC<TCommentsWrapper> = observer((props) => {
|
||||||
const { entityId, activityOperations, comments, getCommentById, isEditingAllowed = true, projectId } = props;
|
const {
|
||||||
|
entityId,
|
||||||
|
activityOperations,
|
||||||
|
comments,
|
||||||
|
getCommentById,
|
||||||
|
isEditingAllowed = true,
|
||||||
|
projectId,
|
||||||
|
sortOrder,
|
||||||
|
} = props;
|
||||||
// router
|
// router
|
||||||
const { workspaceSlug: routerWorkspaceSlug } = useParams();
|
const { workspaceSlug: routerWorkspaceSlug } = useParams();
|
||||||
const workspaceSlug = routerWorkspaceSlug?.toString();
|
const workspaceSlug = routerWorkspaceSlug?.toString();
|
||||||
|
const renderCommentCreate = useMemo(
|
||||||
return (
|
() =>
|
||||||
<div className="relative flex flex-col gap-y-2 h-full overflow-hidden">
|
isEditingAllowed && (
|
||||||
{isEditingAllowed && (
|
|
||||||
<CommentCreate
|
<CommentCreate
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
entityId={entityId}
|
entityId={entityId}
|
||||||
activityOperations={activityOperations}
|
activityOperations={activityOperations}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
|
onSubmitCallback={async (elementId: string) => {
|
||||||
|
const sourceElementId = elementId ?? "";
|
||||||
|
const sourceElement = document.getElementById(sourceElementId);
|
||||||
|
if (sourceElement)
|
||||||
|
await smoothScrollIntoView(sourceElement, { behavior: "smooth", block: "center", duration: 1500 });
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
),
|
||||||
|
[isEditingAllowed, workspaceSlug, entityId, activityOperations, projectId]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative flex flex-col gap-y-2 h-full overflow-hidden">
|
||||||
|
{sortOrder === E_SORT_ORDER.DESC && renderCommentCreate}
|
||||||
<div className="flex-grow py-4 overflow-y-auto">
|
<div className="flex-grow py-4 overflow-y-auto">
|
||||||
{comments?.map((data, index) => {
|
{comments?.map((data, index) => {
|
||||||
let comment;
|
let comment;
|
||||||
|
|
@ -58,6 +79,7 @@ export const CommentsWrapper: FC<TCommentsWrapper> = observer((props) => {
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
{sortOrder === E_SORT_ORDER.ASC && renderCommentCreate}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { FC } from "react";
|
import { FC, useMemo } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
// plane package imports
|
// plane package imports
|
||||||
import { E_SORT_ORDER, TActivityFilters, defaultActivityFilters, EUserPermissions } from "@plane/constants";
|
import { E_SORT_ORDER, TActivityFilters, defaultActivityFilters, EUserPermissions } from "@plane/constants";
|
||||||
|
|
@ -81,6 +81,18 @@ export const IssueActivity: FC<TIssueActivity> = observer((props) => {
|
||||||
const activityOperations = useCommentOperations(workspaceSlug, projectId, issueId);
|
const activityOperations = useCommentOperations(workspaceSlug, projectId, issueId);
|
||||||
|
|
||||||
const project = getProjectById(projectId);
|
const project = getProjectById(projectId);
|
||||||
|
const renderCommentCreationBox = useMemo(
|
||||||
|
() => (
|
||||||
|
<CommentCreate
|
||||||
|
workspaceSlug={workspaceSlug}
|
||||||
|
entityId={issueId}
|
||||||
|
activityOperations={activityOperations}
|
||||||
|
showToolbarInitially
|
||||||
|
projectId={projectId}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
[workspaceSlug, issueId, activityOperations, projectId]
|
||||||
|
);
|
||||||
if (!project) return <></>;
|
if (!project) return <></>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -111,6 +123,7 @@ export const IssueActivity: FC<TIssueActivity> = observer((props) => {
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="min-h-[200px]">
|
<div className="min-h-[200px]">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
|
{!disabled && sortOrder === E_SORT_ORDER.DESC && renderCommentCreationBox}
|
||||||
<IssueActivityCommentRoot
|
<IssueActivityCommentRoot
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
|
|
@ -121,15 +134,7 @@ export const IssueActivity: FC<TIssueActivity> = observer((props) => {
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
sortOrder={sortOrder || E_SORT_ORDER.ASC}
|
sortOrder={sortOrder || E_SORT_ORDER.ASC}
|
||||||
/>
|
/>
|
||||||
{!disabled && (
|
{!disabled && sortOrder === E_SORT_ORDER.ASC && renderCommentCreationBox}
|
||||||
<CommentCreate
|
|
||||||
workspaceSlug={workspaceSlug}
|
|
||||||
entityId={issueId}
|
|
||||||
activityOperations={activityOperations}
|
|
||||||
showToolbarInitially
|
|
||||||
projectId={projectId}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue