Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 5s
Build & Deploy / 🏗️ Build (push) Failing after 14s
Build & Deploy / 🧪 QA (push) Failing after 1m48s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🩺 Health Check (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
89 lines
3.5 KiB
TypeScript
89 lines
3.5 KiB
TypeScript
'use client';
|
|
|
|
import * as React from 'react';
|
|
import { useEffect, useState, useRef } from 'react';
|
|
|
|
interface LinkedInEmbedProps {
|
|
/** The post URL, e.g. "https://www.linkedin.com/posts/xyz" or raw URN */
|
|
url: string;
|
|
className?: string;
|
|
width?: string | number;
|
|
}
|
|
|
|
export function LinkedInEmbed({
|
|
url,
|
|
className = "",
|
|
width = 504
|
|
}: LinkedInEmbedProps) {
|
|
const [isMounted, setIsMounted] = useState(false);
|
|
const [hasError, setHasError] = useState(false);
|
|
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
|
|
// Extract the 19-digit ID from the URL or URN
|
|
const match = url.match(/(\d{19})/);
|
|
const embedId = match ? match[1] : null;
|
|
|
|
useEffect(() => {
|
|
setIsMounted(true);
|
|
}, []);
|
|
|
|
if (!isMounted || !embedId) return null;
|
|
|
|
// LinkedIn technically supports share, ugcPost, and activity. We start with share and natively cycle.
|
|
const initialSrc = `https://www.linkedin.com/embed/feed/update/urn:li:share:${embedId}`;
|
|
|
|
const handleError = () => {
|
|
if (!iframeRef.current) return;
|
|
const currentSrc = iframeRef.current.src;
|
|
|
|
// If the 'share' URN 404s (e.g., restricted post type), fallback to 'activity', then 'ugcPost'
|
|
if (currentSrc.includes('urn:li:share:')) {
|
|
iframeRef.current.src = `https://www.linkedin.com/embed/feed/update/urn:li:activity:${embedId}`;
|
|
} else if (currentSrc.includes('urn:li:activity:')) {
|
|
iframeRef.current.src = `https://www.linkedin.com/embed/feed/update/urn:li:ugcPost:${embedId}`;
|
|
} else {
|
|
// All fallbacks exhausted. The post is truly dead or private.
|
|
setHasError(true);
|
|
}
|
|
};
|
|
|
|
if (hasError) {
|
|
return (
|
|
<div className={`not-prose flex w-full justify-center my-8 ${className}`}>
|
|
<div
|
|
className="flex flex-col items-center justify-center text-center p-8 w-full max-w-[504px] border border-slate-200 border-dashed rounded-lg bg-slate-50 text-slate-500"
|
|
style={{ minHeight: '150px' }}
|
|
>
|
|
<svg className="w-8 h-8 mb-3 text-slate-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
</svg>
|
|
<span className="text-sm font-medium">Beitrag nicht verfügbar</span>
|
|
<span className="text-xs mt-1 text-slate-400">Dieser LinkedIn-Post wurde gelöscht oder die Privatsphäre-Einstellungen verhindern eine Einbettung.</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className={`not-prose flex w-full justify-center my-8 ${className}`}>
|
|
<div
|
|
className="linkedin-embed-container w-full max-w-[504px] border border-slate-200 rounded-lg overflow-hidden shadow-sm bg-white"
|
|
style={{ width, minHeight: '500px' }}
|
|
>
|
|
{/* We use onLoad to detect successful rendering, relying on onError for explicit iframe load crashes */}
|
|
<iframe
|
|
ref={iframeRef}
|
|
src={initialSrc}
|
|
height="100%"
|
|
width="100%"
|
|
frameBorder="0"
|
|
allowFullScreen
|
|
title="Embedded post"
|
|
className="w-full min-h-[500px]"
|
|
onError={handleError}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|