[WEB-926] fix: issue description component update handled in peek overview, issue detail, and inbox issues (#4159)

* fix: issue description mutation

* fix: implemented same issue description logic for issue detail and inbox issue description

* fix: fixed parent issue title dissapearing while loading the issue detail page

* chore: code cleanup

* chore: handled exception when issue in not available in issue detail in issue store

---------

Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
This commit is contained in:
guru_sainath 2024-04-10 14:58:46 +05:30 committed by GitHub
parent c80638090f
commit d0cb00f28a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 51 additions and 56 deletions

View File

@ -2,7 +2,7 @@ import { Dispatch, SetStateAction, useEffect, useMemo } from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { TIssue } from "@plane/types"; import { TIssue } from "@plane/types";
import { Loader, TOAST_TYPE, setToast } from "@plane/ui"; import { TOAST_TYPE, setToast } from "@plane/ui";
// components // components
import { InboxIssueProperties } from "@/components/inbox/content"; import { InboxIssueProperties } from "@/components/inbox/content";
import { import {
@ -13,7 +13,7 @@ import {
TIssueOperations, TIssueOperations,
} from "@/components/issues"; } from "@/components/issues";
// hooks // hooks
import { useEventTracker, useProjectInbox, useUser } from "@/hooks/store"; import { useEventTracker, useUser } from "@/hooks/store";
import useReloadConfirmations from "@/hooks/use-reload-confirmation"; import useReloadConfirmations from "@/hooks/use-reload-confirmation";
// store types // store types
import { IInboxIssueStore } from "@/store/inbox/inbox-issue.store"; import { IInboxIssueStore } from "@/store/inbox/inbox-issue.store";
@ -32,7 +32,6 @@ export const InboxIssueMainContent: React.FC<Props> = observer((props) => {
const { workspaceSlug, projectId, inboxIssue, is_editable, isSubmitting, setIsSubmitting } = props; const { workspaceSlug, projectId, inboxIssue, is_editable, isSubmitting, setIsSubmitting } = props;
// hooks // hooks
const { currentUser } = useUser(); const { currentUser } = useUser();
const { isLoading } = useProjectInbox();
const { setShowAlert } = useReloadConfirmations(isSubmitting === "submitting"); const { setShowAlert } = useReloadConfirmations(isSubmitting === "submitting");
const { captureIssueEvent } = useEventTracker(); const { captureIssueEvent } = useEventTracker();
@ -131,22 +130,16 @@ export const InboxIssueMainContent: React.FC<Props> = observer((props) => {
value={issue.name} value={issue.name}
/> />
{isLoading ? ( <IssueDescriptionInput
<Loader className="h-[150px] space-y-2 overflow-hidden rounded-md border border-custom-border-200 p-2 py-2"> workspaceSlug={workspaceSlug}
<Loader.Item width="100%" height="132px" /> projectId={issue.project_id}
</Loader> issueId={issue.id}
) : ( value={issueDescription}
<IssueDescriptionInput initialValue={issueDescription}
workspaceSlug={workspaceSlug} disabled={!is_editable}
projectId={issue.project_id} issueOperations={issueOperations}
issueId={issue.id} setIsSubmitting={(value) => setIsSubmitting(value)}
value={issueDescription} />
initialValue={issueDescription}
disabled={!is_editable}
issueOperations={issueOperations}
setIsSubmitting={(value) => setIsSubmitting(value)}
/>
)}
{currentUser && ( {currentUser && (
<IssueReaction <IssueReaction

View File

@ -25,6 +25,7 @@ export type IssueDescriptionInputProps = {
export const IssueDescriptionInput: FC<IssueDescriptionInputProps> = (props) => { export const IssueDescriptionInput: FC<IssueDescriptionInputProps> = (props) => {
const { workspaceSlug, projectId, issueId, value, initialValue, disabled, issueOperations, setIsSubmitting } = props; const { workspaceSlug, projectId, issueId, value, initialValue, disabled, issueOperations, setIsSubmitting } = props;
// states // states
const [localIssueId, setLocalIssueId] = useState(issueId);
const [descriptionHTML, setDescriptionHTML] = useState(value); const [descriptionHTML, setDescriptionHTML] = useState(value);
// store hooks // store hooks
const { mentionHighlights, mentionSuggestions } = useMention(); const { mentionHighlights, mentionSuggestions } = useMention();
@ -35,8 +36,14 @@ export const IssueDescriptionInput: FC<IssueDescriptionInputProps> = (props) =>
const workspaceId = getWorkspaceBySlug(workspaceSlug)?.id as string; const workspaceId = getWorkspaceBySlug(workspaceSlug)?.id as string;
useEffect(() => { useEffect(() => {
setDescriptionHTML(value); if (issueId !== localIssueId) {
}, [value]); setDescriptionHTML(undefined);
setLocalIssueId(issueId);
} else {
setDescriptionHTML(value);
}
return () => setDescriptionHTML(undefined);
}, [issueId, localIssueId, value]);
useEffect(() => { useEffect(() => {
if (debouncedValue && debouncedValue !== value) { if (debouncedValue && debouncedValue !== value) {

View File

@ -28,7 +28,6 @@ export const IssueMainContent: React.FC<Props> = observer((props) => {
const { workspaceSlug, projectId, issueId, issueOperations, is_editable } = props; const { workspaceSlug, projectId, issueId, issueOperations, is_editable } = props;
// states // states
const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved"); const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved");
const [issueDescription, setIssueDescription] = useState<string | undefined>(undefined);
// hooks // hooks
const { currentUser } = useUser(); const { currentUser } = useUser();
const { projectStates } = useProjectState(); const { projectStates } = useProjectState();
@ -53,16 +52,12 @@ export const IssueMainContent: React.FC<Props> = observer((props) => {
const currentIssueState = projectStates?.find((s) => s.id === issue.state_id); const currentIssueState = projectStates?.find((s) => s.id === issue.state_id);
useEffect(() => { const issueDescription =
setIssueDescription( issue.description_html !== undefined || issue.description_html !== null
issue.description_html !== undefined || issue.description_html !== null ? issue.description_html != ""
? issue.description_html != "" ? issue.description_html
? issue.description_html : "<p></p>"
: "<p></p>" : undefined;
: undefined
);
return () => setIssueDescription(undefined);
}, [issue.description_html]);
return ( return (
<> <>
@ -99,18 +94,16 @@ export const IssueMainContent: React.FC<Props> = observer((props) => {
value={issue.name} value={issue.name}
/> />
{issue?.description_html === issueDescription && ( <IssueDescriptionInput
<IssueDescriptionInput workspaceSlug={workspaceSlug}
workspaceSlug={workspaceSlug} projectId={issue.project_id}
projectId={issue.project_id} issueId={issue.id}
issueId={issue.id} value={issueDescription}
value={issueDescription} initialValue={issueDescription}
initialValue={issueDescription} disabled={!is_editable}
disabled={!is_editable} issueOperations={issueOperations}
issueOperations={issueOperations} setIsSubmitting={(value) => setIsSubmitting(value)}
setIsSubmitting={(value) => setIsSubmitting(value)} />
/>
)}
{currentUser && ( {currentUser && (
<IssueReaction <IssueReaction

View File

@ -1,4 +1,5 @@
import { FC } from "react"; import { FC } from "react";
import { observer } from "mobx-react";
import Link from "next/link"; import Link from "next/link";
import { MinusCircle } from "lucide-react"; import { MinusCircle } from "lucide-react";
import { TIssue } from "@plane/types"; import { TIssue } from "@plane/types";
@ -19,7 +20,7 @@ export type TIssueParentDetail = {
issueOperations: TIssueOperations; issueOperations: TIssueOperations;
}; };
export const IssueParentDetail: FC<TIssueParentDetail> = (props) => { export const IssueParentDetail: FC<TIssueParentDetail> = observer((props) => {
const { workspaceSlug, projectId, issueId, issue, issueOperations } = props; const { workspaceSlug, projectId, issueId, issue, issueOperations } = props;
// hooks // hooks
const { issueMap } = useIssues(); const { issueMap } = useIssues();
@ -68,4 +69,4 @@ export const IssueParentDetail: FC<TIssueParentDetail> = (props) => {
</div> </div>
</> </>
); );
}; });

View File

@ -66,7 +66,6 @@ export class IssueStore implements IIssueStore {
}; };
let issue: TIssue; let issue: TIssue;
let issuePayload: TIssue;
if (issueType === "ARCHIVED") if (issueType === "ARCHIVED")
issue = await this.issueArchiveService.retrieveArchivedIssue(workspaceSlug, projectId, issueId, query); issue = await this.issueArchiveService.retrieveArchivedIssue(workspaceSlug, projectId, issueId, query);
@ -76,7 +75,7 @@ export class IssueStore implements IIssueStore {
if (!issue) throw new Error("Issue not found"); if (!issue) throw new Error("Issue not found");
issuePayload = { const issuePayload: TIssue = {
id: issue?.id, id: issue?.id,
sequence_id: issue?.sequence_id, sequence_id: issue?.sequence_id,
name: issue?.name, name: issue?.name,
@ -110,8 +109,10 @@ export class IssueStore implements IIssueStore {
// store handlers from issue detail // store handlers from issue detail
// parent // parent
if (issue && issue?.parent && issue?.parent?.id) if (issue && issue?.parent && issue?.parent?.id) {
this.rootIssueDetailStore.rootIssueStore.issues.addIssue([issue.parent]); const parentIssue = await this.issueService.retrieve(workspaceSlug, projectId, issue?.parent?.id);
this.rootIssueDetailStore.rootIssueStore.issues.addIssue([parentIssue]);
}
// assignees // assignees
// labels // labels
// state // state
@ -184,7 +185,7 @@ export class IssueStore implements IIssueStore {
}; };
addModulesToIssue = async (workspaceSlug: string, projectId: string, issueId: string, moduleIds: string[]) => { addModulesToIssue = async (workspaceSlug: string, projectId: string, issueId: string, moduleIds: string[]) => {
const _module = await this.rootIssueDetailStore.rootIssueStore.moduleIssues.addModulesToIssue( const currentModule = await this.rootIssueDetailStore.rootIssueStore.moduleIssues.addModulesToIssue(
workspaceSlug, workspaceSlug,
projectId, projectId,
issueId, issueId,
@ -192,28 +193,28 @@ export class IssueStore implements IIssueStore {
); );
if (moduleIds && moduleIds.length > 0) if (moduleIds && moduleIds.length > 0)
await this.rootIssueDetailStore.activity.fetchActivities(workspaceSlug, projectId, issueId); await this.rootIssueDetailStore.activity.fetchActivities(workspaceSlug, projectId, issueId);
return _module; return currentModule;
}; };
removeModulesFromIssue = async (workspaceSlug: string, projectId: string, issueId: string, moduleIds: string[]) => { removeModulesFromIssue = async (workspaceSlug: string, projectId: string, issueId: string, moduleIds: string[]) => {
const _module = await this.rootIssueDetailStore.rootIssueStore.moduleIssues.removeModulesFromIssue( const currentModule = await this.rootIssueDetailStore.rootIssueStore.moduleIssues.removeModulesFromIssue(
workspaceSlug, workspaceSlug,
projectId, projectId,
issueId, issueId,
moduleIds moduleIds
); );
await this.rootIssueDetailStore.activity.fetchActivities(workspaceSlug, projectId, issueId); await this.rootIssueDetailStore.activity.fetchActivities(workspaceSlug, projectId, issueId);
return _module; return currentModule;
}; };
removeIssueFromModule = async (workspaceSlug: string, projectId: string, moduleId: string, issueId: string) => { removeIssueFromModule = async (workspaceSlug: string, projectId: string, moduleId: string, issueId: string) => {
const _module = await this.rootIssueDetailStore.rootIssueStore.moduleIssues.removeIssueFromModule( const currentModule = await this.rootIssueDetailStore.rootIssueStore.moduleIssues.removeIssueFromModule(
workspaceSlug, workspaceSlug,
projectId, projectId,
moduleId, moduleId,
issueId issueId
); );
await this.rootIssueDetailStore.activity.fetchActivities(workspaceSlug, projectId, issueId); await this.rootIssueDetailStore.activity.fetchActivities(workspaceSlug, projectId, issueId);
return _module; return currentModule;
}; };
} }