embed tweets

This commit is contained in:
Arjun 2026-05-09 12:06:54 +05:30
parent 8737605666
commit e3d2a0988b
7 changed files with 87 additions and 63 deletions

View file

@ -49,6 +49,7 @@
"radix-ui": "^1.4.3",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-tweet": "^3.2.2",
"recharts": "^3.8.0",
"remark-breaks": "^4.0.0",
"sonner": "^2.0.7",

View file

@ -1,6 +1,7 @@
import { mergeAttributes, Node } from '@tiptap/react'
import { ReactNodeViewRenderer, NodeViewWrapper } from '@tiptap/react'
import { X, ExternalLink } from 'lucide-react'
import { Tweet } from 'react-tweet'
import { blocks } from '@x/shared'
function getEmbedUrl(provider: string, url: string): string | null {
@ -24,6 +25,28 @@ function getEmbedUrl(provider: string, url: string): string | null {
return null
}
function extractTweetId(url: string): string | null {
try {
const parsed = new URL(url)
const hostname = parsed.hostname
.toLowerCase()
.replace(/^www\./, '')
.replace(/^mobile\./, '')
if (hostname !== 'twitter.com' && hostname !== 'x.com') return null
const segments = parsed.pathname.split('/').filter(Boolean)
for (let i = 0; i < segments.length - 1; i += 1) {
if ((segments[i] === 'status' || segments[i] === 'statuses') && /^\d+$/.test(segments[i + 1])) {
return segments[i + 1]
}
}
} catch {
return null
}
return null
}
function EmbedBlockView({ node, deleteNode }: { node: { attrs: Record<string, unknown> }; deleteNode: () => void }) {
const raw = node.attrs.data as string
let config: blocks.EmbedBlock | null = null
@ -45,6 +68,7 @@ function EmbedBlockView({ node, deleteNode }: { node: { attrs: Record<string, un
)
}
const tweetId = extractTweetId(config.url)
const embedUrl = getEmbedUrl(config.provider, config.url)
return (
@ -57,7 +81,14 @@ function EmbedBlockView({ node, deleteNode }: { node: { attrs: Record<string, un
>
<X size={14} />
</button>
{embedUrl ? (
{config.provider === 'tweet' && tweetId ? (
<div
className="embed-block-tweet-shell"
onMouseDown={(event) => event.stopPropagation()}
>
<Tweet id={tweetId} />
</div>
) : embedUrl ? (
<div className="embed-block-iframe-container">
<iframe
src={embedUrl}

View file

@ -867,6 +867,16 @@
border: none;
}
.tiptap-editor .ProseMirror .embed-block-tweet-shell {
display: flex;
justify-content: center;
}
.tiptap-editor .ProseMirror .embed-block-tweet-shell .react-tweet-theme {
margin: 0;
max-width: 100%;
}
.tiptap-editor .ProseMirror .embed-block-link {
display: flex;
align-items: center;