feat(post): add links and media info, including embed

This commit is contained in:
plebeius.eth
2024-03-22 16:15:38 +01:00
parent f1af55edf0
commit d0a3e959fc
10 changed files with 572 additions and 53 deletions
@@ -5,13 +5,13 @@
}
.boardNavDesktop a {
color: var(--link-internal-text-color);
text-decoration: var(--link-internal-text-decoration);
color: var(--button-desktop-text-color);
text-decoration: var(--button-desktop-text-color-decoration);
}
.boardNavDesktop a:hover {
color: var(--link-internal-text-color-hover);
text-decoration: var(--link-internal-text-decoration-hover);
color: var(--button-desktop-text-color-hover);
text-decoration: var(--button-desktop-text-color-hover);
}
.navTopRight {
@@ -58,8 +58,8 @@
}
.pageJump a {
color: var(--link-internal-text-color);
text-decoration: var(--link-internal-text-decoration);
color: var(--homepage-box-link-text-color);
text-decoration: var(--homepage-box-link-text-decoration);
}
@media (max-width: 640px) {
+70
View File
@@ -0,0 +1,70 @@
.audioEmbed {
height: 240px !important;
width: 700px !important;
}
@media (max-width: 768px) {
.audioEmbed {
max-height: 250px !important;
width: 100% !important;
}
}
.instagramEmbed {
height: 420px !important;
width: 360px !important;
}
@media (max-width: 768px) {
.instagramEmbed {
max-height: 420px !important;
width: 100% !important;
}
}
.redditEmbed {
width: 500px !important;
height: 520px !important;
}
@media (max-width: 768px) {
.redditEmbed {
width: 100% !important;
height: 520px !important;
}
}
.tiktokEmbed {
width: 400px !important;
height: 780px !important;
}
@media (max-width: 768px) {
.tiktokEmbed {
width: 100% !important;
height: 70vh !important;
}
}
.videoEmbed {
height: 450px !important;
width: 800px !important;
}
@media (max-width: 768px) {
.videoEmbed {
max-height: 250px !important;
width: 100% !important;
}
}
.xEmbed {
height: 580px !important;
}
@media (max-width: 768px) {
.xEmbed {
width: 100% !important;
height: 50vh !important;
}
}
+273
View File
@@ -0,0 +1,273 @@
import styles from './embed.module.css';
interface EmbedProps {
url: string;
}
const Embed = ({ url }: EmbedProps) => {
const parsedUrl = new URL(url);
if (youtubeHosts.has(parsedUrl.host)) {
return <YoutubeEmbed parsedUrl={parsedUrl} />;
}
if (xHosts.has(parsedUrl.host)) {
return <XEmbed parsedUrl={parsedUrl} />;
}
if (redditHosts.has(parsedUrl.host)) {
return <RedditEmbed parsedUrl={parsedUrl} />;
}
if (twitchHosts.has(parsedUrl.host)) {
return <TwitchEmbed parsedUrl={parsedUrl} />;
}
if (tiktokHosts.has(parsedUrl.host)) {
return <TiktokEmbed parsedUrl={parsedUrl} />;
}
if (instagramHosts.has(parsedUrl.host)) {
return <InstagramEmbed parsedUrl={parsedUrl} />;
}
if (odyseeHosts.has(parsedUrl.host)) {
return <OdyseeEmbed parsedUrl={parsedUrl} />;
}
if (bitchuteHosts.has(parsedUrl.host)) {
return <BitchuteEmbed parsedUrl={parsedUrl} />;
}
if (streamableHosts.has(parsedUrl.host)) {
return <StreamableEmbed parsedUrl={parsedUrl} />;
}
if (spotifyHosts.has(parsedUrl.host)) {
return <SpotifyEmbed parsedUrl={parsedUrl} />;
}
};
interface EmbedComponentProps {
parsedUrl: URL;
}
const youtubeHosts = new Set<string>(['youtube.com', 'www.youtube.com', 'youtu.be', 'www.youtu.be', 'm.youtube.com']);
const YoutubeEmbed = ({ parsedUrl }: EmbedComponentProps) => {
let youtubeId;
if (parsedUrl.host.endsWith('youtu.be')) {
youtubeId = parsedUrl.pathname.replaceAll('/', '');
} else {
youtubeId = parsedUrl.searchParams.get('v');
}
return (
<iframe
className={styles.videoEmbed}
height='100%'
width='100%'
allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share'
allowFullScreen
title={parsedUrl.href}
src={`https://www.youtube-nocookie.com/embed/${youtubeId}`}
/>
);
};
const xHosts = new Set<string>(['twitter.com', 'www.twitter.com', 'x.com', 'www.x.com']);
const XEmbed = ({ parsedUrl }: EmbedComponentProps) => {
return (
<iframe
className={styles.xEmbed}
height='100%'
width='100%'
referrerPolicy='no-referrer'
allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share'
title={parsedUrl.href}
srcDoc={`
<blockquote class="twitter-tweet" data-theme="dark">
<a href="${parsedUrl.href.replace('x.com', 'twitter.com')}"></a>
</blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
`}
/>
);
};
const redditHosts = new Set<string>(['reddit.com', 'www.reddit.com', 'old.reddit.com']);
const RedditEmbed = ({ parsedUrl }: EmbedComponentProps) => {
return (
<iframe
className={styles.redditEmbed}
height='100%'
width='100%'
referrerPolicy='no-referrer'
allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share'
title={parsedUrl.href}
srcDoc={`
<style>
/* fix reddit iframe being centered */
iframe {
margin: 0!important;
}
</style>
<blockquote class="reddit-embed-bq" style="height:240px" data-embed-theme="dark">
<a href="${parsedUrl.href}"></a>
</blockquote>
<script async src="https://embed.reddit.com/widgets.js" charset="UTF-8"></script>
`}
/>
);
};
const twitchHosts = new Set<string>(['twitch.tv', 'www.twitch.tv']);
const TwitchEmbed = ({ parsedUrl }: EmbedComponentProps) => {
let iframeUrl;
if (parsedUrl.pathname.startsWith('/videos/')) {
const videoId = parsedUrl.pathname.replace('/videos/', '');
iframeUrl = `https://player.twitch.tv/?video=${videoId}&parent=${window.location.hostname}`;
} else {
const channel = parsedUrl.pathname.replaceAll('/', '');
iframeUrl = `https://player.twitch.tv/?channel=${channel}&parent=${window.location.hostname}`;
}
return (
<iframe
className={styles.videoEmbed}
height='100%'
width='100%'
referrerPolicy='no-referrer'
allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share'
allowFullScreen
title={parsedUrl.href}
src={iframeUrl}
/>
);
};
const tiktokHosts = new Set<string>(['tiktok.com', 'www.tiktok.com']);
const TiktokEmbed = ({ parsedUrl }: EmbedComponentProps) => {
const videoId = parsedUrl.pathname.replace(/.+\/video\//, '').replaceAll('/', '');
return (
<iframe
className={styles.tiktokEmbed}
height='100%'
width='100%'
referrerPolicy='no-referrer'
allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share'
title={parsedUrl.href}
srcDoc={`
<blockquote class="tiktok-embed" data-video-id="${videoId}">
<a></a>
</blockquote>
<script async src="https://www.tiktok.com/embed.js"></script>
`}
/>
);
};
const instagramHosts = new Set<string>(['instagram.com', 'www.instagram.com']);
const InstagramEmbed = ({ parsedUrl }: EmbedComponentProps) => {
const pathNames = parsedUrl.pathname.replace(/\/+$/, '').split('/');
const id = pathNames[pathNames.length - 1];
return (
<iframe
className={styles.instagramEmbed}
height='100%'
width='100%'
referrerPolicy='no-referrer'
allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share'
title={parsedUrl.href}
srcDoc={`
<blockquote class="instagram-media">
<a href="https://www.instagram.com/p/${id}/"></a>
</blockquote>
<script async src="//www.instagram.com/embed.js"></script>
`}
/>
);
};
const odyseeHosts = new Set<string>(['odysee.com', 'www.odysee.com']);
const OdyseeEmbed = ({ parsedUrl }: EmbedComponentProps) => {
const iframeUrl = `https://odysee.com/$/embed${parsedUrl.pathname}`;
return (
<iframe
className={styles.videoEmbed}
height='100%'
width='100%'
referrerPolicy='no-referrer'
allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share'
allowFullScreen
title={parsedUrl.href}
src={iframeUrl}
/>
);
};
const bitchuteHosts = new Set<string>(['bitchute.com', 'www.bitchute.com']);
const BitchuteEmbed = ({ parsedUrl }: EmbedComponentProps) => {
const videoId = parsedUrl.pathname.replace(/\/video\//, '').replaceAll('/', '');
return (
<iframe
className={styles.videoEmbed}
height='100%'
width='100%'
referrerPolicy='no-referrer'
allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share'
allowFullScreen
title={parsedUrl.href}
src={`https://www.bitchute.com/embed/${videoId}/`}
/>
);
};
const streamableHosts = new Set<string>(['streamable.com', 'www.streamable.com']);
const StreamableEmbed = ({ parsedUrl }: EmbedComponentProps) => {
const videoId = parsedUrl.pathname.replaceAll('/', '');
return (
<iframe
className={styles.videoEmbed}
height='100%'
width='100%'
referrerPolicy='no-referrer'
allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share'
allowFullScreen
title={parsedUrl.href}
src={`https://streamable.com/e/${videoId}`}
/>
);
};
const spotifyHosts = new Set<string>(['spotify.com', 'www.spotify.com', 'open.spotify.com']);
const SpotifyEmbed = ({ parsedUrl }: EmbedComponentProps) => {
const iframeUrl = `https://open.spotify.com/embed${parsedUrl.pathname}?theme=0`;
return (
<iframe
className={styles.audioEmbed}
height='100%'
width='100%'
referrerPolicy='no-referrer'
allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share'
allowFullScreen
title={parsedUrl.href}
src={iframeUrl}
/>
);
};
const canEmbedHosts = new Set<string>([
...youtubeHosts,
...xHosts,
...redditHosts,
...twitchHosts,
...tiktokHosts,
...instagramHosts,
...odyseeHosts,
...bitchuteHosts,
...streamableHosts,
...spotifyHosts,
]);
export const canEmbed = (parsedUrl: URL): boolean => canEmbedHosts.has(parsedUrl.host);
export default Embed;
+1
View File
@@ -0,0 +1 @@
export { default, canEmbed } from './embed';
+10
View File
@@ -1,3 +1,13 @@
.thread {
clear: both;
}
.link a {
color: var(--post-link-text-color);
text-decoration: var(--post-link-text-decoration);
}
.link a:hover {
color: var(--post-link-text-color-hover);
text-decoration: var(--post-link-text-decoration-hover);
}
+20 -3
View File
@@ -1,13 +1,30 @@
import { Comment } from '@plebbit/plebbit-react-hooks';
import styles from './post.module.css';
import useReplies from '../../hooks/use-replies';
import { getLinkMediaInfoMemoized } from '../../lib/utils/media-utils';
const Post = ({ post }: Comment) => {
const { content, link, title } = post;
const replies = useReplies(post);
const linkMediaInfo = getLinkMediaInfoMemoized(link);
return (
<div>
<div className={styles.thread}>
<hr />
<h1>{post.title}</h1>
<p>{post.content}</p>
<div className={styles.postContainer}>
<div className={styles.postDesktop}>
{linkMediaInfo?.url && (
<div className={styles.link}>
Link:{' '}
<a href={linkMediaInfo?.url} target='_blank' rel='noopener noreferrer'>
{linkMediaInfo.url.length > 30 ? linkMediaInfo?.url.slice(0, 30) + '...' : linkMediaInfo?.url}
</a>{' '}
({linkMediaInfo?.type})
</div>
)}
<div className={styles.postInfo}></div>
<div className={styles.postMessage}></div>
</div>
</div>
</div>
);
};