[WEB-1244] fix: add better image insertion and replacement logic in the editor (#4508)
* fix: add better image insertion and replacement logic * refactor: image handling in editor * chore: remove passing uploadKey around * refactor: remove unused code * fix: redundant files removed * fix: add is editor ready to discard api to control behvaiours from our app * fix: focus issues and image insertion position when not using slash command * fix: import order fixed
This commit is contained in:
parent
061a447734
commit
ade6eded69
22 changed files with 483 additions and 366 deletions
|
|
@ -18,6 +18,7 @@ import { ISSUE_CREATED } from "@/constants/event-tracker";
|
|||
import { renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
||||
// hooks
|
||||
import { useEventTracker, useProjectInbox, useWorkspace } from "@/hooks/store";
|
||||
import useKeypress from "@/hooks/use-keypress";
|
||||
|
||||
type TInboxIssueCreateRoot = {
|
||||
workspaceSlug: string;
|
||||
|
|
@ -62,8 +63,33 @@ export const InboxIssueCreateRoot: FC<TInboxIssueCreateRoot> = observer((props)
|
|||
[formData]
|
||||
);
|
||||
|
||||
const handleEscKeyDown = (event: KeyboardEvent) => {
|
||||
if (descriptionEditorRef.current?.isEditorReadyToDiscard()) {
|
||||
handleModalClose();
|
||||
} else {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Editor is still processing changes. Please wait before proceeding.",
|
||||
});
|
||||
event.preventDefault(); // Prevent default action if editor is not ready to discard
|
||||
}
|
||||
};
|
||||
|
||||
useKeypress("Escape", handleEscKeyDown);
|
||||
|
||||
const handleFormSubmit = async (event: FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (descriptionEditorRef.current?.isEditorReadyToDiscard()) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Editor is still processing changes. Please wait before proceeding.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const payload: Partial<TIssue> = {
|
||||
name: formData.name || "",
|
||||
description_html: formData.description_html || "<p></p>",
|
||||
|
|
@ -155,7 +181,22 @@ export const InboxIssueCreateRoot: FC<TInboxIssueCreateRoot> = observer((props)
|
|||
<span className="text-xs">Create more</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Button variant="neutral-primary" size="sm" type="button" onClick={handleModalClose}>
|
||||
<Button
|
||||
variant="neutral-primary"
|
||||
size="sm"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (descriptionEditorRef.current?.isEditorReadyToDiscard()) {
|
||||
handleModalClose();
|
||||
} else {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Editor is still processing changes. Please wait before proceeding.",
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
Discard
|
||||
</Button>
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import { getChangedIssuefields, getDescriptionPlaceholder } from "@/helpers/issu
|
|||
import { shouldRenderProject } from "@/helpers/project.helper";
|
||||
// hooks
|
||||
import { useAppRouter, useEstimate, useInstance, useIssueDetail, useProject, useWorkspace } from "@/hooks/store";
|
||||
import useKeypress from "@/hooks/use-keypress";
|
||||
import { useProjectIssueProperties } from "@/hooks/use-project-issue-properties";
|
||||
// services
|
||||
import { AIService } from "@/services/ai.service";
|
||||
|
|
@ -121,6 +122,21 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
|||
const { getProjectById } = useProject();
|
||||
const { areEstimatesEnabledForProject } = useEstimate();
|
||||
|
||||
function handleKeyDown(event: KeyboardEvent) {
|
||||
if (editorRef.current?.isEditorReadyToDiscard()) {
|
||||
onClose();
|
||||
} else {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Editor is still processing changes. Please wait before proceeding.",
|
||||
});
|
||||
event.preventDefault(); // Prevent default action if editor is not ready to discard
|
||||
}
|
||||
}
|
||||
|
||||
useKeypress("Escape", handleKeyDown);
|
||||
|
||||
const {
|
||||
issue: { getIssueById },
|
||||
} = useIssueDetail();
|
||||
|
|
@ -168,6 +184,16 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
|||
const issueName = watch("name");
|
||||
|
||||
const handleFormSubmit = async (formData: Partial<TIssue>, is_draft_issue = false) => {
|
||||
// Check if the editor is ready to discard
|
||||
if (!editorRef.current?.isEditorReadyToDiscard()) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Editor is not ready to discard changes.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const submitData = !data?.id
|
||||
? formData
|
||||
: {
|
||||
|
|
@ -740,7 +766,22 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
|||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="neutral-primary" size="sm" onClick={onClose} tabIndex={getTabIndex("discard_button")}>
|
||||
<Button
|
||||
variant="neutral-primary"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
if (editorRef.current?.isEditorReadyToDiscard()) {
|
||||
onClose();
|
||||
} else {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Editor is still processing changes. Please wait before proceeding.",
|
||||
});
|
||||
}
|
||||
}}
|
||||
tabIndex={getTabIndex("discard_button")}
|
||||
>
|
||||
Discard
|
||||
</Button>
|
||||
{isDraft && (
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
|||
},
|
||||
issueId
|
||||
);
|
||||
|
||||
const handleKeyDown = () => {
|
||||
const slashCommandDropdownElement = document.querySelector("#slash-command");
|
||||
const dropdownElement = document.activeElement?.tagName === "INPUT";
|
||||
|
|
@ -74,6 +75,7 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
|||
if (issueElement) issueElement?.focus();
|
||||
}
|
||||
};
|
||||
|
||||
useKeypress("Escape", handleKeyDown);
|
||||
|
||||
const handleRestore = async () => {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { useEffect } from "react";
|
||||
|
||||
const useKeypress = (key: string, callback: () => void) => {
|
||||
const useKeypress = (key: string, callback: (event: KeyboardEvent) => void) => {
|
||||
useEffect(() => {
|
||||
const handleKeydown = (event: KeyboardEvent) => {
|
||||
if (event.key === key) {
|
||||
callback();
|
||||
callback(event);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ const useKeypress = (key: string, callback: () => void) => {
|
|||
return () => {
|
||||
document.removeEventListener("keydown", handleKeydown);
|
||||
};
|
||||
});
|
||||
}, [key, callback]);
|
||||
};
|
||||
|
||||
export default useKeypress;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue