[FEATURE] Enabled User @mentions and @mention-filters in core editor package (#2544)
* feat: created custom mention component * feat: added mention suggestions and suggestion highlights * feat: created mention suggestion list for displaying mention suggestions * feat: created custom mention text component, for handling click event * feat: exposed mention component * feat: integrated and exposed `mentions` componenet with `editor-core` * feat: integrated mentions extension with the core editor package * feat: exposed suggestion types from mentions * feat: added `mention-suggestion` parameters in `r-t-e` and `l-t-e` * feat: added `IssueMention` model in apiserver models * chore: updated activities background job and added bs4 in requirements * feat: added mention removal logic in issue_activity * chore: exposed mention types from `r-t-e` and `l-t-e` * feat: integrated mentions in side peek view description form * feat: added mentions in issue modal form * feat: created custom react-hook for editor suggestions * feat: integrated mention suggestions block in RichTextEditor * feat: added `mentions` integration in `lite-text-editor` instances * fix: tailwind loading nodemodules from packages * feat: added styles for the mention suggestion list * fix: update module import to resolve build failure * feat: added mentions as an issue filter * feat: added UI Changes to Implement `mention` filters * feat: added `mentions` as a filter option in the header * feat: added mentions in the filter list options * feat: added mentions in default display filter options * feat: added filters in applied and issue params in store * feat: modified types for adding mentions as a filter option * feat: modified `notification-card` to display message when it exists in object * feat: rewrote user mention management upon the changes made in develop * chore: merged debounce PR with the current PR for tracing changes * fix: mentions_filters updated with the new setup * feat: updated requirements for bs4 * feat: modified `mentions-filter` to remove many to many dependency * feat: implemented list manipulation instead of for loop * feat: added readonly functionality in `read-only` editor core * feat: added UI Changes for read-only mode * feat: added mentions store in web Root Store * chore: renamed `use-editor-suggestions` hook * feat: UI Improvements for conditional highlights w.r.t readonly in mentionNode * fix: removed mentions from `filter_set` parameters * fix: minor merge fixes * fix: package lock updates --------- Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
This commit is contained in:
parent
490e032ac6
commit
d511799f31
60 changed files with 1662 additions and 52 deletions
158
web/components/web-view/issue-web-view-form.tsx
Normal file
158
web/components/web-view/issue-web-view-form.tsx
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { Controller } from "react-hook-form";
|
||||
|
||||
// services
|
||||
import { FileService } from "services/file.service";
|
||||
// hooks
|
||||
import { useDebouncedCallback } from "use-debounce";
|
||||
import useReloadConfirmations from "hooks/use-reload-confirmation";
|
||||
// ui
|
||||
import { TextArea } from "@plane/ui";
|
||||
// components
|
||||
import { RichTextEditor } from "@plane/rich-text-editor";
|
||||
import { Label } from "components/web-view";
|
||||
// types
|
||||
import type { IIssue } from "types";
|
||||
import useEditorSuggestions from "hooks/use-editor-suggestions";
|
||||
|
||||
type Props = {
|
||||
isAllowed: boolean;
|
||||
issueDetails: IIssue;
|
||||
submitChanges: (data: Partial<IIssue>) => Promise<void>;
|
||||
register: any;
|
||||
control: any;
|
||||
watch: any;
|
||||
handleSubmit: any;
|
||||
};
|
||||
|
||||
// services
|
||||
const fileService = new FileService();
|
||||
|
||||
export const IssueWebViewForm: React.FC<Props> = (props) => {
|
||||
const { isAllowed, issueDetails, submitChanges, control, watch, handleSubmit } = props;
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
const [characterLimit, setCharacterLimit] = useState(false);
|
||||
const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved");
|
||||
|
||||
const { setShowAlert } = useReloadConfirmations();
|
||||
|
||||
const editorSuggestion = useEditorSuggestions(workspaceSlug as string | undefined, issueDetails.project_detail.id)
|
||||
|
||||
useEffect(() => {
|
||||
if (isSubmitting === "submitted") {
|
||||
setShowAlert(false);
|
||||
setTimeout(async () => {
|
||||
setIsSubmitting("saved");
|
||||
}, 2000);
|
||||
} else if (isSubmitting === "submitting") {
|
||||
setShowAlert(true);
|
||||
}
|
||||
}, [isSubmitting, setShowAlert]);
|
||||
|
||||
const debouncedTitleSave = useDebouncedCallback(async () => {
|
||||
setTimeout(async () => {
|
||||
handleSubmit(handleDescriptionFormSubmit)().finally(() => setIsSubmitting("submitted"));
|
||||
}, 500);
|
||||
}, 1000);
|
||||
|
||||
const handleDescriptionFormSubmit = useCallback(
|
||||
async (formData: Partial<IIssue>) => {
|
||||
if (!formData?.name || formData?.name.length === 0 || formData?.name.length > 255) return;
|
||||
|
||||
await submitChanges({
|
||||
name: formData.name ?? "",
|
||||
description_html: formData.description_html ?? "<p></p>",
|
||||
});
|
||||
},
|
||||
[submitChanges]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col">
|
||||
<Label>Title</Label>
|
||||
<div className="relative">
|
||||
{isAllowed ? (
|
||||
<Controller
|
||||
name="name"
|
||||
control={control}
|
||||
render={({ field: { value } }) => (
|
||||
<TextArea
|
||||
id="name"
|
||||
name="name"
|
||||
value={value}
|
||||
placeholder="Enter issue name"
|
||||
onFocus={() => setCharacterLimit(true)}
|
||||
onChange={() => {
|
||||
setCharacterLimit(false);
|
||||
setIsSubmitting("submitting");
|
||||
debouncedTitleSave();
|
||||
}}
|
||||
required={true}
|
||||
className="min-h-10 block w-full resize-none overflow-hidden rounded border bg-transparent px-3 py-2 text-xl outline-none ring-0 focus:ring-1 focus:ring-custom-primary"
|
||||
role="textbox"
|
||||
disabled={!isAllowed}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<h4 className="break-words text-2xl font-semibold">{issueDetails?.name}</h4>
|
||||
)}
|
||||
{characterLimit && isAllowed && (
|
||||
<div className="pointer-events-none absolute bottom-1 right-1 z-[2] rounded bg-custom-background-100 text-custom-text-200 p-0.5 text-xs">
|
||||
<span className={`${watch("name").length === 0 || watch("name").length > 255 ? "text-red-500" : ""}`}>
|
||||
{watch("name").length}
|
||||
</span>
|
||||
/255
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Description</Label>
|
||||
<div className="relative">
|
||||
<Controller
|
||||
name="description_html"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => {
|
||||
if(value==null)return <></>;
|
||||
return <RichTextEditor
|
||||
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}
|
||||
deleteFile={fileService.deleteImage}
|
||||
value={
|
||||
!value || value === "" || (typeof value === "object" && Object.keys(value).length === 0)
|
||||
? "<p></p>"
|
||||
: value
|
||||
}
|
||||
debouncedUpdatesEnabled={true}
|
||||
setShouldShowAlert={setShowAlert}
|
||||
setIsSubmitting={setIsSubmitting}
|
||||
customClassName={isAllowed ? "min-h-[150px] shadow-sm" : "!p-0 !pt-2 text-custom-text-200"}
|
||||
noBorder={!isAllowed}
|
||||
onChange={(description: Object, description_html: string) => {
|
||||
setShowAlert(true);
|
||||
setIsSubmitting("submitting");
|
||||
onChange(description_html);
|
||||
handleSubmit(handleDescriptionFormSubmit)().finally(() => setIsSubmitting("submitted"));
|
||||
}}
|
||||
mentionSuggestions={editorSuggestion.mentionSuggestions}
|
||||
mentionHighlights={editorSuggestion.mentionHighlights}
|
||||
/>
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className={`absolute right-5 bottom-5 text-xs text-custom-text-200 border border-custom-border-400 rounded-xl w-[6.5rem] py-1 z-10 flex items-center justify-center ${
|
||||
isSubmitting === "saved" ? "fadeOut" : "fadeIn"
|
||||
}`}
|
||||
>
|
||||
{isSubmitting === "submitting" ? "Saving..." : "Saved"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue