[WEB-2348] fix: allow updating comments with just mentions in them (#5471)
* fix: accept mentions while updating comments * chore: remove console log * chore: update empty string helper function
This commit is contained in:
parent
03c28a11e8
commit
bac5b53ffb
7 changed files with 46 additions and 27 deletions
|
|
@ -5,7 +5,7 @@ import { EditorRefApi, ILiteTextEditor, LiteTextEditorWithRef } from "@plane/edi
|
||||||
import { IssueCommentToolbar } from "@/components/editor";
|
import { IssueCommentToolbar } from "@/components/editor";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { isEmptyHtmlString } from "@/helpers/string.helper";
|
import { isCommentEmpty } from "@/helpers/string.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useMention } from "@/hooks/use-mention";
|
import { useMention } from "@/hooks/use-mention";
|
||||||
// services
|
// services
|
||||||
|
|
@ -33,10 +33,7 @@ export const LiteTextEditor = React.forwardRef<EditorRefApi, LiteTextEditorWrapp
|
||||||
function isMutableRefObject<T>(ref: React.ForwardedRef<T>): ref is React.MutableRefObject<T | null> {
|
function isMutableRefObject<T>(ref: React.ForwardedRef<T>): ref is React.MutableRefObject<T | null> {
|
||||||
return !!ref && typeof ref === "object" && "current" in ref;
|
return !!ref && typeof ref === "object" && "current" in ref;
|
||||||
}
|
}
|
||||||
const isEmpty =
|
const isEmpty = isCommentEmpty(props.initialValue);
|
||||||
props.initialValue?.trim() === "" ||
|
|
||||||
props.initialValue === "<p></p>" ||
|
|
||||||
(isEmptyHtmlString(props.initialValue ?? "") && !props.initialValue?.includes("mention-component"));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="border border-custom-border-200 rounded p-3 space-y-3">
|
<div className="border border-custom-border-200 rounded p-3 space-y-3">
|
||||||
|
|
|
||||||
|
|
@ -50,13 +50,29 @@ export const checkEmailValidity = (email: string): boolean => {
|
||||||
return isEmailValid;
|
return isEmailValid;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isEmptyHtmlString = (htmlString: string) => {
|
export const isEmptyHtmlString = (htmlString: string, allowedHTMLTags: string[] = []) => {
|
||||||
// Remove HTML tags using regex
|
// Remove HTML tags using regex
|
||||||
const cleanText = DOMPurify.sanitize(htmlString, { ALLOWED_TAGS: ["img"] });
|
const cleanText = DOMPurify.sanitize(htmlString, { ALLOWED_TAGS: allowedHTMLTags });
|
||||||
// Trim the string and check if it's empty
|
// Trim the string and check if it's empty
|
||||||
return cleanText.trim() === "";
|
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 "<p></p>"
|
||||||
|
* @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 === "<p></p>" || isEmptyHtmlString(comment ?? "", ["img", "mention-component"])
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const replaceUnderscoreIfSnakeCase = (str: string) => str.replace(/_/g, " ");
|
export const replaceUnderscoreIfSnakeCase = (str: string) => str.replace(/_/g, " ");
|
||||||
|
|
||||||
export const capitalizeFirstLetter = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
|
export const capitalizeFirstLetter = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
|
|
@ -9,7 +9,7 @@ import { IssueCommentToolbar } from "@/components/editor";
|
||||||
import { EIssueCommentAccessSpecifier } from "@/constants/issue";
|
import { EIssueCommentAccessSpecifier } from "@/constants/issue";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { isEmptyHtmlString } from "@/helpers/string.helper";
|
import { isCommentEmpty } from "@/helpers/string.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useMember, useMention, useUser } from "@/hooks/store";
|
import { useMember, useMention, useUser } from "@/hooks/store";
|
||||||
// services
|
// services
|
||||||
|
|
@ -59,10 +59,7 @@ export const LiteTextEditor = React.forwardRef<EditorRefApi, LiteTextEditorWrapp
|
||||||
user: currentUser ?? undefined,
|
user: currentUser ?? undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const isEmpty =
|
const isEmpty = isCommentEmpty(props.initialValue);
|
||||||
props.initialValue?.trim() === "" ||
|
|
||||||
props.initialValue === "<p></p>" ||
|
|
||||||
(isEmptyHtmlString(props.initialValue ?? "") && !props.initialValue?.includes("mention-component"));
|
|
||||||
|
|
||||||
function isMutableRefObject<T>(ref: React.ForwardedRef<T>): ref is React.MutableRefObject<T | null> {
|
function isMutableRefObject<T>(ref: React.ForwardedRef<T>): ref is React.MutableRefObject<T | null> {
|
||||||
return !!ref && typeof ref === "object" && "current" in ref;
|
return !!ref && typeof ref === "object" && "current" in ref;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import { LiteTextEditor, LiteTextReadOnlyEditor } from "@/components/editor";
|
||||||
// constants
|
// constants
|
||||||
import { EIssueCommentAccessSpecifier } from "@/constants/issue";
|
import { EIssueCommentAccessSpecifier } from "@/constants/issue";
|
||||||
// helpers
|
// helpers
|
||||||
import { isEmptyHtmlString } from "@/helpers/string.helper";
|
import { isCommentEmpty } from "@/helpers/string.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useIssueDetail, useUser, useWorkspace } from "@/hooks/store";
|
import { useIssueDetail, useUser, useWorkspace } from "@/hooks/store";
|
||||||
// components
|
// components
|
||||||
|
|
@ -80,10 +80,8 @@ export const IssueCommentCard: FC<TIssueCommentCard> = observer((props) => {
|
||||||
isEditing && setFocus("comment_html");
|
isEditing && setFocus("comment_html");
|
||||||
}, [isEditing, setFocus]);
|
}, [isEditing, setFocus]);
|
||||||
|
|
||||||
const isEmpty =
|
const commentHTML = watch("comment_html");
|
||||||
watch("comment_html")?.trim() === "" ||
|
const isEmpty = isCommentEmpty(commentHTML);
|
||||||
watch("comment_html") === "<p></p>" ||
|
|
||||||
isEmptyHtmlString(watch("comment_html") ?? "");
|
|
||||||
|
|
||||||
if (!comment || !currentUser) return <></>;
|
if (!comment || !currentUser) return <></>;
|
||||||
return (
|
return (
|
||||||
|
|
@ -148,7 +146,7 @@ export const IssueCommentCard: FC<TIssueCommentCard> = observer((props) => {
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
ref={editorRef}
|
ref={editorRef}
|
||||||
id={comment.id}
|
id={comment.id}
|
||||||
initialValue={watch("comment_html") ?? ""}
|
initialValue={commentHTML ?? ""}
|
||||||
value={null}
|
value={null}
|
||||||
onChange={(comment_json, comment_html) => setValue("comment_html", comment_html)}
|
onChange={(comment_json, comment_html) => setValue("comment_html", comment_html)}
|
||||||
onEnterKeyPress={(e) => {
|
onEnterKeyPress={(e) => {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import { LiteTextEditor } from "@/components/editor/lite-text-editor/lite-text-e
|
||||||
import { EIssueCommentAccessSpecifier } from "@/constants/issue";
|
import { EIssueCommentAccessSpecifier } from "@/constants/issue";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { isEmptyHtmlString } from "@/helpers/string.helper";
|
import { isCommentEmpty } from "@/helpers/string.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useIssueDetail, useWorkspace } from "@/hooks/store";
|
import { useIssueDetail, useWorkspace } from "@/hooks/store";
|
||||||
// editor
|
// editor
|
||||||
|
|
@ -53,10 +53,7 @@ export const IssueCommentCreate: FC<TIssueCommentCreate> = (props) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const commentHTML = watch("comment_html");
|
const commentHTML = watch("comment_html");
|
||||||
const isEmpty =
|
const isEmpty = isCommentEmpty(commentHTML);
|
||||||
commentHTML?.trim() === "" ||
|
|
||||||
commentHTML === "<p></p>" ||
|
|
||||||
(isEmptyHtmlString(commentHTML ?? "") && !commentHTML?.includes("mention-component"));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ export const DraftIssueLayout: React.FC<DraftIssueProps> = observer((props) => {
|
||||||
if (
|
if (
|
||||||
issueKey === "description_html" &&
|
issueKey === "description_html" &&
|
||||||
changesMade.description_html &&
|
changesMade.description_html &&
|
||||||
isEmptyHtmlString(changesMade.description_html)
|
isEmptyHtmlString(changesMade.description_html, ["img"])
|
||||||
)
|
)
|
||||||
delete changesMade.description_html;
|
delete changesMade.description_html;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -230,9 +230,23 @@ export const checkEmailValidity = (email: string): boolean => {
|
||||||
return isEmailValid;
|
return isEmailValid;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isEmptyHtmlString = (htmlString: string) => {
|
export const isEmptyHtmlString = (htmlString: string, allowedHTMLTags: string[] = []) => {
|
||||||
// Remove HTML tags using regex
|
// Remove HTML tags using regex
|
||||||
const cleanText = DOMPurify.sanitize(htmlString, { ALLOWED_TAGS: ["img"] });
|
const cleanText = DOMPurify.sanitize(htmlString, { ALLOWED_TAGS: allowedHTMLTags });
|
||||||
// Trim the string and check if it's empty
|
// Trim the string and check if it's empty
|
||||||
return cleanText.trim() === "";
|
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 "<p></p>"
|
||||||
|
* @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 === "<p></p>" || isEmptyHtmlString(comment ?? "", ["mention-component"]);
|
||||||
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue