import { ChangeEvent, FC, useCallback, useEffect, useState } from "react"; import { Controller, useForm } from "react-hook-form"; import debounce from "lodash/debounce"; // packages import { RichTextEditor } from "@plane/rich-text-editor"; // hooks import { useMention, useProject, useUser } from "hooks/store"; import useReloadConfirmations from "hooks/use-reload-confirmation"; // components import { IssuePeekOverviewReactions } from "components/issues"; // ui import { TextArea } from "@plane/ui"; // types import { TIssue, IUser } from "@plane/types"; // services import { FileService } from "services/file.service"; // constants import { EUserProjectRoles } from "constants/project"; const fileService = new FileService(); interface IPeekOverviewIssueDetails { workspaceSlug: string; issue: TIssue; issueReactions: any; user: IUser | null; issueUpdate: (issue: Partial) => void; issueReactionCreate: (reaction: string) => void; issueReactionRemove: (reaction: string) => void; isSubmitting: "submitting" | "submitted" | "saved"; setIsSubmitting: (value: "submitting" | "submitted" | "saved") => void; } export const PeekOverviewIssueDetails: FC = (props) => { const { workspaceSlug, issue, issueReactions, user, issueUpdate, issueReactionCreate, issueReactionRemove, isSubmitting, setIsSubmitting, } = props; // states const [characterLimit, setCharacterLimit] = useState(false); // store hooks const { membership: { currentProjectRole }, } = useUser(); const { mentionHighlights, mentionSuggestions } = useMention(); const { getProjectById } = useProject(); // derived values const isAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER; // toast alert const { setShowAlert } = useReloadConfirmations(); // form info const { handleSubmit, watch, reset, control, formState: { errors }, } = useForm({ defaultValues: { name: issue.name, description_html: issue.description_html, }, }); const handleDescriptionFormSubmit = useCallback( async (formData: Partial) => { if (!formData?.name || formData?.name.length === 0 || formData?.name.length > 255) return; await issueUpdate({ ...issue, name: formData.name ?? "", description_html: formData.description_html ?? "

", }); }, [issue, issueUpdate] ); const [localTitleValue, setLocalTitleValue] = useState(""); const [localIssueDescription, setLocalIssueDescription] = useState({ id: issue.id, description_html: issue.description_html, }); // adding issue.description_html or issue.name to dependency array causes // editor rerendering on every save useEffect(() => { if (issue.id) { setLocalIssueDescription({ id: issue.id, description_html: issue.description_html }); setLocalTitleValue(issue.name); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [issue.id]); // TODO: Verify the exhaustive-deps warning // ADDING handleDescriptionFormSubmit TO DEPENDENCY ARRAY PRODUCES ADVERSE EFFECTS // TODO: Verify the exhaustive-deps warning // eslint-disable-next-line react-hooks/exhaustive-deps const debouncedFormSave = useCallback( debounce(async () => { handleSubmit(handleDescriptionFormSubmit)().finally(() => setIsSubmitting("submitted")); }, 1500), [handleSubmit] ); useEffect(() => { if (isSubmitting === "submitted") { setShowAlert(false); setTimeout(async () => { setIsSubmitting("saved"); }, 2000); } else if (isSubmitting === "submitting") { setShowAlert(true); } }, [isSubmitting, setShowAlert, setIsSubmitting]); // reset form values useEffect(() => { if (!issue) return; reset({ ...issue, }); }, [issue, reset]); const projectDetails = getProjectById(issue?.project_id); return ( <> {projectDetails?.identifier}-{issue?.sequence_id}
{isAllowed ? ( (