Skip to content

Commit

Permalink
fix: image restoration fixed and aspect ratio added to old images to …
Browse files Browse the repository at this point in the history
…stop updates on load
  • Loading branch information
Palanikannan1437 committed Oct 15, 2024
1 parent 2fab0b5 commit 501ba51
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const CustomImageBlock: React.FC<CustomImageBlockProps> = (props) => {
src: remoteImageSrc,
setEditorContainer,
} = props;
const { width: nodeWidth, height: nodeHeight, aspectRatio: nodeAspectRatio } = node.attrs;
const { width: nodeWidth, height: nodeHeight, aspectRatio: nodeAspectRatio } = node.attrs as ImageAttributes;
// states
const [size, setSize] = useState<Size>({
width: ensurePixelString(nodeWidth, "35%"),
Expand All @@ -72,6 +72,7 @@ export const CustomImageBlock: React.FC<CustomImageBlockProps> = (props) => {
const containerRef = useRef<HTMLDivElement>(null);
const containerRect = useRef<DOMRect | null>(null);
const imageRef = useRef<HTMLImageElement>(null);
const [onFirstLoadError, setOnFirstLoadError] = useState(false);

const updateAttributesSafely = useCallback(
(attributes: Partial<ImageAttributes>, errorMessage: string) => {
Expand Down Expand Up @@ -137,16 +138,17 @@ export const CustomImageBlock: React.FC<CustomImageBlockProps> = (props) => {
}
}
setInitialResizeComplete(true);
}, [nodeWidth, updateAttributes, editorContainer, nodeAspectRatio]);
}, [nodeWidth, updateAttributes, editorContainer, nodeAspectRatio, size.aspectRatio]);

// for real time resizing
useLayoutEffect(() => {
setSize((prevSize) => ({
...prevSize,
width: ensurePixelString(nodeWidth),
height: ensurePixelString(nodeHeight),
aspectRatio: nodeAspectRatio,
}));
}, [nodeWidth, nodeHeight]);
}, [nodeWidth, nodeHeight, nodeAspectRatio]);

const handleResize = useCallback(
(e: MouseEvent | TouchEvent) => {
Expand All @@ -159,7 +161,7 @@ export const CustomImageBlock: React.FC<CustomImageBlockProps> = (props) => {

setSize((prevSize) => ({ ...prevSize, width: `${newWidth}px`, height: `${newHeight}px` }));
},
[size]
[size.aspectRatio]
);

const handleResizeEnd = useCallback(() => {
Expand All @@ -182,11 +184,15 @@ export const CustomImageBlock: React.FC<CustomImageBlockProps> = (props) => {
window.addEventListener("mousemove", handleResize);
window.addEventListener("mouseup", handleResizeEnd);
window.addEventListener("mouseleave", handleResizeEnd);
window.addEventListener("touchmove", handleResize, { passive: false });
window.addEventListener("touchend", handleResizeEnd);

return () => {
window.removeEventListener("mousemove", handleResize);
window.removeEventListener("mouseup", handleResizeEnd);
window.removeEventListener("mouseleave", handleResizeEnd);
window.removeEventListener("touchmove", handleResize);
window.removeEventListener("touchend", handleResizeEnd);
};
}
}, [isResizing, handleResize, handleResizeEnd]);
Expand All @@ -203,7 +209,7 @@ export const CustomImageBlock: React.FC<CustomImageBlockProps> = (props) => {

// show the image loader if the remote image's src or preview image from filesystem is not set yet (while loading the image post upload) (or)
// if the initial resize (from 35% width and "auto" height attrs to the actual size in px) is not complete
const showImageLoader = !(remoteImageSrc || imageFromFileSystem) || !initialResizeComplete;
const showImageLoader = !(remoteImageSrc || imageFromFileSystem) || !initialResizeComplete || onFirstLoadError;
// show the image utils only if the remote image's (post upload) src is set and the initial resize is complete (but not while we're showing the preview imageFromFileSystem)
const showImageUtils = remoteImageSrc && initialResizeComplete;
// show the image resizer only if the editor is editable, the remote image's (post upload) src is set and the initial resize is complete (but not while we're showing the preview imageFromFileSystem)
Expand Down Expand Up @@ -233,12 +239,22 @@ export const CustomImageBlock: React.FC<CustomImageBlockProps> = (props) => {
onLoad={handleImageLoad}
onError={async (e) => {
try {
setOnFirstLoadError(true);
// this is a type error from tiptap, don't remove await until it's fixed
await editor?.commands.restoreImage(remoteImageSrc);
console.log(
"imageRef width",
imageRef.current.naturalWidth,
imageRef.current.naturalHeight,
imageRef.current.src.split("/")[10]
);
imageRef.current.src = remoteImageSrc;
} catch {
setFailedToLoadImage(true);
console.log("Error while loading image", e);
} finally {
setOnFirstLoadError(false);
}
console.log("error while loading image", e);
}}
width={size.width}
className={cn("image-component block rounded-md", {
Expand Down Expand Up @@ -289,6 +305,7 @@ export const CustomImageBlock: React.FC<CustomImageBlockProps> = (props) => {
}
)}
onMouseDown={handleResizeStart}
onTouchStart={handleResizeStart}
/>
</>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import { useEffect, useRef, useState } from "react";
import { Node as ProsemirrorNode } from "@tiptap/pm/model";
import { Editor, NodeViewWrapper } from "@tiptap/react";
import { Editor, NodeViewProps, NodeViewWrapper } from "@tiptap/react";
// extensions
import { CustomImageBlock, CustomImageUploader, ImageAttributes } from "@/extensions/custom-image";

export type CustomImageNodeViewProps = {
export type CustomImageComponentProps = {
getPos: () => number;
editor: Editor;
node: ProsemirrorNode & {
node: NodeViewProps["node"] & {
attrs: ImageAttributes;
};
updateAttributes: (attrs: Record<string, any>) => void;
selected: boolean;
};

export type CustomImageNodeViewProps = NodeViewProps & CustomImageComponentProps;

export const CustomImageNode = (props: CustomImageNodeViewProps) => {
const { getPos, editor, node, updateAttributes, selected } = props;
const { src: remoteImageSrc } = node.attrs;

const [isUploaded, setIsUploaded] = useState(false);
const [imageFromFileSystem, setImageFromFileSystem] = useState<string | undefined>(undefined);
Expand All @@ -37,14 +39,13 @@ export const CustomImageNode = (props: CustomImageNodeViewProps) => {
// the image is already uploaded if the image-component node has src attribute
// and we need to remove the blob from our file system
useEffect(() => {
const remoteImageSrc = node.attrs.src;
if (remoteImageSrc) {
setIsUploaded(true);
setImageFromFileSystem(undefined);
} else {
setIsUploaded(false);
}
}, [node.attrs.src]);
}, [remoteImageSrc]);

return (
<NodeViewWrapper>
Expand All @@ -55,7 +56,7 @@ export const CustomImageNode = (props: CustomImageNodeViewProps) => {
editorContainer={editorContainer}
editor={editor}
// @ts-expect-error function not expected here, but will still work
src={editor?.commands?.getImageSource?.(node.attrs.src)}
src={editor?.commands?.getImageSource?.(remoteImageSrc)}
getPos={getPos}
node={node}
setEditorContainer={setEditorContainer}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,20 @@
import { ChangeEvent, useCallback, useEffect, useMemo, useRef } from "react";
import { Node as ProsemirrorNode } from "@tiptap/pm/model";
import { Editor } from "@tiptap/core";
import { ImageIcon } from "lucide-react";
// helpers
import { cn } from "@/helpers/common";
// hooks
import { useUploader, useDropZone, uploadFirstImageAndInsertRemaining } from "@/hooks/use-file-upload";
// extensions
import { getImageComponentImageFileMap, ImageAttributes } from "@/extensions/custom-image";
import { type CustomImageComponentProps, getImageComponentImageFileMap } from "@/extensions/custom-image";

export const CustomImageUploader = (props: {
editor: Editor;
failedToLoadImage: boolean;
getPos: () => number;
loadImageFromFileSystem: (file: string) => void;
type CustomImageUploaderProps = CustomImageComponentProps & {
maxFileSize: number;
node: ProsemirrorNode & {
attrs: ImageAttributes;
};
selected: boolean;
loadImageFromFileSystem: (file: string) => void;
failedToLoadImage: boolean;
setIsUploaded: (isUploaded: boolean) => void;
updateAttributes: (attrs: Record<string, any>) => void;
}) => {
};

export const CustomImageUploader = (props: CustomImageUploaderProps) => {
const {
editor,
failedToLoadImage,
Expand All @@ -36,8 +29,8 @@ export const CustomImageUploader = (props: {
// refs
const fileInputRef = useRef<HTMLInputElement>(null);
const hasTriggeredFilePickerRef = useRef(false);
const { id: imageEntityId } = node.attrs;
// derived values
const imageEntityId = node.attrs.id;
const imageComponentImageFileMap = useMemo(() => getImageComponentImageFileMap(editor), [editor]);

const onUpload = useCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,7 @@ export const CustomImageExtension = (props: TFileHandler) => {
},

addProseMirrorPlugins() {
return [
TrackImageDeletionPlugin(this.editor, deleteImage, this.name),
// TrackImageRestorationPlugin(this.editor, restore, this.name),
];
return [TrackImageDeletionPlugin(this.editor, deleteImage, this.name)];
},

addStorage() {
Expand Down
26 changes: 5 additions & 21 deletions packages/editor/src/core/extensions/image/extension.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ReactNodeViewRenderer } from "@tiptap/react";
// helpers
import { insertEmptyParagraphAtNodeBoundaries } from "@/helpers/insert-empty-paragraph-at-node-boundary";
// plugins
import { ImageExtensionStorage, TrackImageDeletionPlugin, TrackImageRestorationPlugin } from "@/plugins/image";
import { ImageExtensionStorage, TrackImageDeletionPlugin } from "@/plugins/image";
// types
import { TFileHandler } from "@/types";
// extensions
Expand All @@ -25,28 +25,9 @@ export const ImageExtension = (fileHandler: TFileHandler) => {
};
},
addProseMirrorPlugins() {
return [
TrackImageDeletionPlugin(this.editor, deleteImage, this.name),
// TrackImageRestorationPlugin(this.editor, restoreImage, this.name),
];
return [TrackImageDeletionPlugin(this.editor, deleteImage, this.name)];
},

// onCreate(this) {
// const imageSources = new Set<string>();
// this.editor.state.doc.descendants((node) => {
// if (node.type.name === this.name) {
// imageSources.add(node.attrs.src);
// }
// });
// imageSources.forEach(async (src) => {
// try {
// await restoreImage(src);
// } catch (error) {
// console.error("Error restoring image: ", error);
// }
// });
// },

// storage to keep track of image states Map<src, isDeleted>
addStorage() {
return {
Expand All @@ -65,6 +46,9 @@ export const ImageExtension = (fileHandler: TFileHandler) => {
height: {
default: null,
},
aspectRatio: {
default: null,
},
};
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ export const ImageExtensionWithoutProps = () =>
height: {
default: null,
},
aspectRatio: {
default: null,
},
};
},
});
3 changes: 3 additions & 0 deletions packages/editor/src/core/extensions/image/read-only-image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ export const ReadOnlyImageExtension = (props: Pick<TFileHandler, "getAssetSrc">)
height: {
default: null,
},
aspectRatio: {
default: null,
},
};
},

Expand Down

0 comments on commit 501ba51

Please sign in to comment.