fix: multiple Embed block issues #363 #362 #375

This commit is contained in:
swve 2024-11-29 00:13:39 +01:00
parent 0f87065806
commit 47c4a6f8f7

View file

@ -1,11 +1,66 @@
import { NodeViewWrapper } from '@tiptap/react'
import React, { useState, useRef, useEffect } from 'react'
import React, { useState, useRef, useEffect, useMemo } from 'react'
import { Upload, Link as LinkIcon, GripVertical, GripHorizontal, AlignCenter, Cuboid, Code } from 'lucide-react'
import { useEditorProvider } from '@components/Contexts/Editor/EditorContext'
import { SiGithub, SiReplit, SiSpotify, SiLoom, SiGooglemaps, SiCodepen, SiCanva, SiNotion, SiGoogledocs, SiGitlab, SiX, SiFigma, SiGiphy } from '@icons-pack/react-simple-icons'
import { useRouter } from 'next/navigation'
import DOMPurify from 'dompurify'
// Add new type for script-based embeds
const SCRIPT_BASED_EMBEDS = {
twitter: { src: 'https://platform.twitter.com/widgets.js', identifier: 'twitter-tweet' },
instagram: { src: 'https://www.instagram.com/embed.js', identifier: 'instagram-media' },
tiktok: { src: 'https://www.tiktok.com/embed.js', identifier: 'tiktok-embed' },
// Add more platforms as needed
};
// Add new memoized component for the embed content
const MemoizedEmbed = React.memo(({ embedUrl, sanitizedEmbedCode, embedType }: {
embedUrl: string;
sanitizedEmbedCode: string;
embedType: 'url' | 'code';
}) => {
useEffect(() => {
if (embedType === 'code' && sanitizedEmbedCode) {
// Check for any matching script-based embeds
const matchingPlatform = Object.entries(SCRIPT_BASED_EMBEDS).find(([_, config]) =>
sanitizedEmbedCode.includes(config.identifier)
);
if (matchingPlatform) {
const [_, config] = matchingPlatform;
const script = document.createElement('script');
script.src = config.src;
script.async = true;
script.charset = 'utf-8';
document.body.appendChild(script);
return () => {
document.body.removeChild(script);
};
}
}
}, [embedType, sanitizedEmbedCode]);
if (embedType === 'url' && embedUrl) {
return (
<iframe
src={embedUrl}
className="w-full h-full"
frameBorder="0"
allowFullScreen
/>
);
}
if (embedType === 'code' && sanitizedEmbedCode) {
return <div dangerouslySetInnerHTML={{ __html: sanitizedEmbedCode }} className="w-full h-full" />;
}
return null;
});
MemoizedEmbed.displayName = 'MemoizedEmbed';
function EmbedObjectsComponent(props: any) {
const [embedType, setEmbedType] = useState<'url' | 'code'>(props.node.attrs.embedType || 'url')
const [embedUrl, setEmbedUrl] = useState(props.node.attrs.embedUrl || '')
@ -13,6 +68,7 @@ function EmbedObjectsComponent(props: any) {
const [embedHeight, setEmbedHeight] = useState(props.node.attrs.embedHeight || 300)
const [embedWidth, setEmbedWidth] = useState(props.node.attrs.embedWidth || '100%')
const [alignment, setAlignment] = useState(props.node.attrs.alignment || 'left')
const [isResizing, setIsResizing] = useState(false)
const resizeRef = useRef<HTMLDivElement>(null)
const editorState = useEditorProvider() as any
@ -78,8 +134,15 @@ function EmbedObjectsComponent(props: any) {
}
};
// Add refs for storing dimensions during resize
const dimensionsRef = useRef({
width: props.node.attrs.embedWidth || '100%',
height: props.node.attrs.embedHeight || 300
})
const handleResizeStart = (event: React.MouseEvent<HTMLDivElement>, direction: 'horizontal' | 'vertical') => {
event.preventDefault()
setIsResizing(true)
const startX = event.clientX
const startY = event.clientY
const startWidth = resizeRef.current?.offsetWidth || 0
@ -92,17 +155,29 @@ function EmbedObjectsComponent(props: any) {
const parentWidth = resizeRef.current.parentElement?.offsetWidth || 1
const widthPercentage = Math.min(100, Math.max(10, (newWidth / parentWidth) * 100))
const newWidthValue = `${widthPercentage}%`
setEmbedWidth(newWidthValue)
props.updateAttributes({ embedWidth: newWidthValue })
// Update ref and DOM directly during resize
dimensionsRef.current.width = newWidthValue
resizeRef.current.style.width = newWidthValue
} else {
const newHeight = Math.max(100, startHeight + e.clientY - startY)
setEmbedHeight(newHeight)
props.updateAttributes({ embedHeight: newHeight })
// Update ref and DOM directly during resize
dimensionsRef.current.height = newHeight
resizeRef.current.style.height = `${newHeight}px`
}
}
}
const handleMouseUp = () => {
setIsResizing(false)
// Only update state and attributes after resize is complete
setEmbedWidth(dimensionsRef.current.width)
setEmbedHeight(dimensionsRef.current.height)
props.updateAttributes({
embedWidth: dimensionsRef.current.width,
embedHeight: dimensionsRef.current.height
})
document.removeEventListener('mousemove', handleMouseMove)
document.removeEventListener('mouseup', handleMouseUp)
}
@ -121,6 +196,19 @@ function EmbedObjectsComponent(props: any) {
window.open(guide, '_blank', 'noopener,noreferrer')
}
// Memoize the embed content
const embedContent = useMemo(() => (
!isResizing && (embedUrl || sanitizedEmbedCode) ? (
<MemoizedEmbed
embedUrl={embedUrl}
sanitizedEmbedCode={sanitizedEmbedCode}
embedType={embedType}
/>
) : (
<div className="w-full h-full bg-gray-200" />
)
), [embedUrl, sanitizedEmbedCode, embedType, isResizing]);
return (
<NodeViewWrapper className="embed-block">
<div
@ -128,16 +216,7 @@ function EmbedObjectsComponent(props: any) {
className={`relative bg-gray-100 rounded-lg overflow-hidden flex justify-center items-center ${alignment === 'center' ? 'mx-auto' : ''}`}
style={{ height: `${embedHeight}px`, width: embedWidth, minWidth: '400px' }}
>
{embedType === 'url' && embedUrl ? (
<iframe
src={embedUrl}
className="w-full h-full"
frameBorder="0"
allowFullScreen
/>
) : embedType === 'code' && sanitizedEmbedCode ? (
<div dangerouslySetInnerHTML={{ __html: sanitizedEmbedCode }} className="w-full h-full" />
) : (
{(embedUrl || sanitizedEmbedCode) ? embedContent : (
<div className="w-full h-full flex flex-col items-center justify-center p-6">
<p className="text-gray-500 mb-4 font-medium tracking-tighter text-lg">Add an embed from :</p>
<div className="flex flex-wrap gap-5 justify-center">
@ -217,3 +296,4 @@ function EmbedObjectsComponent(props: any) {
}
export default EmbedObjectsComponent