mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
youtube video support
This commit is contained in:
parent
cfe3b9e432
commit
56e849729d
9
package-lock.json
generated
9
package-lock.json
generated
@ -22,6 +22,7 @@
|
|||||||
"react-leaflet-cluster": "^2.1.0",
|
"react-leaflet-cluster": "^2.1.0",
|
||||||
"react-markdown": "^9.0.1",
|
"react-markdown": "^9.0.1",
|
||||||
"react-router-dom": "^6.16.0",
|
"react-router-dom": "^6.16.0",
|
||||||
|
"react-string-replace": "^1.1.1",
|
||||||
"react-toastify": "^9.1.3",
|
"react-toastify": "^9.1.3",
|
||||||
"rehype-video": "^2.0.2",
|
"rehype-video": "^2.0.2",
|
||||||
"tributejs": "^5.1.3",
|
"tributejs": "^5.1.3",
|
||||||
@ -4890,6 +4891,14 @@
|
|||||||
"react-dom": ">=16.8"
|
"react-dom": ">=16.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-string-replace": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-string-replace/-/react-string-replace-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-26TUbLzLfHQ5jO5N7y3Mx88eeKo0Ml0UjCQuX4BMfOd/JX+enQqlKpL1CZnmjeBRvQE8TR+ds9j1rqx9CxhKHQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-toastify": {
|
"node_modules/react-toastify": {
|
||||||
"version": "9.1.3",
|
"version": "9.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz",
|
||||||
|
|||||||
@ -55,6 +55,7 @@
|
|||||||
"react-leaflet-cluster": "^2.1.0",
|
"react-leaflet-cluster": "^2.1.0",
|
||||||
"react-markdown": "^9.0.1",
|
"react-markdown": "^9.0.1",
|
||||||
"react-router-dom": "^6.16.0",
|
"react-router-dom": "^6.16.0",
|
||||||
|
"react-string-replace": "^1.1.1",
|
||||||
"react-toastify": "^9.1.3",
|
"react-toastify": "^9.1.3",
|
||||||
"rehype-video": "^2.0.2",
|
"rehype-video": "^2.0.2",
|
||||||
"tributejs": "^5.1.3",
|
"tributejs": "^5.1.3",
|
||||||
|
|||||||
@ -26,51 +26,52 @@ export const TextView = ({ item }: { item?: Item }) => {
|
|||||||
return `[${shortUrl}](${url})`
|
return `[${shortUrl}](${url})`
|
||||||
})
|
})
|
||||||
|
|
||||||
replacedText = replacedText.replace(mailRegex, (url) => {
|
replacedText = replacedText.replace(mailRegex, (url) => {
|
||||||
return `[${url}](mailto:${url})`
|
return `[${url}](mailto:${url})`
|
||||||
})
|
})
|
||||||
|
|
||||||
replacedText = replacedText.replace(hashTagRegex, (match) => {
|
replacedText = replacedText.replace(hashTagRegex, (match) => {
|
||||||
return `[${match}](${match})`
|
return `[${match}](${match})`
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const CustomH1 = ({ children }) => (
|
|
||||||
|
const CustomH1 = ({ children }) => (
|
||||||
<h1 className="tw-text-xl tw-font-bold">{children}</h1>
|
<h1 className="tw-text-xl tw-font-bold">{children}</h1>
|
||||||
);
|
);
|
||||||
const CustomH2 = ({ children }) => (
|
const CustomH2 = ({ children }) => (
|
||||||
<h2 className="tw-text-lg tw-font-bold">{children}</h2>
|
<h2 className="tw-text-lg tw-font-bold">{children}</h2>
|
||||||
);
|
);
|
||||||
const CustomH3 = ({ children }) => (
|
const CustomH3 = ({ children }) => (
|
||||||
<h3 className="tw-text-base tw-font-bold">{children}</h3>
|
<h3 className="tw-text-base tw-font-bold">{children}</h3>
|
||||||
);
|
);
|
||||||
const CustomH4 = ({ children }) => (
|
const CustomH4 = ({ children }) => (
|
||||||
<h4 className="tw-text-base tw-font-bold">{children}</h4>
|
<h4 className="tw-text-base tw-font-bold">{children}</h4>
|
||||||
);
|
);
|
||||||
const CustomH5 = ({ children }) => (
|
const CustomH5 = ({ children }) => (
|
||||||
<h5 className="tw-text-sm tw-font-bold">{children}</h5>
|
<h5 className="tw-text-sm tw-font-bold">{children}</h5>
|
||||||
);
|
);
|
||||||
const CustomH6 = ({ children }) => (
|
const CustomH6 = ({ children }) => (
|
||||||
<h6 className="tw-text-sm tw-font-bold">{children}</h6>
|
<h6 className="tw-text-sm tw-font-bold">{children}</h6>
|
||||||
);
|
);
|
||||||
const CustomParagraph = ({ children }) => (
|
const CustomParagraph = ({ children }) => (
|
||||||
<p className="!tw-my-1">{children}</p>
|
<p className="!tw-my-1">{children}</p>
|
||||||
);
|
);
|
||||||
const CustomUnorderdList = ({ children }) => (
|
const CustomUnorderdList = ({ children }) => (
|
||||||
<ul className="tw-list-disc tw-list-inside">{children}</ul>
|
<ul className="tw-list-disc tw-list-inside">{children}</ul>
|
||||||
);
|
);
|
||||||
const CustomOrderdList = ({ children }) => (
|
const CustomOrderdList = ({ children }) => (
|
||||||
<ol className="tw-list-decimal tw-list-inside">{children}</ol>
|
<ol className="tw-list-decimal tw-list-inside">{children}</ol>
|
||||||
);
|
);
|
||||||
const CustomImage = ({ alt, src, title }) => (
|
const CustomImage = ({ alt, src, title }) => (
|
||||||
<img
|
<img
|
||||||
className="max-w-full rounded-lg shadow-md"
|
className="max-w-full rounded-lg shadow-md"
|
||||||
src={src}
|
src={src}
|
||||||
alt={alt}
|
alt={alt}
|
||||||
title={title}
|
title={title}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
const CustomExternalLink = ({ href, children }) => (
|
const CustomExternalLink = ({ href, children }) => (
|
||||||
<a
|
<a
|
||||||
href={href}
|
href={href}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@ -78,18 +79,22 @@ const CustomExternalLink = ({ href, children }) => (
|
|||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
const CustomHashTagLink = ({ children, tag, item }) => (
|
const CustomHashTagLink = ({ children, tag, item }) => (
|
||||||
<a
|
<a
|
||||||
style={{ color: tag ? tag.color : '#faa' , fontWeight: 'bold', cursor: 'pointer' }}
|
style={{ color: tag ? tag.color : '#faa', fontWeight: 'bold', cursor: 'pointer' }}
|
||||||
key={tag ? tag.id+item!.id : item.id}
|
key={tag ? tag.id + item!.id : item.id}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
addFilterTag(tag!);
|
addFilterTag(tag!);
|
||||||
// map.fitBounds(items)
|
// map.fitBounds(items)
|
||||||
// map.closePopup();
|
// map.closePopup();
|
||||||
}}>{children}</a>
|
}}>{children}</a>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isSpecialYouTubeLink = (url) => {
|
||||||
|
return /(?<=!\()[^)]+(?=\))/g.test(url);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -97,6 +102,22 @@ const CustomHashTagLink = ({ children, tag, item }) => (
|
|||||||
<Markdown rehypePlugins={[rehypeVideo]} components={{
|
<Markdown rehypePlugins={[rehypeVideo]} components={{
|
||||||
p: CustomParagraph,
|
p: CustomParagraph,
|
||||||
a: ({ href, children }) => {
|
a: ({ href, children }) => {
|
||||||
|
// Prüft, ob der Link ein YouTube-Video ist
|
||||||
|
const isYouTubeVideo = href?.startsWith('https://www.youtube.com/watch?v=');
|
||||||
|
|
||||||
|
if (isYouTubeVideo) {
|
||||||
|
const videoId = href?.split('v=')[1].split('&')[0]; // Extrahiert die Video-ID aus der URL
|
||||||
|
const youtubeEmbedUrl = `https://www.youtube-nocookie.com/embed/${videoId}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
|
||||||
|
<iframe className='tw-w-full'
|
||||||
|
src={youtubeEmbedUrl}
|
||||||
|
allowFullScreen
|
||||||
|
/>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
if (href?.startsWith("#")) {
|
if (href?.startsWith("#")) {
|
||||||
const tag = tags.find(t => t.id.toLowerCase() == href.slice(1).toLowerCase())
|
const tag = tags.find(t => t.id.toLowerCase() == href.slice(1).toLowerCase())
|
||||||
return <CustomHashTagLink tag={tag} item={item}>{children}</CustomHashTagLink>;
|
return <CustomHashTagLink tag={tag} item={item}>{children}</CustomHashTagLink>;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user