mirror of
https://github.com/makeplane/plane.git
synced 2026-07-14 14:31:37 +02:00
[WIKI-528] fix: save the issue description on component's unmount as well #3598
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { FC, useCallback, useEffect, useState } from "react";
|
||||
import { FC, useCallback, useEffect, useState, useRef } from "react";
|
||||
import debounce from "lodash/debounce";
|
||||
import { observer } from "mobx-react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
// plane imports
|
||||
import type { EditorReadOnlyRefApi, EditorRefApi, TExtensions } from "@plane/editor";
|
||||
import type { EditorRefApi, TExtensions } from "@plane/editor";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { EFileAssetType, TNameDescriptionLoader } from "@plane/types";
|
||||
import { Loader } from "@plane/ui";
|
||||
@@ -61,6 +61,8 @@ export const DescriptionInput: FC<DescriptionInputProps> = observer((props) => {
|
||||
id: itemId,
|
||||
description_html: initialValue,
|
||||
});
|
||||
// ref to track if there are unsaved changes
|
||||
const hasUnsavedChanges = useRef(false);
|
||||
// store hooks
|
||||
const { getWorkspaceBySlug } = useWorkspace();
|
||||
const { uploadEditorAsset } = useEditorAsset();
|
||||
@@ -93,6 +95,8 @@ export const DescriptionInput: FC<DescriptionInputProps> = observer((props) => {
|
||||
id: itemId,
|
||||
description_html: initialValue === "" ? "<p></p>" : initialValue,
|
||||
});
|
||||
// Reset unsaved changes flag when form is reset
|
||||
hasUnsavedChanges.current = false;
|
||||
}, [initialValue, itemId, reset]);
|
||||
|
||||
// ADDING handleDescriptionFormSubmit TO DEPENDENCY ARRAY PRODUCES ADVERSE EFFECTS
|
||||
@@ -100,11 +104,35 @@ export const DescriptionInput: FC<DescriptionInputProps> = observer((props) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const debouncedFormSave = useCallback(
|
||||
debounce(async () => {
|
||||
handleSubmit(handleDescriptionFormSubmit)().finally(() => setIsSubmitting("submitted"));
|
||||
handleSubmit(handleDescriptionFormSubmit)().finally(() => {
|
||||
setIsSubmitting("submitted");
|
||||
hasUnsavedChanges.current = false;
|
||||
});
|
||||
}, 1500),
|
||||
[handleSubmit, itemId]
|
||||
);
|
||||
|
||||
// Save on unmount if there are unsaved changes
|
||||
useEffect(
|
||||
() => () => {
|
||||
debouncedFormSave.cancel();
|
||||
|
||||
if (hasUnsavedChanges.current) {
|
||||
handleSubmit(handleDescriptionFormSubmit)()
|
||||
.catch((error) => {
|
||||
console.error("Failed to save description on unmount:", error);
|
||||
})
|
||||
.finally(() => {
|
||||
setIsSubmitting("submitted");
|
||||
hasUnsavedChanges.current = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
// since we don't want to save on unmount if there are no unsaved changes, no deps are needed
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{localDescription.description_html ? (
|
||||
@@ -126,6 +154,7 @@ export const DescriptionInput: FC<DescriptionInputProps> = observer((props) => {
|
||||
onChange={(_description: object, description_html: string) => {
|
||||
setIsSubmitting("submitting");
|
||||
onChange(description_html);
|
||||
hasUnsavedChanges.current = true;
|
||||
debouncedFormSave();
|
||||
}}
|
||||
placeholder={
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { FC, useCallback, useEffect } from "react";
|
||||
import { FC, useCallback, useEffect, useRef } from "react";
|
||||
import debounce from "lodash/debounce";
|
||||
import { observer } from "mobx-react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
@@ -44,6 +44,8 @@ export const ProjectDescriptionInput: FC<ProjectDescriptionInputProps> = observe
|
||||
const { uploadEditorAsset } = useEditorAsset();
|
||||
// plane hooks
|
||||
const { t } = useTranslation();
|
||||
// ref to track if there are unsaved changes
|
||||
const hasUnsavedChanges = useRef(false);
|
||||
|
||||
const { handleSubmit, reset, control } = useForm<TProject>({
|
||||
defaultValues: {
|
||||
@@ -71,6 +73,8 @@ export const ProjectDescriptionInput: FC<ProjectDescriptionInputProps> = observe
|
||||
id: project.id,
|
||||
description_html: initialValue === "" ? "<p></p>" : initialValue,
|
||||
});
|
||||
// Reset unsaved changes flag when form is reset
|
||||
hasUnsavedChanges.current = false;
|
||||
}, [initialValue, reset]);
|
||||
|
||||
// ADDING handleDescriptionFormSubmit TO DEPENDENCY ARRAY PRODUCES ADVERSE EFFECTS
|
||||
@@ -78,11 +82,35 @@ export const ProjectDescriptionInput: FC<ProjectDescriptionInputProps> = observe
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const debouncedFormSave = useCallback(
|
||||
debounce(async () => {
|
||||
handleSubmit(handleDescriptionFormSubmit)().finally(() => setIsSubmitting("submitted"));
|
||||
handleSubmit(handleDescriptionFormSubmit)().finally(() => {
|
||||
setIsSubmitting("submitted");
|
||||
hasUnsavedChanges.current = false;
|
||||
});
|
||||
}, 1500),
|
||||
[handleSubmit, project.id]
|
||||
);
|
||||
|
||||
// Save on unmount if there are unsaved changes
|
||||
useEffect(
|
||||
() => () => {
|
||||
debouncedFormSave.cancel();
|
||||
|
||||
if (hasUnsavedChanges.current) {
|
||||
handleSubmit(handleDescriptionFormSubmit)()
|
||||
.catch((error) => {
|
||||
console.error("Failed to save description on unmount:", error);
|
||||
})
|
||||
.finally(() => {
|
||||
setIsSubmitting("submitted");
|
||||
hasUnsavedChanges.current = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
// since we don't want to save on unmount if there are no unsaved changes, no deps are needed
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{project?.description_html === undefined ? (
|
||||
@@ -129,6 +157,7 @@ export const ProjectDescriptionInput: FC<ProjectDescriptionInputProps> = observe
|
||||
onChange={(_description: object, description_html: string) => {
|
||||
setIsSubmitting("submitting");
|
||||
onChange(description_html);
|
||||
hasUnsavedChanges.current = true;
|
||||
debouncedFormSave();
|
||||
}}
|
||||
placeholder={
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { FC, useCallback, useEffect, useState } from "react";
|
||||
import { FC, useCallback, useEffect, useRef, useState } from "react";
|
||||
import debounce from "lodash/debounce";
|
||||
import { observer } from "mobx-react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
@@ -50,6 +50,8 @@ export const TeamspaceDescriptionInput: FC<TeamspaceDescriptionInputProps> = obs
|
||||
id: teamspaceId,
|
||||
description_html: initialValue,
|
||||
});
|
||||
// ref to track if there are unsaved changes
|
||||
const hasUnsavedChanges = useRef(false);
|
||||
|
||||
const setIsSubmitting = (loaderType: TNameDescriptionLoader) => {
|
||||
updateTeamspaceNameDescriptionLoader(teamspaceId, loaderType);
|
||||
@@ -80,17 +82,42 @@ export const TeamspaceDescriptionInput: FC<TeamspaceDescriptionInputProps> = obs
|
||||
id: teamspaceId,
|
||||
description_html: initialValue === "" ? "<p></p>" : initialValue,
|
||||
});
|
||||
hasUnsavedChanges.current = false;
|
||||
}, [initialValue, teamspaceId, reset]);
|
||||
|
||||
// ADDING handleDescriptionFormSubmit TO DEPENDENCY ARRAY PRODUCES ADVERSE EFFECTS
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const debouncedFormSave = useCallback(
|
||||
debounce(async () => {
|
||||
handleSubmit(handleDescriptionFormSubmit)().finally(() => setIsSubmitting("submitted"));
|
||||
handleSubmit(handleDescriptionFormSubmit)().finally(() => {
|
||||
setIsSubmitting("submitted");
|
||||
hasUnsavedChanges.current = false;
|
||||
});
|
||||
}, 1500),
|
||||
[handleSubmit, teamspaceId]
|
||||
);
|
||||
|
||||
// Save on unmount if there are unsaved changes
|
||||
useEffect(
|
||||
() => () => {
|
||||
debouncedFormSave.cancel();
|
||||
|
||||
if (hasUnsavedChanges.current) {
|
||||
handleSubmit(handleDescriptionFormSubmit)()
|
||||
.catch((error) => {
|
||||
console.error("Failed to save description on unmount:", error);
|
||||
})
|
||||
.finally(() => {
|
||||
setIsSubmitting("submitted");
|
||||
hasUnsavedChanges.current = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
// since we don't want to save on unmount if there are no unsaved changes, no deps are needed
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{localTeamspaceDescription.description_html ? (
|
||||
@@ -109,6 +136,7 @@ export const TeamspaceDescriptionInput: FC<TeamspaceDescriptionInputProps> = obs
|
||||
onChange={(description_json: object, description_html: string) => {
|
||||
setIsSubmitting("submitting");
|
||||
onChange(description_html);
|
||||
hasUnsavedChanges.current = true;
|
||||
setValue("description_json", description_json);
|
||||
debouncedFormSave();
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user