fix: Image restoration fixed (marks/unmarks an image to be deleted after a week) (#2859)

* image restoration fixed (marks an image to be deleted after a week)

* removed clgs

* added image constraints

* formatted editor-core package using yarn format

* lite-text-editor nothing to format

* rich-text-editor nothing to format

* formatted document-editor with prettier

* modified file service to follow api change

* fixed more formatting in document editor

* fixed all instances of types with that from the package

* fixed delete to work consistently (minor optimizations turned off)

* stop duplicate images inside editor

* restore image on editor creation

say if user A deletes image number 2, user B was also in the same issue and in their screen the image was there, if user B makes certain changes and that gets saved in backend, according to user B image 2 should exist but since user A deleted it, it'll not get restored and get deleted in 7 days, hence I've added a check such that whenever a issue loads we restore all images by default

* added restore image function with types

* replaced all instances to have restore image logic

* fixed issue detail for peek view

* disabled option to insert table inside a table
This commit is contained in:
M. Palanikannan 2023-11-28 11:34:20 +05:30 committed by sriram veeraghanta
parent 0fcadca53a
commit e01ca97fc9
63 changed files with 471 additions and 225 deletions

View file

@ -3,31 +3,34 @@ import { useState } from "react";
import { IMarking } from "..";
export const useEditorMarkings = () => {
const [markings, setMarkings] = useState<IMarking[]>([])
const [markings, setMarkings] = useState<IMarking[]>([]);
const updateMarkings = (json: any) => {
const nodes = json.content as any[]
const tempMarkings: IMarking[] = []
let h1Sequence: number = 0
let h2Sequence: number = 0
const nodes = json.content as any[];
const tempMarkings: IMarking[] = [];
let h1Sequence: number = 0;
let h2Sequence: number = 0;
if (nodes) {
nodes.forEach((node) => {
if (node.type === "heading" && (node.attrs.level === 1 || node.attrs.level === 2) && node.content) {
if (
node.type === "heading" &&
(node.attrs.level === 1 || node.attrs.level === 2) &&
node.content
) {
tempMarkings.push({
type: "heading",
level: node.attrs.level,
text: node.content[0].text,
sequence: node.attrs.level === 1 ? ++h1Sequence : ++h2Sequence
})
sequence: node.attrs.level === 1 ? ++h1Sequence : ++h2Sequence,
});
}
})
});
}
setMarkings(tempMarkings)
}
setMarkings(tempMarkings);
};
return {
updateMarkings,
markings,
}
}
return {
updateMarkings,
markings,
};
};

View file

@ -14,15 +14,14 @@ import { DocumentDetails } from "./types/editor-types";
import { PageRenderer } from "./components/page-renderer";
import { getMenuOptions } from "./utils/menu-options";
import { useRouter } from "next/router";
export type UploadImage = (file: File) => Promise<string>;
export type DeleteImage = (assetUrlWithWorkspaceId: string) => Promise<any>;
import { UploadImage, DeleteImage, RestoreImage } from "@plane/editor-types";
interface IDocumentEditor {
documentDetails: DocumentDetails;
value: string;
uploadFile: UploadImage;
deleteFile: DeleteImage;
restoreFile: RestoreImage;
customClassName?: string;
editorContentCustomClassNames?: string;
onChange: (json: any, html: string) => void;
@ -62,6 +61,7 @@ const DocumentEditor = ({
value,
uploadFile,
deleteFile,
restoreFile,
customClassName,
forwardedRef,
duplicationConfig,
@ -82,6 +82,7 @@ const DocumentEditor = ({
updateMarkings(json);
},
debouncedUpdatesEnabled,
restoreFile,
setIsSubmitting,
setShouldShowAlert,
value,

View file

@ -19,7 +19,7 @@ import {
HeadingThreeItem,
findTableAncestor,
} from "@plane/editor-core";
import { UploadImage } from "..";
import { UploadImage } from "@plane/editor-types";
export interface BubbleMenuItem {
name: string;

View file

@ -6,8 +6,9 @@ type Props = {
};
export const Icon: React.FC<Props> = ({ iconName, className = "" }) => (
<span className={`material-symbols-rounded text-sm leading-5 font-light ${className}`}>
<span
className={`material-symbols-rounded text-sm leading-5 font-light ${className}`}
>
{iconName}
</span>
);

View file

@ -1 +1 @@
export { FixedMenu } from "./fixed-menu";
export { FixedMenu } from "./fixed-menu";

View file

@ -1,4 +1,4 @@
import * as React from 'react';
import * as React from "react";
// next-themes
import { useTheme } from "next-themes";
@ -69,8 +69,16 @@ export const Tooltip: React.FC<Props> = ({
</div>
}
position={position}
renderTarget={({ isOpen: isTooltipOpen, ref: eleReference, ...tooltipProps }) =>
React.cloneElement(children, { ref: eleReference, ...tooltipProps, ...children.props })
renderTarget={({
isOpen: isTooltipOpen,
ref: eleReference,
...tooltipProps
}) =>
React.cloneElement(children, {
ref: eleReference,
...tooltipProps,
...children.props,
})
}
/>
);

View file

@ -1,8 +1,7 @@
export interface DocumentDetails {
title: string;
created_by: string;
created_on: Date;
last_updated_by: string;
last_updated_at: Date;
created_by: string;
created_on: Date;
last_updated_by: string;
last_updated_at: Date;
}

View file

@ -1,14 +1,13 @@
export interface IDuplicationConfig {
action: () => Promise<void>
action: () => Promise<void>;
}
export interface IPageLockConfig {
is_locked: boolean,
action: () => Promise<void>
locked_by?: string,
is_locked: boolean;
action: () => Promise<void>;
locked_by?: string;
}
export interface IPageArchiveConfig {
is_archived: boolean,
archived_at?: Date,
action: () => Promise<void>
}
is_archived: boolean;
archived_at?: Date;
action: () => Promise<void>;
}

View file

@ -2,34 +2,33 @@ import { Editor } from "@tiptap/react";
import { IMarking } from "..";
function findNthH1(editor: Editor, n: number, level: number): number {
let count = 0;
let pos = 0;
editor.state.doc.descendants((node, position) => {
if (node.type.name === 'heading' && node.attrs.level === level) {
count++;
if (count === n) {
pos = position;
return false;
}
}
});
return pos;
}
function scrollToNode(editor: Editor, pos: number): void {
const headingNode = editor.state.doc.nodeAt(pos);
if (headingNode) {
const headingDOM = editor.view.nodeDOM(pos);
if (headingDOM instanceof HTMLElement) {
headingDOM.scrollIntoView({ behavior: 'smooth' });
let count = 0;
let pos = 0;
editor.state.doc.descendants((node, position) => {
if (node.type.name === "heading" && node.attrs.level === level) {
count++;
if (count === n) {
pos = position;
return false;
}
}
}
});
return pos;
}
export function scrollSummary(editor: Editor, marking: IMarking) {
if (editor) {
const pos = findNthH1(editor, marking.sequence, marking.level)
scrollToNode(editor, pos)
function scrollToNode(editor: Editor, pos: number): void {
const headingNode = editor.state.doc.nodeAt(pos);
if (headingNode) {
const headingDOM = editor.view.nodeDOM(pos);
if (headingDOM instanceof HTMLElement) {
headingDOM.scrollIntoView({ behavior: "smooth" });
}
}
}
export function scrollSummary(editor: Editor, marking: IMarking) {
if (editor) {
const pos = findNthH1(editor, marking.sequence, marking.level);
scrollToNode(editor, pos);
}
}

View file

@ -1,12 +1,12 @@
import { Editor } from "@tiptap/core"
import { Editor } from "@tiptap/core";
export const copyMarkdownToClipboard = (editor: Editor | null) => {
const markdownOutput = editor?.storage.markdown.getMarkdown();
navigator.clipboard.writeText(markdownOutput)
}
navigator.clipboard.writeText(markdownOutput);
};
export const CopyPageLink = () => {
if (window){
navigator.clipboard.writeText(window.location.toString())
if (window) {
navigator.clipboard.writeText(window.location.toString());
}
}
};