add embeds: youtube, odysee, bitchute, streamable

This commit is contained in:
plebeius.eth
2023-07-07 17:48:24 +02:00
parent 97fb0de9bf
commit 106c865b82
9 changed files with 1076 additions and 336 deletions
+3 -1
View File
@@ -229,8 +229,10 @@ const ReplyModal = ({ isOpen, closeModal }) => {
}, [selectedParentCid, anonymousMode, account]); }, [selectedParentCid, anonymousMode, account]);
useEffect(() => { useEffect(() => {
if (anonymousMode) {
updateSigner(); updateSigner();
}, [updateSigner]); }
}, [updateSigner, anonymousMode]);
useEffect(() => { useEffect(() => {
+11 -1
View File
@@ -2016,6 +2016,16 @@ export const BoardForm = styled.div`
} }
video { video {
width: 100%;
height: auto;
}
iframe {
width: 100%;
height: auto;
}
#video-thumbnail-mobile {
width: 100px; width: 100px;
} }
@@ -3804,7 +3814,7 @@ export const BoardForm = styled.div`
color: #707070; color: #707070;
font-weight: 700; font-weight: 700;
background-color: rgb(27,28,30); background-color: rgb(27,28,30);
border: 1px solid #282A2E; border: 1px solid #2D2F33;
background-image: url(assets/buttonfade-dark.png); background-image: url(assets/buttonfade-dark.png);
background-repeat: repeat-x; background-repeat: repeat-x;
text-decoration: none; text-decoration: none;
@@ -1193,7 +1193,13 @@ export const BoardForm = styled.div`
} }
video { video {
width: 100px; width: 100%;
height: auto;
}
iframe {
width: 100%;
height: auto;
} }
img { img {
@@ -2932,7 +2938,7 @@ export const BoardForm = styled.div`
color: #707070; color: #707070;
font-weight: 700; font-weight: 700;
background-color: rgb(27,28,30); background-color: rgb(27,28,30);
border: 1px solid #282A2E; border: 1px solid #2D2F33;
background-image: url(assets/buttonfade-dark.png); background-image: url(assets/buttonfade-dark.png);
background-repeat: repeat-x; background-repeat: repeat-x;
text-decoration: none; text-decoration: none;
+133 -6
View File
@@ -102,6 +102,8 @@ const All = () => {
const [moderatorPermissions, setModeratorPermissions] = useState({}); const [moderatorPermissions, setModeratorPermissions] = useState({});
const [executeAnonMode, setExecuteAnonMode] = useState(false); const [executeAnonMode, setExecuteAnonMode] = useState(false);
const [cidTracker, setCidTracker] = useState({}); const [cidTracker, setCidTracker] = useState({});
const [isThumbnailClicked, setIsThumbnailClicked] = useState({});
const [isMobileThumbnailClicked, setIsMobileThumbnailClicked] = useState({});
const setSelectedThreadCid = (cid) => { const setSelectedThreadCid = (cid) => {
selectedThreadCidRef.current = cid; selectedThreadCidRef.current = cid;
@@ -117,6 +119,20 @@ const All = () => {
const stateString = useFeedStateString(subplebbits); const stateString = useFeedStateString(subplebbits);
const handleThumbnailClick = (index, isMobile=false) => {
if (isMobile) {
setIsMobileThumbnailClicked(prevState => ({
...prevState,
[index]: !prevState[index],
}));
} else {
setIsThumbnailClicked(prevState => ({
...prevState,
[index]: !prevState[index],
}));
}
};
useEffect(() => { useEffect(() => {
let permissions = {}; let permissions = {};
@@ -626,8 +642,44 @@ const All = () => {
commentMediaInfo?.url.length > 30 ? commentMediaInfo?.url.length > 30 ?
commentMediaInfo?.url.slice(0, 30) + "(...)" : commentMediaInfo?.url.slice(0, 30) + "(...)" :
commentMediaInfo?.url commentMediaInfo?.url
}</a>&nbsp;({commentMediaInfo?.type}) }</a>&nbsp;({commentMediaInfo?.type === "iframe" ? "video" : commentMediaInfo?.type})
{isThumbnailClicked[index] ? (
<span>
-[
<span className='reply-link'
style={{textDecoration: 'underline', cursor: 'pointer'}}
onClick={() => {handleThumbnailClick(index)}}>Close</span>
]
</span>
) : null}
</div> </div>
{commentMediaInfo?.type === 'iframe' && (
<div key={`enlarge-${index}`}
className={`img-container ${isThumbnailClicked[index] ? 'expanded-container' : ''}`}>
<span key={`fta-${index}`} className="file-thumb">
{(isThumbnailClicked[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 />
) : (
<img
key={`fti-${index}`}
src={commentMediaInfo.thumbnail}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index)}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
)}
</span>
</div>
)}
{commentMediaInfo?.type === "webpage" ? ( {commentMediaInfo?.type === "webpage" ? (
<div key={`enlarge-${index}`} className="img-container"> <div key={`enlarge-${index}`} className="img-container">
<span key={`fta-${index}`} className="file-thumb"> <span key={`fta-${index}`} className="file-thumb">
@@ -653,11 +705,29 @@ const All = () => {
</div> </div>
) : null} ) : null}
{commentMediaInfo?.type === "video" ? ( {commentMediaInfo?.type === "video" ? (
<div key={`enlarge-${index}`} className={`img-container ${isThumbnailClicked[index] ? 'expanded-container' : ''}`}>
<span key={`fta-${index}`} className="file-thumb"> <span key={`fta-${index}`} className="file-thumb">
<video controls width="" key={`fti-${index}`} {isThumbnailClicked[index] ? (
src={commentMediaInfo.url} alt={commentMediaInfo.type} <video
onError={(e) => e.target.src = fallbackImgUrl} /> className='enlarged'
key={`fti-${index}`}
src={commentMediaInfo.url}
controls
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl}
/>
) : (
<video
key={`fti-${index}`}
src={commentMediaInfo.url}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index)}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl}
/>
)}
</span> </span>
</div>
) : null} ) : null}
{commentMediaInfo?.type === "audio" ? ( {commentMediaInfo?.type === "audio" ? (
<span key={`fta-${index}`} className="file-thumb"> <span key={`fta-${index}`} className="file-thumb">
@@ -1563,6 +1633,41 @@ const All = () => {
</div> </div>
{thread.link ? ( {thread.link ? (
<div key={`mob-f-${index}`} className="file-mobile"> <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">
{(isMobileThumbnailClicked[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 />
</div>
) : (
<img
key={`mob-fti-${index}`}
src={commentMediaInfo.thumbnail}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index, true)}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
)}
{commentMediaInfo?.type === "video" || "iframe" ? (
isMobileThumbnailClicked[index] ? (
<div style={{textAlign: "center", marginTop: "15px", marginBottom: "15px"}}>
<span className='button-mobile' style={{float: "none", cursor: "pointer"}}
onClick={() => {handleThumbnailClick(index, true)}}
>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>
)}
{commentMediaInfo?.url ? ( {commentMediaInfo?.url ? (
commentMediaInfo.type === "webpage" ? ( commentMediaInfo.type === "webpage" ? (
<div key={`enlarge-mob-${index}`} className="img-container"> <div key={`enlarge-mob-${index}`} className="img-container">
@@ -1590,11 +1695,33 @@ const All = () => {
</div> </div>
) : commentMediaInfo.type === "video" ? ( ) : commentMediaInfo.type === "video" ? (
<span key={`mob-ft${thread.cid}`} className="file-thumb-mobile"> <span key={`mob-ft${thread.cid}`} className="file-thumb-mobile">
{isMobileThumbnailClicked[index] ? (
<video key={`fti-${index}`} <video key={`fti-${index}`}
src={commentMediaInfo.url} alt={commentMediaInfo.type} src={commentMediaInfo.url} alt={commentMediaInfo.type}
style={{ pointerEvents: "none" }} controls
style={{ cursor: 'pointer' }}
onError={(e) => e.target.src = fallbackImgUrl} /> onError={(e) => e.target.src = fallbackImgUrl} />
<div key={`mob-fi-${index}`} className="file-info-mobile">{commentMediaInfo?.type}</div> ) : (
<video
key={`fti-${index}`}
src={commentMediaInfo.url}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index, true)}}
style={{cursor: "pointer"}}
id="video-thumbnail-mobile"
onError={(e) => e.target.src = fallbackImgUrl}
/>
)}
{commentMediaInfo?.type === "video" || "iframe" ? (
isMobileThumbnailClicked[index] ? (
<div style={{textAlign: "center", marginTop: "15px", marginBottom: "15px"}}>
<span className='button-mobile' style={{float: "none", cursor: "pointer"}}
onClick={() => {handleThumbnailClick(index, true)}}
>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> </span>
) : commentMediaInfo.type === "audio" ? ( ) : commentMediaInfo.type === "audio" ? (
<span key={`mob-ft${thread.cid}`} className="file-thumb-mobile"> <span key={`mob-ft${thread.cid}`} className="file-thumb-mobile">
+138 -7
View File
@@ -128,6 +128,8 @@ const Board = () => {
const [postOnHoverHeight, setPostOnHoverHeight] = useState(0); const [postOnHoverHeight, setPostOnHoverHeight] = useState(0);
const [executeAnonMode, setExecuteAnonMode] = useState(false); const [executeAnonMode, setExecuteAnonMode] = useState(false);
const [cidTracker, setCidTracker] = useState({}); const [cidTracker, setCidTracker] = useState({});
const [isThumbnailClicked, setIsThumbnailClicked] = useState({});
const [isMobileThumbnailClicked, setIsMobileThumbnailClicked] = useState({});
const setSelectedThreadCid = (cid) => { selectedThreadCidRef.current = cid;} const setSelectedThreadCid = (cid) => { selectedThreadCidRef.current = cid;}
@@ -147,10 +149,26 @@ const Board = () => {
}, [account?.author.address, subplebbit.roles]); }, [account?.author.address, subplebbit.roles]);
const handleThumbnailClick = (index, isMobile=false) => {
if (isMobile) {
setIsMobileThumbnailClicked(prevState => ({
...prevState,
[index]: !prevState[index],
}));
} else {
setIsThumbnailClicked(prevState => ({
...prevState,
[index]: !prevState[index],
}));
}
};
const handleOptionClick = () => { const handleOptionClick = () => {
setOpenMenuCid(null); setOpenMenuCid(null);
}; };
const handleOutsideClick = useCallback((e) => { const handleOutsideClick = useCallback((e) => {
if (openMenuCid !== null && !postMenuCatalogRef.current.contains(e.target)) { if (openMenuCid !== null && !postMenuCatalogRef.current.contains(e.target)) {
setOpenMenuCid(null); setOpenMenuCid(null);
@@ -439,8 +457,10 @@ const Board = () => {
}, [selectedThreadCidRef, anonymousMode, account]); }, [selectedThreadCidRef, anonymousMode, account]);
useEffect(() => { useEffect(() => {
if (anonymousMode) {
updateSigner(); updateSigner();
}, [updateSigner]); }
}, [updateSigner, anonymousMode]);
@@ -1268,8 +1288,44 @@ const Board = () => {
commentMediaInfo?.url.length > 30 ? commentMediaInfo?.url.length > 30 ?
commentMediaInfo?.url.slice(0, 30) + "(...)" : commentMediaInfo?.url.slice(0, 30) + "(...)" :
commentMediaInfo?.url commentMediaInfo?.url
}</a>&nbsp;({commentMediaInfo?.type}) }</a>&nbsp;({commentMediaInfo?.type === "iframe" ? "video" : commentMediaInfo?.type})
{isThumbnailClicked[index] ? (
<span>
-[
<span className='reply-link'
style={{textDecoration: 'underline', cursor: 'pointer'}}
onClick={() => {handleThumbnailClick(index)}}>Close</span>
]
</span>
) : null}
</div> </div>
{commentMediaInfo?.type === 'iframe' && (
<div key={`enlarge-${index}`}
className={`img-container ${isThumbnailClicked[index] ? 'expanded-container' : ''}`}>
<span key={`fta-${index}`} className="file-thumb">
{(isThumbnailClicked[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 />
) : (
<img
key={`fti-${index}`}
src={commentMediaInfo.thumbnail}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index)}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
)}
</span>
</div>
)}
{commentMediaInfo?.type === "webpage" ? ( {commentMediaInfo?.type === "webpage" ? (
<div key={`enlarge-${index}`} className="img-container"> <div key={`enlarge-${index}`} className="img-container">
<span key={`fta-${index}`} className="file-thumb"> <span key={`fta-${index}`} className="file-thumb">
@@ -1295,11 +1351,29 @@ const Board = () => {
</div> </div>
) : null} ) : null}
{commentMediaInfo?.type === "video" ? ( {commentMediaInfo?.type === "video" ? (
<div key={`enlarge-${index}`} className={`img-container ${isThumbnailClicked[index] ? 'expanded-container' : ''}`}>
<span key={`fta-${index}`} className="file-thumb"> <span key={`fta-${index}`} className="file-thumb">
<video controls width="" key={`fti-${index}`} {isThumbnailClicked[index] ? (
src={commentMediaInfo.url} alt={commentMediaInfo.type} <video
onError={(e) => e.target.src = fallbackImgUrl} /> className='enlarged'
key={`fti-${index}`}
src={commentMediaInfo.url}
controls
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl}
/>
) : (
<video
key={`fti-${index}`}
src={commentMediaInfo.url}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index)}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl}
/>
)}
</span> </span>
</div>
) : null} ) : null}
{commentMediaInfo?.type === "audio" ? ( {commentMediaInfo?.type === "audio" ? (
<span key={`fta-${index}`} className="file-thumb"> <span key={`fta-${index}`} className="file-thumb">
@@ -2191,6 +2265,41 @@ const Board = () => {
</div> </div>
{thread.link ? ( {thread.link ? (
<div key={`mob-f-${index}`} className="file-mobile"> <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">
{(isMobileThumbnailClicked[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 />
</div>
) : (
<img
key={`mob-fti-${index}`}
src={commentMediaInfo.thumbnail}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index, true)}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
)}
{commentMediaInfo?.type === "video" || "iframe" ? (
isMobileThumbnailClicked[index] ? (
<div style={{textAlign: "center", marginTop: "15px", marginBottom: "15px"}}>
<span className='button-mobile' style={{float: "none", cursor: "pointer"}}
onClick={() => {handleThumbnailClick(index, true)}}
>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>
)}
{commentMediaInfo?.url ? ( {commentMediaInfo?.url ? (
commentMediaInfo.type === "webpage" ? ( commentMediaInfo.type === "webpage" ? (
<div key={`enlarge-mob-${index}`} className="img-container"> <div key={`enlarge-mob-${index}`} className="img-container">
@@ -2218,11 +2327,33 @@ const Board = () => {
</div> </div>
) : commentMediaInfo.type === "video" ? ( ) : commentMediaInfo.type === "video" ? (
<span key={`mob-ft${thread.cid}`} className="file-thumb-mobile"> <span key={`mob-ft${thread.cid}`} className="file-thumb-mobile">
{isMobileThumbnailClicked[index] ? (
<video key={`fti-${index}`} <video key={`fti-${index}`}
src={commentMediaInfo.url} alt={commentMediaInfo.type} src={commentMediaInfo.url} alt={commentMediaInfo.type}
style={{ pointerEvents: "none" }} controls
style={{ cursor: 'pointer' }}
onError={(e) => e.target.src = fallbackImgUrl} /> onError={(e) => e.target.src = fallbackImgUrl} />
<div key={`mob-fi-${index}`} className="file-info-mobile">{commentMediaInfo?.type}</div> ) : (
<video
key={`fti-${index}`}
src={commentMediaInfo.url}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index, true)}}
style={{cursor: "pointer"}}
id="video-thumbnail-mobile"
onError={(e) => e.target.src = fallbackImgUrl}
/>
)}
{commentMediaInfo?.type === "video" || "iframe" ? (
isMobileThumbnailClicked[index] ? (
<div style={{textAlign: "center", marginTop: "15px", marginBottom: "15px"}}>
<span className='button-mobile' style={{float: "none", cursor: "pointer"}}
onClick={() => {handleThumbnailClick(index, true)}}
>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> </span>
) : commentMediaInfo.type === "audio" ? ( ) : commentMediaInfo.type === "audio" ? (
<span key={`mob-ft${thread.cid}`} className="file-thumb-mobile"> <span key={`mob-ft${thread.cid}`} className="file-thumb-mobile">
+201 -38
View File
@@ -12,6 +12,7 @@ import PostLoader from '../PostLoader';
import SettingsModal from '../modals/SettingsModal'; import SettingsModal from '../modals/SettingsModal';
import getCommentMediaInfo from '../../utils/getCommentMediaInfo'; import getCommentMediaInfo from '../../utils/getCommentMediaInfo';
import getDate from '../../utils/getDate'; import getDate from '../../utils/getDate';
import handleImageClick from '../../utils/handleImageClick';
import handleQuoteClick from '../../utils/handleQuoteClick'; import handleQuoteClick from '../../utils/handleQuoteClick';
import handleStyleChange from '../../utils/handleStyleChange'; import handleStyleChange from '../../utils/handleStyleChange';
import useError from '../../hooks/useError'; import useError from '../../hooks/useError';
@@ -35,6 +36,23 @@ const Pending = () => {
const account = useAccount(); const account = useAccount();
const comment = useAccountComment({commentIndex: index}); const comment = useAccountComment({commentIndex: index});
const [, setNewErrorMessage] = useError(); const [, setNewErrorMessage] = useError();
const [isThumbnailClicked, setIsThumbnailClicked] = useState({});
const [isMobileThumbnailClicked, setIsMobileThumbnailClicked] = useState({});
const handleThumbnailClick = (index, isMobile=false) => {
if (isMobile) {
setIsMobileThumbnailClicked(prevState => ({
...prevState,
[index]: !prevState[index],
}));
} else {
setIsThumbnailClicked(prevState => ({
...prevState,
[index]: !prevState[index],
}));
}
};
useEffect(() => { useEffect(() => {
setSelectedAddress(comment?.subplebbitAddress); setSelectedAddress(comment?.subplebbitAddress);
@@ -138,6 +156,7 @@ const Pending = () => {
] ]
</span> </span>
<div id="board-nav-mobile" style={{ top: visible ? 0 : '-23px' }}> <div id="board-nav-mobile" style={{ top: visible ? 0 : '-23px' }}>
<div className="nav-container">
<div className="board-select"> <div className="board-select">
<strong>Board</strong> <strong>Board</strong>
&nbsp; &nbsp;
@@ -148,18 +167,18 @@ const Pending = () => {
<option key={`option-${subplebbit.address}`} value={subplebbit.address} <option key={`option-${subplebbit.address}`} value={subplebbit.address}
>{subplebbit.title ? subplebbit.title : subplebbit.address}</option> >{subplebbit.title ? subplebbit.title : subplebbit.address}</option>
))} ))}
</select> </select> 
<span style={{cursor: 'pointer'}} onClick={ <span id="button-span" style={{cursor: 'pointer'}} onClick={
() => alert( () => alert(
'To create a board, first you have to run a full node.\nYou can run a full node by simply browsing with the plebbit desktop app. After you download it, open it, wait for it loading, then click on "Home" in the top left, then "Create Community".\n\nAfter you create the community, you can go back to plebchan at any time to see it as a board by pasting its address (begins with p/12D3KooW...) in the search bar, which is in the Home.\n\nNote:\n\n- Your community will be online for as long as you leave the app open, because it functions like a server for the community.\n- The longer you leave the app open, the more data you are seeding to the protocol, which helps performance for everybody.\n - All the data in the plebbit protocol is just text, which is extremely lightweight. All media is generated by links, which is text, embedded by the clients.\n\nDownload the plebbit app here: https://github.com/plebbit/plebbit-react/releases\n\nYou can also use a CLI: https://github.com/plebbit/plebbit-cli\n\nRunning boards in the plebchan app is a planned feature.\n\n' 'To create a board, first you have to run a full node.\nYou can run a full node by simply browsing with the plebbit desktop app. After you download it, open it, wait for it loading, then click on "Home" in the top left, then "Create Community".\n\nAfter you create the community, you can go back to plebchan at any time to see it as a board by pasting its address (begins with p/12D3KooW...) in the search bar, which is in the Home.\n\nNote:\n\n- Your community will be online for as long as you leave the app open, because it functions like a server for the community.\n- The longer you leave the app open, the more data you are seeding to the protocol, which helps performance for everybody.\n - All the data in the plebbit protocol is just text, which is extremely lightweight. All media is generated by links, which is text, embedded by the clients.\n\nDownload the plebbit app here: https://github.com/plebbit/plebbit-react/releases\n\nYou can also use a CLI: https://github.com/plebbit/plebbit-cli\n\nRunning boards in the plebchan app is a planned feature.\n\n'
) )
}>Create Board</span> }>Create Board</span>
</div> </div>
<div className="page-jump"> <div className="page-jump">
<Link to={`/profile/c/${index}/settings`} onClick={() => setIsSettingsOpen(true)}>Settings</Link> <Link to={`/p/${selectedAddress}/settings`} onClick={() => setIsSettingsOpen(true)}>Settings</Link>
&nbsp; &nbsp;
<Link to="/" onClick={() => handleStyleChange({target: {value: "Yotsuba"}} <Link to="/" onClick={() => {handleStyleChange({target: {value: "Yotsuba"}}); window.scrollTo(0, 0);}}>Home</Link>
)}>Home</Link> </div>
</div> </div>
</div> </div>
<div id="separator-mobile">&nbsp;</div> <div id="separator-mobile">&nbsp;</div>
@@ -229,34 +248,106 @@ const Pending = () => {
<div className="post op"> <div className="post op">
<div className="post-info"> <div className="post-info">
{commentMediaInfo?.url ? ( {commentMediaInfo?.url ? (
<div key={`f-${"pending"}`} className="file" style={{marginBottom: "5px"}}> <div key={`f-${index}`} className="file" style={{marginBottom: "5px"}}>
<div key={`ft-${"pending"}`} className="file-text"> <div key={`ft-${index}`} className="file-text">
Link:&nbsp; Link:&nbsp;
<a key={`fa-${"pending"}`} href={commentMediaInfo.url} <a key={`fa-${index}`} href={commentMediaInfo.url} target="_blank"
target="_blank" rel="noreferrer">{ rel="noopener noreferrer">{
commentMediaInfo?.url.length > 30 ? commentMediaInfo?.url.length > 30 ?
commentMediaInfo?.url.slice(0, 30) + "(...)" : commentMediaInfo?.url.slice(0, 30) + "(...)" :
commentMediaInfo?.url commentMediaInfo?.url
}</a>&nbsp;({commentMediaInfo?.type}) }</a>&nbsp;({commentMediaInfo?.type === "iframe" ? "video" : commentMediaInfo?.type})
</div> {isThumbnailClicked[index] ? (
{commentMediaInfo?.type === "webpage" ? ( <span>
<span key={`fta-${"pending"}`} className="file-thumb"> -[
<img key={`fti-${"pending"}`} src={comment.thumbnailUrl} alt="thumbnail" onError={(e) => e.target.src = fallbackImgUrl} /> <span className='reply-link'
style={{textDecoration: 'underline', cursor: 'pointer'}}
onClick={() => {handleThumbnailClick(index)}}>Close</span>
]
</span> </span>
) : null} ) : null}
</div>
{commentMediaInfo?.type === 'iframe' && (
<div key={`enlarge-${index}`}
className={`img-container ${isThumbnailClicked[index] ? 'expanded-container' : ''}`}>
<span key={`fta-${index}`} className="file-thumb">
{(isThumbnailClicked[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 />
) : (
<img
key={`fti-${index}`}
src={commentMediaInfo.thumbnail}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index)}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
)}
</span>
</div>
)}
{commentMediaInfo?.type === "webpage" ? (
<div key={`enlarge-${index}`} className="img-container">
<span key={`fta-${index}`} className="file-thumb">
{comment.thumbnailUrl ? (
<img key={`fti-${index}`}
src={commentMediaInfo.thumbnail} alt={commentMediaInfo.type}
onClick={handleImageClick}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
) : null}
</span>
</div>
) : null}
{commentMediaInfo?.type === "image" ? ( {commentMediaInfo?.type === "image" ? (
<span key={`fta-${"pending"}`} className="file-thumb"> <div key={`enlarge-${index}`} className="img-container">
<img key={`fti-${"pending"}`} src={commentMediaInfo.url} alt={commentMediaInfo.type} onError={(e) => e.target.src = fallbackImgUrl} /> <span key={`fta-${index}`} className="file-thumb">
<img key={`fti-${index}`}
src={commentMediaInfo.url} alt={commentMediaInfo.type}
onClick={handleImageClick}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
</span> </span>
</div>
) : null} ) : null}
{commentMediaInfo?.type === "video" ? ( {commentMediaInfo?.type === "video" ? (
<span key={`fta-${"pending"}`} className="file-thumb"> <div key={`enlarge-${index}`} className={`img-container ${isThumbnailClicked[index] ? 'expanded-container' : ''}`}>
<video controls key={`fti-${"pending"}`} src={commentMediaInfo.url} alt={commentMediaInfo.type} onError={(e) => e.target.src = fallbackImgUrl} /> <span key={`fta-${index}`} className="file-thumb">
{isThumbnailClicked[index] ? (
<video
className='enlarged'
key={`fti-${index}`}
src={commentMediaInfo.url}
controls
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl}
/>
) : (
<video
key={`fti-${index}`}
src={commentMediaInfo.url}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index)}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl}
/>
)}
</span> </span>
</div>
) : null} ) : null}
{commentMediaInfo?.type === "audio" ? ( {commentMediaInfo?.type === "audio" ? (
<span key={`fta-${"pending"}`} className="file-thumb"> <span key={`fta-${index}`} className="file-thumb">
<audio controls key={`fti-${"pending"}`} src={commentMediaInfo.url} alt={commentMediaInfo.type} onError={(e) => e.target.src = fallbackImgUrl} /> <audio controls key={`fti-${index}`}
src={commentMediaInfo.url} alt={commentMediaInfo.type}
onError={(e) => e.target.src = fallbackImgUrl} />
</span> </span>
) : null} ) : null}
</div> </div>
@@ -360,37 +451,109 @@ const Pending = () => {
<span key="pending-mob" style={{color: 'red', fontWeight: '700'}}>Pending</span> <span key="pending-mob" style={{color: 'red', fontWeight: '700'}}>Pending</span>
</span> </span>
</div> </div>
{comment.link ? (
<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">
{(isMobileThumbnailClicked[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 />
</div>
) : (
<img
key={`mob-fti-${index}`}
src={commentMediaInfo.thumbnail}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index, true)}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
)}
{commentMediaInfo?.type === "video" || "iframe" ? (
isMobileThumbnailClicked[index] ? (
<div style={{textAlign: "center", marginTop: "15px", marginBottom: "15px"}}>
<span className='button-mobile' style={{float: "none", cursor: "pointer"}}
onClick={() => {handleThumbnailClick(index, true)}}
>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>
)}
{commentMediaInfo?.url ? ( {commentMediaInfo?.url ? (
commentMediaInfo.type === "webpage" ? ( commentMediaInfo.type === "webpage" ? (
<div key={`mob-f-${"pending"}`} className="file-mobile"> <div key={`enlarge-mob-${index}`} className="img-container">
<span key={`mob-ft${"pending"}`} className="file-thumb-mobile"> <span key={`mob-ft${comment.cid}`} className="file-thumb-mobile">
<img key={`mob-img-${"pending"}`} src={comment.thumbnailUrl} alt="thumbnail" onError={(e) => e.target.src = fallbackImgUrl} /> {comment.thumbnailUrl ? (
<div key={`mob-fi-${"pending"}`} className="file-info-mobile">{commentMediaInfo.type}</div> <img key={`mob-img-${index}`}
src={commentMediaInfo.thumbnail} alt="thumbnail"
onClick={handleImageClick}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
) : null}
<div key={`mob-fi-${index}`} className="file-info-mobile">{commentMediaInfo?.type}</div>
</span> </span>
</div> </div>
) : commentMediaInfo.type === "image" ? ( ) : commentMediaInfo.type === "image" ? (
<div key={`mob-f-${"pending"}`} className="file-mobile"> <div key={`enlarge-mob-${index}`} className="img-container">
<span key={`mob-ft${"pending"}`} className="file-thumb-mobile"> <span key={`mob-ft${comment.cid}`} className="file-thumb-mobile">
<img key={`mob-img-${"pending"}`} src={commentMediaInfo.url} alt={commentMediaInfo.type} onError={(e) => e.target.src = fallbackImgUrl} /> <img key={`mob-img-${index}`}
<div key={`mob-fi-${"pending"}`} className="file-info-mobile">{commentMediaInfo.type}</div> src={commentMediaInfo.url} alt={commentMediaInfo.type}
onClick={handleImageClick}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
<div key={`mob-fi-${index}`} className="file-info-mobile">{commentMediaInfo?.type}</div>
</span> </span>
</div> </div>
) : commentMediaInfo.type === "video" ? ( ) : commentMediaInfo.type === "video" ? (
<div key={`mob-f-${"pending"}`} className="file-mobile"> <span key={`mob-ft${comment.cid}`} className="file-thumb-mobile">
<span key={`mob-ft${"pending"}`} className="file-thumb-mobile"> {isMobileThumbnailClicked[index] ? (
<video controls key={`mob-img-${"pending"}`} src={commentMediaInfo.url} alt={commentMediaInfo.type} onError={(e) => e.target.src = fallbackImgUrl} /> <video key={`fti-${index}`}
<div key={`mob-fi-${"pending"}`} className="file-info-mobile">{commentMediaInfo.type}</div> src={commentMediaInfo.url} alt={commentMediaInfo.type}
</span> controls
style={{ cursor: 'pointer' }}
onError={(e) => e.target.src = fallbackImgUrl} />
) : (
<video
key={`fti-${index}`}
src={commentMediaInfo.url}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index, true)}}
style={{cursor: "pointer"}}
id="video-thumbnail-mobile"
onError={(e) => e.target.src = fallbackImgUrl}
/>
)}
{commentMediaInfo?.type === "video" || "iframe" ? (
isMobileThumbnailClicked[index] ? (
<div style={{textAlign: "center", marginTop: "15px", marginBottom: "15px"}}>
<span className='button-mobile' style={{float: "none", cursor: "pointer"}}
onClick={() => {handleThumbnailClick(index, true)}}
>Close</span>
</div> </div>
) : (
<div key={`mob-fi-${index}`} className="file-info-mobile">video</div>
)) : <div key={`mob-fi-${index}`} className="file-info-mobile">video</div>}
</span>
) : commentMediaInfo.type === "audio" ? ( ) : commentMediaInfo.type === "audio" ? (
<div key={`mob-f-${"pending"}`} className="file-mobile"> <span key={`mob-ft${comment.cid}`} className="file-thumb-mobile">
<span key={`mob-ft${"pending"}`} className="file-thumb-mobile"> <audio key={`mob-img-${index}`}
<audio controls key={`mob-img-${"pending"}`} src={commentMediaInfo.url} alt={commentMediaInfo.type} onError={(e) => e.target.src = fallbackImgUrl} /> src={commentMediaInfo.url} alt={commentMediaInfo.type}
<div key={`mob-fi-${"pending"}`} className="file-info-mobile">{commentMediaInfo.type}</div> onError={(e) => e.target.src = fallbackImgUrl} />
<div key={`mob-fi-${index}`} className="file-info-mobile">{commentMediaInfo?.type}</div>
</span> </span>
</div>
) : null ) : null
) : null} ) : null}
</div>
) : null}
<blockquote key={`mob-bq-${"pending"}`} className="post-message-mobile"> <blockquote key={`mob-bq-${"pending"}`} className="post-message-mobile">
{comment.content ? ( {comment.content ? (
<> <>
+134 -6
View File
@@ -102,6 +102,8 @@ const Subscriptions = () => {
const [moderatorPermissions, setModeratorPermissions] = useState({}); const [moderatorPermissions, setModeratorPermissions] = useState({});
const [executeAnonMode, setExecuteAnonMode] = useState(false); const [executeAnonMode, setExecuteAnonMode] = useState(false);
const [cidTracker, setCidTracker] = useState({}); const [cidTracker, setCidTracker] = useState({});
const [isThumbnailClicked, setIsThumbnailClicked] = useState({});
const [isMobileThumbnailClicked, setIsMobileThumbnailClicked] = useState({});
const setSelectedThreadCid = (cid) => { const setSelectedThreadCid = (cid) => {
selectedThreadCidRef.current = cid; selectedThreadCidRef.current = cid;
@@ -116,6 +118,21 @@ const Subscriptions = () => {
const stateString = useFeedStateString(subplebbits); const stateString = useFeedStateString(subplebbits);
const handleThumbnailClick = (index, isMobile=false) => {
if (isMobile) {
setIsMobileThumbnailClicked(prevState => ({
...prevState,
[index]: !prevState[index],
}));
} else {
setIsThumbnailClicked(prevState => ({
...prevState,
[index]: !prevState[index],
}));
}
};
useEffect(() => { useEffect(() => {
let permissions = {}; let permissions = {};
@@ -636,8 +653,44 @@ const Subscriptions = () => {
commentMediaInfo?.url.length > 30 ? commentMediaInfo?.url.length > 30 ?
commentMediaInfo?.url.slice(0, 30) + "(...)" : commentMediaInfo?.url.slice(0, 30) + "(...)" :
commentMediaInfo?.url commentMediaInfo?.url
}</a>&nbsp;({commentMediaInfo?.type}) }</a>&nbsp;({commentMediaInfo?.type === "iframe" ? "video" : commentMediaInfo?.type})
{isThumbnailClicked[index] ? (
<span>
-[
<span className='reply-link'
style={{textDecoration: 'underline', cursor: 'pointer'}}
onClick={() => {handleThumbnailClick(index)}}>Close</span>
]
</span>
) : null}
</div> </div>
{commentMediaInfo?.type === 'iframe' && (
<div key={`enlarge-${index}`}
className={`img-container ${isThumbnailClicked[index] ? 'expanded-container' : ''}`}>
<span key={`fta-${index}`} className="file-thumb">
{(isThumbnailClicked[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 />
) : (
<img
key={`fti-${index}`}
src={commentMediaInfo.thumbnail}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index)}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
)}
</span>
</div>
)}
{commentMediaInfo?.type === "webpage" ? ( {commentMediaInfo?.type === "webpage" ? (
<div key={`enlarge-${index}`} className="img-container"> <div key={`enlarge-${index}`} className="img-container">
<span key={`fta-${index}`} className="file-thumb"> <span key={`fta-${index}`} className="file-thumb">
@@ -663,11 +716,29 @@ const Subscriptions = () => {
</div> </div>
) : null} ) : null}
{commentMediaInfo?.type === "video" ? ( {commentMediaInfo?.type === "video" ? (
<div key={`enlarge-${index}`} className={`img-container ${isThumbnailClicked[index] ? 'expanded-container' : ''}`}>
<span key={`fta-${index}`} className="file-thumb"> <span key={`fta-${index}`} className="file-thumb">
<video controls width="" key={`fti-${index}`} {isThumbnailClicked[index] ? (
src={commentMediaInfo.url} alt={commentMediaInfo.type} <video
onError={(e) => e.target.src = fallbackImgUrl} /> className='enlarged'
key={`fti-${index}`}
src={commentMediaInfo.url}
controls
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl}
/>
) : (
<video
key={`fti-${index}`}
src={commentMediaInfo.url}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index)}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl}
/>
)}
</span> </span>
</div>
) : null} ) : null}
{commentMediaInfo?.type === "audio" ? ( {commentMediaInfo?.type === "audio" ? (
<span key={`fta-${index}`} className="file-thumb"> <span key={`fta-${index}`} className="file-thumb">
@@ -1573,6 +1644,41 @@ const Subscriptions = () => {
</div> </div>
{thread.link ? ( {thread.link ? (
<div key={`mob-f-${index}`} className="file-mobile"> <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">
{(isMobileThumbnailClicked[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 />
</div>
) : (
<img
key={`mob-fti-${index}`}
src={commentMediaInfo.thumbnail}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index, true)}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
)}
{commentMediaInfo?.type === "video" || "iframe" ? (
isMobileThumbnailClicked[index] ? (
<div style={{textAlign: "center", marginTop: "15px", marginBottom: "15px"}}>
<span className='button-mobile' style={{float: "none", cursor: "pointer"}}
onClick={() => {handleThumbnailClick(index, true)}}
>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>
)}
{commentMediaInfo?.url ? ( {commentMediaInfo?.url ? (
commentMediaInfo.type === "webpage" ? ( commentMediaInfo.type === "webpage" ? (
<div key={`enlarge-mob-${index}`} className="img-container"> <div key={`enlarge-mob-${index}`} className="img-container">
@@ -1600,11 +1706,33 @@ const Subscriptions = () => {
</div> </div>
) : commentMediaInfo.type === "video" ? ( ) : commentMediaInfo.type === "video" ? (
<span key={`mob-ft${thread.cid}`} className="file-thumb-mobile"> <span key={`mob-ft${thread.cid}`} className="file-thumb-mobile">
{isMobileThumbnailClicked[index] ? (
<video key={`fti-${index}`} <video key={`fti-${index}`}
src={commentMediaInfo.url} alt={commentMediaInfo.type} src={commentMediaInfo.url} alt={commentMediaInfo.type}
style={{ pointerEvents: "none" }} controls
style={{ cursor: 'pointer' }}
onError={(e) => e.target.src = fallbackImgUrl} /> onError={(e) => e.target.src = fallbackImgUrl} />
<div key={`mob-fi-${index}`} className="file-info-mobile">{commentMediaInfo?.type}</div> ) : (
<video
key={`fti-${index}`}
src={commentMediaInfo.url}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index, true)}}
style={{cursor: "pointer"}}
id="video-thumbnail-mobile"
onError={(e) => e.target.src = fallbackImgUrl}
/>
)}
{commentMediaInfo?.type === "video" || "iframe" ? (
isMobileThumbnailClicked[index] ? (
<div style={{textAlign: "center", marginTop: "15px", marginBottom: "15px"}}>
<span className='button-mobile' style={{float: "none", cursor: "pointer"}}
onClick={() => {handleThumbnailClick(index, true)}}
>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> </span>
) : commentMediaInfo.type === "audio" ? ( ) : commentMediaInfo.type === "audio" ? (
<span key={`mob-ft${thread.cid}`} className="file-thumb-mobile"> <span key={`mob-ft${thread.cid}`} className="file-thumb-mobile">
+164 -37
View File
@@ -111,6 +111,8 @@ const Thread = () => {
const [postOnHoverHeight, setPostOnHoverHeight] = useState(0); const [postOnHoverHeight, setPostOnHoverHeight] = useState(0);
const [executeAnonMode, setExecuteAnonMode] = useState(false); const [executeAnonMode, setExecuteAnonMode] = useState(false);
const [cidTracker, setCidTracker] = useState({}); const [cidTracker, setCidTracker] = useState({});
const [isThumbnailClicked, setIsThumbnailClicked] = useState({});
const [isMobileThumbnailClicked, setIsMobileThumbnailClicked] = useState({});
useAnonMode(selectedThread, anonymousMode && executeAnonMode); useAnonMode(selectedThread, anonymousMode && executeAnonMode);
@@ -126,6 +128,21 @@ const Thread = () => {
const fallbackImgUrl = "assets/filedeleted-res.gif"; const fallbackImgUrl = "assets/filedeleted-res.gif";
const handleThumbnailClick = (index, isMobile=false) => {
if (isMobile) {
setIsMobileThumbnailClicked(prevState => ({
...prevState,
[index]: !prevState[index],
}));
} else {
setIsThumbnailClicked(prevState => ({
...prevState,
[index]: !prevState[index],
}));
}
};
useEffect(() => { useEffect(() => {
if (subplebbit.roles !== undefined) { if (subplebbit.roles !== undefined) {
const role = subplebbit.roles[account?.author?.address]?.role; const role = subplebbit.roles[account?.author?.address]?.role;
@@ -397,8 +414,10 @@ const Thread = () => {
}, [selectedThread, anonymousMode, account]); }, [selectedThread, anonymousMode, account]);
useEffect(() => { useEffect(() => {
if (anonymousMode) {
updateSigner(); updateSigner();
}, [updateSigner]); }
}, [updateSigner, anonymousMode]);
useEffect(() => { useEffect(() => {
@@ -795,21 +814,57 @@ const Thread = () => {
<div className="post op op-desktop"> <div className="post op op-desktop">
<div className="post-info"> <div className="post-info">
{commentMediaInfo?.url ? ( {commentMediaInfo?.url ? (
<div key={`f-${comment.cid}`} className="file" style={{marginBottom: "5px"}}> <div key={`f-${index}`} className="file" style={{marginBottom: "5px"}}>
<div key={`ft-${comment.cid}`} className="file-text"> <div key={`ft-${index}`} className="file-text">
Link:&nbsp; Link:&nbsp;
<a key={`fa-${comment.cid}`} href={commentMediaInfo.url} <a key={`fa-${index}`} href={commentMediaInfo.url} target="_blank"
target="_blank" rel="noreferrer">{ rel="noopener noreferrer">{
commentMediaInfo?.url.length > 30 ? commentMediaInfo?.url.length > 30 ?
commentMediaInfo?.url.slice(0, 30) + "(...)" : commentMediaInfo?.url.slice(0, 30) + "(...)" :
commentMediaInfo?.url commentMediaInfo?.url
}</a>&nbsp;({commentMediaInfo?.type}) }</a>&nbsp;({commentMediaInfo?.type === "iframe" ? "video" : commentMediaInfo?.type})
{isThumbnailClicked[index] ? (
<span>
-[
<span className='reply-link'
style={{textDecoration: 'underline', cursor: 'pointer'}}
onClick={() => {handleThumbnailClick(index)}}>Close</span>
]
</span>
) : null}
</div> </div>
{commentMediaInfo?.type === 'iframe' && (
<div key={`enlarge-${index}`}
className={`img-container ${isThumbnailClicked[index] ? 'expanded-container' : ''}`}>
<span key={`fta-${index}`} className="file-thumb">
{(isThumbnailClicked[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 />
) : (
<img
key={`fti-${index}`}
src={commentMediaInfo.thumbnail}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index)}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
)}
</span>
</div>
)}
{commentMediaInfo?.type === "webpage" ? ( {commentMediaInfo?.type === "webpage" ? (
<div key="enlarge" className="img-container"> <div key={`enlarge-${index}`} className="img-container">
<span key={`fta-${comment.cid}`} className="file-thumb"> <span key={`fta-${index}`} className="file-thumb">
{comment.thumbnailUrl ? ( {comment.thumbnailUrl ? (
<img key={`fti-${comment.cid}`} <img key={`fti-${index}`}
src={commentMediaInfo.thumbnail} alt={commentMediaInfo.type} src={commentMediaInfo.thumbnail} alt={commentMediaInfo.type}
onClick={handleImageClick} onClick={handleImageClick}
style={{cursor: "pointer"}} style={{cursor: "pointer"}}
@@ -819,9 +874,9 @@ const Thread = () => {
</div> </div>
) : null} ) : null}
{commentMediaInfo?.type === "image" ? ( {commentMediaInfo?.type === "image" ? (
<div key="enlarge" className="img-container"> <div key={`enlarge-${index}`} className="img-container">
<span key={`fta-${comment.cid}`} className="file-thumb"> <span key={`fta-${index}`} className="file-thumb">
<img key={`fti-${comment.cid}`} <img key={`fti-${index}`}
src={commentMediaInfo.url} alt={commentMediaInfo.type} src={commentMediaInfo.url} alt={commentMediaInfo.type}
onClick={handleImageClick} onClick={handleImageClick}
style={{cursor: "pointer"}} style={{cursor: "pointer"}}
@@ -830,15 +885,33 @@ const Thread = () => {
</div> </div>
) : null} ) : null}
{commentMediaInfo?.type === "video" ? ( {commentMediaInfo?.type === "video" ? (
<span key={`fta-${comment.cid}`} className="file-thumb"> <div key={`enlarge-${index}`} className={`img-container ${isThumbnailClicked[index] ? 'expanded-container' : ''}`}>
<video controls key={`fti-${comment.cid}`} <span key={`fta-${index}`} className="file-thumb">
src={commentMediaInfo.url} alt={commentMediaInfo.type} {isThumbnailClicked[index] ? (
onError={(e) => e.target.src = fallbackImgUrl} /> <video
className='enlarged'
key={`fti-${index}`}
src={commentMediaInfo.url}
controls
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl}
/>
) : (
<video
key={`fti-${index}`}
src={commentMediaInfo.url}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index)}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl}
/>
)}
</span> </span>
</div>
) : null} ) : null}
{commentMediaInfo?.type === "audio" ? ( {commentMediaInfo?.type === "audio" ? (
<span key={`fta-${comment.cid}`} className="file-thumb"> <span key={`fta-${index}`} className="file-thumb">
<audio controls key={`fti-${comment.cid}`} <audio controls key={`fti-${index}`}
src={commentMediaInfo.url} alt={commentMediaInfo.type} src={commentMediaInfo.url} alt={commentMediaInfo.type}
onError={(e) => e.target.src = fallbackImgUrl} /> onError={(e) => e.target.src = fallbackImgUrl} />
</span> </span>
@@ -1559,55 +1632,109 @@ const Thread = () => {
}}}>{comment.shortCid}</Link> }}}>{comment.shortCid}</Link>
</span> </span>
</div> </div>
{comment.link ? (
<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">
{(isMobileThumbnailClicked[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 />
</div>
) : (
<img
key={`mob-fti-${index}`}
src={commentMediaInfo.thumbnail}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index, true)}}
style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} />
)}
{commentMediaInfo?.type === "video" || "iframe" ? (
isMobileThumbnailClicked[index] ? (
<div style={{textAlign: "center", marginTop: "15px", marginBottom: "15px"}}>
<span className='button-mobile' style={{float: "none", cursor: "pointer"}}
onClick={() => {handleThumbnailClick(index, true)}}
>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>
)}
{commentMediaInfo?.url ? ( {commentMediaInfo?.url ? (
commentMediaInfo.type === "webpage" ? ( commentMediaInfo.type === "webpage" ? (
<div key={`mob-f-${comment.cid}`} className="file-mobile"> <div key={`enlarge-mob-${index}`} className="img-container">
<div key="enlarge-reply-mob" className="img-container">
<span key={`mob-ft${comment.cid}`} className="file-thumb-mobile"> <span key={`mob-ft${comment.cid}`} className="file-thumb-mobile">
{comment.thumbnailUrl ? ( {comment.thumbnailUrl ? (
<img key={`mob-img-${comment.cid}`} <img key={`mob-img-${index}`}
src={commentMediaInfo.thumbnail} alt={commentMediaInfo.type} src={commentMediaInfo.thumbnail} alt="thumbnail"
onClick={handleImageClick} onClick={handleImageClick}
style={{cursor: "pointer"}} style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} /> onError={(e) => e.target.src = fallbackImgUrl} />
) : null} ) : null}
<div key={`mob-fi-${comment.cid}`} className="file-info-mobile">{commentMediaInfo.type}</div> <div key={`mob-fi-${index}`} className="file-info-mobile">{commentMediaInfo?.type}</div>
</span> </span>
</div> </div>
</div>
) : commentMediaInfo.type === "image" ? ( ) : commentMediaInfo.type === "image" ? (
<div key={`mob-f-${comment.cid}`} className="file-mobile"> <div key={`enlarge-mob-${index}`} className="img-container">
<div key="enlarge-reply-mob" className="img-container">
<span key={`mob-ft${comment.cid}`} className="file-thumb-mobile"> <span key={`mob-ft${comment.cid}`} className="file-thumb-mobile">
<img key={`mob-img-${comment.cid}`} <img key={`mob-img-${index}`}
src={commentMediaInfo.url} alt={commentMediaInfo.type} src={commentMediaInfo.url} alt={commentMediaInfo.type}
onClick={handleImageClick} onClick={handleImageClick}
style={{cursor: "pointer"}} style={{cursor: "pointer"}}
onError={(e) => e.target.src = fallbackImgUrl} /> onError={(e) => e.target.src = fallbackImgUrl} />
<div key={`mob-fi-${comment.cid}`} className="file-info-mobile">{commentMediaInfo.type}</div> <div key={`mob-fi-${index}`} className="file-info-mobile">{commentMediaInfo?.type}</div>
</span> </span>
</div> </div>
</div>
) : commentMediaInfo.type === "video" ? ( ) : commentMediaInfo.type === "video" ? (
<div key={`mob-f-${comment.cid}`} className="file-mobile">
<span key={`mob-ft${comment.cid}`} className="file-thumb-mobile"> <span key={`mob-ft${comment.cid}`} className="file-thumb-mobile">
<video controls key={`mob-img-${comment.cid}`} {isMobileThumbnailClicked[index] ? (
<video key={`fti-${index}`}
src={commentMediaInfo.url} alt={commentMediaInfo.type} src={commentMediaInfo.url} alt={commentMediaInfo.type}
controls
style={{ cursor: 'pointer' }}
onError={(e) => e.target.src = fallbackImgUrl} /> onError={(e) => e.target.src = fallbackImgUrl} />
<div key={`mob-fi-${comment.cid}`} className="file-info-mobile">{commentMediaInfo.type}</div> ) : (
</span> <video
key={`fti-${index}`}
src={commentMediaInfo.url}
alt="thumbnail"
onClick={() => {handleThumbnailClick(index, true)}}
style={{cursor: "pointer"}}
id="video-thumbnail-mobile"
onError={(e) => e.target.src = fallbackImgUrl}
/>
)}
{commentMediaInfo?.type === "video" || "iframe" ? (
isMobileThumbnailClicked[index] ? (
<div style={{textAlign: "center", marginTop: "15px", marginBottom: "15px"}}>
<span className='button-mobile' style={{float: "none", cursor: "pointer"}}
onClick={() => {handleThumbnailClick(index, true)}}
>Close</span>
</div> </div>
) : (
<div key={`mob-fi-${index}`} className="file-info-mobile">video</div>
)) : <div key={`mob-fi-${index}`} className="file-info-mobile">video</div>}
</span>
) : commentMediaInfo.type === "audio" ? ( ) : commentMediaInfo.type === "audio" ? (
<div key={`mob-f-${comment.cid}`} className="file-mobile">
<span key={`mob-ft${comment.cid}`} className="file-thumb-mobile"> <span key={`mob-ft${comment.cid}`} className="file-thumb-mobile">
<audio controls key={`mob-img-${comment.cid}`} <audio key={`mob-img-${index}`}
src={commentMediaInfo.url} alt={commentMediaInfo.type} src={commentMediaInfo.url} alt={commentMediaInfo.type}
onError={(e) => e.target.src = fallbackImgUrl} /> onError={(e) => e.target.src = fallbackImgUrl} />
<div key={`mob-fi-${comment.cid}`} className="file-info-mobile">{commentMediaInfo.type}</div> <div key={`mob-fi-${index}`} className="file-info-mobile">{commentMediaInfo?.type}</div>
</span> </span>
</div>
) : null ) : null
) : null} ) : null}
</div>
) : null}
<blockquote key={`mob-bq-${comment.cid}`} className="post-message-mobile"> <blockquote key={`mob-bq-${comment.cid}`} className="post-message-mobile">
{comment.content ? ( {comment.content ? (
<> <>
+51 -5
View File
@@ -7,13 +7,55 @@ const getCommentMediaInfo = (comment) => {
if (comment?.link) { if (comment?.link) {
try { try {
new URL(comment.link); const url = new URL(comment.link);
} catch (error) { const host = url.hostname;
// console.error("Invalid URL:", comment.link); let embedUrl = null;
return;
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}`;
} 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`;
// }
} else if (host.includes('bitchute.com')) {
const videoId = url.pathname.split('/')[2];
embedUrl = `https://www.bitchute.com/embed/${videoId}/`;
} 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}`;
} }
const mime = extName(new URL(comment?.link).pathname.replace('/', ''))[0]?.mime; if (embedUrl) {
return {
url: comment.link,
embedUrl,
type: 'iframe',
thumbnail: comment.thumbnailUrl,
};
}
const mime = extName(url.pathname.replace('/', ''))[0]?.mime;
if (mime?.startsWith('image')) { if (mime?.startsWith('image')) {
return { return {
@@ -26,6 +68,7 @@ const getCommentMediaInfo = (comment) => {
return { return {
url: comment.link, url: comment.link,
type: 'video', type: 'video',
thumbnail: comment.thumbnailUrl,
}; };
} }
@@ -35,6 +78,9 @@ const getCommentMediaInfo = (comment) => {
type: 'audio', type: 'audio',
}; };
} }
} catch (error) {
return;
}
} }
if (comment?.thumbnailUrl && comment?.thumbnailUrl !== comment?.link) { if (comment?.thumbnailUrl && comment?.thumbnailUrl !== comment?.link) {