Merge branch 'development' into master

This commit is contained in:
plebeius.eth
2023-09-09 21:26:25 +02:00
committed by GitHub
7 changed files with 592 additions and 228 deletions
+259
View File
@@ -0,0 +1,259 @@
const Embed = ({url}) => {
const parsedUrl = new URL(url);
if (youtubeHosts.has(parsedUrl.host)) {
return <YoutubeEmbed parsedUrl={parsedUrl} />;
}
if (twitterHosts.has(parsedUrl.host)) {
return <TwitterEmbed 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} />;
}
};
const youtubeHosts = new Set(['youtube.com', 'www.youtube.com', 'youtu.be', 'www.youtu.be']);
const YoutubeEmbed = ({parsedUrl}) => {
let youtubeId;
if (parsedUrl.host.endsWith('youtu.be')) {
youtubeId = parsedUrl.pathname.replaceAll('/', '');
}
else {
youtubeId = parsedUrl.searchParams.get('v');
}
return <iframe
className='enlarged youtube-embed'
height="100%"
width="100%"
frameborder="0"
credentialless
referrerpolicy='no-referrer'
allow="accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen
title={parsedUrl.href}
src={`https://www.youtube.com/embed/${youtubeId}`}
/>;
};
const twitterHosts = new Set(['twitter.com', 'www.twitter.com', 'x.com', 'www.x.com']);
const TwitterEmbed = ({parsedUrl}) => {
return <iframe
className='enlarged twitter-embed'
height="100%"
width="100%"
frameborder="0"
credentialless
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}"></a>
</blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
`}
/>;
};
const redditHosts = new Set(['reddit.com', 'www.reddit.com', 'old.reddit.com']);
const RedditEmbed = ({parsedUrl}) => {
return <iframe
className='enlarged reddit-embed'
height="100%"
width="100%"
frameborder="0"
credentialless
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(['twitch.tv', 'www.twitch.tv']);
const TwitchEmbed = ({parsedUrl}) => {
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='enlarged twitch-embed'
height="100%"
width="100%"
frameborder="0"
credentialless
referrerpolicy='no-referrer'
allow="accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen
title={parsedUrl.href}
src={iframeUrl}
/>;
};
const tiktokHosts = new Set(['tiktok.com', 'www.tiktok.com']);
const TiktokEmbed = ({parsedUrl}) => {
const videoId = parsedUrl.pathname.replace(/.+\/video\//, '').replaceAll('/', '');
return <iframe
className='enlarged tiktok-embed'
height="100%"
width="100%"
frameborder="0"
credentialless
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(['instagram.com', 'www.instagram.com']);
const InstagramEmbed = ({parsedUrl}) => {
const pathNames = parsedUrl.pathname.replace(/\/+$/, '').split('/');
const id = pathNames[pathNames.length - 1];
return <iframe
className='enlarged instagram-embed'
height="100%"
width="100%"
frameborder="0"
credentialless
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(['odysee.com', 'www.odysee.com']);
const OdyseeEmbed = ({parsedUrl}) => {
const iframeUrl = `https://odysee.com/$/embed${parsedUrl.pathname}`;
return <iframe
className='enlarged odysee-embed'
height="100%"
width="100%"
frameborder="0"
credentialless
referrerpolicy='no-referrer'
allow="accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen
title={parsedUrl.href}
src={iframeUrl}
/>;
};
const bitchuteHosts = new Set(['bitchute.com', 'www.bitchute.com']);
const BitchuteEmbed = ({parsedUrl}) => {
const videoId = parsedUrl.pathname.replace(/\/video\//, '').replaceAll('/', '');
return <iframe
className='enlarged bitchute-embed'
height="100%"
width="100%"
frameborder="0"
credentialless
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(['streamable.com', 'www.streamable.com']);
const StreamableEmbed = ({parsedUrl}) => {
const videoId = parsedUrl.pathname.replaceAll('/', '');
return <iframe
className='enlarged streamable-embed'
height="100%"
width="100%"
frameborder="0"
credentialless
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(['spotify.com', 'www.spotify.com', 'open.spotify.com']);
const SpotifyEmbed = ({parsedUrl}) => {
const iframeUrl = `https://open.spotify.com/embed${parsedUrl.pathname}?theme=0`
return <iframe
className='enlarged spotify-embed'
height="100%"
width="100%"
frameborder="0"
credentialless
referrerpolicy='no-referrer'
allow="accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen
title={parsedUrl.href}
src={iframeUrl}
/>;
};
const canEmbedHosts = new Set([
...youtubeHosts, ...twitterHosts, ...redditHosts, ...twitchHosts,
...tiktokHosts, ...instagramHosts, ...odyseeHosts, ...bitchuteHosts,
...streamableHosts, ...spotifyHosts
]);
export const canEmbed = (parsedUrl) => canEmbedHosts.has(parsedUrl.host);
export default Embed;
+2 -1
View File
@@ -1,3 +1,4 @@
import React from 'react';
import {useComment, useAuthorAddress} from '@plebbit/plebbit-react-hooks';
function VerifiedAuthor({ commentCid, children }) {
@@ -7,4 +8,4 @@ function VerifiedAuthor({ commentCid, children }) {
return children({ authorAddress, shortAuthorAddress });
}
export default VerifiedAuthor;
export default React.memo(VerifiedAuthor);
+41 -4
View File
@@ -42,10 +42,47 @@ export const GlobalStyle = createGlobalStyle`
display: inline-block;
}
.enlarged {
display: block;
max-width: 100% !important;
max-height: 100% !important;
@media (min-width: 480px) {
.enlarged {
display: block;
max-width: 100% !important;
max-height: 100% !important;
}
.instagram-embed {
height: 420px;
width: 360px;
}
.streamable-embed,
.bitchute-embed,
.odysee-embed,
.youtube-embed,
.rumble-embed,
.twitch-embed {
height: 450px;
width: 800px;
}
.spotify-embed {
height: 240px;
width: 700px;
}
.tiktok-embed {
width: 400px;
height: 780px;
}
.twitter-embed {
width: 400px;
height: 580px;
}
.reddit-embed {
width: 500px;
height: 260px;
}
}
@media (max-width: 480px) {
+126 -88
View File
@@ -18,6 +18,7 @@ import ModerationModal from '../modals/ModerationModal';
import BoardSettings from '../BoardSettings';
import BoardStats from '../BoardStats';
import EditLabel from '../EditLabel';
import Embed from '../Embed';
import ImageBanner from '../ImageBanner';
import OfflineIndicator from '../OfflineIndicator';
import PendingLabel from '../PendingLabel';
@@ -1413,33 +1414,29 @@ const Board = () => {
commentMediaInfo?.url.length > 30 ?
commentMediaInfo?.url.slice(0, 30) + "(...)" :
commentMediaInfo?.url
}</a>&nbsp;({commentMediaInfo?.type === "iframe" ? "video" : commentMediaInfo?.type})
{isThreadThumbnailClicked[index] && commentMediaInfo.type !== "image" ? (
}</a>&nbsp;{commentMediaInfo?.type === "iframe" ? null : `(${commentMediaInfo?.type})`}
{ (isThreadThumbnailClicked[index] || (commentMediaInfo.type === 'iframe' && !commentMediaInfo.thumbnail)) && (
<span>
-[
[
<span className='reply-link'
style={{textDecoration: 'underline', cursor: 'pointer'}}
onClick={() => {handleThumbnailClick(index, 'thread')}}>Close</span>
onClick={() => {handleThumbnailClick(index, 'thread')}}>
{isThreadThumbnailClicked[index] ? 'Close' : 'Embed'}
</span>
]
</span>
) : null}
)}
</div>
{commentMediaInfo?.type === 'iframe' && (
<div key={`enlarge-${index}`}
className={`img-container ${isThreadThumbnailClicked[index] ? 'expanded-container' : ''}`}>
<span key={`fta-${index}`} className="file-thumb" style={isThreadThumbnailClicked[index] ? {} : {width: displayWidth, height: displayHeight}}>
{(isThreadThumbnailClicked[index] || !commentMediaInfo.thumbnail) && commentMediaInfo.embedUrl ? (
<iframe
className='enlarged'
key={`fti-${index}`}
src={commentMediaInfo.embedUrl}
width={commentMediaInfo.thumbnail ? "560" : "250"}
height="315"
style={{border: "none"}}
title="Embedded content"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen />
) : (
<span key={`fta-${index}`} className="file-thumb" style={
isThreadThumbnailClicked[index] || !commentMediaInfo.thumbnail ?
{} : {width: displayWidth, height: displayHeight
}}>
{isThreadThumbnailClicked[index] && commentMediaInfo.url ? (
<Embed url={commentMediaInfo.url} key={`fti-${index}`} />
) : commentMediaInfo.thumbnail ? (
<img
key={`fti-${index}`}
src={commentMediaInfo.thumbnail}
@@ -1447,7 +1444,7 @@ const Board = () => {
onClick={() => {handleThumbnailClick(index, 'thread')}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
)}
) : null}
</span>
</div>
)}
@@ -2113,33 +2110,53 @@ const Board = () => {
</div>
</div>
{replyMediaInfo?.url ? (
<div key={`f-${index}`} className="file"
style={{marginBottom: "5px"}}>
<div key={`ft-${index}`} className="reply-file-text">
<div key={`f-${index}`} className="file" style={{marginBottom: "5px"}}>
<div key={`ft-${index}`} className="file-text">
Link:&nbsp;
<a key={`fa-${index}`} href={replyMediaInfo.url} target="_blank"
rel="noopener noreferrer">{
replyMediaInfo?.url.length > 30 ?
replyMediaInfo?.url.slice(0, 30) + "(...)" :
replyMediaInfo?.url
}</a>&nbsp;({replyMediaInfo?.type})
{replyMediaInfo?.type === "video" || replyMediaInfo?.type === "iframe" ? (
isReplyThumbnailClicked[index] && commentMediaInfo.type !== "image" ? (
<span>
-[
<span className='reply-link'
style={{textDecoration: 'underline', cursor: 'pointer'}}
onClick={() => {handleThumbnailClick(index, 'reply')}}>Close</span>
]
</span>)
: null)
: null}
}</a>&nbsp;{replyMediaInfo?.type === "iframe" ? null : `(${replyMediaInfo?.type})`}
{ (isReplyThumbnailClicked[index] || (replyMediaInfo.type === 'iframe' && !replyMediaInfo.thumbnail)) && (
<span>
[
<span className='reply-link'
style={{textDecoration: 'underline', cursor: 'pointer'}}
onClick={() => {handleThumbnailClick(index, 'reply')}}>
{isReplyThumbnailClicked[index] ? 'Close' : 'Embed'}
</span>
]
</span>
)}
</div>
{replyMediaInfo?.type === "webpage" ? (
<div key={`enlarge-reply-${index}`} className="img-container">
{replyMediaInfo?.type === 'iframe' && (
<div key={`enlarge-${index}`}
className={`img-container ${isReplyThumbnailClicked[index] ? 'expanded-container' : ''}`}>
<span key={`fta-${index}`} className="file-thumb-reply" style={
isReplyThumbnailClicked[index] || !replyMediaInfo.thumbnail ?
{} : {width: replyDisplayWidth, height: replyDisplayHeight
}}>
{isReplyThumbnailClicked[index] && replyMediaInfo.url ? (
<Embed url={replyMediaInfo.url} key={`fti-${index}`} />
) : replyMediaInfo.thumbnail ? (
<img
key={`fti-${index}`}
src={replyMediaInfo.thumbnail}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index, 'reply')}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
) : null}
</span>
</div>
)}
{replyMediaInfo?.type === "webpage" && (
<div key={`enlarge-${index}`} className="img-container">
<span key={`fta-${index}`} className="file-thumb-reply" style={isReplyThumbnailClicked[index] || !reply.thumbnailUrl ? {} : {width: replyDisplayWidth, height: replyDisplayHeight}}>
{reply.thumbnailUrl ? (
<img key={`fti-${index}`}
<img key={`fti-${index}`}
src={replyMediaInfo.thumbnail} alt={replyMediaInfo.type}
onClick={(e) => {handleImageClick(e); handleThumbnailClick(index, 'reply')}}
style={{cursor: "pointer"}}
@@ -2147,51 +2164,50 @@ const Board = () => {
) : null}
</span>
</div>
) : null}
{replyMediaInfo?.type === "image" ? (
<div key={`enlarge-reply-${index}`} className="img-container">
)}
{replyMediaInfo?.type === "image" && (
<div key={`enlarge-${index}`} className="img-container">
<span key={`fta-${index}`} className="file-thumb-reply" style={isReplyThumbnailClicked[index] ? {} : {width: replyDisplayWidth, height: replyDisplayHeight}}>
<img key={`fti-${index}`}
src={replyMediaInfo.url} alt={replyMediaInfo.type}
<img key={`fti-${index}`}
src={replyMediaInfo.url} alt={replyMediaInfo.type}
onClick={(e) => {handleImageClick(e); handleThumbnailClick(index, 'reply')}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
</span>
</div>
) : null}
{replyMediaInfo?.type === "video" ? (
<div key={`enlarge-reply-${index}`} className={`img-container ${isReplyThumbnailClicked[index] ? 'expanded-container' : ''}`}>
<span key={`fta-${index}`} className="file-thumb-reply" style={isReplyThumbnailClicked[index] ? {} : {width: replyDisplayWidth, height: replyDisplayHeight}}>
{isReplyThumbnailClicked[index] ? (
<video controls
className='enlarged'
key={`fti-${index}`}
src={replyMediaInfo.url}
alt={replyMediaInfo.type}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl}
/>
) : (
<video
key={`fti-${index}`}
src={replyMediaInfo.url}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index, 'reply')}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl}
/>
)}
</span>
</div>
) : null}
{replyMediaInfo?.type === "audio" ? (
<span key={`fta-${index}`} className="file-thumb-reply" style={isReplyThumbnailClicked[index] ? {} : {width: replyDisplayWidth, height: replyDisplayHeight}}>
<audio controls
key={`fti-${index}`}
src={replyMediaInfo.url} alt={replyMediaInfo.type}
)}
{replyMediaInfo?.type === "video" && (
<div key={`enlarge-${index}`} className={`img-container ${isReplyThumbnailClicked[index] ? 'expanded-container' : ''}`}>
<span key={`fta-${index}`} className="file-thumb-reply" style={isReplyThumbnailClicked[index] ? {} : {width: replyDisplayWidth, height: replyDisplayHeight}}>
{isReplyThumbnailClicked[index] ? (
<video
className='enlarged'
key={`fti-${index}`}
src={replyMediaInfo.url}
controls
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl}
/>
) : (
<video
key={`fti-${index}`}
src={replyMediaInfo.url}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index, 'reply')}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl}
/>
)}
</span>
</div>
)}
{replyMediaInfo?.type === "audio" && (
<span key={`fta-${index}`} className="file-thumb-reply">
<audio controls key={`fti-${index}`}
src={replyMediaInfo.url} alt={replyMediaInfo.type}
onError={(e) => e.target.src = fallbackImgUrl} />
</span>
) : null}
)}
</div>
) : null}
{reply.content ? (
@@ -2511,17 +2527,11 @@ const Board = () => {
{commentMediaInfo?.type === 'iframe' && (
<div key={`enlarge-mob-${index}`} className="img-container">
<span key={`mob-fta-${index}`} className="file-thumb-mobile" style={isMobileThreadThumbnailClicked[index] ? {} : {marginBottom: '25px'}}>
{(isMobileThreadThumbnailClicked[index] || !commentMediaInfo.thumbnail) && commentMediaInfo.embedUrl ? (
{(isMobileThreadThumbnailClicked[index] && commentMediaInfo.url ? (
<div style={{width: "92vw"}}>
<iframe
key={`mob-fti-${index}`}
src={commentMediaInfo.embedUrl}
style={{border: "none", height: "250px"}}
title="Embedded content"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen />
<Embed url={commentMediaInfo.url} key={`fti-mobile-${index}`} />
</div>
) : (
) : commentMediaInfo.thumbnail ? (
<img
key={`mob-fti-${index}`}
src={commentMediaInfo.thumbnail}
@@ -2529,7 +2539,7 @@ const Board = () => {
onClick={() => {handleThumbnailClick(index, 'mobileThread')}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
)}
) : <span onClick={() => {handleThumbnailClick(index, 'mobileThread')}}>Embed</span>)}
{commentMediaInfo?.type === "video" || commentMediaInfo?.type === "iframe" ? (
isMobileThreadThumbnailClicked[index] ? (
<div style={{textAlign: "center", marginTop: "15px", marginBottom: "15px"}}>
@@ -2538,8 +2548,8 @@ const Board = () => {
>Close</span>
</div>
) : (
<div key={`mob-fi-${index}`} className="file-info-mobile">video</div>
)) : <div key={`mob-fi-${index}`} className="file-info-mobile">video</div>}
<div key={`mob-fi-${index}`} className="file-info-mobile">webpage</div>
)) : <div key={`mob-fi-${index}`} className="file-info-mobile">webpage</div>}
</span>
</div>
)}
@@ -2744,7 +2754,35 @@ const Board = () => {
{reply.link ? (
<div key={`mob-f-${index}`} className="file-mobile">
{replyMediaInfo?.url ? (
replyMediaInfo.type === "webpage" ? (
replyMediaInfo.type === "iframe" ? (
<div key={`enlarge-mob-${index}`} className="img-container">
<span key={`mob-fta-${index}`} className="file-thumb-mobile" style={isMobileReplyThumbnailClicked[index] ? {} : {marginBottom: '25px'}}>
{(isMobileReplyThumbnailClicked[index] && replyMediaInfo.url ? (
<div style={{width: "92vw"}}>
<Embed url={replyMediaInfo.url} key={`fti-mobile-reply-${index}`} />
</div>
) : replyMediaInfo.thumbnail ? (
<img
key={`mob-fti-${index}`}
src={replyMediaInfo.thumbnail}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index, 'mobileReply')}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
) : <span onClick={() => {handleThumbnailClick(index, 'mobileReply')}}>Embed</span>)}
{replyMediaInfo?.type === "video" || replyMediaInfo?.type === "iframe" ? (
isMobileReplyThumbnailClicked[index] ? (
<div style={{textAlign: "center", marginTop: "15px", marginBottom: "15px"}}>
<span className='button-mobile' style={{float: "none", cursor: "pointer"}}
onClick={() => {handleThumbnailClick(index, 'mobileReply')}}
>Close</span>
</div>
) : (
<div key={`mob-fi-${index}`} className="file-info-mobile">webpage</div>
)) : <div key={`mob-fi-${index}`} className="file-info-mobile">webpage</div>}
</span>
</div>
) : replyMediaInfo.type === "webpage" ? (
<div key={`enlarge-mob-reply-${index}`} className="img-container">
<span key={`mob-ft${reply.cid}`} className="file-thumb-mobile" style={isMobileReplyThumbnailClicked[index] || !reply.thumbnailUrl ? {} : {width: displayWidthMobile, height: displayHeightMobile}}>
{reply.thumbnailUrl ? (
@@ -2769,7 +2807,7 @@ const Board = () => {
</span>
</div>
) : replyMediaInfo.type === "video" ? (
<span key={`mob-ft${thread.cid}`} className="file-thumb-mobile" style={isMobileReplyThumbnailClicked[index] ? {} : {marginBottom: '25px'}}>
<span key={`mob-ft${reply.cid}`} className="file-thumb-mobile" style={isMobileReplyThumbnailClicked[index] ? {} : {marginBottom: '25px'}}>
{isMobileReplyThumbnailClicked[index] ? (
<video key={`fti-${index}`}
src={replyMediaInfo.url} alt={replyMediaInfo.type}
+3 -3
View File
@@ -448,7 +448,7 @@ const CatalogPost = ({post}) => {
</BoardForm>
<Link style={{all: "unset", cursor: "pointer"}} to={`/p/${selectedAddress}/rules`}>
<div className="teaser" style={{maxHeight: '312px'}}>
<div style={{cursor: 'text'}}>
<div style={{cursor: 'pointer'}}>
<span ref={textRef}
onMouseOver={() => {
setIsHoveringOnThread('rules');
@@ -621,7 +621,7 @@ const CatalogPost = ({post}) => {
<Link style={{all: "unset", cursor: "pointer"}} to={`/p/${selectedAddress}/description`}
onClick={() => setSelectedThread("description")}>
<div className="teaser" style={{maxHeight: '170px'}}>
<div style={{cursor: 'text'}}>
<div style={{cursor: 'pointer'}}>
<span ref={textRef}
onMouseOver={() => {
setIsHoveringOnThread('description');
@@ -920,7 +920,7 @@ const CatalogPost = ({post}) => {
<Link style={{all: "unset", cursor: "pointer"}} key={`link2-`} to={`/p/${selectedAddress}/c/${thread.cid}`}
onClick={() => setSelectedThread(thread.cid)}>
<div key={`t-`} className="teaser" style={{maxHeight: `calc(320px - ${displayHeight})`}}>
<div style={{cursor:"text"}}>
<div style={{cursor:"pointer"}}>
<span key={`teaser-width${thread.cid}`} ref={textRef}
onMouseOver={() => {
setIsHoveringOnThread(thread.cid);
+153 -95
View File
@@ -13,6 +13,7 @@ import { AlertModal } from '../styled/modals/AlertModal.styled';
import { PostMenuCatalog } from '../styled/views/Catalog.styled';
import BoardStats from '../BoardStats';
import EditLabel from '../EditLabel';
import Embed from '../Embed';
import ImageBanner from '../ImageBanner';
import OfflineIndicator from '../OfflineIndicator';
import PendingLabel from '../PendingLabel';
@@ -121,6 +122,7 @@ const Thread = () => {
const [isThreadThumbnailClicked, setIsThreadThumbnailClicked] = useState({});
const [isReplyThumbnailClicked, setIsReplyThumbnailClicked] = useState({});
const [isMobileThreadThumbnailClicked, setIsMobileThreadThumbnailClicked] = useState({});
const [isMobileReplyThumbnailClicked, setIsMobileReplyThumbnailClicked] = useState({});
const [isCreateBoardOpen, setIsCreateBoardOpen] = useState(false);
@@ -157,6 +159,12 @@ const Thread = () => {
[index]: !prevState[index],
}));
break;
case 'mobileReply':
setIsMobileReplyThumbnailClicked(prevState => ({
...prevState,
[index]: !prevState[index],
}));
break;
default:
break;
}
@@ -862,33 +870,26 @@ const Thread = () => {
commentMediaInfo?.url.length > 30 ?
commentMediaInfo?.url.slice(0, 30) + "(...)" :
commentMediaInfo?.url
}</a>&nbsp;({commentMediaInfo?.type === "iframe" ? "video" : commentMediaInfo?.type})
{isThreadThumbnailClicked[index] ? (
}</a>&nbsp;{commentMediaInfo?.type === "iframe" ? null : `(${commentMediaInfo?.type})`}
{ (isThreadThumbnailClicked[index] || (commentMediaInfo.type === 'iframe' && !commentMediaInfo.thumbnail)) && (
<span>
-[
[
<span className='reply-link'
style={{textDecoration: 'underline', cursor: 'pointer'}}
onClick={() => {handleThumbnailClick(index, 'thread')}}>Close</span>
onClick={() => {handleThumbnailClick(index, 'thread')}}>
{isThreadThumbnailClicked[index] ? 'Close' : 'Embed'}
</span>
]
</span>
) : null}
)}
</div>
{commentMediaInfo?.type === 'iframe' && (
<div key={`enlarge-${index}`}
className={`img-container ${isThreadThumbnailClicked[index] ? 'expanded-container' : ''}`}>
<span key={`fta-${index}`} className="file-thumb">
{(isThreadThumbnailClicked[index] || !commentMediaInfo.thumbnail) && commentMediaInfo.embedUrl ? (
<iframe
className='enlarged'
key={`fti-${index}`}
src={commentMediaInfo.embedUrl}
width={commentMediaInfo.thumbnail ? "560" : "250"}
height="315"
style={{border: "none"}}
title="Embedded content"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen />
) : (
{isThreadThumbnailClicked[index] && commentMediaInfo.url ? (
<Embed url={commentMediaInfo.url} key={`fti-${index}`} />
) : commentMediaInfo.thumbnail ? (
<img
key={`fti-${index}`}
src={commentMediaInfo.thumbnail}
@@ -896,7 +897,7 @@ const Thread = () => {
onClick={() => {handleThumbnailClick(index, 'thread')}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
)}
) : null}
</span>
</div>
)}
@@ -1521,27 +1522,48 @@ const Thread = () => {
</div>
</div>
{replyMediaInfo?.url ? (
<div key={`f-${index}`} className="file"
style={{marginBottom: "5px"}}>
<div key={`ft-${index}`} className="reply-file-text">
<div key={`f-${index}`} className="file" style={{marginBottom: "5px"}}>
<div key={`ft-${index}`} className="file-text">
Link:&nbsp;
<a key={`fa-${index}`} href={replyMediaInfo.url} target="_blank"
rel="noopener noreferrer">{
replyMediaInfo?.url.length > 30 ?
replyMediaInfo?.url.slice(0, 30) + "(...)" :
replyMediaInfo?.url
}</a>&nbsp;({replyMediaInfo?.type})
{replyMediaInfo?.type === "video" || replyMediaInfo?.type === "iframe" ? (
isReplyThumbnailClicked[index] ? (
<span>
-[
<span className='reply-link'
style={{textDecoration: 'underline', cursor: 'pointer'}}
onClick={() => {handleThumbnailClick(index, 'reply')}}>Close</span>
]
</span>) : null)
: null}
}</a>&nbsp;{replyMediaInfo?.type === "iframe" ? null : `(${replyMediaInfo?.type})`}
{ (isReplyThumbnailClicked[index] || (replyMediaInfo.type === 'iframe' && !replyMediaInfo.thumbnail)) && (
<span>
[
<span className='reply-link'
style={{textDecoration: 'underline', cursor: 'pointer'}}
onClick={() => {handleThumbnailClick(index, 'reply')}}>
{isReplyThumbnailClicked[index] ? 'Close' : 'Embed'}
</span>
]
</span>
)}
</div>
{replyMediaInfo?.type === 'iframe' && (
<div key={`enlarge-${index}`}
className={`img-container ${isReplyThumbnailClicked[index] ? 'expanded-container' : ''}`}>
<span key={`fta-${index}`} className="file-thumb-reply" style={
isReplyThumbnailClicked[index] || !replyMediaInfo.thumbnail ?
{} : {width: replyDisplayWidth, height: replyDisplayHeight
}}>
{isReplyThumbnailClicked[index] && replyMediaInfo.url ? (
<Embed url={replyMediaInfo.url} key={`fti-${index}`} />
) : replyMediaInfo.thumbnail ? (
<img
key={`fti-${index}`}
src={replyMediaInfo.thumbnail}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index, 'reply')}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
) : null}
</span>
</div>
)}
{replyMediaInfo?.type === "webpage" ? (
<div key={`enlarge-reply-${index}`} className="img-container">
<span key={`fta-${index}`} className="file-thumb-reply" style={isReplyThumbnailClicked[index] ? {} : {width: replyDisplayWidth, height: replyDisplayHeight}}>
@@ -1796,39 +1818,33 @@ const Thread = () => {
<div key={`mob-f-${index}`} className="file-mobile">
{commentMediaInfo?.type === 'iframe' && (
<div key={`enlarge-mob-${index}`} className="img-container">
<span key={`mob-fta-${index}`} className="file-thumb-mobile">
{(isMobileThreadThumbnailClicked[index] || !commentMediaInfo.thumbnail) && commentMediaInfo.embedUrl ? (
<div style={{width: "92vw"}}>
<iframe
key={`mob-fti-${index}`}
src={commentMediaInfo.embedUrl}
style={{border: "none", height: "250px"}}
title="Embedded content"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen />
<span key={`mob-fta-${index}`} className="file-thumb-mobile" style={isMobileThreadThumbnailClicked[index] ? {} : {marginBottom: '25px'}}>
{(isMobileThreadThumbnailClicked[index] && commentMediaInfo.url ? (
<div style={{width: "92vw"}}>
<Embed url={commentMediaInfo.url} key={`fti-mobile-${index}`} />
</div>
) : commentMediaInfo.thumbnail ? (
<img
key={`mob-fti-${index}`}
src={commentMediaInfo.thumbnail}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index, 'mobileThread')}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
) : <span onClick={() => {handleThumbnailClick(index, 'mobileThread')}}>Embed</span>)}
{commentMediaInfo?.type === "video" || commentMediaInfo?.type === "iframe" ? (
isMobileThreadThumbnailClicked[index] ? (
<div style={{textAlign: "center", marginTop: "15px", marginBottom: "15px"}}>
<span className='button-mobile' style={{float: "none", cursor: "pointer"}}
onClick={() => {handleThumbnailClick(index, 'mobileThread')}}
>Close</span>
</div>
) : (
<img
key={`mob-fti-${index}`}
src={commentMediaInfo.thumbnail}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index, 'mobileThread')}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
)}
{commentMediaInfo?.type === "video" || commentMediaInfo?.type === "iframe" ? (
isMobileThreadThumbnailClicked[index] ? (
<div style={{textAlign: "center", marginTop: "15px", marginBottom: "15px"}}>
<span className='button-mobile' style={{float: "none", cursor: "pointer"}}
onClick={() => {handleThumbnailClick(index, 'mobileThread')}}
>Close</span>
</div>
) : (
<div key={`mob-fi-${index}`} className="file-info-mobile">video</div>
)) : <div key={`mob-fi-${index}`} className="file-info-mobile">video</div>}
</span>
</div>
)}
<div key={`mob-fi-${index}`} className="file-info-mobile">webpage</div>
)) : <div key={`mob-fi-${index}`} className="file-info-mobile">webpage</div>}
</span>
</div>
)}
{commentMediaInfo?.url ? (
commentMediaInfo.type === "webpage" ? (
<div key={`enlarge-mob-${index}`} className="img-container">
@@ -1996,47 +2012,89 @@ const Thread = () => {
{reply.link ? (
<div key={`mob-f-${reply.cid}`} className="file-mobile">
{replyMediaInfo?.url ? (
replyMediaInfo.type === "webpage" ? (
<div key="enlarge-reply-mob" className="img-container">
replyMediaInfo.type === "iframe" ? (
<div key={`enlarge-mob-${index}`} className="img-container">
<span key={`mob-fta-${index}`} className="file-thumb-mobile" style={isMobileReplyThumbnailClicked[index] ? {} : {marginBottom: '25px'}}>
{(isMobileReplyThumbnailClicked[index] && replyMediaInfo.url ? (
<div style={{width: "92vw"}}>
<Embed url={replyMediaInfo.url} key={`fti-mobile-reply-${index}`} />
</div>
) : replyMediaInfo.thumbnail ? (
<img
key={`mob-fti-${index}`}
src={replyMediaInfo.thumbnail}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index, 'mobileReply')}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
) : <span onClick={() => {handleThumbnailClick(index, 'mobileReply')}}>Embed</span>)}
{replyMediaInfo?.type === "video" || replyMediaInfo?.type === "iframe" ? (
isMobileReplyThumbnailClicked[index] ? (
<div style={{textAlign: "center", marginTop: "15px", marginBottom: "15px"}}>
<span className='button-mobile' style={{float: "none", cursor: "pointer",
borderRadius: '3px', padding: '6px 10px 5px',}}
onClick={() => {handleThumbnailClick(index, 'mobileReply')}}
>Close</span>
</div>
) : (
<div key={`mob-fi-${index}`} className="file-info-mobile">webpage</div>
)) : <div key={`mob-fi-${index}`} className="file-info-mobile">webpage</div>}
</span>
</div>
) : replyMediaInfo.type === "webpage" ? (
<div key={`enlarge-mob-reply-${index}`} className="img-container">
<span key={`mob-ft${reply.cid}`} className="file-thumb-mobile">
{reply.thumbnailUrl ? (
<img key={`mob-img-${reply.cid}`}
src={replyMediaInfo.thumbnail} alt={replyMediaInfo.type}
onClick={handleImageClick}
<img key={`mob-img-${index}`}
src={replyMediaInfo.thumbnail} alt="thumbnail"
onClick={(e) => {handleImageClick(e); handleThumbnailClick(index, 'mobileReply')}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
) : null}
<div key={`mob-fi-${reply.cid}`} className="file-info-mobile">{replyMediaInfo.type}</div>
<div key={`mob-fi-${index}`} className="file-info-mobile">{replyMediaInfo.type}</div>
</span>
</div>
) : replyMediaInfo.type === "image" ? (
<div key="enlarge-reply-mob" className="img-container">
) : replyMediaInfo.type === "image" ? (
<div key={`enlarge-mob-reply-${index}`} className="img-container">
<span key={`mob-ft${reply.cid}`} className="file-thumb-mobile">
<img key={`mob-img-${index}`}
src={replyMediaInfo.url} alt={replyMediaInfo.type}
onClick={(e) => {handleImageClick(e); handleThumbnailClick(index, 'mobileReply')}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
<div key={`mob-fi-${index}`} className="file-info-mobile">{replyMediaInfo.type}</div>
</span>
</div>
) : replyMediaInfo.type === "video" ? (
<span key={`mob-ft${reply.cid}`} className="file-thumb-mobile">
<img key={`mob-img-${reply.cid}`}
src={replyMediaInfo.url} alt={replyMediaInfo.type}
onClick={handleImageClick}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
<div key={`mob-fi-${reply.cid}`} className="file-info-mobile">{replyMediaInfo.type}</div>
{isMobileReplyThumbnailClicked[index] ? (
<video key={`fti-${index}`}
src={replyMediaInfo.url} alt={replyMediaInfo.type}
controls
style={{ all: 'unset', width: '100%', height: '100%', cursor: 'pointer'}}
onError={(e) => e.target.src = fallbackImgUrl} />
) : (
<video
key={`fti-${index}`}
src={replyMediaInfo.url}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index, 'mobileReply')}}
style={{cursor: "pointer"}}
id="video-thumbnail-mobile"
onError={(e) => e.target.src = fallbackImgUrl}
/>
)}
</span>
</div>
) : replyMediaInfo.type === "video" ? (
<span key={`mob-ft${reply.cid}`} className="file-thumb-mobile">
<video key={`fti-${reply.cid}`}
src={replyMediaInfo.url} alt={replyMediaInfo.type}
style={{ pointerEvents: "none" }}
onError={(e) => e.target.src = fallbackImgUrl} />
<div key={`mob-fi-${reply.cid}`} className="file-info-mobile">{replyMediaInfo.type}</div>
</span>
) : replyMediaInfo.type === "audio" ? (
<span key={`mob-ft${reply.cid}`} className="file-thumb-mobile">
<audio key={`mob-img-${reply.cid}`}
src={replyMediaInfo.url} alt={replyMediaInfo.type}
onError={(e) => e.target.src = fallbackImgUrl} />
<div key={`mob-fi-${reply.cid}`} className="file-info-mobile">{replyMediaInfo.type}</div>
</span>
) : null
) : null}
) : replyMediaInfo.type === "audio" ? (
<span key={`mob-ft${reply.cid}`} className="file-thumb-mobile">
<audio key={`mob-img-${index}`}
src={replyMediaInfo.url} alt={replyMediaInfo.type}
onError={(e) => e.target.src = fallbackImgUrl} />
<div key={`mob-fi-${index}`} className="file-info-mobile">{replyMediaInfo.type}</div>
</span>
) : null
) : null}
</div>
) : null}
<blockquote key={`mob-pm-${index}`} className="post-message-mobile">
+8 -37
View File
@@ -1,4 +1,5 @@
import extName from 'ext-name';
import { canEmbed } from '../components/Embed';
const getCommentMediaInfo = (comment) => {
if (!comment?.thumbnailUrl && !comment?.link) {
@@ -9,56 +10,26 @@ const getCommentMediaInfo = (comment) => {
try {
const url = new URL(comment.link);
const host = url.hostname;
let embedUrl = null;
let scrapedThumbnailUrl;
if (['youtube.com', 'www.youtube.com', 'youtu.be'].includes(host)) {
const videoId = host === 'youtu.be' ? url.pathname.slice(1) : url.searchParams.get('v');
embedUrl = `https://www.youtube-nocookie.com/embed/${videoId}`;
const thumbnailUrl = `https://img.youtube.com/vi/${videoId}/sddefault.jpg`;
return {
url: comment.link,
embedUrl,
type: 'iframe',
thumbnail: comment.thumbnailUrl || thumbnailUrl,
};
} else if (host.includes('odysee.com')) {
const pathComponents = url.pathname.split('/');
const channelAndId = pathComponents[1];
const videoAndId = pathComponents[2];
embedUrl = `https://odysee.com/$/embed/${channelAndId}/${videoAndId}`;
// TODO: add support for Rumble, video id is dynamic
// } else if (host.includes('rumble.com')) {
// const regex = /(\/v.+?)-/;
// const match = regex.exec(url.pathname);
// if (match) {
// const videoId = match[1].slice(1);
// embedUrl = `https://rumble.com/embed/${videoId}/?pub=4`;
// }
scrapedThumbnailUrl = `https://img.youtube.com/vi/${videoId}/sddefault.jpg`;
} else if (host.includes('bitchute.com')) {
const videoId = url.pathname.split('/')[2];
embedUrl = `https://www.bitchute.com/embed/${videoId}/`;
scrapedThumbnailUrl = `https://static-3.bitchute.com/live/cover_images/F61vWF4shy8s/${videoId}_640x360.jpg`;
} else if (host.includes('streamable.com')) {
const videoId = url.pathname.split('/')[1];
embedUrl = `https://streamable.com/e/${videoId}?quality=highest`;
// TODO: Add support for Giphy (doesn't have thumbnails, size is variable)
// } else if (host.includes('giphy.com')) {
// const hyphenComponents = url.pathname.split('-');
// const gifId = hyphenComponents[hyphenComponents.length - 1];
// embedUrl = `https://giphy.com/embed/${gifId}`;
scrapedThumbnailUrl = `https://cdn-cf-east.streamable.com/image/${videoId}.jpg`;
}
if (embedUrl) {
if (canEmbed(url)) {
return {
url: comment.link,
embedUrl,
type: 'iframe',
thumbnail: comment.thumbnailUrl,
thumbnail: comment.thumbnailUrl || scrapedThumbnailUrl,
};
}