[WEB-496] fix: issue comment (#3796)

* fix: issue comment empty string and on enter functionality

* fix: empty html tag validation added

* fix: purifying dom contents before saving comments

---------

Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
This commit is contained in:
Anmol Singh Bhatia 2024-02-26 16:11:35 +05:30 committed by GitHub
parent e1f13af084
commit 5c089f0223
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 45 additions and 19 deletions

View File

@ -11,6 +11,8 @@ import { TIssueComment } from "@plane/types";
// icons
import { Globe2, Lock } from "lucide-react";
import { useMention, useWorkspace } from "hooks/store";
// helpers
import { isEmptyHtmlString } from "helpers/string.helper";
const fileService = new FileService();
@ -51,10 +53,10 @@ export const IssueCommentCreate: FC<TIssueCommentCreate> = (props) => {
const {
handleSubmit,
control,
watch,
formState: { isSubmitting },
reset,
watch,
} = useForm<Partial<TIssueComment>>({ defaultValues: { comment_html: "" } });
} = useForm<Partial<TIssueComment>>({ defaultValues: { comment_html: "<p></p>" } });
const onSubmit = async (formData: Partial<TIssueComment>) => {
await activityOperations.createComment(formData).finally(() => {
@ -63,14 +65,19 @@ export const IssueCommentCreate: FC<TIssueCommentCreate> = (props) => {
});
};
const isEmpty =
watch("comment_html") === "" ||
watch("comment_html")?.trim() === "" ||
watch("comment_html") === "<p></p>" ||
isEmptyHtmlString(watch("comment_html") ?? "");
return (
<div
// onKeyDown={(e) => {
// if (e.key === "Enter" && !e.shiftKey) {
// e.preventDefault();
// // handleSubmit(onSubmit)(e);
// }
// }}
onKeyDown={(e) => {
if (e.key === "Enter" && !e.shiftKey && !isEmpty) {
handleSubmit(onSubmit)(e);
}
}}
>
<Controller
name="access"
@ -81,15 +88,12 @@ export const IssueCommentCreate: FC<TIssueCommentCreate> = (props) => {
control={control}
render={({ field: { value, onChange } }) => (
<LiteTextEditorWithRef
onEnterKeyPress={(e) => {
handleSubmit(onSubmit)(e);
}}
cancelUploadImage={fileService.cancelUpload}
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}
deleteFile={fileService.getDeleteImageFunction(workspaceId)}
restoreFile={fileService.getRestoreImageFunction(workspaceId)}
ref={editorRef}
value={value ?? ""}
value={!value ? "<p></p>" : value}
customClassName="p-2"
editorContentCustomClassNames="min-h-[35px]"
debouncedUpdatesEnabled={false}
@ -105,7 +109,7 @@ export const IssueCommentCreate: FC<TIssueCommentCreate> = (props) => {
}
submitButton={
<Button
disabled={isSubmitting || watch("comment_html") === ""}
disabled={isSubmitting || isEmpty}
variant="primary"
type="submit"
className="!px-2.5 !py-1.5 !text-xs"

View File

@ -4,6 +4,7 @@ import {
PROJECT_ISSUES_LIST_WITH_PARAMS,
VIEW_ISSUES,
} from "constants/fetch-keys";
import * as DOMPurify from 'dompurify';
export const addSpaceIfCamelCase = (str: string) => {
if (str === undefined || str === null) return "";
@ -171,10 +172,10 @@ export const getFetchKeysForIssueMutation = (options: {
const ganttFetchKey = cycleId
? { ganttFetchKey: CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), ganttParams) }
: moduleId
? { ganttFetchKey: MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), ganttParams) }
: viewId
? { ganttFetchKey: VIEW_ISSUES(viewId.toString(), viewGanttParams) }
: { ganttFetchKey: PROJECT_ISSUES_LIST_WITH_PARAMS(projectId?.toString() ?? "", ganttParams) };
? { ganttFetchKey: MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), ganttParams) }
: viewId
? { ganttFetchKey: VIEW_ISSUES(viewId.toString(), viewGanttParams) }
: { ganttFetchKey: PROJECT_ISSUES_LIST_WITH_PARAMS(projectId?.toString() ?? "", ganttParams) };
return {
...ganttFetchKey,
@ -224,3 +225,10 @@ export const checkEmailValidity = (email: string): boolean => {
return isEmailValid;
};
export const isEmptyHtmlString = (htmlString: string) => {
// Remove HTML tags using regex
const cleanText = DOMPurify.sanitize(htmlString, { ALLOWED_TAGS: [] });
// Trim the string and check if it's empty
return cleanText.trim() === "";
};

View File

@ -34,6 +34,7 @@
"clsx": "^2.0.0",
"cmdk": "^0.2.0",
"date-fns": "^2.30.0",
"dompurify": "^3.0.9",
"dotenv": "^16.0.3",
"js-cookie": "^3.0.1",
"lodash": "^4.17.21",
@ -61,6 +62,7 @@
"uuid": "^9.0.0"
},
"devDependencies": {
"@types/dompurify": "^3.0.5",
"@types/js-cookie": "^3.0.2",
"@types/lodash": "^4.14.202",
"@types/node": "18.0.6",

View File

@ -2641,6 +2641,13 @@
resolved "https://registry.yarnpkg.com/@types/dom4/-/dom4-2.0.4.tgz#427a4ce8590727aed5ce0fe39a64f175a57fdc1c"
integrity sha512-PD+wqNhrjWFjAlSVd18jvChZvOXB2SOwAILBmuYev5zswBats5qmzs/QFoooLKd2omj9BT05a8MeSeRmXLGY+Q==
"@types/dompurify@^3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-3.0.5.tgz#02069a2fcb89a163bacf1a788f73cb415dd75cb7"
integrity sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==
dependencies:
"@types/trusted-types" "*"
"@types/estree@*", "@types/estree@^1.0.0":
version "1.0.5"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4"
@ -2805,7 +2812,7 @@
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@18.2.42", "@types/react@^18.2.42":
"@types/react@*", "@types/react@^18.2.42":
version "18.2.42"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.42.tgz#6f6b11a904f6d96dda3c2920328a97011a00aba7"
integrity sha512-c1zEr96MjakLYus/wPnuWDo1/zErfdU9rNsIGmE+NV71nx88FG9Ttgo5dqorXTu/LImX2f63WBP986gJkMPNbA==
@ -2843,7 +2850,7 @@
resolved "https://registry.yarnpkg.com/@types/throttle-debounce/-/throttle-debounce-2.1.0.tgz#1c3df624bfc4b62f992d3012b84c56d41eab3776"
integrity sha512-5eQEtSCoESnh2FsiLTxE121IiE60hnMqcb435fShf4bpLRjEu1Eoekht23y6zXS9Ts3l+Szu3TARnTsA0GkOkQ==
"@types/trusted-types@^2.0.2":
"@types/trusted-types@*", "@types/trusted-types@^2.0.2":
version "2.0.7"
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11"
integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==
@ -4082,6 +4089,11 @@ dom4@^2.1.5:
resolved "https://registry.yarnpkg.com/dom4/-/dom4-2.1.6.tgz#c90df07134aa0dbd81ed4d6ba1237b36fc164770"
integrity sha512-JkCVGnN4ofKGbjf5Uvc8mmxaATIErKQKSgACdBXpsQ3fY6DlIpAyWfiBSrGkttATssbDCp3psiAKWXk5gmjycA==
dompurify@^3.0.9:
version "3.0.9"
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.0.9.tgz#b3f362f24b99f53498c75d43ecbd784b0b3ad65e"
integrity sha512-uyb4NDIvQ3hRn6NiC+SIFaP4mJ/MdXlvtunaqK9Bn6dD3RuB/1S/gasEjDHD8eiaqdSael2vBv+hOs7Y+jhYOQ==
dot-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"