216 lines
6.5 KiB
JavaScript
216 lines
6.5 KiB
JavaScript
import { memo, useEffect, useRef, useState } from 'react'
|
|
import classNames from 'classnames'
|
|
import useDarkMode from './dark-mode'
|
|
import styles from './text.module.css'
|
|
import { Button } from 'react-bootstrap'
|
|
import { TwitterTweetEmbed } from 'react-twitter-embed'
|
|
import YouTube from 'react-youtube'
|
|
|
|
function TweetSkeleton ({ className }) {
|
|
return (
|
|
<div className={classNames(styles.tweetsSkeleton, className)}>
|
|
<div className={styles.tweetSkeleton}>
|
|
<div className={`${styles.img} clouds`} />
|
|
<div className={styles.content1}>
|
|
<div className={`${styles.line} clouds`} />
|
|
<div className={`${styles.line} clouds`} />
|
|
<div className={`${styles.line} clouds`} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export const NostrEmbed = memo(function NostrEmbed ({ src, className, topLevel, darkMode, id }) {
|
|
const [show, setShow] = useState(false)
|
|
const iframeRef = useRef(null)
|
|
|
|
useEffect(() => {
|
|
if (!iframeRef.current) return
|
|
|
|
const setHeightFromIframe = (e) => {
|
|
if (e.origin !== 'https://njump.me' || !e?.data?.height || e.source !== iframeRef.current.contentWindow) return
|
|
iframeRef.current.height = `${e.data.height}px`
|
|
}
|
|
|
|
window?.addEventListener('message', setHeightFromIframe)
|
|
|
|
const handleIframeLoad = () => {
|
|
iframeRef.current.contentWindow.postMessage({ setDarkMode: darkMode }, '*')
|
|
}
|
|
|
|
if (iframeRef.current.complete) {
|
|
handleIframeLoad()
|
|
} else {
|
|
iframeRef.current.addEventListener('load', handleIframeLoad)
|
|
}
|
|
|
|
// https://github.com/vercel/next.js/issues/39451
|
|
iframeRef.current.src = `https://njump.me/${id}?embed=yes`
|
|
|
|
return () => {
|
|
window?.removeEventListener('message', setHeightFromIframe)
|
|
iframeRef.current?.removeEventListener('load', handleIframeLoad)
|
|
}
|
|
}, [iframeRef.current, darkMode])
|
|
|
|
return (
|
|
<div className={classNames(styles.nostrContainer, !show && styles.twitterContained, className)}>
|
|
<iframe
|
|
ref={iframeRef}
|
|
width={topLevel ? '550px' : '350px'}
|
|
style={{ maxWidth: '100%' }}
|
|
height={iframeRef.current?.height || (topLevel ? '200px' : '150px')}
|
|
frameBorder='0'
|
|
sandbox='allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox'
|
|
allow=''
|
|
/>
|
|
{!show &&
|
|
<Button size='md' variant='info' className={styles.twitterShowFull} onClick={() => setShow(true)}>
|
|
<div>show full note</div>
|
|
<small className='fw-normal fst-italic'>or other stuff</small>
|
|
</Button>}
|
|
</div>
|
|
)
|
|
})
|
|
|
|
const SpotifyEmbed = function SpotifyEmbed ({ src, className }) {
|
|
const iframeRef = useRef(null)
|
|
|
|
// https://open.spotify.com/track/1KFxcj3MZrpBGiGA8ZWriv?si=f024c3aa52294aa1
|
|
// Remove any additional path segments
|
|
const url = new URL(src)
|
|
url.pathname = url.pathname.replace(/\/intl-\w+\//, '/')
|
|
|
|
useEffect(() => {
|
|
if (!iframeRef.current) return
|
|
|
|
const id = url.pathname.split('/').pop()
|
|
|
|
// https://developer.spotify.com/documentation/embeds/tutorials/using-the-iframe-api
|
|
window.onSpotifyIframeApiReady = (IFrameAPI) => {
|
|
const options = {
|
|
uri: `spotify:episode:${id}`
|
|
}
|
|
const callback = (EmbedController) => {}
|
|
IFrameAPI.createController(iframeRef.current, options, callback)
|
|
}
|
|
|
|
return () => { window.onSpotifyIframeApiReady = null }
|
|
}, [iframeRef.current, url.pathname])
|
|
|
|
return (
|
|
<div className={classNames(styles.spotifyWrapper, className)}>
|
|
<iframe
|
|
ref={iframeRef}
|
|
title='Spotify Web Player'
|
|
src={`https://open.spotify.com/embed${url.pathname}`}
|
|
width='100%'
|
|
height='152'
|
|
allowFullScreen
|
|
frameBorder='0'
|
|
allow='encrypted-media; clipboard-write;'
|
|
style={{ borderRadius: '12px' }}
|
|
sandbox='allow-scripts allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-presentation'
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const Embed = memo(function Embed ({ src, provider, id, meta, className, topLevel, onError }) {
|
|
const [darkMode] = useDarkMode()
|
|
const [overflowing, setOverflowing] = useState(true)
|
|
const [show, setShow] = useState(false)
|
|
|
|
// This Twitter embed could use similar logic to the video embeds below
|
|
if (provider === 'twitter') {
|
|
return (
|
|
<div className={classNames(styles.twitterContainer, !show && styles.twitterContained, className)}>
|
|
<TwitterTweetEmbed
|
|
tweetId={id}
|
|
options={{ theme: darkMode ? 'dark' : 'light', width: topLevel ? '550px' : '350px' }}
|
|
key={darkMode ? '1' : '2'}
|
|
placeholder={<TweetSkeleton className={className} />}
|
|
onLoad={() => setOverflowing(true)}
|
|
/>
|
|
{overflowing && !show &&
|
|
<Button size='lg' variant='info' className={styles.twitterShowFull} onClick={() => setShow(true)}>
|
|
show full tweet
|
|
</Button>}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (provider === 'nostr') {
|
|
return (
|
|
<NostrEmbed src={src} className={className} topLevel={topLevel} id={id} darkMode={darkMode} />
|
|
)
|
|
}
|
|
|
|
if (provider === 'wavlake') {
|
|
return (
|
|
<div className={classNames(styles.wavlakeWrapper, className)}>
|
|
<iframe
|
|
src={`https://embed.wavlake.com/track/${id}`} width='100%' height='380' frameBorder='0'
|
|
allow='encrypted-media'
|
|
sandbox='allow-scripts allow-popups allow-popups-to-escape-sandbox allow-forms allow-same-origin'
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (provider === 'spotify') {
|
|
return (
|
|
<SpotifyEmbed src={src} className={className} />
|
|
)
|
|
}
|
|
|
|
if (provider === 'youtube') {
|
|
return (
|
|
<div className={classNames(styles.videoWrapper, className)}>
|
|
<YouTube
|
|
videoId={id} className={styles.videoContainer} opts={{
|
|
playerVars: {
|
|
start: meta?.start || 0
|
|
}
|
|
}}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (provider === 'rumble') {
|
|
return (
|
|
<div className={classNames(styles.videoWrapper, className)}>
|
|
<div className={styles.videoContainer}>
|
|
<iframe
|
|
title='Rumble Video'
|
|
allowFullScreen
|
|
src={meta?.href}
|
|
sandbox='allow-scripts'
|
|
/>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (provider === 'peertube') {
|
|
return (
|
|
<div className={classNames(styles.videoWrapper, className)}>
|
|
<div className={styles.videoContainer}>
|
|
<iframe
|
|
title='PeerTube Video'
|
|
allowFullScreen
|
|
src={meta?.href}
|
|
sandbox='allow-scripts'
|
|
/>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return null
|
|
})
|
|
|
|
export default Embed
|