From 754d1f9f31ced1a7574d8a8194ebab5fc3a9d2aa Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Fri, 30 Aug 2024 17:26:34 +0530 Subject: [PATCH 1/3] fix: accept mentions while updating comments --- .../components/editor/lite-text-editor.tsx | 7 ++----- space/helpers/string.helper.ts | 20 ++++++++++++++++--- .../lite-text-editor/lite-text-editor.tsx | 7 ++----- .../issue-activity/comments/comment-card.tsx | 12 +++++------ .../comments/comment-create.tsx | 7 ++----- web/helpers/string.helper.ts | 18 +++++++++++++++-- 6 files changed, 45 insertions(+), 26 deletions(-) diff --git a/space/core/components/editor/lite-text-editor.tsx b/space/core/components/editor/lite-text-editor.tsx index 3cc8347a173..186f44a1011 100644 --- a/space/core/components/editor/lite-text-editor.tsx +++ b/space/core/components/editor/lite-text-editor.tsx @@ -5,7 +5,7 @@ import { EditorRefApi, ILiteTextEditor, LiteTextEditorWithRef } from "@plane/edi import { IssueCommentToolbar } from "@/components/editor"; // helpers import { cn } from "@/helpers/common.helper"; -import { isEmptyHtmlString } from "@/helpers/string.helper"; +import { isCommentEmpty } from "@/helpers/string.helper"; // hooks import { useMention } from "@/hooks/use-mention"; // services @@ -33,10 +33,7 @@ export const LiteTextEditor = React.forwardRef(ref: React.ForwardedRef): ref is React.MutableRefObject { return !!ref && typeof ref === "object" && "current" in ref; } - const isEmpty = - props.initialValue?.trim() === "" || - props.initialValue === "

" || - (isEmptyHtmlString(props.initialValue ?? "") && !props.initialValue?.includes("mention-component")); + const isEmpty = isCommentEmpty(props.initialValue); return (
diff --git a/space/helpers/string.helper.ts b/space/helpers/string.helper.ts index fe7df96dae3..d1b8231467b 100644 --- a/space/helpers/string.helper.ts +++ b/space/helpers/string.helper.ts @@ -50,13 +50,27 @@ export const checkEmailValidity = (email: string): boolean => { return isEmailValid; }; -export const isEmptyHtmlString = (htmlString: string) => { +export const isEmptyHtmlString = (htmlString: string, allowedHTMLTags: string[] = []) => { // Remove HTML tags using regex - const cleanText = DOMPurify.sanitize(htmlString, { ALLOWED_TAGS: ["img"] }); + const cleanText = DOMPurify.sanitize(htmlString, { ALLOWED_TAGS: ["img", ...allowedHTMLTags] }); // Trim the string and check if it's empty return cleanText.trim() === ""; }; +/** + * @description this function returns whether a comment is empty or not by checking for the following conditions- + * 1. If comment is undefined + * 2. If comment is an empty string + * 3. If comment is "

" + * @param {string | undefined} comment + * @returns {boolean} + */ +export const isCommentEmpty = (comment: string | undefined): boolean => { + // return true if comment is undefined + if (!comment) return true; + return comment?.trim() === "" || comment === "

" || isEmptyHtmlString(comment ?? "", ["mention-component"]); +}; + export const replaceUnderscoreIfSnakeCase = (str: string) => str.replace(/_/g, " "); -export const capitalizeFirstLetter = (str: string) => str.charAt(0).toUpperCase() + str.slice(1); \ No newline at end of file +export const capitalizeFirstLetter = (str: string) => str.charAt(0).toUpperCase() + str.slice(1); diff --git a/web/core/components/editor/lite-text-editor/lite-text-editor.tsx b/web/core/components/editor/lite-text-editor/lite-text-editor.tsx index 4ded0e84c9f..8036e4c8d4e 100644 --- a/web/core/components/editor/lite-text-editor/lite-text-editor.tsx +++ b/web/core/components/editor/lite-text-editor/lite-text-editor.tsx @@ -9,7 +9,7 @@ import { IssueCommentToolbar } from "@/components/editor"; import { EIssueCommentAccessSpecifier } from "@/constants/issue"; // helpers import { cn } from "@/helpers/common.helper"; -import { isEmptyHtmlString } from "@/helpers/string.helper"; +import { isCommentEmpty } from "@/helpers/string.helper"; // hooks import { useMember, useMention, useUser } from "@/hooks/store"; // services @@ -59,10 +59,7 @@ export const LiteTextEditor = React.forwardRef

" || - (isEmptyHtmlString(props.initialValue ?? "") && !props.initialValue?.includes("mention-component")); + const isEmpty = isCommentEmpty(props.initialValue); function isMutableRefObject(ref: React.ForwardedRef): ref is React.MutableRefObject { return !!ref && typeof ref === "object" && "current" in ref; diff --git a/web/core/components/issues/issue-detail/issue-activity/comments/comment-card.tsx b/web/core/components/issues/issue-detail/issue-activity/comments/comment-card.tsx index 798964365ea..b55fb8f1994 100644 --- a/web/core/components/issues/issue-detail/issue-activity/comments/comment-card.tsx +++ b/web/core/components/issues/issue-detail/issue-activity/comments/comment-card.tsx @@ -13,7 +13,7 @@ import { LiteTextEditor, LiteTextReadOnlyEditor } from "@/components/editor"; // constants import { EIssueCommentAccessSpecifier } from "@/constants/issue"; // helpers -import { isEmptyHtmlString } from "@/helpers/string.helper"; +import { isCommentEmpty } from "@/helpers/string.helper"; // hooks import { useIssueDetail, useUser, useWorkspace } from "@/hooks/store"; // components @@ -80,10 +80,10 @@ export const IssueCommentCard: FC = observer((props) => { isEditing && setFocus("comment_html"); }, [isEditing, setFocus]); - const isEmpty = - watch("comment_html")?.trim() === "" || - watch("comment_html") === "

" || - isEmptyHtmlString(watch("comment_html") ?? ""); + const commentHTML = watch("comment_html"); + const isEmpty = isCommentEmpty(commentHTML); + + console.log("isEmpty", isEmpty); if (!comment || !currentUser) return <>; return ( @@ -148,7 +148,7 @@ export const IssueCommentCard: FC = observer((props) => { workspaceSlug={workspaceSlug} ref={editorRef} id={comment.id} - initialValue={watch("comment_html") ?? ""} + initialValue={commentHTML ?? ""} value={null} onChange={(comment_json, comment_html) => setValue("comment_html", comment_html)} onEnterKeyPress={(e) => { diff --git a/web/core/components/issues/issue-detail/issue-activity/comments/comment-create.tsx b/web/core/components/issues/issue-detail/issue-activity/comments/comment-create.tsx index d8c86ec1828..c5013442f62 100644 --- a/web/core/components/issues/issue-detail/issue-activity/comments/comment-create.tsx +++ b/web/core/components/issues/issue-detail/issue-activity/comments/comment-create.tsx @@ -8,7 +8,7 @@ import { LiteTextEditor } from "@/components/editor/lite-text-editor/lite-text-e import { EIssueCommentAccessSpecifier } from "@/constants/issue"; // helpers import { cn } from "@/helpers/common.helper"; -import { isEmptyHtmlString } from "@/helpers/string.helper"; +import { isCommentEmpty } from "@/helpers/string.helper"; // hooks import { useIssueDetail, useWorkspace } from "@/hooks/store"; // editor @@ -53,10 +53,7 @@ export const IssueCommentCreate: FC = (props) => { }); const commentHTML = watch("comment_html"); - const isEmpty = - commentHTML?.trim() === "" || - commentHTML === "

" || - (isEmptyHtmlString(commentHTML ?? "") && !commentHTML?.includes("mention-component")); + const isEmpty = isCommentEmpty(commentHTML); return (
{ return isEmailValid; }; -export const isEmptyHtmlString = (htmlString: string) => { +export const isEmptyHtmlString = (htmlString: string, allowedHTMLTags: string[] = []) => { // Remove HTML tags using regex - const cleanText = DOMPurify.sanitize(htmlString, { ALLOWED_TAGS: ["img"] }); + const cleanText = DOMPurify.sanitize(htmlString, { ALLOWED_TAGS: ["img", ...allowedHTMLTags] }); // Trim the string and check if it's empty return cleanText.trim() === ""; }; + +/** + * @description this function returns whether a comment is empty or not by checking for the following conditions- + * 1. If comment is undefined + * 2. If comment is an empty string + * 3. If comment is "

" + * @param {string | undefined} comment + * @returns {boolean} + */ +export const isCommentEmpty = (comment: string | undefined): boolean => { + // return true if comment is undefined + if (!comment) return true; + return comment?.trim() === "" || comment === "

" || isEmptyHtmlString(comment ?? "", ["mention-component"]); +}; From cacbda6c32b1ab72891b0c72c816203b1a34ecb3 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Fri, 30 Aug 2024 17:27:22 +0530 Subject: [PATCH 2/3] chore: remove console log --- .../issue-detail/issue-activity/comments/comment-card.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/web/core/components/issues/issue-detail/issue-activity/comments/comment-card.tsx b/web/core/components/issues/issue-detail/issue-activity/comments/comment-card.tsx index b55fb8f1994..d9b77430ceb 100644 --- a/web/core/components/issues/issue-detail/issue-activity/comments/comment-card.tsx +++ b/web/core/components/issues/issue-detail/issue-activity/comments/comment-card.tsx @@ -83,8 +83,6 @@ export const IssueCommentCard: FC = observer((props) => { const commentHTML = watch("comment_html"); const isEmpty = isCommentEmpty(commentHTML); - console.log("isEmpty", isEmpty); - if (!comment || !currentUser) return <>; return ( Date: Fri, 30 Aug 2024 17:40:44 +0530 Subject: [PATCH 3/3] chore: update empty string helper function --- space/helpers/string.helper.ts | 6 ++++-- .../components/issues/issue-modal/draft-issue-layout.tsx | 2 +- web/helpers/string.helper.ts | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/space/helpers/string.helper.ts b/space/helpers/string.helper.ts index d1b8231467b..6c60e4bb9f0 100644 --- a/space/helpers/string.helper.ts +++ b/space/helpers/string.helper.ts @@ -52,7 +52,7 @@ export const checkEmailValidity = (email: string): boolean => { export const isEmptyHtmlString = (htmlString: string, allowedHTMLTags: string[] = []) => { // Remove HTML tags using regex - const cleanText = DOMPurify.sanitize(htmlString, { ALLOWED_TAGS: ["img", ...allowedHTMLTags] }); + const cleanText = DOMPurify.sanitize(htmlString, { ALLOWED_TAGS: allowedHTMLTags }); // Trim the string and check if it's empty return cleanText.trim() === ""; }; @@ -68,7 +68,9 @@ export const isEmptyHtmlString = (htmlString: string, allowedHTMLTags: string[] export const isCommentEmpty = (comment: string | undefined): boolean => { // return true if comment is undefined if (!comment) return true; - return comment?.trim() === "" || comment === "

" || isEmptyHtmlString(comment ?? "", ["mention-component"]); + return ( + comment?.trim() === "" || comment === "

" || isEmptyHtmlString(comment ?? "", ["img", "mention-component"]) + ); }; export const replaceUnderscoreIfSnakeCase = (str: string) => str.replace(/_/g, " "); diff --git a/web/core/components/issues/issue-modal/draft-issue-layout.tsx b/web/core/components/issues/issue-modal/draft-issue-layout.tsx index da8622dc8d2..49bb1734de5 100644 --- a/web/core/components/issues/issue-modal/draft-issue-layout.tsx +++ b/web/core/components/issues/issue-modal/draft-issue-layout.tsx @@ -74,7 +74,7 @@ export const DraftIssueLayout: React.FC = observer((props) => { if ( issueKey === "description_html" && changesMade.description_html && - isEmptyHtmlString(changesMade.description_html) + isEmptyHtmlString(changesMade.description_html, ["img"]) ) delete changesMade.description_html; }); diff --git a/web/helpers/string.helper.ts b/web/helpers/string.helper.ts index eb81cdf57b3..de0d8ac9d44 100644 --- a/web/helpers/string.helper.ts +++ b/web/helpers/string.helper.ts @@ -232,7 +232,7 @@ export const checkEmailValidity = (email: string): boolean => { export const isEmptyHtmlString = (htmlString: string, allowedHTMLTags: string[] = []) => { // Remove HTML tags using regex - const cleanText = DOMPurify.sanitize(htmlString, { ALLOWED_TAGS: ["img", ...allowedHTMLTags] }); + const cleanText = DOMPurify.sanitize(htmlString, { ALLOWED_TAGS: allowedHTMLTags }); // Trim the string and check if it's empty return cleanText.trim() === ""; };